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 = !;
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>,

View File

@@ -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>,
}
@@ -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,
@@ -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),

View File

@@ -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

View File

@@ -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;
@@ -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),

View File

@@ -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) {
@@ -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(())
}
@@ -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>>,
}
@@ -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())
}
}
@@ -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>>
for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
@@ -391,10 +390,10 @@ impl<'a, 'b, 'mir, 'tcx, M> HashStable<StableHashingContext<'b>>
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);
}
}

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() {
// 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.tcx,
&self.machine,
&self.memory,
&self.stack[..],
)