move CTFE engine snapshot state out of miri engine into CTFE machine instance
This commit is contained in:
parent
ff6422d7a3
commit
169f7911e9
|
@ -22,7 +22,7 @@ use rustc::ty::subst::Subst;
|
|||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
|
||||
use syntax::ast::Mutability;
|
||||
use syntax::source_map::Span;
|
||||
use syntax::source_map::{Span, DUMMY_SP};
|
||||
|
||||
use rustc::mir::interpret::{
|
||||
EvalResult, EvalError, EvalErrorKind, GlobalId,
|
||||
|
@ -31,6 +31,7 @@ use rustc::mir::interpret::{
|
|||
use interpret::{self,
|
||||
Place, PlaceTy, MemPlace, OpTy, Operand, Value,
|
||||
EvalContext, StackPopCleanup, MemoryKind,
|
||||
snapshot,
|
||||
};
|
||||
|
||||
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
||||
|
@ -38,10 +39,10 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
|||
instance: Instance<'tcx>,
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
span: Span,
|
||||
) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
|
||||
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
|
||||
debug!("mk_borrowck_eval_cx: {:?}", instance);
|
||||
let param_env = tcx.param_env(instance.def_id());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator::new(), ());
|
||||
// insert a stack frame so any queries have the correct substs
|
||||
ecx.stack.push(interpret::Frame {
|
||||
block: mir::START_BLOCK,
|
||||
|
@ -60,10 +61,10 @@ pub fn mk_eval_cx<'a, 'tcx>(
|
|||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
|
||||
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
|
||||
debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
|
||||
let span = tcx.def_span(instance.def_id());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator::new(), ());
|
||||
let mir = ecx.load_mir(instance.def)?;
|
||||
// insert a stack frame so any queries have the correct substs
|
||||
ecx.push_stack_frame(
|
||||
|
@ -77,18 +78,17 @@ pub fn mk_eval_cx<'a, 'tcx>(
|
|||
}
|
||||
|
||||
pub fn eval_promoted<'a, 'mir, 'tcx>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
ecx.with_fresh_body(|ecx| {
|
||||
eval_body_using_ecx(ecx, cid, Some(mir), param_env)
|
||||
})
|
||||
let mut ecx = mk_borrowck_eval_cx(tcx, cid.instance, mir, DUMMY_SP).unwrap();
|
||||
eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env)
|
||||
}
|
||||
|
||||
pub fn op_to_const<'tcx>(
|
||||
ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
|
||||
ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
|
||||
op: OpTy<'tcx>,
|
||||
normalize: bool,
|
||||
) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
|
||||
|
@ -128,19 +128,19 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
|||
cid: GlobalId<'tcx>,
|
||||
mir: Option<&'mir mir::Mir<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> (EvalResult<'tcx, OpTy<'tcx>>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
|
||||
) -> (EvalResult<'tcx, OpTy<'tcx>>, CompileTimeEvalContext<'a, 'mir, 'tcx>) {
|
||||
// we start out with the best span we have
|
||||
// and try improving it down the road when more information is available
|
||||
let span = tcx.def_span(cid.instance.def_id());
|
||||
let span = mir.map(|mir| mir.span).unwrap_or(span);
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator::new(), ());
|
||||
let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
|
||||
(r, ecx)
|
||||
}
|
||||
|
||||
// Returns a pointer to where the result lives
|
||||
fn eval_body_using_ecx<'a, 'mir, 'tcx>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
|
||||
fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
ecx: &mut CompileTimeEvalContext<'_, 'mir, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
mir: Option<&'mir mir::Mir<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -187,17 +187,12 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
|
|||
Ok(ret.into())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct CompileTimeEvaluator;
|
||||
|
||||
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
|
||||
fn into(self) -> EvalError<'tcx> {
|
||||
EvalErrorKind::MachineError(self.to_string()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(struct CompileTimeEvaluator {});
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ConstEvalError {
|
||||
NeedsRfc(String),
|
||||
|
@ -234,14 +229,39 @@ impl Error for ConstEvalError {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
||||
// Extra machine state for CTFE, and the Machine instance
|
||||
pub struct CompileTimeEvaluator<'a, 'mir, 'tcx: 'a+'mir> {
|
||||
/// When this value is negative, it indicates the number of interpreter
|
||||
/// steps *until* the loop detector is enabled. When it is positive, it is
|
||||
/// the number of steps after the detector has been enabled modulo the loop
|
||||
/// detector period.
|
||||
pub(super) steps_since_detector_enabled: isize,
|
||||
|
||||
/// Extra state to detect loops.
|
||||
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'a, 'mir, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx> CompileTimeEvaluator<'a, 'mir, 'tcx> {
|
||||
fn new() -> Self {
|
||||
CompileTimeEvaluator {
|
||||
loop_detector: Default::default(),
|
||||
steps_since_detector_enabled: -snapshot::STEPS_UNTIL_DETECTOR_ENABLED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CompileTimeEvalContext<'a, 'mir, 'tcx> =
|
||||
EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>;
|
||||
|
||||
impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
|
||||
for CompileTimeEvaluator<'a, 'mir, 'tcx>
|
||||
{
|
||||
type MemoryData = ();
|
||||
type MemoryKinds = !;
|
||||
|
||||
const MUT_STATIC_KIND: Option<!> = None; // no mutating of statics allowed
|
||||
const DETECT_LOOPS: bool = true;
|
||||
|
||||
fn find_fn<'a>(
|
||||
fn find_fn(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
|
@ -275,7 +295,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
}))
|
||||
}
|
||||
|
||||
fn call_intrinsic<'a>(
|
||||
fn call_intrinsic(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
|
@ -291,7 +311,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
)
|
||||
}
|
||||
|
||||
fn ptr_op<'a>(
|
||||
fn ptr_op(
|
||||
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_bin_op: mir::BinOp,
|
||||
_left: Scalar,
|
||||
|
@ -304,14 +324,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
)
|
||||
}
|
||||
|
||||
fn find_foreign_static<'a>(
|
||||
fn find_foreign_static(
|
||||
_tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
_def_id: DefId,
|
||||
) -> EvalResult<'tcx, &'tcx Allocation> {
|
||||
err!(ReadForeignStatic)
|
||||
}
|
||||
|
||||
fn box_alloc<'a>(
|
||||
fn box_alloc(
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
|
@ -319,6 +339,36 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
|
||||
{
|
||||
let steps = &mut ecx.machine.steps_since_detector_enabled;
|
||||
|
||||
*steps += 1;
|
||||
if *steps < 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
*steps %= snapshot::DETECTOR_SNAPSHOT_PERIOD;
|
||||
if *steps != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if ecx.machine.loop_detector.is_empty() {
|
||||
// First run of the loop detector
|
||||
|
||||
// FIXME(#49980): make this warning a lint
|
||||
ecx.tcx.sess.span_warn(ecx.frame().span,
|
||||
"Constant evaluating a complex constant, this might take some time");
|
||||
}
|
||||
|
||||
ecx.machine.loop_detector.observe_and_analyze(
|
||||
&ecx.tcx,
|
||||
&ecx.memory,
|
||||
&ecx.stack[..],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Project to a field of a (variant of a) const
|
||||
|
|
|
@ -21,7 +21,7 @@ use rustc_apfloat::Float;
|
|||
|
||||
use super::{EvalContext, Machine, PlaceTy, OpTy, Value};
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
|
||||
match ty.sty {
|
||||
ty::RawPtr(ty::TypeAndMut { ty, .. }) |
|
||||
|
|
|
@ -14,7 +14,6 @@ use std::mem;
|
|||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
use rustc::ich::StableHashingContext;
|
||||
use rustc::mir;
|
||||
use rustc::ty::layout::{
|
||||
self, Size, Align, HasDataLayout, LayoutOf, TyLayout
|
||||
|
@ -23,7 +22,6 @@ use rustc::ty::subst::{Subst, Substs};
|
|||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::ty::query::TyCtxtAt;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
|
||||
use rustc::mir::interpret::{
|
||||
GlobalId, Scalar, FrameInfo, AllocId,
|
||||
EvalResult, EvalErrorKind,
|
||||
|
@ -38,9 +36,7 @@ use super::{
|
|||
Memory, Machine
|
||||
};
|
||||
|
||||
use super::snapshot::InfiniteLoopDetector;
|
||||
|
||||
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
|
||||
/// Stores the `Machine` instance.
|
||||
pub machine: M,
|
||||
|
||||
|
@ -55,19 +51,6 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
|||
|
||||
/// The virtual call stack.
|
||||
pub(crate) stack: Vec<Frame<'mir, 'tcx>>,
|
||||
|
||||
/// The maximum number of stack frames allowed
|
||||
pub(super) stack_limit: usize,
|
||||
|
||||
/// When this value is negative, it indicates the number of interpreter
|
||||
/// steps *until* the loop detector is enabled. When it is positive, it is
|
||||
/// the number of steps after the detector has been enabled modulo the loop
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
/// A stack frame.
|
||||
|
@ -112,29 +95,6 @@ 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,
|
||||
hcx: &mut StableHashingContext<'a>,
|
||||
hasher: &mut StableHasher<W>) {
|
||||
|
||||
let Frame {
|
||||
mir,
|
||||
instance,
|
||||
span,
|
||||
return_to_block,
|
||||
return_place,
|
||||
locals,
|
||||
block,
|
||||
stmt,
|
||||
} = self;
|
||||
|
||||
(mir, instance, span, return_to_block).hash_stable(hcx, hasher);
|
||||
(return_place, locals, block, stmt).hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum StackPopCleanup {
|
||||
/// Jump to the next block in the caller, or cause UB if None (that's a function
|
||||
|
@ -147,21 +107,6 @@ 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>)
|
||||
{
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// State of a local variable
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum LocalValue<Id=AllocId> {
|
||||
|
@ -189,19 +134,16 @@ impl<'tcx> LocalValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(enum self::LocalValue {
|
||||
Dead,
|
||||
Live(x),
|
||||
});
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
|
||||
for &'b EvalContext<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &layout::TargetDataLayout {
|
||||
&self.tcx.data_layout
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
#[inline]
|
||||
|
@ -210,24 +152,27 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>
|
||||
impl<'b, 'a, 'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for &'b EvalContext<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'a, 'mir, 'tcx>
|
||||
{
|
||||
#[inline]
|
||||
fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
|
||||
*self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx>
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> {
|
||||
*self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for &'a EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> layout::HasTyCtxt<'tcx>
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
#[inline]
|
||||
fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> {
|
||||
*self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> LayoutOf
|
||||
for &'b EvalContext<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
type Ty = Ty<'tcx>;
|
||||
type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
|
||||
|
||||
|
@ -238,8 +183,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for &'a EvalContext<'a, 'm
|
|||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> LayoutOf
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
type Ty = Ty<'tcx>;
|
||||
type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
|
||||
|
||||
|
@ -249,9 +195,7 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf
|
|||
}
|
||||
}
|
||||
|
||||
const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
|
||||
|
||||
impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn new(
|
||||
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -264,22 +208,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
|
|||
param_env,
|
||||
memory: Memory::new(tcx, memory_data),
|
||||
stack: Vec::new(),
|
||||
stack_limit: tcx.sess.const_eval_stack_frame_limit,
|
||||
loop_detector: Default::default(),
|
||||
steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_fresh_body<F: FnOnce(&mut Self) -> R, R>(&mut self, f: F) -> R {
|
||||
let stack = mem::replace(&mut self.stack, Vec::new());
|
||||
let steps = mem::replace(&mut self.steps_since_detector_enabled,
|
||||
-STEPS_UNTIL_DETECTOR_ENABLED);
|
||||
let r = f(self);
|
||||
self.stack = stack;
|
||||
self.steps_since_detector_enabled = steps;
|
||||
r
|
||||
}
|
||||
|
||||
pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
|
||||
&self.memory
|
||||
}
|
||||
|
@ -553,7 +484,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
|
|||
self.frame_mut().locals = locals;
|
||||
}
|
||||
|
||||
if self.stack.len() > self.stack_limit {
|
||||
if self.stack.len() > self.tcx.sess.const_eval_stack_frame_limit {
|
||||
err!(StackFrameLimitReached)
|
||||
} else {
|
||||
Ok(())
|
||||
|
|
|
@ -46,7 +46,7 @@ fn numeric_intrinsic<'tcx>(
|
|||
Ok(Scalar::from_uint(bits_out, size))
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Returns whether emulation happened.
|
||||
pub fn emulate_intrinsic(
|
||||
&mut self,
|
||||
|
|
|
@ -20,20 +20,22 @@ use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
|
|||
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 {
|
||||
/// and some use case dependent behaviour can instead be applied.
|
||||
/// FIXME: We should be able to get rid of the 'a here if we can get rid of the 'a in
|
||||
/// `snapshot::EvalSnapshot`.
|
||||
pub trait Machine<'a, 'mir, 'tcx>: Sized {
|
||||
/// Additional data that can be accessed via the Memory
|
||||
type MemoryData: Clone + Eq;
|
||||
type MemoryData;
|
||||
|
||||
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
|
||||
type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq;
|
||||
type MemoryKinds: ::std::fmt::Debug + Copy + 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;
|
||||
/// Called before a basic block terminator is executed.
|
||||
/// You can use this to detect endlessly running programs.
|
||||
fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>;
|
||||
|
||||
/// Entry point to all function calls.
|
||||
///
|
||||
|
@ -45,7 +47,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
|
|||
/// nor just jump to `ret`, but instead push their own stack frame.)
|
||||
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
|
||||
/// was used.
|
||||
fn find_fn<'a>(
|
||||
fn find_fn(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
|
@ -55,7 +57,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
|
|||
|
||||
/// Directly process an intrinsic without pushing a stack frame.
|
||||
/// If this returns successfully, the engine will take care of jumping to the next block.
|
||||
fn call_intrinsic<'a>(
|
||||
fn call_intrinsic(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
|
@ -66,7 +68,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
|
|||
/// This can be called multiple times for the same static item and should return consistent
|
||||
/// results. Once the item is *written* the first time, as usual for statics a copy is
|
||||
/// made and this function is not called again.
|
||||
fn find_foreign_static<'a>(
|
||||
fn find_foreign_static(
|
||||
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
) -> EvalResult<'tcx, &'tcx Allocation>;
|
||||
|
@ -75,7 +77,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
|
|||
/// value, and for the `Offset` operation that is inherently about pointers.
|
||||
///
|
||||
/// Returns a (value, overflowed) pair if the operation succeeded
|
||||
fn ptr_op<'a>(
|
||||
fn ptr_op(
|
||||
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
bin_op: mir::BinOp,
|
||||
left: Scalar,
|
||||
|
@ -87,13 +89,13 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
|
|||
/// Heap allocations via the `box` keyword
|
||||
///
|
||||
/// Returns a pointer to the allocated memory
|
||||
fn box_alloc<'a>(
|
||||
fn box_alloc(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx>;
|
||||
|
||||
/// Execute a validation operation
|
||||
fn validation_op<'a>(
|
||||
fn validation_op(
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_op: ::rustc::mir::ValidationOp,
|
||||
_operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,
|
||||
|
|
|
@ -39,8 +39,9 @@ pub enum MemoryKind<T> {
|
|||
Machine(T),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
// `Memory` has to depend on the `Machine` because some of its operations
|
||||
// (e.g. `get`) call a `Machine` hook.
|
||||
pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
|
||||
/// Additional data required by the Machine
|
||||
pub data: M::MemoryData,
|
||||
|
||||
|
@ -59,13 +60,15 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
|||
pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a Memory<'a, 'mir, 'tcx, M> {
|
||||
impl<'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
|
||||
for &'b Memory<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &TargetDataLayout {
|
||||
&self.tcx.data_layout
|
||||
}
|
||||
}
|
||||
impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
|
||||
impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
|
||||
for &'b &'c mut Memory<'a, 'mir, 'tcx, M>
|
||||
{
|
||||
#[inline]
|
||||
|
@ -74,7 +77,23 @@ impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
// FIXME: Really we shouldnt clone memory, ever. Snapshot machinery should instad
|
||||
// carefully copy only the reachable parts.
|
||||
impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>>
|
||||
Clone for Memory<'a, 'mir, 'tcx, M>
|
||||
where M::MemoryData: Clone
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Memory {
|
||||
data: self.data.clone(),
|
||||
alloc_map: self.alloc_map.clone(),
|
||||
dead_alloc_map: self.dead_alloc_map.clone(),
|
||||
tcx: self.tcx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
|
||||
Memory {
|
||||
data,
|
||||
|
@ -279,7 +298,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Allocation accessors
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
/// Helper function to obtain the global (tcx) allocation for a static
|
||||
fn get_static_alloc(
|
||||
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
|
@ -491,7 +510,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Byte accessors
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
/// The last argument controls whether we error out when there are undefined
|
||||
/// or pointer bytes. You should never call this, call `get_bytes` or
|
||||
/// `get_bytes_with_undef_and_ptr` instead,
|
||||
|
@ -564,7 +583,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Reading and writing
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
/// mark an allocation as static and initialized, either mutable or not
|
||||
pub fn intern_static(
|
||||
&mut self,
|
||||
|
@ -877,7 +896,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Relocations
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
/// Return all relocations overlapping with the given ptr-offset pair.
|
||||
fn relocations(
|
||||
&self,
|
||||
|
@ -950,7 +969,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Undefined bytes
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
// FIXME(solson): This is a very naive, slow version.
|
||||
fn copy_undef_mask(
|
||||
&mut self,
|
||||
|
|
|
@ -17,7 +17,7 @@ mod operand;
|
|||
mod machine;
|
||||
mod memory;
|
||||
mod operator;
|
||||
mod snapshot;
|
||||
pub(crate) mod snapshot; // for const_eval
|
||||
mod step;
|
||||
mod terminator;
|
||||
mod traits;
|
||||
|
|
|
@ -82,11 +82,6 @@ impl<'tcx> Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(enum ::interpret::Value {
|
||||
Scalar(x),
|
||||
ScalarPair(x, y),
|
||||
});
|
||||
|
||||
// ScalarPair needs a type to interpret, so we often have a value and a type together
|
||||
// as input for binary and cast operations.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -132,11 +127,6 @@ impl Operand {
|
|||
}
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(enum ::interpret::Operand {
|
||||
Immediate(x),
|
||||
Indirect(x),
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct OpTy<'tcx> {
|
||||
crate op: Operand, // ideally we'd make this private, but const_prop needs this
|
||||
|
@ -206,7 +196,7 @@ fn from_known_layout<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Try reading a value in memory; this is interesting particularily for ScalarPair.
|
||||
/// Return None if the layout does not permit loading this as a value.
|
||||
pub(super) fn try_read_value_from_mplace(
|
||||
|
|
|
@ -18,7 +18,7 @@ use rustc::mir::interpret::{EvalResult, Scalar};
|
|||
use super::{EvalContext, PlaceTy, Value, Machine, ValTy};
|
||||
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
|
||||
/// and a boolean signifying the potential overflow to the destination.
|
||||
pub fn binop_with_overflow(
|
||||
|
@ -47,7 +47,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn binary_char_op(
|
||||
&self,
|
||||
bin_op: mir::BinOp,
|
||||
|
|
|
@ -13,13 +13,10 @@
|
|||
//! 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;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout};
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
|
||||
|
||||
use rustc::mir::interpret::{
|
||||
GlobalId, AllocId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic
|
||||
|
@ -39,12 +36,6 @@ pub struct MemPlace<Id=AllocId> {
|
|||
pub extra: Option<Scalar<Id>>,
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(struct ::interpret::MemPlace {
|
||||
ptr,
|
||||
align,
|
||||
extra,
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Place<Id=AllocId> {
|
||||
/// A place referring to a value allocated in the `Memory` system.
|
||||
|
@ -58,23 +49,6 @@ 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>)
|
||||
{
|
||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||
match self {
|
||||
Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),
|
||||
|
||||
Place::Local { frame, local } => {
|
||||
frame.hash_stable(hcx, hasher);
|
||||
local.hash_stable(hcx, hasher);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct PlaceTy<'tcx> {
|
||||
place: Place,
|
||||
|
@ -255,7 +229,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Take a value, which represents a (thin or fat) reference, and make it a place.
|
||||
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref`.
|
||||
pub fn ref_to_mplace(
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
//! during const-evaluation by taking snapshots of the state of the interpreter
|
||||
//! at regular intervals.
|
||||
|
||||
// This lives in `interpret` because it needs access to all sots of private state. However,
|
||||
// it is not used by the general miri engine, just by CTFE.
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
|
||||
use rustc::ich::{StableHashingContext, StableHashingContextProvider};
|
||||
use rustc::mir;
|
||||
|
@ -21,9 +25,18 @@ use syntax::ast::Mutability;
|
|||
use syntax::source_map::Span;
|
||||
|
||||
use super::eval_context::{LocalValue, StackPopCleanup};
|
||||
use super::{Frame, Memory, Machine, Operand, MemPlace, Place, Value};
|
||||
use super::{Frame, Memory, Operand, MemPlace, Place, Value};
|
||||
use const_eval::CompileTimeEvaluator;
|
||||
|
||||
pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
/// Number of steps until the detector even starts doing anything.
|
||||
/// Also, a warning is shown to the user when this number is reached.
|
||||
pub(crate) const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
|
||||
/// The number of steps between loop detector snapshots.
|
||||
/// Should be a power of two for performance reasons.
|
||||
pub(crate) const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
|
||||
/// The set of all `EvalSnapshot` *hashes* observed by this detector.
|
||||
///
|
||||
/// When a collision occurs in this table, we store the full snapshot in
|
||||
|
@ -35,34 +48,20 @@ pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mi
|
|||
/// An `EvalSnapshot` will only be fully cloned once it has caused a
|
||||
/// collision in `hashes`. As a result, the detector must observe at least
|
||||
/// *two* full cycles of an infinite loop before it triggers.
|
||||
snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx, M>>,
|
||||
snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
'tcx: 'a + 'mir,
|
||||
{
|
||||
fn default() -> Self {
|
||||
InfiniteLoopDetector {
|
||||
hashes: FxHashSet::default(),
|
||||
snapshots: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
'tcx: 'a + 'mir,
|
||||
impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
|
||||
{
|
||||
/// Returns `true` if the loop detector has not yet observed a snapshot.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.hashes.is_empty()
|
||||
}
|
||||
|
||||
pub fn observe_and_analyze(
|
||||
pub fn observe_and_analyze<'b>(
|
||||
&mut self,
|
||||
tcx: &TyCtxt<'b, 'tcx, 'tcx>,
|
||||
memory: &Memory<'a, 'mir, 'tcx, M>,
|
||||
memory: &Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>,
|
||||
stack: &[Frame<'mir, 'tcx>],
|
||||
) -> EvalResult<'tcx, ()> {
|
||||
|
||||
|
@ -179,7 +178,7 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId
|
|||
|
||||
impl_snapshot_for!(struct Pointer {
|
||||
alloc_id,
|
||||
offset -> *offset,
|
||||
offset -> *offset, // just copy offset verbatim
|
||||
});
|
||||
|
||||
impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar
|
||||
|
@ -203,12 +202,34 @@ impl_snapshot_for!(enum ScalarMaybeUndef {
|
|||
Undef,
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(struct ::interpret::MemPlace {
|
||||
ptr,
|
||||
align,
|
||||
extra,
|
||||
});
|
||||
impl_snapshot_for!(struct MemPlace {
|
||||
ptr,
|
||||
extra,
|
||||
align -> *align,
|
||||
align -> *align, // just copy alignment verbatim
|
||||
});
|
||||
|
||||
// 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>)
|
||||
{
|
||||
mem::discriminant(self).hash_stable(hcx, hasher);
|
||||
match self {
|
||||
Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),
|
||||
|
||||
Place::Local { frame, local } => {
|
||||
frame.hash_stable(hcx, hasher);
|
||||
local.hash_stable(hcx, hasher);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, Ctx> Snapshot<'a, Ctx> for Place
|
||||
where Ctx: SnapshotContext<'a>,
|
||||
{
|
||||
|
@ -226,16 +247,28 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Place
|
|||
}
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(enum ::interpret::Value {
|
||||
Scalar(x),
|
||||
ScalarPair(x, y),
|
||||
});
|
||||
impl_snapshot_for!(enum Value {
|
||||
Scalar(s),
|
||||
ScalarPair(s, t),
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(enum ::interpret::Operand {
|
||||
Immediate(x),
|
||||
Indirect(x),
|
||||
});
|
||||
impl_snapshot_for!(enum Operand {
|
||||
Immediate(v),
|
||||
Indirect(m),
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(enum ::interpret::LocalValue {
|
||||
Dead,
|
||||
Live(x),
|
||||
});
|
||||
impl_snapshot_for!(enum LocalValue {
|
||||
Live(v),
|
||||
Dead,
|
||||
|
@ -280,6 +313,21 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
|
|||
}
|
||||
}
|
||||
|
||||
// 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>)
|
||||
{
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
struct FrameSnapshot<'a, 'tcx: 'a> {
|
||||
instance: &'a ty::Instance<'tcx>,
|
||||
|
@ -291,6 +339,28 @@ struct FrameSnapshot<'a, 'tcx: 'a> {
|
|||
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,
|
||||
hcx: &mut StableHashingContext<'a>,
|
||||
hasher: &mut StableHasher<W>) {
|
||||
|
||||
let Frame {
|
||||
mir,
|
||||
instance,
|
||||
span,
|
||||
return_to_block,
|
||||
return_place,
|
||||
locals,
|
||||
block,
|
||||
stmt,
|
||||
} = self;
|
||||
|
||||
(mir, instance, span, return_to_block).hash_stable(hcx, hasher);
|
||||
(return_place, locals, block, stmt).hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
|
||||
where Ctx: SnapshotContext<'a>,
|
||||
{
|
||||
|
@ -320,22 +390,8 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
struct MemorySnapshot<'a, 'mir: 'a, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx> + 'a> {
|
||||
data: &'a M::MemoryData,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
{
|
||||
fn snapshot<'b: 'a>(&'b self) -> MemorySnapshot<'b, 'mir, 'tcx, M> {
|
||||
let Memory { data, .. } = self;
|
||||
MemorySnapshot { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
impl<'a, 'b, 'mir, 'tcx: 'a+'mir> SnapshotContext<'b>
|
||||
for Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>
|
||||
{
|
||||
fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> {
|
||||
self.get(*id).ok()
|
||||
|
@ -343,16 +399,17 @@ 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>> {
|
||||
memory: Memory<'a, 'mir, 'tcx, M>,
|
||||
/// We assume the `CompileTimeEvaluator` has no interesting extra state that
|
||||
/// is worth considering here.
|
||||
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir> {
|
||||
memory: Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>,
|
||||
stack: Vec<Frame<'mir, 'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
impl<'a, 'mir, 'tcx: 'a + 'mir> EvalSnapshot<'a, 'mir, 'tcx>
|
||||
{
|
||||
fn new(
|
||||
memory: &Memory<'a, 'mir, 'tcx, M>,
|
||||
memory: &Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>,
|
||||
stack: &[Frame<'mir, 'tcx>]
|
||||
) -> Self {
|
||||
EvalSnapshot {
|
||||
|
@ -361,16 +418,17 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
|
|||
}
|
||||
}
|
||||
|
||||
fn snapshot<'b: 'a>(&'b self)
|
||||
-> (MemorySnapshot<'b, 'mir, 'tcx, M>, Vec<FrameSnapshot<'a, 'tcx>>)
|
||||
// Used to compare two snapshots
|
||||
fn snapshot(&'b self)
|
||||
-> Vec<FrameSnapshot<'b, 'tcx>>
|
||||
{
|
||||
let EvalSnapshot{ memory, stack } = self;
|
||||
(memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect())
|
||||
// Start with the stack, iterate and recursively snapshot
|
||||
self.stack.iter().map(|frame| frame.snapshot(&self.memory)).collect()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
impl<'a, 'mir, 'tcx> Hash for EvalSnapshot<'a, 'mir, 'tcx>
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2)
|
||||
|
@ -383,26 +441,24 @@ 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>,
|
||||
impl<'a, 'b, 'mir, 'tcx> HashStable<StableHashingContext<'b>>
|
||||
for EvalSnapshot<'a, 'mir, 'tcx>
|
||||
{
|
||||
fn hash_stable<W: StableHasherResult>(
|
||||
&self,
|
||||
hcx: &mut StableHashingContext<'b>,
|
||||
hasher: &mut StableHasher<W>)
|
||||
{
|
||||
// Not hashing memory: Avoid hashing memory all the time during execution
|
||||
let EvalSnapshot{ memory: _, stack } = self;
|
||||
stack.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> Eq for EvalSnapshot<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
impl<'a, 'mir, 'tcx> Eq for EvalSnapshot<'a, 'mir, 'tcx>
|
||||
{}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M> PartialEq for EvalSnapshot<'a, 'mir, 'tcx, M>
|
||||
where M: Machine<'mir, 'tcx>,
|
||||
impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.snapshot() == other.snapshot()
|
||||
|
|
|
@ -45,45 +45,7 @@ fn binop_right_homogeneous(op: mir::BinOp) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn inc_step_counter_and_detect_loops(&mut self) -> EvalResult<'tcx, ()> {
|
||||
/// The number of steps between loop detector snapshots.
|
||||
/// Should be a power of two for performance reasons.
|
||||
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
|
||||
|
||||
{
|
||||
let steps = &mut self.steps_since_detector_enabled;
|
||||
|
||||
*steps += 1;
|
||||
if *steps < 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
*steps %= DETECTOR_SNAPSHOT_PERIOD;
|
||||
if *steps != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if !M::DETECT_LOOPS {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.loop_detector.is_empty() {
|
||||
// First run of the loop detector
|
||||
|
||||
// FIXME(#49980): make this warning a lint
|
||||
self.tcx.sess.span_warn(self.frame().span,
|
||||
"Constant evaluating a complex constant, this might take some time");
|
||||
}
|
||||
|
||||
self.loop_detector.observe_and_analyze(
|
||||
&self.tcx,
|
||||
&self.memory,
|
||||
&self.stack[..],
|
||||
)
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn run(&mut self) -> EvalResult<'tcx> {
|
||||
while self.step()? {}
|
||||
Ok(())
|
||||
|
@ -108,7 +70,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
return Ok(true);
|
||||
}
|
||||
|
||||
self.inc_step_counter_and_detect_loops()?;
|
||||
M::before_terminator(self)?;
|
||||
|
||||
let terminator = basic_block.terminator();
|
||||
assert_eq!(old_frames, self.cur_frame());
|
||||
|
|
|
@ -20,7 +20,7 @@ use super::{
|
|||
EvalContext, Machine, Value, OpTy, Place, PlaceTy, Operand, StackPopCleanup
|
||||
};
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
pub fn goto_block(&mut self, target: Option<mir::BasicBlock>) -> EvalResult<'tcx> {
|
||||
if let Some(target) = target {
|
||||
|
|
|
@ -16,7 +16,7 @@ use syntax::ast::Mutability;
|
|||
|
||||
use super::{EvalContext, Machine, MemoryKind};
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
||||
/// objects.
|
||||
///
|
||||
|
|
|
@ -95,7 +95,7 @@ fn path_format(path: &Vec<PathElem>) -> String {
|
|||
out
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn validate_scalar(
|
||||
&self,
|
||||
value: ScalarMaybeUndef,
|
||||
|
|
|
@ -69,7 +69,7 @@ type Const<'tcx> = (OpTy<'tcx>, Span);
|
|||
|
||||
/// Finds optimization opportunities on the MIR.
|
||||
struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
|
||||
ecx: EvalContext<'a, 'b, 'tcx, CompileTimeEvaluator>,
|
||||
ecx: EvalContext<'a, 'b, 'tcx, CompileTimeEvaluator<'a, 'b, 'tcx>>,
|
||||
mir: &'b Mir<'tcx>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
source: MirSource,
|
||||
|
@ -310,7 +310,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
// cannot use `const_eval` here, because that would require having the MIR
|
||||
// for the current function available, but we're producing said MIR right now
|
||||
let res = self.use_ecx(source_info, |this| {
|
||||
eval_promoted(&mut this.ecx, cid, this.mir, this.param_env)
|
||||
eval_promoted(this.tcx, cid, this.mir, this.param_env)
|
||||
})?;
|
||||
trace!("evaluated promoted {:?} to {:?}", promoted, res);
|
||||
Some((res, source_info.span))
|
||||
|
|
Loading…
Reference in New Issue