From 360db516ccf358bd4b35c483ae44634a74c66c0b Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Tue, 20 Dec 2022 00:51:17 +0000 Subject: [PATCH 01/19] Create stable metric to measure long computation in Const Eval This patch adds a `MirPass` that tracks the number of back-edges and function calls in the CFG, adds a new MIR instruction to increment a counter every time they are encountered during Const Eval, and emit a warning if a configured limit is breached. --- compiler/rustc_borrowck/src/dataflow.rs | 1 + compiler/rustc_borrowck/src/invalidation.rs | 3 +- compiler/rustc_borrowck/src/lib.rs | 3 +- compiler/rustc_borrowck/src/type_check/mod.rs | 1 + .../rustc_codegen_ssa/src/mir/statement.rs | 1 + .../src/const_eval/eval_queries.rs | 3 + .../src/const_eval/machine.rs | 1 + .../src/interpret/eval_context.rs | 5 + .../rustc_const_eval/src/interpret/place.rs | 11 +++ .../rustc_const_eval/src/interpret/step.rs | 5 + .../src/transform/check_consts/check.rs | 1 + .../src/transform/validate.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 1 + compiler/rustc_middle/src/mir/query.rs | 8 +- compiler/rustc_middle/src/mir/spanview.rs | 1 + compiler/rustc_middle/src/mir/syntax.rs | 7 +- compiler/rustc_middle/src/mir/visit.rs | 1 + .../rustc_mir_dataflow/src/impls/liveness.rs | 1 + .../src/impls/storage_liveness.rs | 1 + .../src/move_paths/builder.rs | 1 + .../rustc_mir_dataflow/src/value_analysis.rs | 3 +- .../rustc_mir_transform/src/check_unsafety.rs | 1 + .../rustc_mir_transform/src/coverage/spans.rs | 2 + .../rustc_mir_transform/src/ctfe_limit.rs | 92 +++++++++++++++++++ .../src/dead_store_elimination.rs | 1 + compiler/rustc_mir_transform/src/dest_prop.rs | 1 + compiler/rustc_mir_transform/src/generator.rs | 1 + compiler/rustc_mir_transform/src/lib.rs | 11 ++- .../src/remove_noop_landing_pads.rs | 1 + .../src/separate_const_switch.rs | 2 + compiler/rustc_mir_transform/src/simplify.rs | 2 +- .../stable-metric/ctfe-labelled-loop.rs | 25 +++++ .../stable-metric/ctfe-labelled-loop.stderr | 4 + .../stable-metric/ctfe-recursion.rs | 15 +++ .../stable-metric/ctfe-recursion.stderr | 4 + .../stable-metric/ctfe-simple-loop.rs | 16 ++++ .../stable-metric/ctfe-simple-loop.stderr | 4 + 37 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/ctfe_limit.rs create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 8c4885770ad37..2821677c5371f 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -393,6 +393,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { | mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Coverage(..) | mir::StatementKind::Intrinsic(..) + | mir::StatementKind::ConstEvalCounter | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 6fd9290058c36..6217676d5c150 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -91,7 +91,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { LocalMutationIsAllowed::Yes, ); } - StatementKind::Nop + StatementKind::ConstEvalCounter + | StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 73ea7314b75cc..8f8fae2c630b5 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -620,7 +620,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx flow_state, ); } - StatementKind::Nop + StatementKind::ConstEvalCounter + | StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 81bd4c2a783e9..06087b0c579d8 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1258,6 +1258,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | StatementKind::StorageDead(..) | StatementKind::Retag { .. } | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 19452c8cdc805..60fbceb344d88 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -91,6 +91,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) + | mir::StatementKind::ConstEvalCounter | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 18e01567ca35e..041e9d413575e 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -22,6 +22,8 @@ use crate::interpret::{ RefTracking, StackPopCleanup, }; +use tracing::info; + const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \ so this check might be overzealous. Please open an issue on the rustc \ repository if you believe it should not be considered undefined behavior."; @@ -33,6 +35,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( body: &'mir mir::Body<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); + info!("HERE body is {:#?}", body); let tcx = *ecx.tcx; assert!( cid.promoted.is_some() diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4709514c82e85..befc71ce6a0dd 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -369,6 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } } + #[instrument(skip(ecx), ret)] fn load_mir( ecx: &InterpCx<'mir, 'tcx, Self>, instance: ty::InstanceDef<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index d13fed7a9c263..cc97564e8fc28 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -46,6 +46,9 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// The recursion limit (cached from `tcx.recursion_limit(())`) pub recursion_limit: Limit, + + pub const_eval_limit: u32, + pub const_eval_counter: u32, } // The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread @@ -408,6 +411,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { param_env, memory: Memory::new(), recursion_limit: tcx.recursion_limit(), + const_eval_limit: 20, + const_eval_counter: 0, } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 274af61ee7c1d..271a3a74fe319 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -293,6 +293,17 @@ where Prov: Provenance + 'static, M: Machine<'mir, 'tcx, Provenance = Prov>, { + pub fn increment_const_eval_counter(&mut self) { + self.const_eval_counter = self.const_eval_counter + 1; + if self.const_eval_counter == self.const_eval_limit { + let mut warn = self.tcx.sess.struct_warn(format!( + "Const eval counter limit ({}) has been crossed", + self.const_eval_limit + )); + warn.emit(); + } + } + /// Take a value, which represents a (thin or wide) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. /// diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index fad4cb06cd6fe..0f0eb5aadd71d 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -129,6 +129,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // FIXME(#73156): Handle source code coverage in const eval Coverage(..) => {} + // FIXME(bryangarza): Update this to do some logic!!! + ConstEvalCounter => { + self.increment_const_eval_counter(); + } + // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 79f1737e32b21..16b504dd9d491 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -693,6 +693,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index dd168a9ac3cd3..4ad699c0395e3 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -766,6 +766,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 4da893e4c0716..dae7e84e415f0 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1461,6 +1461,7 @@ impl Debug for Statement<'_> { } Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind), Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"), + ConstEvalCounter => write!(fmt, "ConstEvalCounter"), Nop => write!(fmt, "nop"), } } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index a8a4532223c2d..c281fe00591ae 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -441,10 +441,14 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam) -> &'tcx Body<'tcx> { - if let Some((did, param_did)) = def.as_const_arg() { + let res = if let Some((did, param_did)) = def.as_const_arg() { + info!("calling mir_for_ctfe_of_const_arg for DedId {did:?}"); self.mir_for_ctfe_of_const_arg((did, param_did)) } else { + info!("calling mir_for_ctfe for DefId {:?}", def.did); self.mir_for_ctfe(def.did) - } + }; + //info!("RES OF CALLING MIR_FOR_CTFE_OPT_CONST_ARG: {:#?}", res); + res } } diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 887ee57157540..7efe1fde09343 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -250,6 +250,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { AscribeUserType(..) => "AscribeUserType", Coverage(..) => "Coverage", Intrinsic(..) => "Intrinsic", + ConstEvalCounter => "ConstEvalCounter", Nop => "Nop", } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 52c2b10cbbea9..faf903a594901 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -286,7 +286,10 @@ pub enum StatementKind<'tcx> { /// This is permitted for both generators and ADTs. This does not necessarily write to the /// entire place; instead, it writes to the minimum set of bytes as required by the layout for /// the type. - SetDiscriminant { place: Box>, variant_index: VariantIdx }, + SetDiscriminant { + place: Box>, + variant_index: VariantIdx, + }, /// Deinitializes the place. /// @@ -355,6 +358,8 @@ pub enum StatementKind<'tcx> { /// This avoids adding a new block and a terminator for simple intrinsics. Intrinsic(Box>), + ConstEvalCounter, + /// No-op. Useful for deleting instructions without affecting statement indices. Nop, } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 1a264d2d5af9a..3ddac5e11fbc5 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -427,6 +427,7 @@ macro_rules! make_mir_visitor { } } } + StatementKind::ConstEvalCounter => {} StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 923dc16c11b07..2890fa32cc915 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -271,6 +271,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => None, }; if let Some(destination) = destination { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 8d379b90a86db..fcf0ce9d82118 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -141,6 +141,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::Retag(..) | StatementKind::Intrinsic(..) diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index f46fd118bde5d..0195693a7cb0e 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -331,6 +331,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 0522c657939f5..6bdbda909d7bd 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -84,7 +84,8 @@ pub trait ValueAnalysis<'tcx> { StatementKind::Retag(..) => { // We don't track references. } - StatementKind::Nop + StatementKind::ConstEvalCounter + | StatementKind::Nop | StatementKind::FakeRead(..) | StatementKind::Coverage(..) | StatementKind::AscribeUserType(..) => (), diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index adf6ae4c7270f..837233953e7ad 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -104,6 +104,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => { // safe (at least as emitted during MIR construction) } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 31d5541a31b6b..f973c1ed28f4a 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -802,6 +802,8 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option | StatementKind::StorageDead(_) // Coverage should not be encountered, but don't inject coverage coverage | StatementKind::Coverage(_) + // Ignore `ConstEvalCounter`s + | StatementKind::ConstEvalCounter // Ignore `Nop`s | StatementKind::Nop => None, diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs new file mode 100644 index 0000000000000..dc54b983c0e2c --- /dev/null +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -0,0 +1,92 @@ +use crate::MirPass; + +use rustc_middle::mir::{BasicBlock, Body, Statement, StatementKind, TerminatorKind}; +use rustc_middle::ty::TyCtxt; + +use tracing::{info, instrument}; + +pub struct CtfeLimit; + +impl<'tcx> MirPass<'tcx> for CtfeLimit { + #[instrument(skip(self, _tcx, body))] + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let doms = body.basic_blocks.dominators(); + //info!("Got body with {} basic blocks: {:#?}", body.basic_blocks.len(), body.basic_blocks); + //info!("With doms: {doms:?}"); + + /* + for (index, basic_block) in body.basic_blocks.iter().enumerate() { + info!("bb{index}: {basic_block:#?}") + }*/ + for (index, basic_block) in body.basic_blocks.iter().enumerate() { + info!( + "bb{index} -> successors = {:?}", + basic_block.terminator().successors().collect::>() + ); + } + for (index, basic_block) in body.basic_blocks.iter().enumerate() { + info!("bb{index} -> unwind = {:?}", basic_block.terminator().unwind()) + } + + let mut dominators = Vec::new(); + for idom in 0..body.basic_blocks.len() { + let mut nodes = Vec::new(); + for inode in 0..body.basic_blocks.len() { + let dom = BasicBlock::from_usize(idom); + let node = BasicBlock::from_usize(inode); + if doms.is_reachable(dom) + && doms.is_reachable(node) + && doms.is_dominated_by(node, dom) + { + //info!("{idom} dominates {inode}"); + nodes.push(true); + } else { + nodes.push(false); + } + } + dominators.push(nodes); + } + /* + for idom in 0..body.basic_blocks.len() { + print!("{idom} | dom | "); + for inode in 0..body.basic_blocks.len() { + if dominators[idom][inode] { + print!("{inode} | "); + } else { + print!(" | "); + } + } + print!("\n"); + } + */ + + for (index, basic_block) in body.basic_blocks_mut().iter_mut().enumerate() { + // info!("bb{index}: {basic_block:#?}"); + //info!("bb{index} -> successors = {:?}", basic_block.terminator().successors().collect::>()); + let is_back_edge_or_fn_call = 'label: { + match basic_block.terminator().kind { + TerminatorKind::Call { .. } => { + break 'label true; + } + _ => (), + } + for successor in basic_block.terminator().successors() { + let s_index = successor.as_usize(); + if dominators[s_index][index] { + info!("{s_index} to {index} is a loop"); + break 'label true; + } + } + false + }; + if is_back_edge_or_fn_call { + basic_block.statements.push(Statement { + source_info: basic_block.terminator().source_info, + kind: StatementKind::ConstEvalCounter, + }); + info!("New basic block statements vector: {:?}", basic_block.statements); + } + } + info!("With doms: {doms:?}"); + } +} diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 09546330cec92..9dbfb089dc665 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -53,6 +53,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS | StatementKind::StorageDead(_) | StatementKind::Coverage(_) | StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => (), StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 08e296a837127..20ffb0ab33404 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -577,6 +577,7 @@ impl WriteInfo { self.add_place(**place); } StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::Coverage(_) | StatementKind::StorageLive(_) diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 39c61a34afcbd..0df732aa22bad 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1583,6 +1583,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 20b7fdcfe6d4d..e5c8127bea140 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -55,6 +55,7 @@ mod const_goto; mod const_prop; mod const_prop_lint; mod coverage; +mod ctfe_limit; mod dataflow_const_prop; mod dead_store_elimination; mod deaggregator; @@ -349,11 +350,14 @@ fn mir_promoted( /// Compute the MIR that is used during CTFE (and thus has no optimizations run on it) fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> { let did = def_id.expect_local(); - if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { + let body = if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { tcx.mir_for_ctfe_of_const_arg(def) } else { tcx.arena.alloc(inner_mir_for_ctfe(tcx, ty::WithOptConstParam::unknown(did))) - } + }; + //info!("MIR_FOR_CTFE (DefId = {def_id:?}) body res: {:#?}", body); + info!("MIR_FOR_CTFE (DefId = {def_id:?})"); + body } /// Same as `mir_for_ctfe`, but used to get the MIR of a const generic parameter. @@ -447,6 +451,7 @@ fn mir_drops_elaborated_and_const_checked( run_analysis_to_runtime_passes(tcx, &mut body); + //info!("MIR after runtime passes: {:#?}", body); tcx.alloc_steal_mir(body) } @@ -517,6 +522,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // CTFE support for aggregates. &deaggregator::Deaggregator, &Lint(const_prop_lint::ConstProp), + &ctfe_limit::CtfeLimit, ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); } @@ -617,6 +623,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst); debug!("body: {:#?}", body); run_optimization_passes(tcx, &mut body); + //info!("body after OPTIMIZATION: {:#?}", body); debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index f1bbf2ea7e8ea..e3a03aa08af4b 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -35,6 +35,7 @@ impl RemoveNoopLandingPads { | StatementKind::StorageDead(_) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => { // These are all noops in a landing pad } diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index 2f116aaa95849..a24d2d34d791b 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -250,6 +250,7 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData< | StatementKind::Coverage(_) | StatementKind::StorageDead(_) | StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } @@ -318,6 +319,7 @@ fn find_determining_place<'tcx>( | StatementKind::AscribeUserType(_, _) | StatementKind::Coverage(_) | StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} // If the discriminant is set, it is always set diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8f6abe7a912fe..7b6fa2baf2f95 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -517,7 +517,7 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { self.super_statement(statement, location); } - StatementKind::Nop => {} + StatementKind::ConstEvalCounter | StatementKind::Nop => {} StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs new file mode 100644 index 0000000000000..71f29ce8731f0 --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs @@ -0,0 +1,25 @@ +// check-pass +#![feature(const_for)] + +const fn labelled_loop() -> u32 { + let mut n = 0; + 'outer: loop { + 'inner: loop { + n = n + 1; + if n > 5 && n <= 10 { + n = n + 1; + continue 'inner + } + if n > 30 { + break 'outer + } + } + } + n +} + +const X: u32 = labelled_loop(); + +fn main() { + println!("{X}"); +} diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr new file mode 100644 index 0000000000000..183bed3b75b25 --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr @@ -0,0 +1,4 @@ +warning: Const eval counter limit (20) has been crossed + +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs new file mode 100644 index 0000000000000..00b4fc258593a --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs @@ -0,0 +1,15 @@ +// check-pass + +const fn recurse(n: u32) -> u32 { + if n == 0 { + n + } else { + recurse(n - 1) + } +} + +const X: u32 = recurse(30); + +fn main() { + println!("{X}"); +} diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr new file mode 100644 index 0000000000000..183bed3b75b25 --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr @@ -0,0 +1,4 @@ +warning: Const eval counter limit (20) has been crossed + +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs new file mode 100644 index 0000000000000..74dc74734b45e --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs @@ -0,0 +1,16 @@ +// check-pass +const fn simple_loop(n: u32) -> u32 { + let mut index = 0; + let mut res = 0; + while index < n { + res = res + index; + index = index + 1; + } + res +} + +const X: u32 = simple_loop(30); + +fn main() { + println!("{X}"); +} diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr new file mode 100644 index 0000000000000..183bed3b75b25 --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr @@ -0,0 +1,4 @@ +warning: Const eval counter limit (20) has been crossed + +warning: 1 warning emitted + From 026a67377f2b28968d293a92cad3f92e22f76b6d Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 03:38:34 +0000 Subject: [PATCH 02/19] Clean up CtfeLimit MirPass --- .../rustc_mir_transform/src/ctfe_limit.rs | 105 ++++++------------ .../const-eval/stable-metric/ctfe-fn-call.rs | 33 ++++++ .../stable-metric/ctfe-fn-call.stderr | 4 + 3 files changed, 68 insertions(+), 74 deletions(-) create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs create mode 100644 src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index dc54b983c0e2c..f2c99b1943307 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -1,92 +1,49 @@ use crate::MirPass; -use rustc_middle::mir::{BasicBlock, Body, Statement, StatementKind, TerminatorKind}; +use rustc_middle::mir::{BasicBlockData, Body, Statement, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; -use tracing::{info, instrument}; - pub struct CtfeLimit; impl<'tcx> MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let doms = body.basic_blocks.dominators(); - //info!("Got body with {} basic blocks: {:#?}", body.basic_blocks.len(), body.basic_blocks); - //info!("With doms: {doms:?}"); - - /* - for (index, basic_block) in body.basic_blocks.iter().enumerate() { - info!("bb{index}: {basic_block:#?}") - }*/ - for (index, basic_block) in body.basic_blocks.iter().enumerate() { - info!( - "bb{index} -> successors = {:?}", - basic_block.terminator().successors().collect::>() - ); - } - for (index, basic_block) in body.basic_blocks.iter().enumerate() { - info!("bb{index} -> unwind = {:?}", basic_block.terminator().unwind()) - } - - let mut dominators = Vec::new(); - for idom in 0..body.basic_blocks.len() { - let mut nodes = Vec::new(); - for inode in 0..body.basic_blocks.len() { - let dom = BasicBlock::from_usize(idom); - let node = BasicBlock::from_usize(inode); - if doms.is_reachable(dom) + let mut indices = Vec::new(); + for (node, node_data) in body.basic_blocks.iter_enumerated() { + if let TerminatorKind::Call { .. } = node_data.terminator().kind { + indices.push(node); + continue; + } + // Back edges in a CFG indicate loops + for (potential_dom, _) in body.basic_blocks.iter_enumerated() { + if doms.is_reachable(potential_dom) && doms.is_reachable(node) - && doms.is_dominated_by(node, dom) + && doms.is_dominated_by(node, potential_dom) + && node_data + .terminator() + .successors() + .into_iter() + .any(|succ| succ == potential_dom) { - //info!("{idom} dominates {inode}"); - nodes.push(true); - } else { - nodes.push(false); + indices.push(node); + continue; } } - dominators.push(nodes); } - /* - for idom in 0..body.basic_blocks.len() { - print!("{idom} | dom | "); - for inode in 0..body.basic_blocks.len() { - if dominators[idom][inode] { - print!("{inode} | "); - } else { - print!(" | "); - } - } - print!("\n"); - } - */ - - for (index, basic_block) in body.basic_blocks_mut().iter_mut().enumerate() { - // info!("bb{index}: {basic_block:#?}"); - //info!("bb{index} -> successors = {:?}", basic_block.terminator().successors().collect::>()); - let is_back_edge_or_fn_call = 'label: { - match basic_block.terminator().kind { - TerminatorKind::Call { .. } => { - break 'label true; - } - _ => (), - } - for successor in basic_block.terminator().successors() { - let s_index = successor.as_usize(); - if dominators[s_index][index] { - info!("{s_index} to {index} is a loop"); - break 'label true; - } - } - false - }; - if is_back_edge_or_fn_call { - basic_block.statements.push(Statement { - source_info: basic_block.terminator().source_info, - kind: StatementKind::ConstEvalCounter, - }); - info!("New basic block statements vector: {:?}", basic_block.statements); - } + for index in indices { + insert_counter( + body.basic_blocks_mut() + .get_mut(index) + .expect("basic_blocks index {index} should exist"), + ); } - info!("With doms: {doms:?}"); } } + +fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { + basic_block_data.statements.push(Statement { + source_info: basic_block_data.terminator().source_info, + kind: StatementKind::ConstEvalCounter, + }); +} diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs new file mode 100644 index 0000000000000..33488bd1d1c55 --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs @@ -0,0 +1,33 @@ +// check-pass + +const fn foo() {} + +const fn call_foo() -> u32 { + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + foo(); + 0 +} + +const X: u32 = call_foo(); + +fn main() { + println!("{X}"); +} diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr new file mode 100644 index 0000000000000..183bed3b75b25 --- /dev/null +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr @@ -0,0 +1,4 @@ +warning: Const eval counter limit (20) has been crossed + +warning: 1 warning emitted + From b763f9094fadc06fd65b906d5e8db0a9fd8ec6ba Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 03:49:48 +0000 Subject: [PATCH 03/19] Remove debugging-related code --- compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 3 --- compiler/rustc_const_eval/src/const_eval/machine.rs | 1 - compiler/rustc_const_eval/src/interpret/step.rs | 1 - compiler/rustc_middle/src/mir/query.rs | 8 ++------ compiler/rustc_mir_transform/src/lib.rs | 9 ++------- 5 files changed, 4 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 041e9d413575e..18e01567ca35e 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -22,8 +22,6 @@ use crate::interpret::{ RefTracking, StackPopCleanup, }; -use tracing::info; - const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \ so this check might be overzealous. Please open an issue on the rustc \ repository if you believe it should not be considered undefined behavior."; @@ -35,7 +33,6 @@ fn eval_body_using_ecx<'mir, 'tcx>( body: &'mir mir::Body<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); - info!("HERE body is {:#?}", body); let tcx = *ecx.tcx; assert!( cid.promoted.is_some() diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index befc71ce6a0dd..4709514c82e85 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -369,7 +369,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } } - #[instrument(skip(ecx), ret)] fn load_mir( ecx: &InterpCx<'mir, 'tcx, Self>, instance: ty::InstanceDef<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 0f0eb5aadd71d..6c5594bc1b086 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -129,7 +129,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // FIXME(#73156): Handle source code coverage in const eval Coverage(..) => {} - // FIXME(bryangarza): Update this to do some logic!!! ConstEvalCounter => { self.increment_const_eval_counter(); } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index c281fe00591ae..a8a4532223c2d 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -441,14 +441,10 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam) -> &'tcx Body<'tcx> { - let res = if let Some((did, param_did)) = def.as_const_arg() { - info!("calling mir_for_ctfe_of_const_arg for DedId {did:?}"); + if let Some((did, param_did)) = def.as_const_arg() { self.mir_for_ctfe_of_const_arg((did, param_did)) } else { - info!("calling mir_for_ctfe for DefId {:?}", def.did); self.mir_for_ctfe(def.did) - }; - //info!("RES OF CALLING MIR_FOR_CTFE_OPT_CONST_ARG: {:#?}", res); - res + } } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index e5c8127bea140..9a786d0c8d62a 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -350,14 +350,11 @@ fn mir_promoted( /// Compute the MIR that is used during CTFE (and thus has no optimizations run on it) fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> { let did = def_id.expect_local(); - let body = if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { + if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { tcx.mir_for_ctfe_of_const_arg(def) } else { tcx.arena.alloc(inner_mir_for_ctfe(tcx, ty::WithOptConstParam::unknown(did))) - }; - //info!("MIR_FOR_CTFE (DefId = {def_id:?}) body res: {:#?}", body); - info!("MIR_FOR_CTFE (DefId = {def_id:?})"); - body + } } /// Same as `mir_for_ctfe`, but used to get the MIR of a const generic parameter. @@ -451,7 +448,6 @@ fn mir_drops_elaborated_and_const_checked( run_analysis_to_runtime_passes(tcx, &mut body); - //info!("MIR after runtime passes: {:#?}", body); tcx.alloc_steal_mir(body) } @@ -623,7 +619,6 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst); debug!("body: {:#?}", body); run_optimization_passes(tcx, &mut body); - //info!("body after OPTIMIZATION: {:#?}", body); debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); From 009beb00bcbaf5367937e50196d7d40d5d112068 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 04:43:13 +0000 Subject: [PATCH 04/19] Change code to use map insead of for-loop --- .../rustc_mir_transform/src/ctfe_limit.rs | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index f2c99b1943307..462be8afaafeb 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -1,6 +1,8 @@ use crate::MirPass; -use rustc_middle::mir::{BasicBlockData, Body, Statement, StatementKind, TerminatorKind}; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, +}; use rustc_middle::ty::TyCtxt; pub struct CtfeLimit; @@ -9,28 +11,28 @@ impl<'tcx> MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let doms = body.basic_blocks.dominators(); - let mut indices = Vec::new(); - for (node, node_data) in body.basic_blocks.iter_enumerated() { - if let TerminatorKind::Call { .. } = node_data.terminator().kind { - indices.push(node); - continue; - } - // Back edges in a CFG indicate loops - for (potential_dom, _) in body.basic_blocks.iter_enumerated() { - if doms.is_reachable(potential_dom) - && doms.is_reachable(node) - && doms.is_dominated_by(node, potential_dom) - && node_data - .terminator() - .successors() - .into_iter() - .any(|succ| succ == potential_dom) - { - indices.push(node); - continue; - } - } - } + let indices: Vec = + body.basic_blocks + .iter_enumerated() + .filter_map(|(node, node_data)| { + if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) || + // Back edges in a CFG indicate loops + body.basic_blocks.iter_enumerated().any(|(potential_dom, _)| { + doms.is_reachable(potential_dom) + && doms.is_reachable(node) + && doms.is_dominated_by(node, potential_dom) + && node_data + .terminator() + .successors() + .into_iter() + .any(|succ| succ == potential_dom) + }) { + Some(node) + } else { + None + } + }) + .collect(); for index in indices { insert_counter( body.basic_blocks_mut() From 8d99b0fc8d732bcef84127bf431517922878461f Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 19:37:33 +0000 Subject: [PATCH 05/19] Abstract out has_back_edge fn --- .../rustc_mir_transform/src/ctfe_limit.rs | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index 462be8afaafeb..1ff8b792dca36 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -1,7 +1,7 @@ use crate::MirPass; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, + BasicBlock, BasicBlockData, BasicBlocks, Body, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::TyCtxt; @@ -10,29 +10,20 @@ pub struct CtfeLimit; impl<'tcx> MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let doms = body.basic_blocks.dominators(); - let indices: Vec = - body.basic_blocks - .iter_enumerated() - .filter_map(|(node, node_data)| { - if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) || + let indices: Vec = body + .basic_blocks + .iter_enumerated() + .filter_map(|(node, node_data)| { + if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) // Back edges in a CFG indicate loops - body.basic_blocks.iter_enumerated().any(|(potential_dom, _)| { - doms.is_reachable(potential_dom) - && doms.is_reachable(node) - && doms.is_dominated_by(node, potential_dom) - && node_data - .terminator() - .successors() - .into_iter() - .any(|succ| succ == potential_dom) - }) { - Some(node) - } else { - None - } - }) - .collect(); + || has_back_edge(&body.basic_blocks, node, &node_data) + { + Some(node) + } else { + None + } + }) + .collect(); for index in indices { insert_counter( body.basic_blocks_mut() @@ -43,6 +34,20 @@ impl<'tcx> MirPass<'tcx> for CtfeLimit { } } +fn has_back_edge( + basic_blocks: &BasicBlocks<'_>, + node: BasicBlock, + node_data: &BasicBlockData<'_>, +) -> bool { + let doms = basic_blocks.dominators(); + basic_blocks.indices().any(|potential_dom| { + doms.is_reachable(potential_dom) + && doms.is_reachable(node) + && doms.is_dominated_by(node, potential_dom) + && node_data.terminator().successors().into_iter().any(|succ| succ == potential_dom) + }) +} + fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { basic_block_data.statements.push(Statement { source_info: basic_block_data.terminator().source_info, From eea42733ac070b62492037107ee38028abb71f1a Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 23:14:29 +0000 Subject: [PATCH 06/19] Replace terminator-based const eval limit - Remove logic that limits const eval based on terminators, and use the stable metric instead (back edges + fn calls) - Add unstable flag `tiny-const-eval-limit` to add UI tests that do not have to go up to the regular 2M step limit --- .../src/const_eval/machine.rs | 4 +-- .../src/interpret/eval_context.rs | 5 ---- .../rustc_const_eval/src/interpret/machine.rs | 6 ++-- .../rustc_const_eval/src/interpret/place.rs | 11 ------- .../rustc_const_eval/src/interpret/step.rs | 4 +-- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_middle/src/ty/context.rs | 8 ++++- compiler/rustc_session/src/options.rs | 2 ++ .../compiler-flags/tiny-const-eval-limit.md | 6 ++++ .../const-eval/stable-metric/ctfe-fn-call.rs | 9 ++++-- .../stable-metric/ctfe-fn-call.stderr | 20 +++++++++++-- .../stable-metric/ctfe-labelled-loop.rs | 26 +++++++--------- .../stable-metric/ctfe-labelled-loop.stderr | 30 +++++++++++++++++-- .../stable-metric/ctfe-recursion.rs | 7 +++-- .../stable-metric/ctfe-recursion.stderr | 25 ++++++++++++++-- .../stable-metric/ctfe-simple-loop.rs | 11 ++++--- .../stable-metric/ctfe-simple-loop.stderr | 24 +++++++++++++-- 17 files changed, 138 insertions(+), 61 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4709514c82e85..a5bc121485d8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -561,8 +561,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); } - fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - // The step limit has already been hit in a previous call to `before_terminator`. + fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + // The step limit has already been hit in a previous call to `increment_const_eval_counter`. if ecx.machine.steps_remaining == 0 { return Ok(()); } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index cc97564e8fc28..d13fed7a9c263 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -46,9 +46,6 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// The recursion limit (cached from `tcx.recursion_limit(())`) pub recursion_limit: Limit, - - pub const_eval_limit: u32, - pub const_eval_counter: u32, } // The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread @@ -411,8 +408,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { param_env, memory: Memory::new(), recursion_limit: tcx.recursion_limit(), - const_eval_limit: 20, - const_eval_counter: 0, } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 248953de86728..1f63a4ac53784 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -243,10 +243,10 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx.stack_mut()[frame].locals[local].access_mut() } - /// Called before a basic block terminator is executed. - /// You can use this to detect endlessly running programs. + /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction. + /// You can use this to detect long or endlessly running programs. #[inline] - fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn increment_const_eval_counter(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 271a3a74fe319..274af61ee7c1d 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -293,17 +293,6 @@ where Prov: Provenance + 'static, M: Machine<'mir, 'tcx, Provenance = Prov>, { - pub fn increment_const_eval_counter(&mut self) { - self.const_eval_counter = self.const_eval_counter + 1; - if self.const_eval_counter == self.const_eval_limit { - let mut warn = self.tcx.sess.struct_warn(format!( - "Const eval counter limit ({}) has been crossed", - self.const_eval_limit - )); - warn.emit(); - } - } - /// Take a value, which represents a (thin or wide) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. /// diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 6c5594bc1b086..7668e890c7bc3 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -62,8 +62,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return Ok(true); } - M::before_terminator(self)?; - let terminator = basic_block.terminator(); self.terminator(terminator)?; Ok(true) @@ -130,7 +128,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Coverage(..) => {} ConstEvalCounter => { - self.increment_const_eval_counter(); + M::increment_const_eval_counter(self)?; } // Defined to do nothing. These are added by optimization passes, to avoid changing the diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index f94bc4d4c66ac..52a4e0e74181f 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -802,6 +802,7 @@ fn test_unstable_options_tracking_hash() { tracked!(teach, true); tracked!(thinlto, Some(true)); tracked!(thir_unsafeck, true); + tracked!(tiny_const_eval_limit, true); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(trait_solver, TraitSolver::Chalk); tracked!(translate_remapped_path_to_local_path, false); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ce04d8d21f4cd..8a61fd2e029bc 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -75,6 +75,8 @@ use std::iter; use std::mem; use std::ops::{Bound, Deref}; +const TINY_CONST_EVAL_LIMIT: Limit = Limit(20); + pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self @@ -1078,7 +1080,11 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn const_eval_limit(self) -> Limit { - self.limits(()).const_eval_limit + if self.sess.opts.unstable_opts.tiny_const_eval_limit { + TINY_CONST_EVAL_LIMIT + } else { + self.limits(()).const_eval_limit + } } pub fn all_traits(self) -> impl Iterator + 'tcx { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 7b5fd6cc2a81d..789af0c7bf966 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1616,6 +1616,8 @@ options! { "measure time of each LLVM pass (default: no)"), time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + tiny_const_eval_limit: bool = (false, parse_bool, [TRACKED], + "sets a tiny, non-configurable limit for const eval; useful for compiler tests"), #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] tls_model: Option = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), diff --git a/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md b/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md new file mode 100644 index 0000000000000..51c5fd69c6377 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md @@ -0,0 +1,6 @@ +# `tiny-const-eval-limit` + +-------------------- + +The `-Ztiny-const-eval-limit` compiler flag sets a tiny, non-configurable limit for const eval. +This flag should only be used by const eval tests in the rustc test suite. diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs index 33488bd1d1c55..c59596238e140 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs @@ -1,4 +1,5 @@ -// check-pass +// check-fail +// compile-flags: -Z tiny-const-eval-limit const fn foo() {} @@ -8,21 +9,23 @@ const fn call_foo() -> u32 { foo(); foo(); foo(); + foo(); foo(); foo(); foo(); foo(); + foo(); foo(); foo(); foo(); foo(); + foo(); foo(); foo(); - foo(); - foo(); + foo(); //~ ERROR evaluation of constant value failed [E0080] 0 } diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr index 183bed3b75b25..ed70975af341d 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr @@ -1,4 +1,20 @@ -warning: Const eval counter limit (20) has been crossed +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-fn-call.rs:28:5 + | +LL | foo(); + | ^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) + | +note: inside `call_foo` + --> $DIR/ctfe-fn-call.rs:28:5 + | +LL | foo(); + | ^^^^^ +note: inside `X` + --> $DIR/ctfe-fn-call.rs:32:16 + | +LL | const X: u32 = call_foo(); + | ^^^^^^^^^^ -warning: 1 warning emitted +error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs index 71f29ce8731f0..c10b8d8379119 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs @@ -1,24 +1,18 @@ -// check-pass -#![feature(const_for)] +// check-fail +// compile-flags: -Z tiny-const-eval-limit -const fn labelled_loop() -> u32 { - let mut n = 0; - 'outer: loop { - 'inner: loop { - n = n + 1; - if n > 5 && n <= 10 { - n = n + 1; - continue 'inner - } - if n > 30 { - break 'outer - } +const fn labelled_loop(n: u32) -> u32 { + let mut i = 0; + 'mylabel: loop { //~ ERROR evaluation of constant value failed [E0080] + if i > n { + break 'mylabel } + i += 1; } - n + 0 } -const X: u32 = labelled_loop(); +const X: u32 = labelled_loop(19); fn main() { println!("{X}"); diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr index 183bed3b75b25..d9404edd5b108 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr @@ -1,4 +1,30 @@ -warning: Const eval counter limit (20) has been crossed +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-labelled-loop.rs:6:5 + | +LL | / 'mylabel: loop { +LL | | if i > n { +LL | | break 'mylabel +LL | | } +LL | | i += 1; +LL | | } + | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`) + | +note: inside `labelled_loop` + --> $DIR/ctfe-labelled-loop.rs:6:5 + | +LL | / 'mylabel: loop { +LL | | if i > n { +LL | | break 'mylabel +LL | | } +LL | | i += 1; +LL | | } + | |_____^ +note: inside `X` + --> $DIR/ctfe-labelled-loop.rs:15:16 + | +LL | const X: u32 = labelled_loop(19); + | ^^^^^^^^^^^^^^^^^ -warning: 1 warning emitted +error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs index 00b4fc258593a..80ff835f3e8dd 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs @@ -1,14 +1,15 @@ -// check-pass +// check-fail +// compile-flags: -Z tiny-const-eval-limit const fn recurse(n: u32) -> u32 { if n == 0 { n } else { - recurse(n - 1) + recurse(n - 1) //~ ERROR evaluation of constant value failed [E0080] } } -const X: u32 = recurse(30); +const X: u32 = recurse(19); fn main() { println!("{X}"); diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr index 183bed3b75b25..ed9a31119427a 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr @@ -1,4 +1,25 @@ -warning: Const eval counter limit (20) has been crossed +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-recursion.rs:8:9 + | +LL | recurse(n - 1) + | ^^^^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) + | +note: inside `recurse` + --> $DIR/ctfe-recursion.rs:8:9 + | +LL | recurse(n - 1) + | ^^^^^^^^^^^^^^ +note: [... 18 additional calls inside `recurse` ...] + --> $DIR/ctfe-recursion.rs:8:9 + | +LL | recurse(n - 1) + | ^^^^^^^^^^^^^^ +note: inside `X` + --> $DIR/ctfe-recursion.rs:12:16 + | +LL | const X: u32 = recurse(19); + | ^^^^^^^^^^^ -warning: 1 warning emitted +error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs index 74dc74734b45e..ca0eec93c5dac 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs @@ -1,15 +1,14 @@ -// check-pass +// check-fail +// compile-flags: -Z tiny-const-eval-limit const fn simple_loop(n: u32) -> u32 { let mut index = 0; - let mut res = 0; - while index < n { - res = res + index; + while index < n { //~ ERROR evaluation of constant value failed [E0080] index = index + 1; } - res + 0 } -const X: u32 = simple_loop(30); +const X: u32 = simple_loop(19); fn main() { println!("{X}"); diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr index 183bed3b75b25..83ff275de7049 100644 --- a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr +++ b/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr @@ -1,4 +1,24 @@ -warning: Const eval counter limit (20) has been crossed +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-simple-loop.rs:5:5 + | +LL | / while index < n { +LL | | index = index + 1; +LL | | } + | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`) + | +note: inside `simple_loop` + --> $DIR/ctfe-simple-loop.rs:5:5 + | +LL | / while index < n { +LL | | index = index + 1; +LL | | } + | |_____^ +note: inside `X` + --> $DIR/ctfe-simple-loop.rs:11:16 + | +LL | const X: u32 = simple_loop(19); + | ^^^^^^^^^^^^^^^ -warning: 1 warning emitted +error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. From 164ff640131cf90f5a6e8639a5cdc7ece297ba83 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 23:44:16 +0000 Subject: [PATCH 07/19] Update codegen cranelift for ConstEvalCounter --- compiler/rustc_codegen_cranelift/src/base.rs | 1 + compiler/rustc_codegen_cranelift/src/constant.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 89d955e8bf2e1..6e584c308c109 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -789,6 +789,7 @@ fn codegen_stmt<'tcx>( StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Deinit(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::FakeRead(..) | StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 51450897bfc11..49c4f1aaaefc6 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -530,6 +530,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } From 80a3d2ad0ceaabd353d4800339045a45559385c7 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Thu, 29 Dec 2022 23:50:53 +0000 Subject: [PATCH 08/19] Update Clippy for ConstEvalCounter --- src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index e5d7da682813c..d127b896deacf 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -240,6 +240,7 @@ fn check_statement<'tcx>( | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => Ok(()), } } From 08de246cd71a67915d4931aa37bc4f1c6374be0e Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Fri, 30 Dec 2022 00:24:41 +0000 Subject: [PATCH 09/19] Move CtfeLimit to mir_const's set of passes --- compiler/rustc_borrowck/src/lib.rs | 4 ++-- compiler/rustc_mir_transform/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 8f8fae2c630b5..e21f6e6a67cd9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -610,6 +610,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx // Doesn't have any language semantics | StatementKind::Coverage(..) // Does not actually affect borrowck + | StatementKind::ConstEvalCounter | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( @@ -620,8 +621,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx flow_state, ); } - StatementKind::ConstEvalCounter - | StatementKind::Nop + StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9a786d0c8d62a..e1388e678f1f7 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -299,6 +299,7 @@ fn mir_const(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> &Steal< // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, // Just a lint + &ctfe_limit::CtfeLimit, ], None, ); @@ -518,7 +519,6 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // CTFE support for aggregates. &deaggregator::Deaggregator, &Lint(const_prop_lint::ConstProp), - &ctfe_limit::CtfeLimit, ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); } From 172662dede507cc678747cc3d090f2ae744733cf Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Fri, 30 Dec 2022 00:34:17 +0000 Subject: [PATCH 10/19] Add back Machine::before_terminator(...) method Added it back because it's used by Miri, but in the compiler itself, it will not do anything (just return `Ok(())`. --- compiler/rustc_const_eval/src/const_eval/machine.rs | 5 +++++ compiler/rustc_const_eval/src/interpret/machine.rs | 6 ++++++ compiler/rustc_const_eval/src/interpret/step.rs | 2 ++ 3 files changed, 13 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index a5bc121485d8c..e51f52783d4c1 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -561,6 +561,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); } + // Not used here, but used by Miri (see `src/tools/miri/src/machine.rs`). + fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { // The step limit has already been hit in a previous call to `increment_const_eval_counter`. if ecx.machine.steps_remaining == 0 { diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 1f63a4ac53784..76ed7b80f8d81 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -243,6 +243,12 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx.stack_mut()[frame].locals[local].access_mut() } + /// Called before a basic block terminator is executed. + #[inline] + fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction. /// You can use this to detect long or endlessly running programs. #[inline] diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 7668e890c7bc3..d101937fd7406 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -62,6 +62,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return Ok(true); } + M::before_terminator(self)?; + let terminator = basic_block.terminator(); self.terminator(terminator)?; Ok(true) From d3c13a010295002211fae61f2cac2fbe374bc0fc Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Fri, 30 Dec 2022 01:55:16 +0000 Subject: [PATCH 11/19] Revert "Move CtfeLimit to mir_const's set of passes" This reverts commit 332542a92223b2800ed372d2d461921147f29477. --- compiler/rustc_borrowck/src/lib.rs | 4 ++-- compiler/rustc_mir_transform/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e21f6e6a67cd9..8f8fae2c630b5 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -610,7 +610,6 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx // Doesn't have any language semantics | StatementKind::Coverage(..) // Does not actually affect borrowck - | StatementKind::ConstEvalCounter | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( @@ -621,7 +620,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx flow_state, ); } - StatementKind::Nop + StatementKind::ConstEvalCounter + | StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index e1388e678f1f7..9a786d0c8d62a 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -299,7 +299,6 @@ fn mir_const(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> &Steal< // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, // Just a lint - &ctfe_limit::CtfeLimit, ], None, ); @@ -519,6 +518,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // CTFE support for aggregates. &deaggregator::Deaggregator, &Lint(const_prop_lint::ConstProp), + &ctfe_limit::CtfeLimit, ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); } From 999d19d8aa39422e16c55598aef25c2fa56e0540 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Wed, 4 Jan 2023 04:20:14 +0000 Subject: [PATCH 12/19] Move CtfeLimit MirPass to inner_mir_for_ctfe --- compiler/rustc_mir_transform/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9a786d0c8d62a..f12e04cccd404 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -411,6 +411,8 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - } } + pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None); + debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE"); body @@ -518,7 +520,6 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // CTFE support for aggregates. &deaggregator::Deaggregator, &Lint(const_prop_lint::ConstProp), - &ctfe_limit::CtfeLimit, ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); } From aae331d610f318cd2d472c39f1e09b02d4f83d81 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Wed, 4 Jan 2023 04:29:27 +0000 Subject: [PATCH 13/19] During MirBorrowck, ignore ConstEvalCounter --- compiler/rustc_borrowck/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 8f8fae2c630b5..bc81abe4005c9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -609,7 +609,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx StatementKind::AscribeUserType(..) // Doesn't have any language semantics | StatementKind::Coverage(..) - // Does not actually affect borrowck + // These do not actually affect borrowck + | StatementKind::ConstEvalCounter | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( @@ -620,8 +621,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx flow_state, ); } - StatementKind::ConstEvalCounter - | StatementKind::Nop + StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { From 75b7c6c8ecc6865823e64ff6b2ab5b018c8ca15b Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Wed, 4 Jan 2023 05:04:03 +0000 Subject: [PATCH 14/19] Bless and update consts tests --- tests/ui/consts/const-eval/infinite_loop.stderr | 9 ++++++--- tests/ui/consts/const-eval/issue-52475.rs | 4 ++-- tests/ui/consts/const-eval/issue-52475.stderr | 9 ++++++--- .../consts/const_limit/const_eval_limit_reached.stderr | 9 ++++++--- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/ui/consts/const-eval/infinite_loop.stderr b/tests/ui/consts/const-eval/infinite_loop.stderr index 8b58cb279f376..f30bfaf3f958c 100644 --- a/tests/ui/consts/const-eval/infinite_loop.stderr +++ b/tests/ui/consts/const-eval/infinite_loop.stderr @@ -1,8 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/infinite_loop.rs:6:15 + --> $DIR/infinite_loop.rs:6:9 | -LL | while n != 0 { - | ^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | / while n != 0 { +LL | | +LL | | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; +LL | | } + | |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to previous error diff --git a/tests/ui/consts/const-eval/issue-52475.rs b/tests/ui/consts/const-eval/issue-52475.rs index ce65407bbab0b..307c1a6683410 100644 --- a/tests/ui/consts/const-eval/issue-52475.rs +++ b/tests/ui/consts/const-eval/issue-52475.rs @@ -2,8 +2,8 @@ fn main() { let _ = [(); { let mut x = &0; let mut n = 0; - while n < 5 { - n = (n + 1) % 5; //~ ERROR evaluation of constant value failed + while n < 5 { //~ ERROR evaluation of constant value failed [E0080] + n = (n + 1) % 5; x = &0; // Materialize a new AllocId } 0 diff --git a/tests/ui/consts/const-eval/issue-52475.stderr b/tests/ui/consts/const-eval/issue-52475.stderr index 8536ff02c6dae..3aa6bd277ddcb 100644 --- a/tests/ui/consts/const-eval/issue-52475.stderr +++ b/tests/ui/consts/const-eval/issue-52475.stderr @@ -1,8 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-52475.rs:6:17 + --> $DIR/issue-52475.rs:5:9 | -LL | n = (n + 1) % 5; - | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | / while n < 5 { +LL | | n = (n + 1) % 5; +LL | | x = &0; // Materialize a new AllocId +LL | | } + | |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to previous error diff --git a/tests/ui/consts/const_limit/const_eval_limit_reached.stderr b/tests/ui/consts/const_limit/const_eval_limit_reached.stderr index 850aebdfb2a6d..a8e8ae9bb088a 100644 --- a/tests/ui/consts/const_limit/const_eval_limit_reached.stderr +++ b/tests/ui/consts/const_limit/const_eval_limit_reached.stderr @@ -1,8 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const_eval_limit_reached.rs:6:11 + --> $DIR/const_eval_limit_reached.rs:6:5 | -LL | while x != 1000 { - | ^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | / while x != 1000 { +LL | | +LL | | x += 1; +LL | | } + | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to previous error From a8c9528e0692d723736eb090bb10ab6145d4bf8c Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Wed, 4 Jan 2023 05:59:37 +0000 Subject: [PATCH 15/19] Bless z-help test for new flag --- tests/rustdoc-ui/z-help.stdout | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 4bdecdc1b7944..3d7919826b574 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -172,6 +172,7 @@ -Z threads=val -- use a thread pool with N threads -Z time-llvm-passes=val -- measure time of each LLVM pass (default: no) -Z time-passes=val -- measure time of each rustc pass (default: no) + -Z tiny-const-eval-limit=val -- sets a tiny, non-configurable limit for const eval; useful for compiler tests -Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) -Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no) -Z track-diagnostics=val -- tracks where in rustc a diagnostic was emitted From f9982ea24a55a1912198bc49578f7af090e487e9 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Fri, 6 Jan 2023 16:26:56 +0000 Subject: [PATCH 16/19] Add comment on cause of panic in dominators algorithm --- .../src/graph/dominators/mod.rs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 6398a501983cf..38a687af7e696 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -135,7 +135,47 @@ pub fn dominators(graph: G) -> Dominators { // This loop computes the semi[w] for w. semi[w] = w; for v in graph.predecessors(pre_order_to_real[w]) { - // Reachable vertices may have unreachable predecessors, so ignore any of them + // TL;DR: Reachable vertices may have unreachable predecessors, so ignore any of them. + // + // Ignore blocks which are not connected to the entry block. + // + // The algorithm that was used to traverse the graph and build the + // `pre_order_to_real` and `real_to_pre_order` vectors does so by + // starting from the entry block and following the successors. + // Therefore, any blocks not reachable from the entry block will be + // set to `None` in the `pre_order_to_real` vector. + // + // For example, in this graph, A and B should be skipped: + // + // ┌─────┐ + // │ │ + // └──┬──┘ + // │ + // ┌──▼──┐ ┌─────┐ + // │ │ │ A │ + // └──┬──┘ └──┬──┘ + // │ │ + // ┌───────┴───────┐ │ + // │ │ │ + // ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ + // │ │ │ │ │ B │ + // └──┬──┘ └──┬──┘ └──┬──┘ + // │ └──────┬─────┘ + // ┌──▼──┐ │ + // │ │ │ + // └──┬──┘ ┌──▼──┐ + // │ │ │ + // │ └─────┘ + // ┌──▼──┐ + // │ │ + // └──┬──┘ + // │ + // ┌──▼──┐ + // │ │ + // └─────┘ + // + // ...this may be the case if a MirPass modifies the CFG to remove + // or rearrange certain blocks/edges. let Some(v) = real_to_pre_order[v] else { continue }; From 7618163a1caf0d6dfa5618c8369742720c90ef6b Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Fri, 6 Jan 2023 16:39:24 +0000 Subject: [PATCH 17/19] Add comments and remove unnecessary code --- compiler/rustc_const_eval/src/const_eval/machine.rs | 5 ----- compiler/rustc_middle/src/mir/syntax.rs | 9 +++++---- compiler/rustc_mir_transform/src/ctfe_limit.rs | 2 ++ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e51f52783d4c1..a5bc121485d8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -561,11 +561,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); } - // Not used here, but used by Miri (see `src/tools/miri/src/machine.rs`). - fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) - } - fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { // The step limit has already been hit in a previous call to `increment_const_eval_counter`. if ecx.machine.steps_remaining == 0 { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index faf903a594901..549bc65d6d79c 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -286,10 +286,7 @@ pub enum StatementKind<'tcx> { /// This is permitted for both generators and ADTs. This does not necessarily write to the /// entire place; instead, it writes to the minimum set of bytes as required by the layout for /// the type. - SetDiscriminant { - place: Box>, - variant_index: VariantIdx, - }, + SetDiscriminant { place: Box>, variant_index: VariantIdx }, /// Deinitializes the place. /// @@ -358,6 +355,10 @@ pub enum StatementKind<'tcx> { /// This avoids adding a new block and a terminator for simple intrinsics. Intrinsic(Box>), + /// Instructs the const eval interpreter to increment a counter; this counter is used to track + /// how many steps the interpreter has taken. It is used to prevent the user from writing const + /// code that runs for too long or infinitely. Other than in the const eval interpreter, this + /// is a no-op. ConstEvalCounter, /// No-op. Useful for deleting instructions without affecting statement indices. diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index 1ff8b792dca36..76db4a09d9153 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -1,3 +1,5 @@ +//! A pass that inserts the `ConstEvalCounter` instruction into any blocks that have a back edge +//! (thus indicating there is a loop in the CFG), or whose terminator is a function call. use crate::MirPass; use rustc_middle::mir::{ From 1bbd655888ec50220e6cd34e846c816c1cad8f17 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Fri, 6 Jan 2023 22:04:25 +0000 Subject: [PATCH 18/19] Improve efficiency of has_back_edge(...) --- .../src/graph/dominators/mod.rs | 7 +++++++ .../rustc_mir_transform/src/ctfe_limit.rs | 21 ++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 38a687af7e696..0a21a4249c829 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -304,13 +304,18 @@ fn compress( } } +/// Tracks the list of dominators for each node. #[derive(Clone, Debug)] pub struct Dominators { post_order_rank: IndexVec, + // Even though we track only the immediate dominator of each node, it's + // possible to get its full list of dominators by looking up the dominator + // of each dominator. (See the `impl Iterator for Iter` definition). immediate_dominators: IndexVec>, } impl Dominators { + /// Whether the given Node has an immediate dominator. pub fn is_reachable(&self, node: Node) -> bool { self.immediate_dominators[node].is_some() } @@ -320,6 +325,8 @@ impl Dominators { self.immediate_dominators[node].unwrap() } + /// Provides an iterator over each dominator up the CFG, for the given Node. + /// See the `impl Iterator for Iter` definition to understand how this works. pub fn dominators(&self, node: Node) -> Iter<'_, Node> { assert!(self.is_reachable(node), "node {node:?} is not reachable"); Iter { dominators: self, node: Some(node) } diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index 76db4a09d9153..7d127032179b4 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -2,8 +2,9 @@ //! (thus indicating there is a loop in the CFG), or whose terminator is a function call. use crate::MirPass; +use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, BasicBlocks, Body, Statement, StatementKind, TerminatorKind, + BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::TyCtxt; @@ -12,13 +13,14 @@ pub struct CtfeLimit; impl<'tcx> MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let doms = body.basic_blocks.dominators(); let indices: Vec = body .basic_blocks .iter_enumerated() .filter_map(|(node, node_data)| { if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) // Back edges in a CFG indicate loops - || has_back_edge(&body.basic_blocks, node, &node_data) + || has_back_edge(&doms, node, &node_data) { Some(node) } else { @@ -37,17 +39,16 @@ impl<'tcx> MirPass<'tcx> for CtfeLimit { } fn has_back_edge( - basic_blocks: &BasicBlocks<'_>, + doms: &Dominators, node: BasicBlock, node_data: &BasicBlockData<'_>, ) -> bool { - let doms = basic_blocks.dominators(); - basic_blocks.indices().any(|potential_dom| { - doms.is_reachable(potential_dom) - && doms.is_reachable(node) - && doms.is_dominated_by(node, potential_dom) - && node_data.terminator().successors().into_iter().any(|succ| succ == potential_dom) - }) + if !doms.is_reachable(node) { + return false; + } + // Check if any of the dominators of the node are also the node's successor. + doms.dominators(node) + .any(|dom| node_data.terminator().successors().into_iter().any(|succ| succ == dom)) } fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { From bdb815a22ab00450dcc010a99309c24c475432a6 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Tue, 17 Jan 2023 22:35:05 +0000 Subject: [PATCH 19/19] Move const-eval/stable-metric ui tests --- .../const-eval/stable-metric/ctfe-fn-call.rs | 0 .../stable-metric/ctfe-fn-call.stderr | 0 .../stable-metric/ctfe-labelled-loop.rs | 0 .../stable-metric/ctfe-labelled-loop.stderr | 0 .../stable-metric/ctfe-recursion.rs | 0 .../stable-metric/ctfe-recursion.stderr | 0 .../stable-metric/ctfe-simple-loop.rs | 0 .../stable-metric/ctfe-simple-loop.stderr | 0 .../stable-metric/dominators-edge-case.rs | 19 +++++++++++++++++++ 9 files changed, 19 insertions(+) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-recursion.rs (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs (100%) rename {src/test => tests}/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr (100%) create mode 100644 tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs rename to tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr rename to tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs rename to tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr rename to tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.rs similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.rs rename to tests/ui/consts/const-eval/stable-metric/ctfe-recursion.rs diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr rename to tests/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs rename to tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs diff --git a/src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr similarity index 100% rename from src/test/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr rename to tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr diff --git a/tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs b/tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs new file mode 100644 index 0000000000000..0b0f361809f20 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs @@ -0,0 +1,19 @@ +// check-pass +// +// Exercising an edge case which was found during Stage 2 compilation. +// Compilation would fail for this code when running the `CtfeLimit` +// MirPass (specifically when looking up the dominators). +#![crate_type="lib"] + +const DUMMY: Expr = Expr::Path(ExprPath { + attrs: Vec::new(), + path: Vec::new(), +}); + +pub enum Expr { + Path(ExprPath), +} +pub struct ExprPath { + pub attrs: Vec<()>, + pub path: Vec<()>, +}