Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

miri loop detector hashing #54076

Merged
merged 1 commit into from
Sep 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
type MemoryKinds = !;

const MUT_STATIC_KIND: Option<!> = None; // no mutating of statics allowed
const DETECT_LOOPS: bool = true;

fn find_fn<'a>(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
Expand Down
8 changes: 7 additions & 1 deletion src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
/// detector period.
pub(super) steps_since_detector_enabled: isize,

/// Extra state to detect loops.
/// FIXME: Move this to the CTFE machine's state, out of the general miri engine.
pub(super) loop_detector: InfiniteLoopDetector<'a, 'mir, 'tcx, M>,
}

Expand Down Expand Up @@ -110,6 +112,7 @@ pub struct Frame<'mir, 'tcx: 'mir> {
pub stmt: usize,
}

// Not using the macro because that does not support types depending on 'tcx
impl<'a, 'mir, 'tcx: 'mir> HashStable<StableHashingContext<'a>> for Frame<'mir, 'tcx> {
fn hash_stable<W: StableHasherResult>(
&self,
Expand Down Expand Up @@ -144,11 +147,14 @@ pub enum StackPopCleanup {
None { cleanup: bool },
}

// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup {
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher),
StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher),
Expand Down
14 changes: 7 additions & 7 deletions src/librustc_mir/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@
//! This separation exists to ensure that no fancy miri features like
//! interpreting common C functions leak into CTFE.

use std::hash::Hash;

use rustc::hir::def_id::DefId;
use rustc::ich::StableHashingContext;
use rustc::mir::interpret::{Allocation, EvalResult, Scalar};
use rustc::mir;
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
use rustc_data_structures::stable_hasher::HashStable;

use super::{EvalContext, PlaceTy, OpTy};

/// Methods of this trait signifies a point where CTFE evaluation would fail
/// and some use case dependent behaviour can instead be applied
pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash + for<'a> HashStable<StableHashingContext<'a>> {
pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// Additional data that can be accessed via the Memory
type MemoryData: Clone + Eq + Hash + for<'a> HashStable<StableHashingContext<'a>>;
type MemoryData: Clone + Eq;

/// Additional memory kinds a machine wishes to distinguish from the builtin ones
type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq + Hash;
type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq;

/// The memory kind to use for mutated statics -- or None if those are not supported.
const MUT_STATIC_KIND: Option<Self::MemoryKinds>;

/// Whether to attempt to detect infinite loops (any kind of infinite
/// execution, really).
const DETECT_LOOPS: bool;

/// Entry point to all function calls.
///
/// Returns either the mir to use for the call, or `None` if execution should
Expand Down
7 changes: 5 additions & 2 deletions src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//! All high-level functions to write to memory work on places as destinations.

use std::convert::TryFrom;
use std::mem;

use rustc::ich::StableHashingContext;
use rustc::mir;
Expand Down Expand Up @@ -57,11 +58,13 @@ pub enum Place<Id=AllocId> {
},
}

// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for Place {
fn hash_stable<W: StableHasherResult>(
&self, hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {

hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),

Expand Down
29 changes: 14 additions & 15 deletions src/librustc_mir/interpret/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,13 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
pub fn observe_and_analyze(
&mut self,
tcx: &TyCtxt<'b, 'tcx, 'tcx>,
machine: &M,
memory: &Memory<'a, 'mir, 'tcx, M>,
stack: &[Frame<'mir, 'tcx>],
) -> EvalResult<'tcx, ()> {

let mut hcx = tcx.get_stable_hashing_context();
let mut hasher = StableHasher::<u64>::new();
(machine, stack).hash_stable(&mut hcx, &mut hasher);
stack.hash_stable(&mut hcx, &mut hasher);
let hash = hasher.finish();

if self.hashes.insert(hash) {
Expand All @@ -79,7 +78,7 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>

info!("snapshotting the state of the interpreter");

if self.snapshots.insert(EvalSnapshot::new(machine, memory, stack)) {
if self.snapshots.insert(EvalSnapshot::new(memory, stack)) {
// Spurious collision or first cycle
return Ok(())
}
Expand Down Expand Up @@ -345,7 +344,6 @@ impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M>

/// The virtual machine state during const-evaluation at a given point in time.
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
machine: M,
memory: Memory<'a, 'mir, 'tcx, M>,
stack: Vec<Frame<'mir, 'tcx>>,
}
Expand All @@ -354,21 +352,20 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
{
fn new(
machine: &M,
memory: &Memory<'a, 'mir, 'tcx, M>,
stack: &[Frame<'mir, 'tcx>]) -> Self {

stack: &[Frame<'mir, 'tcx>]
) -> Self {
EvalSnapshot {
machine: machine.clone(),
memory: memory.clone(),
stack: stack.into(),
}
}

fn snapshot<'b: 'a>(&'b self)
-> (&'b M, MemorySnapshot<'b, 'mir, 'tcx, M>, Vec<FrameSnapshot<'a, 'tcx>>) {
let EvalSnapshot{ machine, memory, stack } = self;
(&machine, memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect())
-> (MemorySnapshot<'b, 'mir, 'tcx, M>, Vec<FrameSnapshot<'a, 'tcx>>)
{
let EvalSnapshot{ memory, stack } = self;
(memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect())
}
}

Expand All @@ -384,17 +381,19 @@ impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M>
}
}

// Not using the macro because we need special handling for `memory`, which the macro
// does not support at the same time as the extra bounds on the type.
impl<'a, 'b, 'mir, 'tcx, M> HashStable<StableHashingContext<'b>>
for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
{
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'b>,
hasher: &mut StableHasher<W>) {

let EvalSnapshot{ machine, memory, stack } = self;
(machine, &memory.data, stack).hash_stable(hcx, hasher);
hasher: &mut StableHasher<W>)
{
let EvalSnapshot{ memory: _, stack } = self;
stack.hash_stable(hcx, hasher);
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/librustc_mir/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
}
}

if !M::DETECT_LOOPS {
return Ok(());
}

if self.loop_detector.is_empty() {
// First run of the loop detector

Expand All @@ -75,7 +79,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {

self.loop_detector.observe_and_analyze(
&self.tcx,
&self.machine,
&self.memory,
&self.stack[..],
)
Expand Down