From c0f0f457b8e6723a6e3d1e29a6aa91a36edfc169 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 5 Jan 2019 12:55:30 +0100 Subject: [PATCH 1/6] Don't actually create a full MIR stack frame when not needed --- src/librustc_mir/const_eval.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index b9432997b7e19..947b94b61da8f 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -68,12 +68,36 @@ pub fn mk_eval_cx<'a, '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, CompileTimeInterpreter::new()); - let mir = ecx.load_mir(instance.def)?; + let mir = mir::Mir::new( + ::std::iter::once( + mir::BasicBlockData { + statements: Vec::new(), + is_cleanup: false, + terminator: Some(mir::Terminator { + source_info: mir::SourceInfo { + scope: mir::OUTERMOST_SOURCE_SCOPE, + span: DUMMY_SP, + }, + kind: mir::TerminatorKind::Return, + }), + } + ).collect(), // basic blocks + IndexVec::new(), // source_scopes + mir::ClearCrossCrate::Clear, // source_scope_local_data + IndexVec::new(), // promoted + None, // yield ty + ::std::iter::once(mir::LocalDecl::new_return_place(tcx.types.unit, DUMMY_SP)).collect(), + IndexVec::new(), //user_type_annotations + 0, // arg_count + Vec::new(), // upvar_decls + DUMMY_SP, // span + Vec::new(), // control_flow_destroyed + ); // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( instance, - mir.span, - mir, + span, + tcx.alloc_mir(mir), None, StackPopCleanup::Goto(None), // never pop )?; From 14e662d8c56c54b6c04256cdf202e6636c96516b Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 8 Jan 2019 10:54:24 +0100 Subject: [PATCH 2/6] Manually push a stack frame where no valid frame is needed --- src/librustc_mir/const_eval.rs | 47 +++++++++------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 947b94b61da8f..8a93328659eec 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -43,6 +43,16 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> { debug!("mk_borrowck_eval_cx: {:?}", instance); let param_env = tcx.param_env(instance.def_id()); + mk_eval_cx_inner(tcx, instance, mir, span, param_env) +} + +fn mk_eval_cx_inner<'a, 'mir, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: Instance<'tcx>, + mir: &'mir mir::Mir<'tcx>, + span: Span, + param_env: ty::ParamEnv<'tcx>, +) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> { let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new()); // insert a stack frame so any queries have the correct substs // cannot use `push_stack_frame`; if we do `const_prop` explodes @@ -67,41 +77,8 @@ pub fn mk_eval_cx<'a, 'tcx>( ) -> 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, CompileTimeInterpreter::new()); - let mir = mir::Mir::new( - ::std::iter::once( - mir::BasicBlockData { - statements: Vec::new(), - is_cleanup: false, - terminator: Some(mir::Terminator { - source_info: mir::SourceInfo { - scope: mir::OUTERMOST_SOURCE_SCOPE, - span: DUMMY_SP, - }, - kind: mir::TerminatorKind::Return, - }), - } - ).collect(), // basic blocks - IndexVec::new(), // source_scopes - mir::ClearCrossCrate::Clear, // source_scope_local_data - IndexVec::new(), // promoted - None, // yield ty - ::std::iter::once(mir::LocalDecl::new_return_place(tcx.types.unit, DUMMY_SP)).collect(), - IndexVec::new(), //user_type_annotations - 0, // arg_count - Vec::new(), // upvar_decls - DUMMY_SP, // span - Vec::new(), // control_flow_destroyed - ); - // insert a stack frame so any queries have the correct substs - ecx.push_stack_frame( - instance, - span, - tcx.alloc_mir(mir), - None, - StackPopCleanup::Goto(None), // never pop - )?; - Ok(ecx) + let mir = tcx.optimized_mir(instance.def.def_id()); + mk_eval_cx_inner(tcx, instance, mir, span, param_env) } pub(crate) fn eval_promoted<'a, 'mir, 'tcx>( From cea282bcfda7bf47a95eae7be822467a97d0a9b2 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 8 Jan 2019 10:54:40 +0100 Subject: [PATCH 3/6] Make `mk_eval_cx` private to const eval --- src/librustc_mir/const_eval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 8a93328659eec..6f464030b331d 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -70,7 +70,7 @@ fn mk_eval_cx_inner<'a, 'mir, 'tcx>( Ok(ecx) } -pub fn mk_eval_cx<'a, 'tcx>( +fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, param_env: ty::ParamEnv<'tcx>, From f174b73cafae574f62e5d890bdfc74835774b1f2 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 9 Jan 2019 16:08:07 +0100 Subject: [PATCH 4/6] Document the `mk_*_eval_cx` functions --- src/librustc_mir/const_eval.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 6f464030b331d..9b62743e9c64c 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -35,6 +35,8 @@ const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000; /// Should be a power of two for performance reasons. const DETECTOR_SNAPSHOT_PERIOD: isize = 256; +/// Warning: do not use this function if you expect to start interpreting the given `Mir`. +/// The `EvalContext` is only meant to be used to query values from constants and statics. pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, @@ -46,6 +48,8 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( mk_eval_cx_inner(tcx, instance, mir, span, param_env) } +/// This is just a helper function to reduce code duplication between `mk_borrowck_eval_cx` and +/// `mk_eval_cx`. Do not call this function directly. fn mk_eval_cx_inner<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, @@ -54,8 +58,9 @@ fn mk_eval_cx_inner<'a, 'mir, 'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> { let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new()); - // insert a stack frame so any queries have the correct substs - // cannot use `push_stack_frame`; if we do `const_prop` explodes + // Insert a stack frame so any queries have the correct substs. + // We also avoid all the extra work performed by push_stack_frame, + // like initializing local variables ecx.stack.push(interpret::Frame { block: mir::START_BLOCK, locals: IndexVec::new(), @@ -70,6 +75,9 @@ fn mk_eval_cx_inner<'a, 'mir, 'tcx>( Ok(ecx) } +/// Warning: do not use this function if you expect to start interpreting the given `Mir`. +/// The `EvalContext` is only meant to be used to do field and index projections into constants for +/// `simd_shuffle` and const patterns in match arms. fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, From cd5a9e04da9a2a76e79dde90c394cd0b7c570a42 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 9 Jan 2019 16:27:33 +0100 Subject: [PATCH 5/6] Explain the arguments of the `mk_*_eval_cx` functions --- src/librustc_mir/const_eval.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 9b62743e9c64c..594496fe9722c 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -37,6 +37,14 @@ const DETECTOR_SNAPSHOT_PERIOD: isize = 256; /// Warning: do not use this function if you expect to start interpreting the given `Mir`. /// The `EvalContext` is only meant to be used to query values from constants and statics. +/// +/// This function is used during const propagation. We cannot use `mk_eval_cx`, because copy +/// propagation happens *during* the computation of the MIR of the current function. So if we +/// tried to call the `optimized_mir` query, we'd get a cycle error because we are (transitively) +/// inside the `optimized_mir` query of the `Instance` given. +/// +/// Since we are looking at the MIR of the function in an abstract manner, we don't have a +/// `ParamEnv` available to us. This function creates a `ParamEnv` for the given instance. pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, @@ -78,6 +86,11 @@ fn mk_eval_cx_inner<'a, 'mir, 'tcx>( /// Warning: do not use this function if you expect to start interpreting the given `Mir`. /// The `EvalContext` is only meant to be used to do field and index projections into constants for /// `simd_shuffle` and const patterns in match arms. +/// +/// The function containing the `match` that is currently being analyzed may have generic bounds +/// that inform us about the generic bounds of the constant. E.g. using an associated constant +/// of a function's generic parameter will require knowledge about the bounds on the generic +/// parameter. fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, From dec79e44705992d607b001cd349c98807761d3d8 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 9 Jan 2019 16:34:31 +0100 Subject: [PATCH 6/6] Not seeing the forest because there are too many trees in the way --- src/librustc_mir/const_eval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 594496fe9722c..d79e0a817eeed 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -90,7 +90,7 @@ fn mk_eval_cx_inner<'a, 'mir, 'tcx>( /// The function containing the `match` that is currently being analyzed may have generic bounds /// that inform us about the generic bounds of the constant. E.g. using an associated constant /// of a function's generic parameter will require knowledge about the bounds on the generic -/// parameter. +/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>,