Rollup merge of #54076 - RalfJung:miri-snapshot, r=oli-obk

miri loop detector hashing

* fix enum hashing to also consider discriminant
* do not hash extra machine state
* standalone miri is not interested in loop detection, so let it opt-out

In the future I think we want to move the hashing logic out of the miri engine, this is CTFE-only.

r? @oli-obk
This commit is contained in:
kennytm
2018-09-13 10:02:20 +08:00
6 changed files with 38 additions and 26 deletions

View File

@@ -239,6 +239,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
type MemoryKinds = !; type MemoryKinds = !;
const MUT_STATIC_KIND: Option<!> = None; // no mutating of statics allowed const MUT_STATIC_KIND: Option<!> = None; // no mutating of statics allowed
const DETECT_LOOPS: bool = true;
fn find_fn<'a>( fn find_fn<'a>(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,

View File

@@ -65,6 +65,8 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
/// detector period. /// detector period.
pub(super) steps_since_detector_enabled: isize, 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>, pub(super) loop_detector: InfiniteLoopDetector<'a, 'mir, 'tcx, M>,
} }
@@ -110,6 +112,7 @@ pub struct Frame<'mir, 'tcx: 'mir> {
pub stmt: usize, 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> { impl<'a, 'mir, 'tcx: 'mir> HashStable<StableHashingContext<'a>> for Frame<'mir, 'tcx> {
fn hash_stable<W: StableHasherResult>( fn hash_stable<W: StableHasherResult>(
&self, &self,
@@ -144,11 +147,14 @@ pub enum StackPopCleanup {
None { cleanup: bool }, None { cleanup: bool },
} }
// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup { impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup {
fn hash_stable<W: StableHasherResult>( fn hash_stable<W: StableHasherResult>(
&self, &self,
hcx: &mut StableHashingContext<'a>, hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) { hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self { match self {
StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher), StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher),
StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher), StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher),

View File

@@ -12,29 +12,29 @@
//! This separation exists to ensure that no fancy miri features like //! This separation exists to ensure that no fancy miri features like
//! interpreting common C functions leak into CTFE. //! interpreting common C functions leak into CTFE.
use std::hash::Hash;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ich::StableHashingContext;
use rustc::mir::interpret::{Allocation, EvalResult, Scalar}; use rustc::mir::interpret::{Allocation, EvalResult, Scalar};
use rustc::mir; use rustc::mir;
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt}; use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
use rustc_data_structures::stable_hasher::HashStable;
use super::{EvalContext, PlaceTy, OpTy}; use super::{EvalContext, PlaceTy, OpTy};
/// Methods of this trait signifies a point where CTFE evaluation would fail /// Methods of this trait signifies a point where CTFE evaluation would fail
/// and some use case dependent behaviour can instead be applied /// 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 /// 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 /// 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. /// The memory kind to use for mutated statics -- or None if those are not supported.
const MUT_STATIC_KIND: Option<Self::MemoryKinds>; 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. /// Entry point to all function calls.
/// ///
/// Returns either the mir to use for the call, or `None` if execution should /// Returns either the mir to use for the call, or `None` if execution should

View File

@@ -13,6 +13,7 @@
//! All high-level functions to write to memory work on places as destinations. //! All high-level functions to write to memory work on places as destinations.
use std::convert::TryFrom; use std::convert::TryFrom;
use std::mem;
use rustc::ich::StableHashingContext; use rustc::ich::StableHashingContext;
use rustc::mir; use rustc::mir;
@@ -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 { impl<'a> HashStable<StableHashingContext<'a>> for Place {
fn hash_stable<W: StableHasherResult>( fn hash_stable<W: StableHasherResult>(
&self, hcx: &mut StableHashingContext<'a>, &self, hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) { hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self { match self {
Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher), Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),

View File

@@ -62,14 +62,13 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
pub fn observe_and_analyze( pub fn observe_and_analyze(
&mut self, &mut self,
tcx: &TyCtxt<'b, 'tcx, 'tcx>, tcx: &TyCtxt<'b, 'tcx, 'tcx>,
machine: &M,
memory: &Memory<'a, 'mir, 'tcx, M>, memory: &Memory<'a, 'mir, 'tcx, M>,
stack: &[Frame<'mir, 'tcx>], stack: &[Frame<'mir, 'tcx>],
) -> EvalResult<'tcx, ()> { ) -> EvalResult<'tcx, ()> {
let mut hcx = tcx.get_stable_hashing_context(); let mut hcx = tcx.get_stable_hashing_context();
let mut hasher = StableHasher::<u64>::new(); 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(); let hash = hasher.finish();
if self.hashes.insert(hash) { if self.hashes.insert(hash) {
@@ -79,7 +78,7 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
info!("snapshotting the state of the interpreter"); 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 // Spurious collision or first cycle
return Ok(()) return Ok(())
} }
@@ -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. /// The virtual machine state during const-evaluation at a given point in time.
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
machine: M,
memory: Memory<'a, 'mir, 'tcx, M>, memory: Memory<'a, 'mir, 'tcx, M>,
stack: Vec<Frame<'mir, 'tcx>>, stack: Vec<Frame<'mir, 'tcx>>,
} }
@@ -354,21 +352,20 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>, where M: Machine<'mir, 'tcx>,
{ {
fn new( fn new(
machine: &M,
memory: &Memory<'a, 'mir, 'tcx, M>, memory: &Memory<'a, 'mir, 'tcx, M>,
stack: &[Frame<'mir, 'tcx>]) -> Self { stack: &[Frame<'mir, 'tcx>]
) -> Self {
EvalSnapshot { EvalSnapshot {
machine: machine.clone(),
memory: memory.clone(), memory: memory.clone(),
stack: stack.into(), stack: stack.into(),
} }
} }
fn snapshot<'b: 'a>(&'b self) fn snapshot<'b: 'a>(&'b self)
-> (&'b M, MemorySnapshot<'b, 'mir, 'tcx, M>, Vec<FrameSnapshot<'a, 'tcx>>) { -> (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()) let EvalSnapshot{ memory, stack } = self;
(memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect())
} }
} }
@@ -384,6 +381,8 @@ 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>> impl<'a, 'b, 'mir, 'tcx, M> HashStable<StableHashingContext<'b>>
for EvalSnapshot<'a, 'mir, 'tcx, M> for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>, where M: Machine<'mir, 'tcx>,
@@ -391,10 +390,10 @@ impl<'a, 'b, 'mir, 'tcx, M> HashStable<StableHashingContext<'b>>
fn hash_stable<W: StableHasherResult>( fn hash_stable<W: StableHasherResult>(
&self, &self,
hcx: &mut StableHashingContext<'b>, hcx: &mut StableHashingContext<'b>,
hasher: &mut StableHasher<W>) { hasher: &mut StableHasher<W>)
{
let EvalSnapshot{ machine, memory, stack } = self; let EvalSnapshot{ memory: _, stack } = self;
(machine, &memory.data, stack).hash_stable(hcx, hasher); stack.hash_stable(hcx, hasher);
} }
} }

View File

@@ -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() { if self.loop_detector.is_empty() {
// First run of the loop detector // First run of the loop detector
@@ -75,7 +79,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
self.loop_detector.observe_and_analyze( self.loop_detector.observe_and_analyze(
&self.tcx, &self.tcx,
&self.machine,
&self.memory, &self.memory,
&self.stack[..], &self.stack[..],
) )