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 8c4885770ad..2821677c537 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 6fd9290058c..6217676d5c1 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 73ea7314b75..8f8fae2c630 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 81bd4c2a783..06087b0c579 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 19452c8cdc8..60fbceb344d 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 18e01567ca3..041e9d41357 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 4709514c82e..befc71ce6a0 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 d13fed7a9c2..cc97564e8fc 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 274af61ee7c..271a3a74fe3 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 fad4cb06cd6..0f0eb5aadd7 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 79f1737e32b..16b504dd9d4 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 dd168a9ac3c..4ad699c0395 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 4da893e4c07..dae7e84e415 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 a8a4532223c..c281fe00591 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<DefId>) -> &'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 887ee571575..7efe1fde093 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 52c2b10cbbe..faf903a5949 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<Place<'tcx>>, variant_index: VariantIdx },
+    SetDiscriminant {
+        place: Box<Place<'tcx>>,
+        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<NonDivergingIntrinsic<'tcx>>),
 
+    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 1a264d2d5af..3ddac5e11fb 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 923dc16c11b..2890fa32cc9 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 8d379b90a86..fcf0ce9d821 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 f46fd118bde..0195693a7cb 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 0522c657939..6bdbda909d7 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 adf6ae4c727..837233953e7 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 31d5541a31b..f973c1ed28f 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<Span>
         | 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 00000000000..dc54b983c0e
--- /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::<Vec<BasicBlock>>()
+            );
+        }
+        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::<Vec<BasicBlock>>());
+            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 09546330cec..9dbfb089dc6 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 08e296a8371..20ffb0ab334 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 39c61a34afc..0df732aa22b 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 20b7fdcfe6d..e5c8127bea1 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 f1bbf2ea7e8..e3a03aa08af 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 2f116aaa958..a24d2d34d79 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 8f6abe7a912..7b6fa2baf2f 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 00000000000..71f29ce8731
--- /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 00000000000..183bed3b75b
--- /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 00000000000..00b4fc25859
--- /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 00000000000..183bed3b75b
--- /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 00000000000..74dc74734b4
--- /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 00000000000..183bed3b75b
--- /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     | 113 ++++++------------
 .../const-eval/stable-metric/ctfe-fn-call.rs  |  33 +++++
 .../stable-metric/ctfe-fn-call.stderr         |   4 +
 3 files changed, 72 insertions(+), 78 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 dc54b983c0e..f2c99b19433 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::<Vec<BasicBlock>>()
+        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;
+                }
+            }
+        }
+        for index in indices {
+            insert_counter(
+                body.basic_blocks_mut()
+                    .get_mut(index)
+                    .expect("basic_blocks index {index} should exist"),
             );
         }
-        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::<Vec<BasicBlock>>());
-            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:?}");
     }
 }
+
+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 00000000000..33488bd1d1c
--- /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 00000000000..183bed3b75b
--- /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 041e9d41357..18e01567ca3 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 befc71ce6a0..4709514c82e 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 0f0eb5aadd7..6c5594bc1b0 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 c281fe00591..a8a4532223c 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<DefId>) -> &'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 e5c8127bea1..9a786d0c8d6 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 f2c99b19433..462be8afaaf 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<BasicBlock> =
+            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 462be8afaaf..1ff8b792dca 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<BasicBlock> =
-            body.basic_blocks
-                .iter_enumerated()
-                .filter_map(|(node, node_data)| {
-                    if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) ||
+        let indices: Vec<BasicBlock> = 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 4709514c82e..a5bc121485d 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 cc97564e8fc..d13fed7a9c2 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 248953de867..1f63a4ac537 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 271a3a74fe3..274af61ee7c 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 6c5594bc1b0..7668e890c7b 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 f94bc4d4c66..52a4e0e7418 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 ce04d8d21f4..8a61fd2e029 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<Item = DefId> + 'tcx {
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 7b5fd6cc2a8..789af0c7bf9 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<TlsModel> = (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 00000000000..51c5fd69c63
--- /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 33488bd1d1c..c59596238e1 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 183bed3b75b..ed70975af34 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 71f29ce8731..c10b8d83791 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 183bed3b75b..d9404edd5b1 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 00b4fc25859..80ff835f3e8 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 183bed3b75b..ed9a3111942 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 74dc74734b4..ca0eec93c5d 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 183bed3b75b..83ff275de70 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 89d955e8bf2..6e584c308c1 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 51450897bfc..49c4f1aaaef 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 e5d7da68281..d127b896dea 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 8f8fae2c630..e21f6e6a67c 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 9a786d0c8d6..e1388e678f1 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<LocalDefId>) -> &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 a5bc121485d..e51f52783d4 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 1f63a4ac537..76ed7b80f8d 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 7668e890c7b..d101937fd74 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 e21f6e6a67c..8f8fae2c630 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 e1388e678f1..9a786d0c8d6 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<LocalDefId>) -> &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 9a786d0c8d6..f12e04cccd4 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<LocalDefId>) -
         }
     }
 
+    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 8f8fae2c630..bc81abe4005 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 8b58cb279f3..f30bfaf3f95 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 ce65407bbab..307c1a66834 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 8536ff02c6d..3aa6bd277dd 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 850aebdfb2a..a8e8ae9bb08 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 4bdecdc1b79..3d7919826b5 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 6398a501983..38a687af7e6 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<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
         // 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 e51f52783d4..a5bc121485d 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 faf903a5949..549bc65d6d7 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<Place<'tcx>>,
-        variant_index: VariantIdx,
-    },
+    SetDiscriminant { place: Box<Place<'tcx>>, 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<NonDivergingIntrinsic<'tcx>>),
 
+    /// 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 1ff8b792dca..76db4a09d91 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 38a687af7e6..0a21a4249c8 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<N: Idx> {
     post_order_rank: IndexVec<N, usize>,
+    // 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<N, Option<N>>,
 }
 
 impl<Node: Idx> Dominators<Node> {
+    /// 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<Node: Idx> Dominators<Node> {
         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 76db4a09d91..7d127032179 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<BasicBlock> = 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<BasicBlock>,
     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 00000000000..0b0f361809f
--- /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<()>,
+}