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:
@@ -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>,
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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[..],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user