about summary refs log tree commit diff
path: root/compiler/rustc_smir/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_smir/src')
-rw-r--r--compiler/rustc_smir/src/lib.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs12
-rw-r--r--compiler/rustc_smir/src/rustc_internal/pretty.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/alloc.rs39
-rw-r--r--compiler/rustc_smir/src/rustc_smir/builder.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs16
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/abi.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/error.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mir.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mod.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs62
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs1
-rw-r--r--compiler/rustc_smir/src/stable_mir/abi.rs457
-rw-r--r--compiler/rustc_smir/src/stable_mir/compiler_interface.rs288
-rw-r--r--compiler/rustc_smir/src/stable_mir/crate_def.rs175
-rw-r--r--compiler/rustc_smir/src/stable_mir/error.rs85
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir.rs8
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/alloc.rs88
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/body.rs1109
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/mono.rs308
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/pretty.rs463
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/visit.rs588
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs239
-rw-r--r--compiler/rustc_smir/src/stable_mir/target.rs61
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs1622
-rw-r--r--compiler/rustc_smir/src/stable_mir/visitor.rs226
27 files changed, 5829 insertions, 28 deletions
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index 9f888875306..77e5f3423d1 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -21,3 +21,5 @@ pub mod rustc_internal;
 
 // Make this module private for now since external users should not call these directly.
 mod rustc_smir;
+
+pub mod stable_mir;
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index bb2b2dea2f3..6e13b87c41d 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -21,6 +21,7 @@ use stable_mir::{CrateItem, CrateNum, DefId};
 
 use super::RustcInternal;
 use crate::rustc_smir::Tables;
+use crate::stable_mir;
 
 impl RustcInternal for CrateItem {
     type T<'tcx> = rustc_span::def_id::DefId;
@@ -490,7 +491,6 @@ impl RustcInternal for Abi {
             Abi::CCmseNonSecureCall => rustc_abi::ExternAbi::CCmseNonSecureCall,
             Abi::CCmseNonSecureEntry => rustc_abi::ExternAbi::CCmseNonSecureEntry,
             Abi::System { unwind } => rustc_abi::ExternAbi::System { unwind },
-            Abi::RustIntrinsic => rustc_abi::ExternAbi::RustIntrinsic,
             Abi::RustCall => rustc_abi::ExternAbi::RustCall,
             Abi::Unadjusted => rustc_abi::ExternAbi::Unadjusted,
             Abi::RustCold => rustc_abi::ExternAbi::RustCold,
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index ad38ea228bf..146ba17d14f 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -22,6 +22,7 @@ use stable_mir::ty::IndexedVal;
 
 use crate::rustc_smir::context::TablesWrapper;
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 mod internal;
 pub mod pretty;
@@ -147,6 +148,14 @@ impl<'tcx> Tables<'tcx> {
         stable_mir::ty::CoroutineWitnessDef(self.create_def_id(did))
     }
 
+    pub fn assoc_def(&mut self, did: DefId) -> stable_mir::ty::AssocDef {
+        stable_mir::ty::AssocDef(self.create_def_id(did))
+    }
+
+    pub fn opaque_def(&mut self, did: DefId) -> stable_mir::ty::OpaqueDef {
+        stable_mir::ty::OpaqueDef(self.create_def_id(did))
+    }
+
     pub fn prov(&mut self, aid: AllocId) -> stable_mir::ty::Prov {
         stable_mir::ty::Prov(self.create_alloc_id(aid))
     }
@@ -235,6 +244,7 @@ where
 /// ```ignore(needs-extern-crate)
 /// # extern crate rustc_driver;
 /// # extern crate rustc_interface;
+/// # extern crate rustc_middle;
 /// # #[macro_use]
 /// # extern crate rustc_smir;
 /// # extern crate stable_mir;
@@ -255,6 +265,7 @@ where
 /// ```ignore(needs-extern-crate)
 /// # extern crate rustc_driver;
 /// # extern crate rustc_interface;
+/// # extern crate rustc_middle;
 /// # #[macro_use]
 /// # extern crate rustc_smir;
 /// # extern crate stable_mir;
@@ -319,6 +330,7 @@ macro_rules! run_driver {
         use rustc_driver::{Callbacks, Compilation, run_compiler};
         use rustc_middle::ty::TyCtxt;
         use rustc_interface::interface;
+        use rustc_smir::rustc_internal;
         use stable_mir::CompilerError;
         use std::ops::ControlFlow;
 
diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs
index b752ad71ecc..0710c18746a 100644
--- a/compiler/rustc_smir/src/rustc_internal/pretty.rs
+++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs
@@ -3,6 +3,7 @@ use std::io;
 use rustc_middle::ty::TyCtxt;
 
 use super::run;
+use crate::stable_mir;
 
 pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> {
     writeln!(
diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs
index 52c5b425c14..9cb89634c52 100644
--- a/compiler/rustc_smir/src/rustc_smir/alloc.rs
+++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs
@@ -6,6 +6,7 @@ use stable_mir::mir::Mutability;
 use stable_mir::ty::{Allocation, ProvenanceMap};
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 /// Creates new empty `Allocation` from given `Align`.
 fn new_empty_allocation(align: Align) -> Allocation {
@@ -27,7 +28,7 @@ pub(crate) fn new_allocation<'tcx>(
     tables: &mut Tables<'tcx>,
 ) -> Allocation {
     try_new_allocation(ty, const_value, tables)
-        .expect(&format!("Failed to convert: {const_value:?} to {ty:?}"))
+        .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}"))
 }
 
 #[allow(rustc::usage_of_qualified_ty)]
@@ -36,39 +37,30 @@ pub(crate) fn try_new_allocation<'tcx>(
     const_value: ConstValue<'tcx>,
     tables: &mut Tables<'tcx>,
 ) -> Result<Allocation, Error> {
+    let layout = tables
+        .tcx
+        .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
+        .map_err(|e| e.stable(tables))?;
     Ok(match const_value {
         ConstValue::Scalar(scalar) => {
             let size = scalar.size();
-            let align = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?
-                .align;
-            let mut allocation =
-                rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit);
+            let mut allocation = rustc_middle::mir::interpret::Allocation::new(
+                size,
+                layout.align.abi,
+                AllocInit::Uninit,
+            );
             allocation
                 .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar)
                 .map_err(|e| e.stable(tables))?;
             allocation.stable(tables)
         }
-        ConstValue::ZeroSized => {
-            let align = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?
-                .align;
-            new_empty_allocation(align.abi)
-        }
+        ConstValue::ZeroSized => new_empty_allocation(layout.align.abi),
         ConstValue::Slice { data, meta } => {
             let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
             let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
             let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
             let scalar_meta =
                 rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx);
-            let layout = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?;
             let mut allocation = rustc_middle::mir::interpret::Allocation::new(
                 layout.size,
                 layout.align.abi,
@@ -92,12 +84,7 @@ pub(crate) fn try_new_allocation<'tcx>(
         }
         ConstValue::Indirect { alloc_id, offset } => {
             let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
-            let ty_size = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?
-                .size;
-            allocation_filter(&alloc.0, alloc_range(offset, ty_size), tables)
+            allocation_filter(&alloc.0, alloc_range(offset, layout.size), tables)
         }
     })
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs
index 2eb0cea0e85..64763b71d30 100644
--- a/compiler/rustc_smir/src/rustc_smir/builder.rs
+++ b/compiler/rustc_smir/src/rustc_smir/builder.rs
@@ -10,6 +10,7 @@ use rustc_middle::mir::visit::MutVisitor;
 use rustc_middle::ty::{self, TyCtxt};
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 /// Builds a monomorphic body for a given instance.
 pub(crate) struct BodyBuilder<'tcx> {
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index aa1921fc8e7..240f6f7fe45 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -35,6 +35,7 @@ use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, I
 use crate::rustc_internal::RustcInternal;
 use crate::rustc_smir::builder::BodyBuilder;
 use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate};
+use crate::stable_mir;
 
 impl<'tcx> Context for TablesWrapper<'tcx> {
     fn target_info(&self) -> MachineInfo {
@@ -822,6 +823,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal);
         ty.stable(&mut *tables)
     }
+
+    fn associated_items(&self, def_id: stable_mir::DefId) -> stable_mir::AssocItems {
+        let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        let def_id = tables[def_id];
+        let assoc_items = if tcx.is_trait_alias(def_id) {
+            Vec::new()
+        } else {
+            tcx.associated_item_def_ids(def_id)
+                .iter()
+                .map(|did| tcx.associated_item(*did).stable(&mut *tables))
+                .collect()
+        };
+        assoc_items
+    }
 }
 
 pub(crate) struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
index 62cbab9b723..7ccc785a400 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
@@ -14,6 +14,7 @@ use stable_mir::target::MachineSize as Size;
 use stable_mir::ty::{Align, IndexedVal, VariantIdx};
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx {
     type T = VariantIdx;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/error.rs b/compiler/rustc_smir/src/rustc_smir/convert/error.rs
index 82ecfa630dd..2cde5542483 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/error.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/error.rs
@@ -6,6 +6,7 @@ use rustc_middle::mir::interpret::AllocError;
 use rustc_middle::ty::layout::LayoutError;
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for LayoutError<'tcx> {
     type T = stable_mir::Error;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index bdd6e16a7c1..61b769bce08 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -9,6 +9,7 @@ use stable_mir::ty::{Allocation, ConstantKind, MirConst};
 use stable_mir::{Error, opaque};
 
 use crate::rustc_smir::{Stable, Tables, alloc};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
     type T = stable_mir::mir::Body;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
index a3da563af50..3494de62d83 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
@@ -3,6 +3,7 @@
 use rustc_abi::FieldIdx;
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 mod abi;
 mod error;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index aa0eac628dd..28fc68d5e49 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -7,6 +7,7 @@ use stable_mir::ty::{
 };
 
 use crate::rustc_smir::{Stable, Tables, alloc};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for ty::AliasTyKind {
     type T = stable_mir::ty::AliasKind;
@@ -870,7 +871,6 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
             ExternAbi::CCmseNonSecureCall => Abi::CCmseNonSecureCall,
             ExternAbi::CCmseNonSecureEntry => Abi::CCmseNonSecureEntry,
             ExternAbi::System { unwind } => Abi::System { unwind },
-            ExternAbi::RustIntrinsic => Abi::RustIntrinsic,
             ExternAbi::RustCall => Abi::RustCall,
             ExternAbi::Unadjusted => Abi::Unadjusted,
             ExternAbi::RustCold => Abi::RustCold,
@@ -890,3 +890,63 @@ impl<'tcx> Stable<'tcx> for rustc_session::cstore::ForeignModule {
         }
     }
 }
+
+impl<'tcx> Stable<'tcx> for ty::AssocKind {
+    type T = stable_mir::ty::AssocKind;
+
+    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+        use stable_mir::ty::AssocKind;
+        match self {
+            ty::AssocKind::Const => AssocKind::Const,
+            ty::AssocKind::Fn => AssocKind::Fn,
+            ty::AssocKind::Type => AssocKind::Type,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocItemContainer {
+    type T = stable_mir::ty::AssocItemContainer;
+
+    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+        use stable_mir::ty::AssocItemContainer;
+        match self {
+            ty::AssocItemContainer::Trait => AssocItemContainer::Trait,
+            ty::AssocItemContainer::Impl => AssocItemContainer::Impl,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocItem {
+    type T = stable_mir::ty::AssocItem;
+
+    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+        stable_mir::ty::AssocItem {
+            def_id: tables.assoc_def(self.def_id),
+            name: self.name.to_string(),
+            kind: self.kind.stable(tables),
+            container: self.container.stable(tables),
+            trait_item_def_id: self.trait_item_def_id.map(|did| tables.assoc_def(did)),
+            fn_has_self_parameter: self.fn_has_self_parameter,
+            opt_rpitit_info: self.opt_rpitit_info.map(|rpitit| rpitit.stable(tables)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData {
+    type T = stable_mir::ty::ImplTraitInTraitData;
+
+    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+        use stable_mir::ty::ImplTraitInTraitData;
+        match self {
+            ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => {
+                ImplTraitInTraitData::Trait {
+                    fn_def_id: tables.fn_def(*fn_def_id),
+                    opaque_def_id: tables.opaque_def(*opaque_def_id),
+                }
+            }
+            ty::ImplTraitInTraitData::Impl { fn_def_id } => {
+                ImplTraitInTraitData::Impl { fn_def_id: tables.fn_def(*fn_def_id) }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index c5d33f090a0..dea8f54a670 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -21,6 +21,7 @@ use stable_mir::{CtorKind, ItemKind};
 use tracing::debug;
 
 use crate::rustc_internal::IndexMap;
+use crate::stable_mir;
 
 mod alloc;
 mod builder;
diff --git a/compiler/rustc_smir/src/stable_mir/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs
new file mode 100644
index 00000000000..3842cb7e653
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/abi.rs
@@ -0,0 +1,457 @@
+use std::fmt::{self, Debug};
+use std::num::NonZero;
+use std::ops::RangeInclusive;
+
+use serde::Serialize;
+use stable_mir::compiler_interface::with;
+use stable_mir::mir::FieldIdx;
+use stable_mir::target::{MachineInfo, MachineSize as Size};
+use stable_mir::ty::{Align, IndexedVal, Ty, VariantIdx};
+use stable_mir::{Error, Opaque, error};
+
+use crate::stable_mir;
+
+/// A function ABI definition.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct FnAbi {
+    /// The types of each argument.
+    pub args: Vec<ArgAbi>,
+
+    /// The expected return type.
+    pub ret: ArgAbi,
+
+    /// The count of non-variadic arguments.
+    ///
+    /// Should only be different from `args.len()` when a function is a C variadic function.
+    pub fixed_count: u32,
+
+    /// The ABI convention.
+    pub conv: CallConvention,
+
+    /// Whether this is a variadic C function,
+    pub c_variadic: bool,
+}
+
+/// Information about the ABI of a function's argument, or return value.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct ArgAbi {
+    pub ty: Ty,
+    pub layout: Layout,
+    pub mode: PassMode,
+}
+
+/// How a function argument should be passed in to the target function.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum PassMode {
+    /// Ignore the argument.
+    ///
+    /// The argument is either uninhabited or a ZST.
+    Ignore,
+    /// Pass the argument directly.
+    ///
+    /// The argument has a layout abi of `Scalar` or `Vector`.
+    Direct(Opaque),
+    /// Pass a pair's elements directly in two arguments.
+    ///
+    /// The argument has a layout abi of `ScalarPair`.
+    Pair(Opaque, Opaque),
+    /// Pass the argument after casting it.
+    Cast { pad_i32: bool, cast: Opaque },
+    /// Pass the argument indirectly via a hidden pointer.
+    Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool },
+}
+
+/// The layout of a type, alongside the type itself.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct TyAndLayout {
+    pub ty: Ty,
+    pub layout: Layout,
+}
+
+/// The layout of a type in memory.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct LayoutShape {
+    /// The fields location within the layout
+    pub fields: FieldsShape,
+
+    /// Encodes information about multi-variant layouts.
+    /// Even with `Multiple` variants, a layout still has its own fields! Those are then
+    /// shared between all variants.
+    ///
+    /// To access all fields of this layout, both `fields` and the fields of the active variant
+    /// must be taken into account.
+    pub variants: VariantsShape,
+
+    /// The `abi` defines how this data is passed between functions.
+    pub abi: ValueAbi,
+
+    /// The ABI mandated alignment in bytes.
+    pub abi_align: Align,
+
+    /// The size of this layout in bytes.
+    pub size: Size,
+}
+
+impl LayoutShape {
+    /// Returns `true` if the layout corresponds to an unsized type.
+    #[inline]
+    pub fn is_unsized(&self) -> bool {
+        self.abi.is_unsized()
+    }
+
+    #[inline]
+    pub fn is_sized(&self) -> bool {
+        !self.abi.is_unsized()
+    }
+
+    /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1).
+    pub fn is_1zst(&self) -> bool {
+        self.is_sized() && self.size.bits() == 0 && self.abi_align == 1
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct Layout(usize);
+
+impl Layout {
+    pub fn shape(self) -> LayoutShape {
+        with(|cx| cx.layout_shape(self))
+    }
+}
+
+impl IndexedVal for Layout {
+    fn to_val(index: usize) -> Self {
+        Layout(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
+/// Describes how the fields of a type are shaped in memory.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum FieldsShape {
+    /// Scalar primitives and `!`, which never have fields.
+    Primitive,
+
+    /// All fields start at no offset. The `usize` is the field count.
+    Union(NonZero<usize>),
+
+    /// Array/vector-like placement, with all fields of identical types.
+    Array { stride: Size, count: u64 },
+
+    /// Struct-like placement, with precomputed offsets.
+    ///
+    /// Fields are guaranteed to not overlap, but note that gaps
+    /// before, between and after all the fields are NOT always
+    /// padding, and as such their contents may not be discarded.
+    /// For example, enum variants leave a gap at the start,
+    /// where the discriminant field in the enum layout goes.
+    Arbitrary {
+        /// Offsets for the first byte of each field,
+        /// ordered to match the source definition order.
+        /// I.e.: It follows the same order as [super::ty::VariantDef::fields()].
+        /// This vector does not go in increasing order.
+        offsets: Vec<Size>,
+    },
+}
+
+impl FieldsShape {
+    pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> {
+        match self {
+            FieldsShape::Primitive => vec![],
+            FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(),
+            FieldsShape::Arbitrary { offsets, .. } => {
+                let mut indices = (0..offsets.len()).collect::<Vec<_>>();
+                indices.sort_by_key(|idx| offsets[*idx]);
+                indices
+            }
+        }
+    }
+
+    pub fn count(&self) -> usize {
+        match self {
+            FieldsShape::Primitive => 0,
+            FieldsShape::Union(count) => count.get(),
+            FieldsShape::Array { count, .. } => *count as usize,
+            FieldsShape::Arbitrary { offsets, .. } => offsets.len(),
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum VariantsShape {
+    /// A type with no valid variants. Must be uninhabited.
+    Empty,
+
+    /// Single enum variants, structs/tuples, unions, and all non-ADTs.
+    Single { index: VariantIdx },
+
+    /// Enum-likes with more than one inhabited variant: each variant comes with
+    /// a *discriminant* (usually the same as the variant index but the user can
+    /// assign explicit discriminant values). That discriminant is encoded
+    /// as a *tag* on the machine. The layout of each variant is
+    /// a struct, and they all have space reserved for the tag.
+    /// For enums, the tag is the sole field of the layout.
+    Multiple {
+        tag: Scalar,
+        tag_encoding: TagEncoding,
+        tag_field: usize,
+        variants: Vec<LayoutShape>,
+    },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum TagEncoding {
+    /// The tag directly stores the discriminant, but possibly with a smaller layout
+    /// (so converting the tag to the discriminant can require sign extension).
+    Direct,
+
+    /// Niche (values invalid for a type) encoding the discriminant:
+    /// Discriminant and variant index coincide.
+    /// The variant `untagged_variant` contains a niche at an arbitrary
+    /// offset (field `tag_field` of the enum), which for a variant with
+    /// discriminant `d` is set to
+    /// `(d - niche_variants.start).wrapping_add(niche_start)`.
+    ///
+    /// For example, `Option<(usize, &T)>`  is represented such that
+    /// `None` has a null pointer for the second tuple field, and
+    /// `Some` is the identity function (with a non-null reference).
+    Niche {
+        untagged_variant: VariantIdx,
+        niche_variants: RangeInclusive<VariantIdx>,
+        niche_start: u128,
+    },
+}
+
+/// Describes how values of the type are passed by target ABIs,
+/// in terms of categories of C types there are ABI rules for.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum ValueAbi {
+    Scalar(Scalar),
+    ScalarPair(Scalar, Scalar),
+    Vector {
+        element: Scalar,
+        count: u64,
+    },
+    Aggregate {
+        /// If true, the size is exact, otherwise it's only a lower bound.
+        sized: bool,
+    },
+}
+
+impl ValueAbi {
+    /// Returns `true` if the layout corresponds to an unsized type.
+    pub fn is_unsized(&self) -> bool {
+        match *self {
+            ValueAbi::Scalar(_) | ValueAbi::ScalarPair(..) | ValueAbi::Vector { .. } => false,
+            ValueAbi::Aggregate { sized } => !sized,
+        }
+    }
+}
+
+/// Information about one scalar component of a Rust type.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)]
+pub enum Scalar {
+    Initialized {
+        /// The primitive type used to represent this value.
+        value: Primitive,
+        /// The range that represents valid values.
+        /// The range must be valid for the `primitive` size.
+        valid_range: WrappingRange,
+    },
+    Union {
+        /// Unions never have niches, so there is no `valid_range`.
+        /// Even for unions, we need to use the correct registers for the kind of
+        /// values inside the union, so we keep the `Primitive` type around.
+        /// It is also used to compute the size of the scalar.
+        value: Primitive,
+    },
+}
+
+impl Scalar {
+    pub fn has_niche(&self, target: &MachineInfo) -> bool {
+        match self {
+            Scalar::Initialized { value, valid_range } => {
+                !valid_range.is_full(value.size(target)).unwrap()
+            }
+            Scalar::Union { .. } => false,
+        }
+    }
+}
+
+/// Fundamental unit of memory access and layout.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)]
+pub enum Primitive {
+    /// The `bool` is the signedness of the `Integer` type.
+    ///
+    /// One would think we would not care about such details this low down,
+    /// but some ABIs are described in terms of C types and ISAs where the
+    /// integer arithmetic is done on {sign,zero}-extended registers, e.g.
+    /// a negative integer passed by zero-extension will appear positive in
+    /// the callee, and most operations on it will produce the wrong values.
+    Int {
+        length: IntegerLength,
+        signed: bool,
+    },
+    Float {
+        length: FloatLength,
+    },
+    Pointer(AddressSpace),
+}
+
+impl Primitive {
+    pub fn size(self, target: &MachineInfo) -> Size {
+        match self {
+            Primitive::Int { length, .. } => Size::from_bits(length.bits()),
+            Primitive::Float { length } => Size::from_bits(length.bits()),
+            Primitive::Pointer(_) => target.pointer_width,
+        }
+    }
+}
+
+/// Enum representing the existing integer lengths.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+pub enum IntegerLength {
+    I8,
+    I16,
+    I32,
+    I64,
+    I128,
+}
+
+/// Enum representing the existing float lengths.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+pub enum FloatLength {
+    F16,
+    F32,
+    F64,
+    F128,
+}
+
+impl IntegerLength {
+    pub fn bits(self) -> usize {
+        match self {
+            IntegerLength::I8 => 8,
+            IntegerLength::I16 => 16,
+            IntegerLength::I32 => 32,
+            IntegerLength::I64 => 64,
+            IntegerLength::I128 => 128,
+        }
+    }
+}
+
+impl FloatLength {
+    pub fn bits(self) -> usize {
+        match self {
+            FloatLength::F16 => 16,
+            FloatLength::F32 => 32,
+            FloatLength::F64 => 64,
+            FloatLength::F128 => 128,
+        }
+    }
+}
+
+/// An identifier that specifies the address space that some operation
+/// should operate on. Special address spaces have an effect on code generation,
+/// depending on the target and the address spaces it implements.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
+pub struct AddressSpace(pub u32);
+
+impl AddressSpace {
+    /// The default address space, corresponding to data space.
+    pub const DATA: Self = AddressSpace(0);
+}
+
+/// Inclusive wrap-around range of valid values (bitwise representation), that is, if
+/// start > end, it represents `start..=MAX`, followed by `0..=end`.
+///
+/// That is, for an i8 primitive, a range of `254..=2` means following
+/// sequence:
+///
+///    254 (-2), 255 (-1), 0, 1, 2
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)]
+pub struct WrappingRange {
+    pub start: u128,
+    pub end: u128,
+}
+
+impl WrappingRange {
+    /// Returns `true` if `size` completely fills the range.
+    #[inline]
+    pub fn is_full(&self, size: Size) -> Result<bool, Error> {
+        let Some(max_value) = size.unsigned_int_max() else {
+            return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits()));
+        };
+        if self.start <= max_value && self.end <= max_value {
+            Ok(self.start == (self.end.wrapping_add(1) & max_value))
+        } else {
+            Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits()))
+        }
+    }
+
+    /// Returns `true` if `v` is contained in the range.
+    #[inline(always)]
+    pub fn contains(&self, v: u128) -> bool {
+        if self.wraps_around() {
+            self.start <= v || v <= self.end
+        } else {
+            self.start <= v && v <= self.end
+        }
+    }
+
+    /// Returns `true` if the range wraps around.
+    /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`.
+    /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`.
+    #[inline]
+    pub fn wraps_around(&self) -> bool {
+        self.start > self.end
+    }
+}
+
+impl Debug for WrappingRange {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.start > self.end {
+            write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
+        } else {
+            write!(fmt, "{}..={}", self.start, self.end)?;
+        }
+        Ok(())
+    }
+}
+
+/// General language calling conventions.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum CallConvention {
+    C,
+    Rust,
+
+    Cold,
+    PreserveMost,
+    PreserveAll,
+
+    // Target-specific calling conventions.
+    ArmAapcs,
+    CCmseNonSecureCall,
+    CCmseNonSecureEntry,
+
+    Msp430Intr,
+
+    PtxKernel,
+
+    GpuKernel,
+
+    X86Fastcall,
+    X86Intr,
+    X86Stdcall,
+    X86ThisCall,
+    X86VectorCall,
+
+    X86_64SysV,
+    X86_64Win64,
+
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+
+    RiscvInterrupt,
+}
diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
new file mode 100644
index 00000000000..33d950bb951
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
@@ -0,0 +1,288 @@
+//! Define the interface with the Rust compiler.
+//!
+//! StableMIR users should not use any of the items in this module directly.
+//! These APIs have no stability guarantee.
+
+use std::cell::Cell;
+
+use stable_mir::abi::{FnAbi, Layout, LayoutShape};
+use stable_mir::crate_def::Attribute;
+use stable_mir::mir::alloc::{AllocId, GlobalAlloc};
+use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef};
+use stable_mir::mir::{BinOp, Body, Place, UnOp};
+use stable_mir::target::MachineInfo;
+use stable_mir::ty::{
+    AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef,
+    ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics,
+    ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl,
+    TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef,
+};
+use stable_mir::{
+    AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls,
+    ItemKind, Symbol, TraitDecls, mir,
+};
+
+use crate::stable_mir;
+
+/// This trait defines the interface between stable_mir and the Rust compiler.
+/// Do not use this directly.
+pub trait Context {
+    fn entry_fn(&self) -> Option<CrateItem>;
+    /// Retrieve all items of the local crate that have a MIR associated with them.
+    fn all_local_items(&self) -> CrateItems;
+    /// Retrieve the body of a function.
+    /// This function will panic if the body is not available.
+    fn mir_body(&self, item: DefId) -> mir::Body;
+    /// Check whether the body of a function is available.
+    fn has_body(&self, item: DefId) -> bool;
+    fn foreign_modules(&self, crate_num: CrateNum) -> Vec<ForeignModuleDef>;
+
+    /// Retrieve all functions defined in this crate.
+    fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef>;
+
+    /// Retrieve all static items defined in this crate.
+    fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef>;
+    fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule;
+    fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec<ForeignDef>;
+    fn all_trait_decls(&self) -> TraitDecls;
+    fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls;
+    fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl;
+    fn all_trait_impls(&self) -> ImplTraitDecls;
+    fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls;
+    fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait;
+    fn generics_of(&self, def_id: DefId) -> Generics;
+    fn predicates_of(&self, def_id: DefId) -> GenericPredicates;
+    fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates;
+    /// Get information about the local crate.
+    fn local_crate(&self) -> Crate;
+    /// Retrieve a list of all external crates.
+    fn external_crates(&self) -> Vec<Crate>;
+
+    /// Find a crate with the given name.
+    fn find_crates(&self, name: &str) -> Vec<Crate>;
+
+    /// Returns the name of given `DefId`
+    fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol;
+
+    /// Return registered tool attributes with the given attribute name.
+    ///
+    /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool
+    /// attributes will simply return an empty list.
+    ///
+    /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`.
+    /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
+    fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute>;
+
+    /// Get all tool attributes of a definition.
+    fn all_tool_attrs(&self, def_id: DefId) -> Vec<Attribute>;
+
+    /// Returns printable, human readable form of `Span`
+    fn span_to_string(&self, span: Span) -> String;
+
+    /// Return filename from given `Span`, for diagnostic purposes
+    fn get_filename(&self, span: &Span) -> Filename;
+
+    /// Return lines corresponding to this `Span`
+    fn get_lines(&self, span: &Span) -> LineInfo;
+
+    /// Returns the `kind` of given `DefId`
+    fn item_kind(&self, item: CrateItem) -> ItemKind;
+
+    /// Returns whether this is a foreign item.
+    fn is_foreign_item(&self, item: DefId) -> bool;
+
+    /// Returns the kind of a given foreign item.
+    fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind;
+
+    /// Returns the kind of a given algebraic data type
+    fn adt_kind(&self, def: AdtDef) -> AdtKind;
+
+    /// Returns if the ADT is a box.
+    fn adt_is_box(&self, def: AdtDef) -> bool;
+
+    /// Returns whether this ADT is simd.
+    fn adt_is_simd(&self, def: AdtDef) -> bool;
+
+    /// Returns whether this definition is a C string.
+    fn adt_is_cstr(&self, def: AdtDef) -> bool;
+
+    /// Retrieve the function signature for the given generic arguments.
+    fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig;
+
+    /// Retrieve the intrinsic definition if the item corresponds one.
+    fn intrinsic(&self, item: DefId) -> Option<IntrinsicDef>;
+
+    /// Retrieve the plain function name of an intrinsic.
+    fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol;
+
+    /// Retrieve the closure signature for the given generic arguments.
+    fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig;
+
+    /// The number of variants in this ADT.
+    fn adt_variants_len(&self, def: AdtDef) -> usize;
+
+    /// The name of a variant.
+    fn variant_name(&self, def: VariantDef) -> Symbol;
+    fn variant_fields(&self, def: VariantDef) -> Vec<FieldDef>;
+
+    /// Evaluate constant as a target usize.
+    fn eval_target_usize(&self, cnst: &MirConst) -> Result<u64, Error>;
+    fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result<u64, Error>;
+
+    /// Create a new zero-sized constant.
+    fn try_new_const_zst(&self, ty: Ty) -> Result<MirConst, Error>;
+
+    /// Create a new constant that represents the given string value.
+    fn new_const_str(&self, value: &str) -> MirConst;
+
+    /// Create a new constant that represents the given boolean value.
+    fn new_const_bool(&self, value: bool) -> MirConst;
+
+    /// Create a new constant that represents the given value.
+    fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<MirConst, Error>;
+    fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<TyConst, Error>;
+
+    /// Create a new type from the given kind.
+    fn new_rigid_ty(&self, kind: RigidTy) -> Ty;
+
+    /// Create a new box type, `Box<T>`, for the given inner type `T`.
+    fn new_box_ty(&self, ty: Ty) -> Ty;
+
+    /// Returns the type of given crate item.
+    fn def_ty(&self, item: DefId) -> Ty;
+
+    /// Returns the type of given definition instantiated with the given arguments.
+    fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty;
+
+    /// Returns literal value of a const as a string.
+    fn mir_const_pretty(&self, cnst: &MirConst) -> String;
+
+    /// `Span` of an item
+    fn span_of_an_item(&self, def_id: DefId) -> Span;
+
+    fn ty_const_pretty(&self, ct: TyConstId) -> String;
+
+    /// Obtain the representation of a type.
+    fn ty_pretty(&self, ty: Ty) -> String;
+
+    /// Obtain the representation of a type.
+    fn ty_kind(&self, ty: Ty) -> TyKind;
+
+    // Get the discriminant Ty for this Ty if there's one.
+    fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty;
+
+    /// Get the body of an Instance which is already monomorphized.
+    fn instance_body(&self, instance: InstanceDef) -> Option<Body>;
+
+    /// Get the instance type with generic instantiations applied and lifetimes erased.
+    fn instance_ty(&self, instance: InstanceDef) -> Ty;
+
+    /// Get the instantiation types.
+    fn instance_args(&self, def: InstanceDef) -> GenericArgs;
+
+    /// Get the instance.
+    fn instance_def_id(&self, instance: InstanceDef) -> DefId;
+
+    /// Get the instance mangled name.
+    fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol;
+
+    /// Check if this is an empty DropGlue shim.
+    fn is_empty_drop_shim(&self, def: InstanceDef) -> bool;
+
+    /// Check if this is an empty AsyncDropGlueCtor shim.
+    fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool;
+
+    /// Convert a non-generic crate item into an instance.
+    /// This function will panic if the item is generic.
+    fn mono_instance(&self, def_id: DefId) -> Instance;
+
+    /// Item requires monomorphization.
+    fn requires_monomorphization(&self, def_id: DefId) -> bool;
+
+    /// Resolve an instance from the given function definition and generic arguments.
+    fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option<Instance>;
+
+    /// Resolve an instance for drop_in_place for the given type.
+    fn resolve_drop_in_place(&self, ty: Ty) -> Instance;
+
+    /// Resolve instance for a function pointer.
+    fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option<Instance>;
+
+    /// Resolve instance for a closure with the requested type.
+    fn resolve_closure(
+        &self,
+        def: ClosureDef,
+        args: &GenericArgs,
+        kind: ClosureKind,
+    ) -> Option<Instance>;
+
+    /// Evaluate a static's initializer.
+    fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error>;
+
+    /// Try to evaluate an instance into a constant.
+    fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error>;
+
+    /// Retrieve global allocation for the given allocation ID.
+    fn global_alloc(&self, id: AllocId) -> GlobalAlloc;
+
+    /// Retrieve the id for the virtual table.
+    fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option<AllocId>;
+    fn krate(&self, def_id: DefId) -> Crate;
+    fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol;
+
+    /// Return information about the target machine.
+    fn target_info(&self) -> MachineInfo;
+
+    /// Get an instance ABI.
+    fn instance_abi(&self, def: InstanceDef) -> Result<FnAbi, Error>;
+
+    /// Get the ABI of a function pointer.
+    fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result<FnAbi, Error>;
+
+    /// Get the layout of a type.
+    fn ty_layout(&self, ty: Ty) -> Result<Layout, Error>;
+
+    /// Get the layout shape.
+    fn layout_shape(&self, id: Layout) -> LayoutShape;
+
+    /// Get a debug string representation of a place.
+    fn place_pretty(&self, place: &Place) -> String;
+
+    /// Get the resulting type of binary operation.
+    fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty;
+
+    /// Get the resulting type of unary operation.
+    fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty;
+
+    /// Get all associated items of a definition.
+    fn associated_items(&self, def_id: DefId) -> AssocItems;
+}
+
+// A thread local variable that stores a pointer to the tables mapping between TyCtxt
+// datastructures and stable MIR datastructures
+scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>);
+
+pub fn run<F, T>(context: &dyn Context, f: F) -> Result<T, Error>
+where
+    F: FnOnce() -> T,
+{
+    if TLV.is_set() {
+        Err(Error::from("StableMIR already running"))
+    } else {
+        let ptr: *const () = (&raw const context) as _;
+        TLV.set(&Cell::new(ptr), || Ok(f()))
+    }
+}
+
+/// Execute the given function with access the compiler [Context].
+///
+/// I.e., This function will load the current context and calls a function with it.
+/// Do not nest these, as that will ICE.
+pub(crate) fn with<R>(f: impl FnOnce(&dyn Context) -> R) -> R {
+    assert!(TLV.is_set());
+    TLV.with(|tlv| {
+        let ptr = tlv.get();
+        assert!(!ptr.is_null());
+        f(unsafe { *(ptr as *const &dyn Context) })
+    })
+}
diff --git a/compiler/rustc_smir/src/stable_mir/crate_def.rs b/compiler/rustc_smir/src/stable_mir/crate_def.rs
new file mode 100644
index 00000000000..64f7ef9b314
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/crate_def.rs
@@ -0,0 +1,175 @@
+//! Module that define a common trait for things that represent a crate definition,
+//! such as, a function, a trait, an enum, and any other definitions.
+
+use serde::Serialize;
+use stable_mir::ty::{GenericArgs, Span, Ty};
+use stable_mir::{AssocItems, Crate, Symbol, with};
+
+use crate::stable_mir;
+
+/// A unique identification number for each item accessible for the current compilation unit.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)]
+pub struct DefId(pub(crate) usize);
+
+impl DefId {
+    /// Return fully qualified name of this definition
+    pub fn name(&self) -> Symbol {
+        with(|cx| cx.def_name(*self, false))
+    }
+
+    /// Return a trimmed name of this definition.
+    ///
+    /// This can be used to print more user friendly diagnostic messages.
+    ///
+    /// If a symbol name can only be imported from one place for a type, and as
+    /// long as it was not glob-imported anywhere in the current crate, we trim its
+    /// path and print only the name.
+    ///
+    /// For example, this function may shorten `std::vec::Vec` to just `Vec`,
+    /// as long as there is no other `Vec` importable anywhere.
+    pub fn trimmed_name(&self) -> Symbol {
+        with(|cx| cx.def_name(*self, true))
+    }
+}
+
+/// A trait for retrieving information about a particular definition.
+///
+/// Implementors must provide the implementation of `def_id` which will be used to retrieve
+/// information about a crate's definition.
+pub trait CrateDef {
+    /// Retrieve the unique identifier for the current definition.
+    fn def_id(&self) -> DefId;
+
+    /// Return the fully qualified name of the current definition.
+    ///
+    /// See [`DefId::name`] for more details
+    fn name(&self) -> Symbol {
+        self.def_id().name()
+    }
+
+    /// Return a trimmed name of this definition.
+    ///
+    /// See [`DefId::trimmed_name`] for more details
+    fn trimmed_name(&self) -> Symbol {
+        self.def_id().trimmed_name()
+    }
+
+    /// Return information about the crate where this definition is declared.
+    ///
+    /// This will return the crate number and its name.
+    fn krate(&self) -> Crate {
+        let def_id = self.def_id();
+        with(|cx| cx.krate(def_id))
+    }
+
+    /// Return the span of this definition.
+    fn span(&self) -> Span {
+        let def_id = self.def_id();
+        with(|cx| cx.span_of_an_item(def_id))
+    }
+
+    /// Return registered tool attributes with the given attribute name.
+    ///
+    /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool
+    /// attributes will simply return an empty list.
+    ///
+    /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`.
+    /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
+    fn tool_attrs(&self, attr: &[Symbol]) -> Vec<Attribute> {
+        let def_id = self.def_id();
+        with(|cx| cx.tool_attrs(def_id, attr))
+    }
+
+    /// Return all tool attributes of this definition.
+    fn all_tool_attrs(&self) -> Vec<Attribute> {
+        let def_id = self.def_id();
+        with(|cx| cx.all_tool_attrs(def_id))
+    }
+}
+
+/// A trait that can be used to retrieve a definition's type.
+///
+/// Note that not every CrateDef has a type `Ty`. They should not implement this trait.
+pub trait CrateDefType: CrateDef {
+    /// Returns the type of this crate item.
+    fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.def_id()))
+    }
+
+    /// Retrieve the type of this definition by instantiating and normalizing it with `args`.
+    ///
+    /// This will panic if instantiation fails.
+    fn ty_with_args(&self, args: &GenericArgs) -> Ty {
+        with(|cx| cx.def_ty_with_args(self.def_id(), args))
+    }
+}
+
+/// A trait for retrieving all items from a definition within a crate.
+pub trait CrateDefItems: CrateDef {
+    /// Retrieve all associated items from a definition.
+    fn associated_items(&self) -> AssocItems {
+        with(|cx| cx.associated_items(self.def_id()))
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Attribute {
+    value: String,
+    span: Span,
+}
+
+impl Attribute {
+    pub fn new(value: String, span: Span) -> Attribute {
+        Attribute { value, span }
+    }
+
+    /// Get the span of this attribute.
+    pub fn span(&self) -> Span {
+        self.span
+    }
+
+    /// Get the string representation of this attribute.
+    pub fn as_str(&self) -> &str {
+        &self.value
+    }
+}
+
+macro_rules! crate_def {
+    ( $(#[$attr:meta])*
+      $vis:vis $name:ident $(;)?
+    ) => {
+        $(#[$attr])*
+        #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+        $vis struct $name(pub DefId);
+
+        impl CrateDef for $name {
+            fn def_id(&self) -> DefId {
+                self.0
+            }
+        }
+    };
+}
+
+macro_rules! crate_def_with_ty {
+    ( $(#[$attr:meta])*
+      $vis:vis $name:ident $(;)?
+    ) => {
+        $(#[$attr])*
+        #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+        $vis struct $name(pub DefId);
+
+        impl CrateDef for $name {
+            fn def_id(&self) -> DefId {
+                self.0
+            }
+        }
+
+        impl CrateDefType for $name {}
+    };
+}
+
+macro_rules! impl_crate_def_items {
+    ( $name:ident $(;)? ) => {
+        impl CrateDefItems for $name {}
+    };
+}
diff --git a/compiler/rustc_smir/src/stable_mir/error.rs b/compiler/rustc_smir/src/stable_mir/error.rs
new file mode 100644
index 00000000000..050752e41eb
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/error.rs
@@ -0,0 +1,85 @@
+//! When things go wrong, we need some error handling.
+//! There are a few different types of errors in StableMIR:
+//!
+//! - [CompilerError]: This represents errors that can be raised when invoking the compiler.
+//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled.
+
+use std::fmt::{Debug, Display, Formatter};
+use std::{fmt, io};
+
+macro_rules! error {
+     ($fmt: literal $(,)?) => { Error(format!($fmt)) };
+     ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) };
+}
+
+pub(crate) use error;
+
+/// An error type used to represent an error that has already been reported by the compiler.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum CompilerError<T> {
+    /// Compilation failed, either due to normal errors or ICE.
+    Failed,
+    /// Compilation was interrupted.
+    Interrupted(T),
+    /// Compilation skipped. This happens when users invoke rustc to retrieve information such as
+    /// --version.
+    Skipped,
+}
+
+/// A generic error to represent an API request that cannot be fulfilled.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Error(pub(crate) String);
+
+impl Error {
+    pub fn new(msg: String) -> Self {
+        Self(msg)
+    }
+}
+
+impl From<&str> for Error {
+    fn from(value: &str) -> Self {
+        Self(value.into())
+    }
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        Display::fmt(&self.0, f)
+    }
+}
+
+impl<T> Display for CompilerError<T>
+where
+    T: Display,
+{
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            CompilerError::Failed => write!(f, "Compilation Failed"),
+            CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"),
+            CompilerError::Skipped => write!(f, "Compilation Skipped"),
+        }
+    }
+}
+
+impl<T> Debug for CompilerError<T>
+where
+    T: Debug,
+{
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            CompilerError::Failed => write!(f, "Compilation Failed"),
+            CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"),
+            CompilerError::Skipped => write!(f, "Compilation Skipped"),
+        }
+    }
+}
+
+impl std::error::Error for Error {}
+
+impl<T> std::error::Error for CompilerError<T> where T: Display + Debug {}
+
+impl From<io::Error> for Error {
+    fn from(value: io::Error) -> Self {
+        Error(value.to_string())
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mir.rs b/compiler/rustc_smir/src/stable_mir/mir.rs
new file mode 100644
index 00000000000..413b5152bb3
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mir.rs
@@ -0,0 +1,8 @@
+pub mod alloc;
+mod body;
+pub mod mono;
+pub mod pretty;
+pub mod visit;
+
+pub use body::*;
+pub use visit::{MirVisitor, MutMirVisitor};
diff --git a/compiler/rustc_smir/src/stable_mir/mir/alloc.rs b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
new file mode 100644
index 00000000000..782f52888b7
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
@@ -0,0 +1,88 @@
+//! This module provides methods to retrieve allocation information, such as static variables.
+
+use std::io::Read;
+
+use serde::Serialize;
+use stable_mir::mir::mono::{Instance, StaticDef};
+use stable_mir::target::{Endian, MachineInfo};
+use stable_mir::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty};
+use stable_mir::{Error, with};
+
+use crate::stable_mir;
+
+/// An allocation in the SMIR global memory can be either a function pointer,
+/// a static, or a "real" allocation with some data in it.
+#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
+pub enum GlobalAlloc {
+    /// The alloc ID is used as a function pointer.
+    Function(Instance),
+    /// This alloc ID points to a symbolic (not-reified) vtable.
+    /// The `None` trait ref is used to represent auto traits.
+    VTable(Ty, Option<Binder<ExistentialTraitRef>>),
+    /// The alloc ID points to a "lazy" static variable that did not get computed (yet).
+    /// This is also used to break the cycle in recursive statics.
+    Static(StaticDef),
+    /// The alloc ID points to memory.
+    Memory(Allocation),
+}
+
+impl From<AllocId> for GlobalAlloc {
+    fn from(value: AllocId) -> Self {
+        with(|cx| cx.global_alloc(value))
+    }
+}
+
+impl GlobalAlloc {
+    /// Retrieve the allocation id for a global allocation if it exists.
+    ///
+    /// For `[GlobalAlloc::VTable]`, this will return the allocation for the VTable of the given
+    /// type for the optional trait if the type implements the trait.
+    ///
+    /// This method will always return `None` for allocations other than `[GlobalAlloc::VTable]`.
+    pub fn vtable_allocation(&self) -> Option<AllocId> {
+        with(|cx| cx.vtable_allocation(self))
+    }
+}
+
+/// A unique identification number for each provenance
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)]
+pub struct AllocId(usize);
+
+impl IndexedVal for AllocId {
+    fn to_val(index: usize) -> Self {
+        AllocId(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
+/// Utility function used to read an allocation data into a unassigned integer.
+pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result<u128, Error> {
+    let mut buf = [0u8; size_of::<u128>()];
+    match MachineInfo::target_endianness() {
+        Endian::Little => {
+            bytes.read_exact(&mut buf[..bytes.len()])?;
+            Ok(u128::from_le_bytes(buf))
+        }
+        Endian::Big => {
+            bytes.read_exact(&mut buf[16 - bytes.len()..])?;
+            Ok(u128::from_be_bytes(buf))
+        }
+    }
+}
+
+/// Utility function used to read an allocation data into an assigned integer.
+pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result<i128, Error> {
+    let mut buf = [0u8; size_of::<i128>()];
+    match MachineInfo::target_endianness() {
+        Endian::Little => {
+            bytes.read_exact(&mut buf[..bytes.len()])?;
+            Ok(i128::from_le_bytes(buf))
+        }
+        Endian::Big => {
+            bytes.read_exact(&mut buf[16 - bytes.len()..])?;
+            Ok(i128::from_be_bytes(buf))
+        }
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
new file mode 100644
index 00000000000..b2360545424
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -0,0 +1,1109 @@
+use std::io;
+
+use serde::Serialize;
+use stable_mir::compiler_interface::with;
+use stable_mir::mir::pretty::function_body;
+use stable_mir::ty::{
+    AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability,
+    Region, RigidTy, Ty, TyConst, TyKind, VariantIdx,
+};
+use stable_mir::{Error, Opaque, Span, Symbol};
+
+use crate::stable_mir;
+
+/// The SMIR representation of a single function.
+#[derive(Clone, Debug, Serialize)]
+pub struct Body {
+    pub blocks: Vec<BasicBlock>,
+
+    /// Declarations of locals within the function.
+    ///
+    /// The first local is the return value pointer, followed by `arg_count`
+    /// locals for the function arguments, followed by any user-declared
+    /// variables and temporaries.
+    pub(super) locals: LocalDecls,
+
+    /// The number of arguments this function takes.
+    pub(super) arg_count: usize,
+
+    /// Debug information pertaining to user variables, including captures.
+    pub var_debug_info: Vec<VarDebugInfo>,
+
+    /// Mark an argument (which must be a tuple) as getting passed as its individual components.
+    ///
+    /// This is used for the "rust-call" ABI such as closures.
+    pub(super) spread_arg: Option<Local>,
+
+    /// The span that covers the entire function body.
+    pub span: Span,
+}
+
+pub type BasicBlockIdx = usize;
+
+impl Body {
+    /// Constructs a `Body`.
+    ///
+    /// A constructor is required to build a `Body` from outside the crate
+    /// because the `arg_count` and `locals` fields are private.
+    pub fn new(
+        blocks: Vec<BasicBlock>,
+        locals: LocalDecls,
+        arg_count: usize,
+        var_debug_info: Vec<VarDebugInfo>,
+        spread_arg: Option<Local>,
+        span: Span,
+    ) -> Self {
+        // If locals doesn't contain enough entries, it can lead to panics in
+        // `ret_local`, `arg_locals`, and `inner_locals`.
+        assert!(
+            locals.len() > arg_count,
+            "A Body must contain at least a local for the return value and each of the function's arguments"
+        );
+        Self { blocks, locals, arg_count, var_debug_info, spread_arg, span }
+    }
+
+    /// Return local that holds this function's return value.
+    pub fn ret_local(&self) -> &LocalDecl {
+        &self.locals[RETURN_LOCAL]
+    }
+
+    /// Locals in `self` that correspond to this function's arguments.
+    pub fn arg_locals(&self) -> &[LocalDecl] {
+        &self.locals[1..][..self.arg_count]
+    }
+
+    /// Inner locals for this function. These are the locals that are
+    /// neither the return local nor the argument locals.
+    pub fn inner_locals(&self) -> &[LocalDecl] {
+        &self.locals[self.arg_count + 1..]
+    }
+
+    /// Returns a mutable reference to the local that holds this function's return value.
+    pub(crate) fn ret_local_mut(&mut self) -> &mut LocalDecl {
+        &mut self.locals[RETURN_LOCAL]
+    }
+
+    /// Returns a mutable slice of locals corresponding to this function's arguments.
+    pub(crate) fn arg_locals_mut(&mut self) -> &mut [LocalDecl] {
+        &mut self.locals[1..][..self.arg_count]
+    }
+
+    /// Returns a mutable slice of inner locals for this function.
+    /// Inner locals are those that are neither the return local nor the argument locals.
+    pub(crate) fn inner_locals_mut(&mut self) -> &mut [LocalDecl] {
+        &mut self.locals[self.arg_count + 1..]
+    }
+
+    /// Convenience function to get all the locals in this function.
+    ///
+    /// Locals are typically accessed via the more specific methods `ret_local`,
+    /// `arg_locals`, and `inner_locals`.
+    pub fn locals(&self) -> &[LocalDecl] {
+        &self.locals
+    }
+
+    /// Get the local declaration for this local.
+    pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> {
+        self.locals.get(local)
+    }
+
+    /// Get an iterator for all local declarations.
+    pub fn local_decls(&self) -> impl Iterator<Item = (Local, &LocalDecl)> {
+        self.locals.iter().enumerate()
+    }
+
+    /// Emit the body using the provided name for the signature.
+    pub fn dump<W: io::Write>(&self, w: &mut W, fn_name: &str) -> io::Result<()> {
+        function_body(w, self, fn_name)
+    }
+
+    pub fn spread_arg(&self) -> Option<Local> {
+        self.spread_arg
+    }
+}
+
+type LocalDecls = Vec<LocalDecl>;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct LocalDecl {
+    pub ty: Ty,
+    pub span: Span,
+    pub mutability: Mutability,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
+pub struct BasicBlock {
+    pub statements: Vec<Statement>,
+    pub terminator: Terminator,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct Terminator {
+    pub kind: TerminatorKind,
+    pub span: Span,
+}
+
+impl Terminator {
+    pub fn successors(&self) -> Successors {
+        self.kind.successors()
+    }
+}
+
+pub type Successors = Vec<BasicBlockIdx>;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum TerminatorKind {
+    Goto {
+        target: BasicBlockIdx,
+    },
+    SwitchInt {
+        discr: Operand,
+        targets: SwitchTargets,
+    },
+    Resume,
+    Abort,
+    Return,
+    Unreachable,
+    Drop {
+        place: Place,
+        target: BasicBlockIdx,
+        unwind: UnwindAction,
+    },
+    Call {
+        func: Operand,
+        args: Vec<Operand>,
+        destination: Place,
+        target: Option<BasicBlockIdx>,
+        unwind: UnwindAction,
+    },
+    Assert {
+        cond: Operand,
+        expected: bool,
+        msg: AssertMessage,
+        target: BasicBlockIdx,
+        unwind: UnwindAction,
+    },
+    InlineAsm {
+        template: String,
+        operands: Vec<InlineAsmOperand>,
+        options: String,
+        line_spans: String,
+        destination: Option<BasicBlockIdx>,
+        unwind: UnwindAction,
+    },
+}
+
+impl TerminatorKind {
+    pub fn successors(&self) -> Successors {
+        use self::TerminatorKind::*;
+        match *self {
+            Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. }
+            | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. }
+            | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. }
+            | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => {
+                vec![t, u]
+            }
+            Goto { target: t }
+            | Call { target: None, unwind: UnwindAction::Cleanup(t), .. }
+            | Call { target: Some(t), unwind: _, .. }
+            | Drop { target: t, unwind: _, .. }
+            | Assert { target: t, unwind: _, .. }
+            | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. }
+            | InlineAsm { destination: Some(t), unwind: _, .. } => {
+                vec![t]
+            }
+
+            Return
+            | Resume
+            | Abort
+            | Unreachable
+            | Call { target: None, unwind: _, .. }
+            | InlineAsm { destination: None, unwind: _, .. } => {
+                vec![]
+            }
+            SwitchInt { ref targets, .. } => targets.all_targets(),
+        }
+    }
+
+    pub fn unwind(&self) -> Option<&UnwindAction> {
+        match *self {
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::Return
+            | TerminatorKind::Unreachable
+            | TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::SwitchInt { .. } => None,
+            TerminatorKind::Call { ref unwind, .. }
+            | TerminatorKind::Assert { ref unwind, .. }
+            | TerminatorKind::Drop { ref unwind, .. }
+            | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct InlineAsmOperand {
+    pub in_value: Option<Operand>,
+    pub out_place: Option<Place>,
+    // This field has a raw debug representation of MIR's InlineAsmOperand.
+    // For now we care about place/operand + the rest in a debug format.
+    pub raw_rpr: String,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum UnwindAction {
+    Continue,
+    Unreachable,
+    Terminate,
+    Cleanup(BasicBlockIdx),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AssertMessage {
+    BoundsCheck { len: Operand, index: Operand },
+    Overflow(BinOp, Operand, Operand),
+    OverflowNeg(Operand),
+    DivisionByZero(Operand),
+    RemainderByZero(Operand),
+    ResumedAfterReturn(CoroutineKind),
+    ResumedAfterPanic(CoroutineKind),
+    MisalignedPointerDereference { required: Operand, found: Operand },
+    NullPointerDereference,
+}
+
+impl AssertMessage {
+    pub fn description(&self) -> Result<&'static str, Error> {
+        match self {
+            AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"),
+            AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"),
+            AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"),
+            AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"),
+            AssertMessage::Overflow(BinOp::Rem, _, _) => {
+                Ok("attempt to calculate the remainder with overflow")
+            }
+            AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"),
+            AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"),
+            AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"),
+            AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)),
+            AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"),
+            AssertMessage::RemainderByZero(_) => {
+                Ok("attempt to calculate the remainder with a divisor of zero")
+            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
+                Ok("coroutine resumed after completion")
+            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                _,
+            )) => Ok("`async fn` resumed after completion"),
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
+                CoroutineDesugaring::Gen,
+                _,
+            )) => Ok("`async gen fn` resumed after completion"),
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
+                CoroutineDesugaring::AsyncGen,
+                _,
+            )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"),
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
+                Ok("coroutine resumed after panicking")
+            }
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                _,
+            )) => Ok("`async fn` resumed after panicking"),
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
+                CoroutineDesugaring::Gen,
+                _,
+            )) => Ok("`async gen fn` resumed after panicking"),
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
+                CoroutineDesugaring::AsyncGen,
+                _,
+            )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"),
+
+            AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"),
+            AssertMessage::MisalignedPointerDereference { .. } => {
+                Ok("misaligned pointer dereference")
+            }
+            AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"),
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum BinOp {
+    Add,
+    AddUnchecked,
+    Sub,
+    SubUnchecked,
+    Mul,
+    MulUnchecked,
+    Div,
+    Rem,
+    BitXor,
+    BitAnd,
+    BitOr,
+    Shl,
+    ShlUnchecked,
+    Shr,
+    ShrUnchecked,
+    Eq,
+    Lt,
+    Le,
+    Ne,
+    Ge,
+    Gt,
+    Cmp,
+    Offset,
+}
+
+impl BinOp {
+    /// Return the type of this operation for the given input Ty.
+    /// This function does not perform type checking, and it currently doesn't handle SIMD.
+    pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
+        with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty))
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum UnOp {
+    Not,
+    Neg,
+    PtrMetadata,
+}
+
+impl UnOp {
+    /// Return the type of this operation for the given input Ty.
+    /// This function does not perform type checking, and it currently doesn't handle SIMD.
+    pub fn ty(&self, arg_ty: Ty) -> Ty {
+        with(|ctx| ctx.unop_ty(*self, arg_ty))
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum CoroutineKind {
+    Desugared(CoroutineDesugaring, CoroutineSource),
+    Coroutine(Movability),
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum CoroutineSource {
+    Block,
+    Closure,
+    Fn,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum CoroutineDesugaring {
+    Async,
+
+    Gen,
+
+    AsyncGen,
+}
+
+pub(crate) type LocalDefId = Opaque;
+/// The rustc coverage data structures are heavily tied to internal details of the
+/// coverage implementation that are likely to change, and are unlikely to be
+/// useful to third-party tools for the foreseeable future.
+pub(crate) type Coverage = Opaque;
+
+/// The FakeReadCause describes the type of pattern why a FakeRead statement exists.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum FakeReadCause {
+    ForMatchGuard,
+    ForMatchedPlace(LocalDefId),
+    ForGuardBinding,
+    ForLet(LocalDefId),
+    ForIndex,
+}
+
+/// Describes what kind of retag is to be performed
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)]
+pub enum RetagKind {
+    FnEntry,
+    TwoPhase,
+    Raw,
+    Default,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)]
+pub enum Variance {
+    Covariant,
+    Invariant,
+    Contravariant,
+    Bivariant,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct CopyNonOverlapping {
+    pub src: Operand,
+    pub dst: Operand,
+    pub count: Operand,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum NonDivergingIntrinsic {
+    Assume(Operand),
+    CopyNonOverlapping(CopyNonOverlapping),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct Statement {
+    pub kind: StatementKind,
+    pub span: Span,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum StatementKind {
+    Assign(Place, Rvalue),
+    FakeRead(FakeReadCause, Place),
+    SetDiscriminant { place: Place, variant_index: VariantIdx },
+    Deinit(Place),
+    StorageLive(Local),
+    StorageDead(Local),
+    Retag(RetagKind, Place),
+    PlaceMention(Place),
+    AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance },
+    Coverage(Coverage),
+    Intrinsic(NonDivergingIntrinsic),
+    ConstEvalCounter,
+    Nop,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum Rvalue {
+    /// Creates a pointer with the indicated mutability to the place.
+    ///
+    /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
+    /// `&raw v` or `addr_of!(v)`.
+    AddressOf(RawPtrKind, Place),
+
+    /// Creates an aggregate value, like a tuple or struct.
+    ///
+    /// This is needed because dataflow analysis needs to distinguish
+    /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
+    /// has a destructor.
+    ///
+    /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After
+    /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too.
+    Aggregate(AggregateKind, Vec<Operand>),
+
+    /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second
+    ///   parameter may be a `usize` as well.
+    /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
+    ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be
+    ///   matching, up to the usual caveat of the lifetimes in function pointers.
+    /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
+    ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is
+    ///   truncated as needed.
+    /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
+    ///   types and return a value of that type.
+    /// * The remaining operations accept signed integers, unsigned integers, or floats with
+    ///   matching types and return a value of that type.
+    BinaryOp(BinOp, Operand, Operand),
+
+    /// Performs essentially all of the casts that can be performed via `as`.
+    ///
+    /// This allows for casts from/to a variety of types.
+    Cast(CastKind, Operand, Ty),
+
+    /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
+    ///
+    /// For addition, subtraction, and multiplication on integers the error condition is set when
+    /// the infinite precision result would not be equal to the actual result.
+    CheckedBinaryOp(BinOp, Operand, Operand),
+
+    /// A CopyForDeref is equivalent to a read from a place.
+    /// When such a read happens, it is guaranteed that the only use of the returned value is a
+    /// deref operation, immediately followed by one or more projections.
+    CopyForDeref(Place),
+
+    /// Computes the discriminant of the place, returning it as an integer.
+    /// Returns zero for types without discriminant.
+    ///
+    /// The validity requirements for the underlying value are undecided for this rvalue, see
+    /// [#91095]. Note too that the value of the discriminant is not the same thing as the
+    /// variant index;
+    ///
+    /// [#91095]: https://github.com/rust-lang/rust/issues/91095
+    Discriminant(Place),
+
+    /// Yields the length of the place, as a `usize`.
+    ///
+    /// If the type of the place is an array, this is the array length. For slices (`[T]`, not
+    /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
+    /// ill-formed for places of other types.
+    Len(Place),
+
+    /// Creates a reference to the place.
+    Ref(Region, BorrowKind, Place),
+
+    /// Creates an array where each element is the value of the operand.
+    ///
+    /// This is the cause of a bug in the case where the repetition count is zero because the value
+    /// is not dropped, see [#74836].
+    ///
+    /// Corresponds to source code like `[x; 32]`.
+    ///
+    /// [#74836]: https://github.com/rust-lang/rust/issues/74836
+    Repeat(Operand, TyConst),
+
+    /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
+    ///
+    /// This is different from a normal transmute because dataflow analysis will treat the box as
+    /// initialized but its content as uninitialized. Like other pointer casts, this in general
+    /// affects alias analysis.
+    ShallowInitBox(Operand, Ty),
+
+    /// Creates a pointer/reference to the given thread local.
+    ///
+    /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
+    /// `*const T`, and if neither of those apply a `&T`.
+    ///
+    /// **Note:** This is a runtime operation that actually executes code and is in this sense more
+    /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
+    /// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
+    ///
+    /// **Needs clarification**: Are there weird additional semantics here related to the runtime
+    /// nature of this operation?
+    ThreadLocalRef(stable_mir::CrateItem),
+
+    /// Computes a value as described by the operation.
+    NullaryOp(NullOp, Ty),
+
+    /// Exactly like `BinaryOp`, but less operands.
+    ///
+    /// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
+    /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
+    /// return a value with the same type as their operand.
+    UnaryOp(UnOp, Operand),
+
+    /// Yields the operand unchanged
+    Use(Operand),
+}
+
+impl Rvalue {
+    pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
+        match self {
+            Rvalue::Use(operand) => operand.ty(locals),
+            Rvalue::Repeat(operand, count) => {
+                Ok(Ty::new_array_with_const_len(operand.ty(locals)?, count.clone()))
+            }
+            Rvalue::ThreadLocalRef(did) => Ok(did.ty()),
+            Rvalue::Ref(reg, bk, place) => {
+                let place_ty = place.ty(locals)?;
+                Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy()))
+            }
+            Rvalue::AddressOf(mutability, place) => {
+                let place_ty = place.ty(locals)?;
+                Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy()))
+            }
+            Rvalue::Len(..) => Ok(Ty::usize_ty()),
+            Rvalue::Cast(.., ty) => Ok(*ty),
+            Rvalue::BinaryOp(op, lhs, rhs) => {
+                let lhs_ty = lhs.ty(locals)?;
+                let rhs_ty = rhs.ty(locals)?;
+                Ok(op.ty(lhs_ty, rhs_ty))
+            }
+            Rvalue::CheckedBinaryOp(op, lhs, rhs) => {
+                let lhs_ty = lhs.ty(locals)?;
+                let rhs_ty = rhs.ty(locals)?;
+                let ty = op.ty(lhs_ty, rhs_ty);
+                Ok(Ty::new_tuple(&[ty, Ty::bool_ty()]))
+            }
+            Rvalue::UnaryOp(op, operand) => {
+                let arg_ty = operand.ty(locals)?;
+                Ok(op.ty(arg_ty))
+            }
+            Rvalue::Discriminant(place) => {
+                let place_ty = place.ty(locals)?;
+                place_ty
+                    .kind()
+                    .discriminant_ty()
+                    .ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}"))
+            }
+            Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
+                Ok(Ty::usize_ty())
+            }
+            Rvalue::NullaryOp(NullOp::ContractChecks, _)
+            | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
+            Rvalue::Aggregate(ak, ops) => match *ak {
+                AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
+                AggregateKind::Tuple => Ok(Ty::new_tuple(
+                    &ops.iter().map(|op| op.ty(locals)).collect::<Result<Vec<_>, _>>()?,
+                )),
+                AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)),
+                AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())),
+                AggregateKind::Coroutine(def, ref args, mov) => {
+                    Ok(Ty::new_coroutine(def, args.clone(), mov))
+                }
+                AggregateKind::CoroutineClosure(def, ref args) => {
+                    Ok(Ty::new_coroutine_closure(def, args.clone()))
+                }
+                AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)),
+            },
+            Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)),
+            Rvalue::CopyForDeref(place) => place.ty(locals),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AggregateKind {
+    Array(Ty),
+    Tuple,
+    Adt(AdtDef, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>),
+    Closure(ClosureDef, GenericArgs),
+    // FIXME(stable_mir): Movability here is redundant
+    Coroutine(CoroutineDef, GenericArgs, Movability),
+    CoroutineClosure(CoroutineClosureDef, GenericArgs),
+    RawPtr(Ty, Mutability),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum Operand {
+    Copy(Place),
+    Move(Place),
+    Constant(ConstOperand),
+}
+
+#[derive(Clone, Eq, PartialEq, Serialize)]
+pub struct Place {
+    pub local: Local,
+    /// projection out of a place (access a field, deref a pointer, etc)
+    pub projection: Vec<ProjectionElem>,
+}
+
+impl From<Local> for Place {
+    fn from(local: Local) -> Self {
+        Place { local, projection: vec![] }
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct ConstOperand {
+    pub span: Span,
+    pub user_ty: Option<UserTypeAnnotationIndex>,
+    pub const_: MirConst,
+}
+
+/// Debug information pertaining to a user variable.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct VarDebugInfo {
+    /// The variable name.
+    pub name: Symbol,
+
+    /// Source info of the user variable, including the scope
+    /// within which the variable is visible (to debuginfo).
+    pub source_info: SourceInfo,
+
+    /// The user variable's data is split across several fragments,
+    /// each described by a `VarDebugInfoFragment`.
+    pub composite: Option<VarDebugInfoFragment>,
+
+    /// Where the data for this user variable is to be found.
+    pub value: VarDebugInfoContents,
+
+    /// When present, indicates what argument number this variable is in the function that it
+    /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the
+    /// argument number in the original function before it was inlined.
+    pub argument_index: Option<u16>,
+}
+
+impl VarDebugInfo {
+    /// Return a local variable if this info is related to one.
+    pub fn local(&self) -> Option<Local> {
+        match &self.value {
+            VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local),
+            VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None,
+        }
+    }
+
+    /// Return a constant if this info is related to one.
+    pub fn constant(&self) -> Option<&ConstOperand> {
+        match &self.value {
+            VarDebugInfoContents::Place(_) => None,
+            VarDebugInfoContents::Const(const_op) => Some(const_op),
+        }
+    }
+}
+
+pub type SourceScope = u32;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct SourceInfo {
+    pub span: Span,
+    pub scope: SourceScope,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct VarDebugInfoFragment {
+    pub ty: Ty,
+    pub projection: Vec<ProjectionElem>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum VarDebugInfoContents {
+    Place(Place),
+    Const(ConstOperand),
+}
+
+// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This
+// is so it can be used for both Places (for which the projection elements are of type
+// ProjectionElem<Local, Ty>) and user-provided type annotations (for which the projection elements
+// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use
+// ProjectionElem for Places.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum ProjectionElem {
+    /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place.
+    Deref,
+
+    /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is
+    /// referenced by source-order index rather than the name of the field. The fields type is also
+    /// given.
+    Field(FieldIdx, Ty),
+
+    /// Index into a slice/array. The value of the index is computed at runtime using the `V`
+    /// argument.
+    ///
+    /// Note that this does not also dereference, and so it does not exactly correspond to slice
+    /// indexing in Rust. In other words, in the below Rust code:
+    ///
+    /// ```rust
+    /// let x = &[1, 2, 3, 4];
+    /// let i = 2;
+    /// x[i];
+    /// ```
+    ///
+    /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same
+    /// thing is true of the `ConstantIndex` and `Subslice` projections below.
+    Index(Local),
+
+    /// Index into a slice/array given by offsets.
+    ///
+    /// These indices are generated by slice patterns. Easiest to explain by example:
+    ///
+    /// ```ignore (illustrative)
+    /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
+    /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
+    /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
+    /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
+    /// ```
+    ConstantIndex {
+        /// index or -index (in Python terms), depending on from_end
+        offset: u64,
+        /// The thing being indexed must be at least this long -- otherwise, the
+        /// projection is UB.
+        ///
+        /// For arrays this is always the exact length.
+        min_length: u64,
+        /// Counting backwards from end? This is always false when indexing an
+        /// array.
+        from_end: bool,
+    },
+
+    /// Projects a slice from the base place.
+    ///
+    /// These indices are generated by slice patterns. If `from_end` is true, this represents
+    /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`.
+    Subslice {
+        from: u64,
+        to: u64,
+        /// Whether `to` counts from the start or end of the array/slice.
+        from_end: bool,
+    },
+
+    /// "Downcast" to a variant of an enum or a coroutine.
+    Downcast(VariantIdx),
+
+    /// Like an explicit cast from an opaque type to a concrete type, but without
+    /// requiring an intermediate variable.
+    OpaqueCast(Ty),
+
+    /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
+    /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
+    /// explicit during optimizations and codegen.
+    ///
+    /// This projection doesn't impact the runtime behavior of the program except for potentially changing
+    /// some type metadata of the interpreter or codegen backend.
+    Subtype(Ty),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct UserTypeProjection {
+    pub base: UserTypeAnnotationIndex,
+
+    pub projection: Opaque,
+}
+
+pub type Local = usize;
+
+pub const RETURN_LOCAL: Local = 0;
+
+/// The source-order index of a field in a variant.
+///
+/// For example, in the following types,
+/// ```ignore(illustrative)
+/// enum Demo1 {
+///    Variant0 { a: bool, b: i32 },
+///    Variant1 { c: u8, d: u64 },
+/// }
+/// struct Demo2 { e: u8, f: u16, g: u8 }
+/// ```
+/// `a`'s `FieldIdx` is `0`,
+/// `b`'s `FieldIdx` is `1`,
+/// `c`'s `FieldIdx` is `0`, and
+/// `g`'s `FieldIdx` is `2`.
+pub type FieldIdx = usize;
+
+type UserTypeAnnotationIndex = usize;
+
+/// The possible branch sites of a [TerminatorKind::SwitchInt].
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct SwitchTargets {
+    /// The conditional branches where the first element represents the value that guards this
+    /// branch, and the second element is the branch target.
+    branches: Vec<(u128, BasicBlockIdx)>,
+    /// The `otherwise` branch which will be taken in case none of the conditional branches are
+    /// satisfied.
+    otherwise: BasicBlockIdx,
+}
+
+impl SwitchTargets {
+    /// All possible targets including the `otherwise` target.
+    pub fn all_targets(&self) -> Successors {
+        self.branches.iter().map(|(_, target)| *target).chain(Some(self.otherwise)).collect()
+    }
+
+    /// The `otherwise` branch target.
+    pub fn otherwise(&self) -> BasicBlockIdx {
+        self.otherwise
+    }
+
+    /// The conditional targets which are only taken if the pattern matches the given value.
+    pub fn branches(&self) -> impl Iterator<Item = (u128, BasicBlockIdx)> {
+        self.branches.iter().copied()
+    }
+
+    /// The number of targets including `otherwise`.
+    pub fn len(&self) -> usize {
+        self.branches.len() + 1
+    }
+
+    /// Create a new SwitchTargets from the given branches and `otherwise` target.
+    pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets {
+        SwitchTargets { branches, otherwise }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum BorrowKind {
+    /// Data must be immutable and is aliasable.
+    Shared,
+
+    /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
+    /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
+    Fake(FakeBorrowKind),
+
+    /// Data is mutable and not aliasable.
+    Mut {
+        /// `true` if this borrow arose from method-call auto-ref
+        kind: MutBorrowKind,
+    },
+}
+
+impl BorrowKind {
+    pub fn to_mutable_lossy(self) -> Mutability {
+        match self {
+            BorrowKind::Mut { .. } => Mutability::Mut,
+            BorrowKind::Shared => Mutability::Not,
+            // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation.
+            BorrowKind::Fake(_) => Mutability::Not,
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum RawPtrKind {
+    Mut,
+    Const,
+    FakeForPtrMetadata,
+}
+
+impl RawPtrKind {
+    pub fn to_mutable_lossy(self) -> Mutability {
+        match self {
+            RawPtrKind::Mut { .. } => Mutability::Mut,
+            RawPtrKind::Const => Mutability::Not,
+            // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation.
+            RawPtrKind::FakeForPtrMetadata => Mutability::Not,
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum MutBorrowKind {
+    Default,
+    TwoPhaseBorrow,
+    ClosureCapture,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum FakeBorrowKind {
+    /// A shared (deep) borrow. Data must be immutable and is aliasable.
+    Deep,
+    /// The immediately borrowed place must be immutable, but projections from
+    /// it don't need to be. This is used to prevent match guards from replacing
+    /// the scrutinee. For example, a fake borrow of `a.b` doesn't
+    /// conflict with a mutable borrow of `a.b.c`.
+    Shallow,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum Mutability {
+    Not,
+    Mut,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum Safety {
+    Safe,
+    Unsafe,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum PointerCoercion {
+    /// Go from a fn-item type to a fn-pointer type.
+    ReifyFnPointer,
+
+    /// Go from a safe fn pointer to an unsafe fn pointer.
+    UnsafeFnPointer,
+
+    /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer.
+    /// It cannot convert a closure that requires unsafe.
+    ClosureFnPointer(Safety),
+
+    /// Go from a mut raw pointer to a const raw pointer.
+    MutToConstPointer,
+
+    /// Go from `*const [T; N]` to `*const T`
+    ArrayToPointer,
+
+    /// Unsize a pointer/reference value, e.g., `&[T; n]` to
+    /// `&[T]`. Note that the source could be a thin or wide pointer.
+    /// This will do things like convert thin pointers to wide
+    /// pointers, or convert structs containing thin pointers to
+    /// structs containing wide pointers, or convert between wide
+    /// pointers.
+    Unsize,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum CastKind {
+    // FIXME(smir-rename): rename this to PointerExposeProvenance
+    PointerExposeAddress,
+    PointerWithExposedProvenance,
+    PointerCoercion(PointerCoercion),
+    // FIXME(smir-rename): change this to PointerCoercion(DynStar)
+    DynStar,
+    IntToInt,
+    FloatToInt,
+    FloatToFloat,
+    IntToFloat,
+    PtrToPtr,
+    FnPtrToPtr,
+    Transmute,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum NullOp {
+    /// Returns the size of a value of that type.
+    SizeOf,
+    /// Returns the minimum alignment of a type.
+    AlignOf,
+    /// Returns the offset of a field.
+    OffsetOf(Vec<(VariantIdx, FieldIdx)>),
+    /// cfg!(ub_checks), but at codegen time
+    UbChecks,
+    /// cfg!(contract_checks), but at codegen time
+    ContractChecks,
+}
+
+impl Operand {
+    /// Get the type of an operand relative to the local declaration.
+    ///
+    /// In order to retrieve the correct type, the `locals` argument must match the list of all
+    /// locals from the function body where this operand originates from.
+    ///
+    /// Errors indicate a malformed operand or incompatible locals list.
+    pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
+        match self {
+            Operand::Copy(place) | Operand::Move(place) => place.ty(locals),
+            Operand::Constant(c) => Ok(c.ty()),
+        }
+    }
+}
+
+impl ConstOperand {
+    pub fn ty(&self) -> Ty {
+        self.const_.ty()
+    }
+}
+
+impl Place {
+    /// Resolve down the chain of projections to get the type referenced at the end of it.
+    /// E.g.:
+    /// Calling `ty()` on `var.field` should return the type of `field`.
+    ///
+    /// In order to retrieve the correct type, the `locals` argument must match the list of all
+    /// locals from the function body where this place originates from.
+    pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
+        self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty))
+    }
+}
+
+impl ProjectionElem {
+    /// Get the expected type after applying this projection to a given place type.
+    pub fn ty(&self, place_ty: Ty) -> Result<Ty, Error> {
+        let ty = place_ty;
+        match &self {
+            ProjectionElem::Deref => Self::deref_ty(ty),
+            ProjectionElem::Field(_idx, fty) => Ok(*fty),
+            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty),
+            ProjectionElem::Subslice { from, to, from_end } => {
+                Self::subslice_ty(ty, *from, *to, *from_end)
+            }
+            ProjectionElem::Downcast(_) => Ok(ty),
+            ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty),
+        }
+    }
+
+    fn index_ty(ty: Ty) -> Result<Ty, Error> {
+        ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}"))
+    }
+
+    fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result<Ty, Error> {
+        let ty_kind = ty.kind();
+        match ty_kind {
+            TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty),
+            TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array(
+                inner,
+                to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?,
+            ),
+            TyKind::RigidTy(RigidTy::Array(inner, size)) => {
+                let size = size.eval_target_usize()?;
+                let len = size - from - to;
+                Ty::try_new_array(inner, len)
+            }
+            _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))),
+        }
+    }
+
+    fn deref_ty(ty: Ty) -> Result<Ty, Error> {
+        let deref_ty = ty
+            .kind()
+            .builtin_deref(true)
+            .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?;
+        Ok(deref_ty.ty)
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
new file mode 100644
index 00000000000..0c3e4fb7c90
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
@@ -0,0 +1,308 @@
+use std::fmt::{Debug, Formatter};
+use std::io;
+
+use serde::Serialize;
+use stable_mir::abi::FnAbi;
+use stable_mir::crate_def::CrateDef;
+use stable_mir::mir::Body;
+use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
+use stable_mir::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with};
+
+use crate::stable_mir;
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum MonoItem {
+    Fn(Instance),
+    Static(StaticDef),
+    GlobalAsm(Opaque),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize)]
+pub struct Instance {
+    /// The type of instance.
+    pub kind: InstanceKind,
+    /// An ID used to get the instance definition from the compiler.
+    /// Do not use this field directly.
+    pub def: InstanceDef,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub enum InstanceKind {
+    /// A user defined item.
+    Item,
+    /// A compiler intrinsic function.
+    Intrinsic,
+    /// A virtual function definition stored in a VTable.
+    /// The `idx` field indicates the position in the VTable for this instance.
+    Virtual { idx: usize },
+    /// A compiler generated shim.
+    Shim,
+}
+
+impl Instance {
+    /// Get the arguments this instance was instantiated with.
+    pub fn args(&self) -> GenericArgs {
+        with(|cx| cx.instance_args(self.def))
+    }
+
+    /// Get the body of an Instance.
+    ///
+    /// The body will be eagerly monomorphized and all constants will already be evaluated.
+    ///
+    /// This method will return the intrinsic fallback body if one was defined.
+    pub fn body(&self) -> Option<Body> {
+        with(|context| context.instance_body(self.def))
+    }
+
+    /// Check whether this instance has a body available.
+    ///
+    /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide
+    /// whether to specialize the intrinsic or to use its fallback body.
+    ///
+    /// For more information on fallback body, see <https://github.com/rust-lang/rust/issues/93145>.
+    ///
+    /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build
+    /// the StableMIR body.
+    pub fn has_body(&self) -> bool {
+        with(|cx| cx.has_body(self.def.def_id()))
+    }
+
+    pub fn is_foreign_item(&self) -> bool {
+        with(|cx| cx.is_foreign_item(self.def.def_id()))
+    }
+
+    /// Get the instance type with generic instantiations applied and lifetimes erased.
+    pub fn ty(&self) -> Ty {
+        with(|context| context.instance_ty(self.def))
+    }
+
+    /// Retrieve information about this instance binary interface.
+    pub fn fn_abi(&self) -> Result<FnAbi, Error> {
+        with(|cx| cx.instance_abi(self.def))
+    }
+
+    /// Retrieve the instance's mangled name used for calling the given instance.
+    ///
+    /// This will also look up the correct name of instances from upstream crates.
+    pub fn mangled_name(&self) -> Symbol {
+        with(|context| context.instance_mangled_name(self.def))
+    }
+
+    /// Retrieve the instance name for diagnostic messages.
+    ///
+    /// This will return the specialized name, e.g., `std::vec::Vec<u8>::new`.
+    pub fn name(&self) -> Symbol {
+        with(|context| context.instance_name(self.def, false))
+    }
+
+    /// Return a trimmed name of the given instance including its args.
+    ///
+    /// If a symbol name can only be imported from one place for a type, and as
+    /// long as it was not glob-imported anywhere in the current crate, we trim its
+    /// path and print only the name.
+    pub fn trimmed_name(&self) -> Symbol {
+        with(|context| context.instance_name(self.def, true))
+    }
+
+    /// Retrieve the plain intrinsic name of an instance if it's an intrinsic.
+    ///
+    /// The plain name does not include type arguments (as `trimmed_name` does),
+    /// which is more convenient to match with intrinsic symbols.
+    pub fn intrinsic_name(&self) -> Option<Symbol> {
+        match self.kind {
+            InstanceKind::Intrinsic => {
+                Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name()))
+            }
+            InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None,
+        }
+    }
+
+    /// Resolve an instance starting from a function definition and generic arguments.
+    pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, Error> {
+        with(|context| {
+            context
+                .resolve_instance(def, args)
+                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
+        })
+    }
+
+    /// Resolve the drop in place for a given type.
+    pub fn resolve_drop_in_place(ty: Ty) -> Instance {
+        with(|cx| cx.resolve_drop_in_place(ty))
+    }
+
+    /// Resolve an instance for a given function pointer.
+    pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result<Instance, Error> {
+        with(|context| {
+            context
+                .resolve_for_fn_ptr(def, args)
+                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
+        })
+    }
+
+    /// Resolve a closure with the expected kind.
+    pub fn resolve_closure(
+        def: ClosureDef,
+        args: &GenericArgs,
+        kind: ClosureKind,
+    ) -> Result<Instance, Error> {
+        with(|context| {
+            context
+                .resolve_closure(def, args, kind)
+                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
+        })
+    }
+
+    /// Check whether this instance is an empty shim.
+    ///
+    /// Allow users to check if this shim can be ignored when called directly.
+    ///
+    /// We have decided not to export different types of Shims to StableMIR users, however, this
+    /// is a query that can be very helpful for users when processing DropGlue.
+    ///
+    /// When generating code for a Drop terminator, users can ignore an empty drop glue.
+    /// These shims are only needed to generate a valid Drop call done via VTable.
+    pub fn is_empty_shim(&self) -> bool {
+        self.kind == InstanceKind::Shim
+            && with(|cx| {
+                cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def)
+            })
+    }
+
+    /// Try to constant evaluate the instance into a constant with the given type.
+    ///
+    /// This can be used to retrieve a constant that represents an intrinsic return such as
+    /// `type_id`.
+    pub fn try_const_eval(&self, const_ty: Ty) -> Result<Allocation, Error> {
+        with(|cx| cx.eval_instance(self.def, const_ty))
+    }
+
+    /// Emit the body of this instance if it has one.
+    pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
+        if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) }
+    }
+}
+
+impl Debug for Instance {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Instance")
+            .field("kind", &self.kind)
+            .field("def", &self.mangled_name())
+            .field("args", &self.args())
+            .finish()
+    }
+}
+
+/// Try to convert a crate item into an instance.
+/// The item cannot be generic in order to be converted into an instance.
+impl TryFrom<CrateItem> for Instance {
+    type Error = stable_mir::Error;
+
+    fn try_from(item: CrateItem) -> Result<Self, Self::Error> {
+        with(|context| {
+            let def_id = item.def_id();
+            if !context.requires_monomorphization(def_id) {
+                Ok(context.mono_instance(def_id))
+            } else {
+                Err(Error::new("Item requires monomorphization".to_string()))
+            }
+        })
+    }
+}
+
+/// Try to convert an instance into a crate item.
+/// Only user defined instances can be converted.
+impl TryFrom<Instance> for CrateItem {
+    type Error = stable_mir::Error;
+
+    fn try_from(value: Instance) -> Result<Self, Self::Error> {
+        with(|context| {
+            if value.kind == InstanceKind::Item && context.has_body(value.def.def_id()) {
+                Ok(CrateItem(context.instance_def_id(value.def)))
+            } else {
+                Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind)))
+            }
+        })
+    }
+}
+
+impl From<Instance> for MonoItem {
+    fn from(value: Instance) -> Self {
+        MonoItem::Fn(value)
+    }
+}
+
+impl From<StaticDef> for MonoItem {
+    fn from(value: StaticDef) -> Self {
+        MonoItem::Static(value)
+    }
+}
+
+impl From<StaticDef> for CrateItem {
+    fn from(value: StaticDef) -> Self {
+        CrateItem(value.0)
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct InstanceDef(usize);
+
+impl CrateDef for InstanceDef {
+    fn def_id(&self) -> DefId {
+        with(|context| context.instance_def_id(*self))
+    }
+}
+
+crate_def! {
+    /// Holds information about a static variable definition.
+    #[derive(Serialize)]
+    pub StaticDef;
+}
+
+impl TryFrom<CrateItem> for StaticDef {
+    type Error = stable_mir::Error;
+
+    fn try_from(value: CrateItem) -> Result<Self, Self::Error> {
+        if matches!(value.kind(), ItemKind::Static) {
+            Ok(StaticDef(value.0))
+        } else {
+            Err(Error::new(format!("Expected a static item, but found: {value:?}")))
+        }
+    }
+}
+
+impl TryFrom<Instance> for StaticDef {
+    type Error = stable_mir::Error;
+
+    fn try_from(value: Instance) -> Result<Self, Self::Error> {
+        StaticDef::try_from(CrateItem::try_from(value)?)
+    }
+}
+
+impl From<StaticDef> for Instance {
+    fn from(value: StaticDef) -> Self {
+        // A static definition should always be convertible to an instance.
+        with(|cx| cx.mono_instance(value.def_id()))
+    }
+}
+
+impl StaticDef {
+    /// Return the type of this static definition.
+    pub fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.0))
+    }
+
+    /// Evaluate a static's initializer, returning the allocation of the initializer's memory.
+    pub fn eval_initializer(&self) -> Result<Allocation, Error> {
+        with(|cx| cx.eval_static_initializer(*self))
+    }
+}
+
+impl IndexedVal for InstanceDef {
+    fn to_val(index: usize) -> Self {
+        InstanceDef(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
new file mode 100644
index 00000000000..439ebe978e5
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
@@ -0,0 +1,463 @@
+//! Implement methods to pretty print stable MIR body.
+use std::fmt::Debug;
+use std::io::Write;
+use std::{fmt, io, iter};
+
+use fmt::{Display, Formatter};
+use stable_mir::mir::{
+    Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents,
+};
+use stable_mir::ty::{AdtKind, AssocKind, IndexedVal, MirConst, Ty, TyConst};
+use stable_mir::{Body, CrateDef, Mutability, with};
+
+use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
+use crate::stable_mir;
+
+impl Display for Ty {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        with(|ctx| write!(f, "{}", ctx.ty_pretty(*self)))
+    }
+}
+
+impl Display for AssocKind {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            AssocKind::Fn => write!(f, "method"),
+            AssocKind::Const => write!(f, "associated const"),
+            AssocKind::Type => write!(f, "associated type"),
+        }
+    }
+}
+
+impl Debug for Place {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        with(|ctx| write!(f, "{}", ctx.place_pretty(self)))
+    }
+}
+
+pub(crate) fn function_body<W: Write>(writer: &mut W, body: &Body, name: &str) -> io::Result<()> {
+    write!(writer, "fn {name}(")?;
+    let mut sep = "";
+    for (index, local) in body.arg_locals().iter().enumerate() {
+        write!(writer, "{}_{}: {}", sep, index + 1, local.ty)?;
+        sep = ", ";
+    }
+    write!(writer, ")")?;
+
+    let return_local = body.ret_local();
+    writeln!(writer, " -> {} {{", return_local.ty)?;
+
+    body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> {
+        if index == 0 || index > body.arg_count {
+            writeln!(writer, "    let {}_{}: {};", pretty_mut(local.mutability), index, local.ty)
+        } else {
+            Ok(())
+        }
+    })?;
+
+    body.var_debug_info.iter().try_for_each(|info| {
+        let content = match &info.value {
+            VarDebugInfoContents::Place(place) => {
+                format!("{place:?}")
+            }
+            VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_),
+        };
+        writeln!(writer, "    debug {} => {};", info.name, content)
+    })?;
+
+    body.blocks
+        .iter()
+        .enumerate()
+        .map(|(index, block)| -> io::Result<()> {
+            writeln!(writer, "    bb{index}: {{")?;
+            let _ = block
+                .statements
+                .iter()
+                .map(|statement| -> io::Result<()> {
+                    pretty_statement(writer, &statement.kind)?;
+                    Ok(())
+                })
+                .collect::<Vec<_>>();
+            pretty_terminator(writer, &block.terminator.kind)?;
+            writeln!(writer, "    }}").unwrap();
+            Ok(())
+        })
+        .collect::<Result<Vec<_>, _>>()?;
+    writeln!(writer, "}}")?;
+    Ok(())
+}
+
+fn pretty_statement<W: Write>(writer: &mut W, statement: &StatementKind) -> io::Result<()> {
+    const INDENT: &str = "        ";
+    match statement {
+        StatementKind::Assign(place, rval) => {
+            write!(writer, "{INDENT}{place:?} = ")?;
+            pretty_rvalue(writer, rval)?;
+            writeln!(writer, ";")
+        }
+        // FIXME: Add rest of the statements
+        StatementKind::FakeRead(cause, place) => {
+            writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});")
+        }
+        StatementKind::SetDiscriminant { place, variant_index } => {
+            writeln!(writer, "{INDENT}discriminant({place:?} = {};", variant_index.to_index())
+        }
+        StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"),
+        StatementKind::StorageLive(local) => {
+            writeln!(writer, "{INDENT}StorageLive(_{local});")
+        }
+        StatementKind::StorageDead(local) => {
+            writeln!(writer, "{INDENT}StorageDead(_{local});")
+        }
+        StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"),
+        StatementKind::PlaceMention(place) => {
+            writeln!(writer, "{INDENT}PlaceMention({place:?};")
+        }
+        StatementKind::ConstEvalCounter => {
+            writeln!(writer, "{INDENT}ConstEvalCounter;")
+        }
+        StatementKind::Nop => writeln!(writer, "{INDENT}nop;"),
+        StatementKind::AscribeUserType { .. }
+        | StatementKind::Coverage(_)
+        | StatementKind::Intrinsic(_) => {
+            // FIX-ME: Make them pretty.
+            writeln!(writer, "{INDENT}{statement:?};")
+        }
+    }
+}
+
+fn pretty_terminator<W: Write>(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> {
+    pretty_terminator_head(writer, terminator)?;
+    let successors = terminator.successors();
+    let successor_count = successors.len();
+    let labels = pretty_successor_labels(terminator);
+
+    let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_)));
+    let fmt_unwind = |w: &mut W| -> io::Result<()> {
+        write!(w, "unwind ")?;
+        match terminator.unwind() {
+            None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
+            Some(UnwindAction::Continue) => write!(w, "continue"),
+            Some(UnwindAction::Unreachable) => write!(w, "unreachable"),
+            Some(UnwindAction::Terminate) => write!(w, "terminate"),
+        }
+    };
+
+    match (successor_count, show_unwind) {
+        (0, false) => {}
+        (0, true) => {
+            write!(writer, " -> ")?;
+            fmt_unwind(writer)?;
+        }
+        (1, false) => write!(writer, " -> bb{:?}", successors[0])?,
+        _ => {
+            write!(writer, " -> [")?;
+            for (i, target) in successors.iter().enumerate() {
+                if i > 0 {
+                    write!(writer, ", ")?;
+                }
+                write!(writer, "{}: bb{:?}", labels[i], target)?;
+            }
+            if show_unwind {
+                write!(writer, ", ")?;
+                fmt_unwind(writer)?;
+            }
+            write!(writer, "]")?;
+        }
+    };
+
+    writeln!(writer, ";")
+}
+
+fn pretty_terminator_head<W: Write>(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> {
+    use self::TerminatorKind::*;
+    const INDENT: &str = "        ";
+    match terminator {
+        Goto { .. } => write!(writer, "{INDENT}goto"),
+        SwitchInt { discr, .. } => {
+            write!(writer, "{INDENT}switchInt({})", pretty_operand(discr))
+        }
+        Resume => write!(writer, "{INDENT}resume"),
+        Abort => write!(writer, "{INDENT}abort"),
+        Return => write!(writer, "{INDENT}return"),
+        Unreachable => write!(writer, "{INDENT}unreachable"),
+        Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"),
+        Call { func, args, destination, .. } => {
+            write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?;
+            let mut args_iter = args.iter();
+            args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?;
+            args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?;
+            write!(writer, ")")
+        }
+        Assert { cond, expected, msg, target: _, unwind: _ } => {
+            write!(writer, "{INDENT}assert(")?;
+            if !expected {
+                write!(writer, "!")?;
+            }
+            write!(writer, "{}, ", pretty_operand(cond))?;
+            pretty_assert_message(writer, msg)?;
+            write!(writer, ")")
+        }
+        InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"),
+    }
+}
+
+fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> {
+    use self::TerminatorKind::*;
+    match terminator {
+        Call { target: None, unwind: UnwindAction::Cleanup(_), .. }
+        | InlineAsm { destination: None, .. } => vec!["unwind".into()],
+        Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![],
+        Goto { .. } => vec!["".to_string()],
+        SwitchInt { targets, .. } => targets
+            .branches()
+            .map(|(val, _target)| format!("{val}"))
+            .chain(iter::once("otherwise".into()))
+            .collect(),
+        Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
+        Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
+            vec!["return".into(), "unwind".into()]
+        }
+        Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
+        Assert { unwind: UnwindAction::Cleanup(_), .. } => {
+            vec!["success".into(), "unwind".into()]
+        }
+        Assert { unwind: _, .. } => vec!["success".into()],
+        InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()],
+    }
+}
+
+fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::Result<()> {
+    match msg {
+        AssertMessage::BoundsCheck { len, index } => {
+            let pretty_len = pretty_operand(len);
+            let pretty_index = pretty_operand(index);
+            write!(
+                writer,
+                "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}"
+            )
+        }
+        AssertMessage::Overflow(BinOp::Add, l, r) => {
+            let pretty_l = pretty_operand(l);
+            let pretty_r = pretty_operand(r);
+            write!(
+                writer,
+                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
+            )
+        }
+        AssertMessage::Overflow(BinOp::Sub, l, r) => {
+            let pretty_l = pretty_operand(l);
+            let pretty_r = pretty_operand(r);
+            write!(
+                writer,
+                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
+            )
+        }
+        AssertMessage::Overflow(BinOp::Mul, l, r) => {
+            let pretty_l = pretty_operand(l);
+            let pretty_r = pretty_operand(r);
+            write!(
+                writer,
+                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
+            )
+        }
+        AssertMessage::Overflow(BinOp::Div, l, r) => {
+            let pretty_l = pretty_operand(l);
+            let pretty_r = pretty_operand(r);
+            write!(
+                writer,
+                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
+            )
+        }
+        AssertMessage::Overflow(BinOp::Rem, l, r) => {
+            let pretty_l = pretty_operand(l);
+            let pretty_r = pretty_operand(r);
+            write!(
+                writer,
+                "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
+            )
+        }
+        AssertMessage::Overflow(BinOp::Shr, _, r) => {
+            let pretty_r = pretty_operand(r);
+            write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}")
+        }
+        AssertMessage::Overflow(BinOp::Shl, _, r) => {
+            let pretty_r = pretty_operand(r);
+            write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}")
+        }
+        AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op),
+        AssertMessage::OverflowNeg(op) => {
+            let pretty_op = pretty_operand(op);
+            write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}")
+        }
+        AssertMessage::DivisionByZero(op) => {
+            let pretty_op = pretty_operand(op);
+            write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}")
+        }
+        AssertMessage::RemainderByZero(op) => {
+            let pretty_op = pretty_operand(op);
+            write!(
+                writer,
+                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}"
+            )
+        }
+        AssertMessage::MisalignedPointerDereference { required, found } => {
+            let pretty_required = pretty_operand(required);
+            let pretty_found = pretty_operand(found);
+            write!(
+                writer,
+                "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}"
+            )
+        }
+        AssertMessage::NullPointerDereference => {
+            write!(writer, "\"null pointer dereference occurred\"")
+        }
+        AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
+            write!(writer, "{}", msg.description().unwrap())
+        }
+    }
+}
+
+fn pretty_operand(operand: &Operand) -> String {
+    match operand {
+        Operand::Copy(copy) => {
+            format!("{copy:?}")
+        }
+        Operand::Move(mv) => {
+            format!("move {mv:?}")
+        }
+        Operand::Constant(cnst) => pretty_mir_const(&cnst.const_),
+    }
+}
+
+fn pretty_mir_const(literal: &MirConst) -> String {
+    with(|cx| cx.mir_const_pretty(literal))
+}
+
+fn pretty_ty_const(ct: &TyConst) -> String {
+    with(|cx| cx.ty_const_pretty(ct.id))
+}
+
+fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
+    match rval {
+        Rvalue::AddressOf(mutability, place) => {
+            write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place)
+        }
+        Rvalue::Aggregate(aggregate_kind, operands) => {
+            // FIXME: Add pretty_aggregate function that returns a pretty string
+            pretty_aggregate(writer, aggregate_kind, operands)
+        }
+        Rvalue::BinaryOp(bin, op1, op2) => {
+            write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
+        }
+        Rvalue::Cast(_, op, ty) => {
+            write!(writer, "{} as {}", pretty_operand(op), ty)
+        }
+        Rvalue::CheckedBinaryOp(bin, op1, op2) => {
+            write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
+        }
+        Rvalue::CopyForDeref(deref) => {
+            write!(writer, "CopyForDeref({deref:?})")
+        }
+        Rvalue::Discriminant(place) => {
+            write!(writer, "discriminant({place:?})")
+        }
+        Rvalue::Len(len) => {
+            write!(writer, "len({len:?})")
+        }
+        Rvalue::Ref(_, borrowkind, place) => {
+            let kind = match borrowkind {
+                BorrowKind::Shared => "&",
+                BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ",
+                BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
+                BorrowKind::Mut { .. } => "&mut ",
+            };
+            write!(writer, "{kind}{place:?}")
+        }
+        Rvalue::Repeat(op, cnst) => {
+            write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst))
+        }
+        Rvalue::ShallowInitBox(_, _) => Ok(()),
+        Rvalue::ThreadLocalRef(item) => {
+            write!(writer, "thread_local_ref{item:?}")
+        }
+        Rvalue::NullaryOp(nul, ty) => {
+            write!(writer, "{nul:?}::<{ty}>() \" \"")
+        }
+        Rvalue::UnaryOp(un, op) => {
+            write!(writer, "{:?}({})", un, pretty_operand(op))
+        }
+        Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)),
+    }
+}
+
+fn pretty_aggregate<W: Write>(
+    writer: &mut W,
+    aggregate_kind: &AggregateKind,
+    operands: &Vec<Operand>,
+) -> io::Result<()> {
+    let suffix = match aggregate_kind {
+        AggregateKind::Array(_) => {
+            write!(writer, "[")?;
+            "]"
+        }
+        AggregateKind::Tuple => {
+            write!(writer, "(")?;
+            ")"
+        }
+        AggregateKind::Adt(def, var, _, _, _) => {
+            if def.kind() == AdtKind::Enum {
+                write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?;
+            } else {
+                write!(writer, "{}", def.variant(*var).unwrap().name())?;
+            }
+            if operands.is_empty() {
+                return Ok(());
+            }
+            // FIXME: Change this once we have CtorKind in StableMIR.
+            write!(writer, "(")?;
+            ")"
+        }
+        AggregateKind::Closure(def, _) => {
+            write!(writer, "{{closure@{}}}(", def.span().diagnostic())?;
+            ")"
+        }
+        AggregateKind::Coroutine(def, _, _) => {
+            write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?;
+            ")"
+        }
+        AggregateKind::CoroutineClosure(def, _) => {
+            write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?;
+            ")"
+        }
+        AggregateKind::RawPtr(ty, mutability) => {
+            write!(
+                writer,
+                "*{} {ty} from (",
+                if *mutability == Mutability::Mut { "mut" } else { "const" }
+            )?;
+            ")"
+        }
+    };
+    let mut separator = "";
+    for op in operands {
+        write!(writer, "{}{}", separator, pretty_operand(op))?;
+        separator = ", ";
+    }
+    write!(writer, "{suffix}")
+}
+
+fn pretty_mut(mutability: Mutability) -> &'static str {
+    match mutability {
+        Mutability::Not => " ",
+        Mutability::Mut => "mut ",
+    }
+}
+
+fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str {
+    match kind {
+        RawPtrKind::Const => "const",
+        RawPtrKind::Mut => "mut",
+        RawPtrKind::FakeForPtrMetadata => "const (fake)",
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mir/visit.rs b/compiler/rustc_smir/src/stable_mir/mir/visit.rs
new file mode 100644
index 00000000000..786693ea98d
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mir/visit.rs
@@ -0,0 +1,588 @@
+//! # The Stable MIR Visitor
+//!
+//! ## Overview
+//!
+//! We currently only support an immutable visitor.
+//! The structure of this visitor is similar to the ones internal to `rustc`,
+//! and it follows the following conventions:
+//!
+//! For every mir item, the trait has a `visit_<item>` and a `super_<item>` method.
+//! - `visit_<item>`, by default, calls `super_<item>`
+//! - `super_<item>`, by default, destructures the `<item>` and calls `visit_<sub_item>` for
+//!   all sub-items that compose the original item.
+//!
+//! In order to implement a visitor, override the `visit_*` methods for the types you are
+//! interested in analyzing, and invoke (within that method call)
+//! `self.super_*` to continue to the traverse.
+//! Avoid calling `super` methods in other circumstances.
+//!
+//! For the most part, we do not destructure things external to the
+//! MIR, e.g., types, spans, etc, but simply visit them and stop.
+//! This avoids duplication with other visitors like `TypeFoldable`.
+//!
+//! ## Updating
+//!
+//! The code is written in a very deliberate style intended to minimize
+//! the chance of things being overlooked.
+//!
+//! Use pattern matching to reference fields and ensure that all
+//! matches are exhaustive.
+//!
+//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
+//! That means you never write `..` to skip over fields, nor do you write `_`
+//! to skip over variants in a `match`.
+//!
+//! The only place that `_` is acceptable is to match a field (or
+//! variant argument) that does not require visiting.
+
+use stable_mir::mir::*;
+use stable_mir::ty::{GenericArgs, MirConst, Region, Ty, TyConst};
+use stable_mir::{Error, Opaque, Span};
+
+use crate::stable_mir;
+
+macro_rules! make_mir_visitor {
+    ($visitor_trait_name:ident, $($mutability:ident)?) => {
+        pub trait $visitor_trait_name {
+            fn visit_body(&mut self, body: &$($mutability)? Body) {
+                self.super_body(body)
+            }
+
+            fn visit_basic_block(&mut self, bb: &$($mutability)? BasicBlock) {
+                self.super_basic_block(bb)
+            }
+
+            fn visit_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
+                self.super_ret_decl(local, decl)
+            }
+
+            fn visit_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
+                self.super_arg_decl(local, decl)
+            }
+
+            fn visit_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
+                self.super_local_decl(local, decl)
+            }
+
+            fn visit_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) {
+                self.super_statement(stmt, location)
+            }
+
+            fn visit_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) {
+                self.super_terminator(term, location)
+            }
+
+            fn visit_span(&mut self, span: &$($mutability)? Span) {
+                self.super_span(span)
+            }
+
+            fn visit_place(&mut self, place: &$($mutability)? Place, ptx: PlaceContext, location: Location) {
+                self.super_place(place, ptx, location)
+            }
+
+            visit_place_fns!($($mutability)?);
+
+            fn visit_local(&mut self, local: &$($mutability)? Local, ptx: PlaceContext, location: Location) {
+                let _ = (local, ptx, location);
+            }
+
+            fn visit_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) {
+                self.super_rvalue(rvalue, location)
+            }
+
+            fn visit_operand(&mut self, operand: &$($mutability)? Operand, location: Location) {
+                self.super_operand(operand, location)
+            }
+
+            fn visit_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) {
+                self.super_user_type_projection(projection)
+            }
+
+            fn visit_ty(&mut self, ty: &$($mutability)? Ty, location: Location) {
+                let _ = location;
+                self.super_ty(ty)
+            }
+
+            fn visit_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) {
+                self.super_const_operand(constant, location)
+            }
+
+            fn visit_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) {
+                self.super_mir_const(constant, location)
+            }
+
+            fn visit_ty_const(&mut self, constant: &$($mutability)? TyConst, location: Location) {
+                let _ = location;
+                self.super_ty_const(constant)
+            }
+
+            fn visit_region(&mut self, region: &$($mutability)? Region, location: Location) {
+                let _ = location;
+                self.super_region(region)
+            }
+
+            fn visit_args(&mut self, args: &$($mutability)? GenericArgs, location: Location) {
+                let _ = location;
+                self.super_args(args)
+            }
+
+            fn visit_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) {
+                self.super_assert_msg(msg, location)
+            }
+
+            fn visit_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) {
+                self.super_var_debug_info(var_debug_info);
+            }
+
+            fn super_body(&mut self, body: &$($mutability)? Body) {
+                super_body!(self, body, $($mutability)?);
+            }
+
+            fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) {
+                let BasicBlock { statements, terminator } = bb;
+                for stmt in statements {
+                    self.visit_statement(stmt, Location(stmt.span));
+                }
+                self.visit_terminator(terminator, Location(terminator.span));
+            }
+
+            fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
+                let _ = local;
+                let LocalDecl { ty, span, .. } = decl;
+                self.visit_ty(ty, Location(*span));
+            }
+
+            fn super_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
+                self.super_local_decl(local, decl)
+            }
+
+            fn super_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) {
+                self.super_local_decl(local, decl)
+            }
+
+            fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) {
+                let Statement { kind, span } = stmt;
+                self.visit_span(span);
+                match kind {
+                    StatementKind::Assign(place, rvalue) => {
+                        self.visit_place(place, PlaceContext::MUTATING, location);
+                        self.visit_rvalue(rvalue, location);
+                    }
+                    StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => {
+                        self.visit_place(place, PlaceContext::NON_MUTATING, location);
+                    }
+                    StatementKind::SetDiscriminant { place, .. }
+                    | StatementKind::Deinit(place)
+                    | StatementKind::Retag(_, place) => {
+                        self.visit_place(place, PlaceContext::MUTATING, location);
+                    }
+                    StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
+                        self.visit_local(local, PlaceContext::NON_USE, location);
+                    }
+                    StatementKind::AscribeUserType { place, projections, variance: _ } => {
+                        self.visit_place(place, PlaceContext::NON_USE, location);
+                        self.visit_user_type_projection(projections);
+                    }
+                    StatementKind::Coverage(coverage) => visit_opaque(coverage),
+                    StatementKind::Intrinsic(intrisic) => match intrisic {
+                        NonDivergingIntrinsic::Assume(operand) => {
+                            self.visit_operand(operand, location);
+                        }
+                        NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
+                            src,
+                            dst,
+                            count,
+                        }) => {
+                            self.visit_operand(src, location);
+                            self.visit_operand(dst, location);
+                            self.visit_operand(count, location);
+                        }
+                    },
+                    StatementKind::ConstEvalCounter | StatementKind::Nop => {}
+                }
+            }
+
+            fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) {
+                let Terminator { kind, span } = term;
+                self.visit_span(span);
+                match kind {
+                    TerminatorKind::Goto { .. }
+                    | TerminatorKind::Resume
+                    | TerminatorKind::Abort
+                    | TerminatorKind::Unreachable => {}
+                    TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
+                        self.visit_operand(cond, location);
+                        self.visit_assert_msg(msg, location);
+                    }
+                    TerminatorKind::Drop { place, target: _, unwind: _ } => {
+                        self.visit_place(place, PlaceContext::MUTATING, location);
+                    }
+                    TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => {
+                        self.visit_operand(func, location);
+                        for arg in args {
+                            self.visit_operand(arg, location);
+                        }
+                        self.visit_place(destination, PlaceContext::MUTATING, location);
+                    }
+                    TerminatorKind::InlineAsm { operands, .. } => {
+                        for op in operands {
+                            let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op;
+                            if let Some(input) = in_value {
+                                self.visit_operand(input, location);
+                            }
+                            if let Some(output) = out_place {
+                                self.visit_place(output, PlaceContext::MUTATING, location);
+                            }
+                        }
+                    }
+                    TerminatorKind::Return => {
+                        let $($mutability)? local = RETURN_LOCAL;
+                        self.visit_local(&$($mutability)? local, PlaceContext::NON_MUTATING, location);
+                    }
+                    TerminatorKind::SwitchInt { discr, targets: _ } => {
+                        self.visit_operand(discr, location);
+                    }
+                }
+            }
+
+            fn super_span(&mut self, span: &$($mutability)? Span) {
+                let _ = span;
+            }
+
+            fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) {
+                match rvalue {
+                    Rvalue::AddressOf(mutability, place) => {
+                        let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut };
+                        self.visit_place(place, pcx, location);
+                    }
+                    Rvalue::Aggregate(_, operands) => {
+                        for op in operands {
+                            self.visit_operand(op, location);
+                        }
+                    }
+                    Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
+                        self.visit_operand(lhs, location);
+                        self.visit_operand(rhs, location);
+                    }
+                    Rvalue::Cast(_, op, ty) => {
+                        self.visit_operand(op, location);
+                        self.visit_ty(ty, location);
+                    }
+                    Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => {
+                        self.visit_place(place, PlaceContext::NON_MUTATING, location);
+                    }
+                    Rvalue::Ref(region, kind, place) => {
+                        self.visit_region(region, location);
+                        let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) };
+                        self.visit_place(place, pcx, location);
+                    }
+                    Rvalue::Repeat(op, constant) => {
+                        self.visit_operand(op, location);
+                        self.visit_ty_const(constant, location);
+                    }
+                    Rvalue::ShallowInitBox(op, ty) => {
+                        self.visit_ty(ty, location);
+                        self.visit_operand(op, location)
+                    }
+                    Rvalue::ThreadLocalRef(_) => {}
+                    Rvalue::NullaryOp(_, ty) => {
+                        self.visit_ty(ty, location);
+                    }
+                    Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => {
+                        self.visit_operand(op, location);
+                    }
+                }
+            }
+
+            fn super_operand(&mut self, operand: &$($mutability)? Operand, location: Location) {
+                match operand {
+                    Operand::Copy(place) | Operand::Move(place) => {
+                        self.visit_place(place, PlaceContext::NON_MUTATING, location)
+                    }
+                    Operand::Constant(constant) => {
+                        self.visit_const_operand(constant, location);
+                    }
+                }
+            }
+
+            fn super_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) {
+                // This is a no-op on mir::Visitor.
+                let _ = projection;
+            }
+
+            fn super_ty(&mut self, ty: &$($mutability)? Ty) {
+                let _ = ty;
+            }
+
+            fn super_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) {
+                let ConstOperand { span, user_ty: _, const_ } = constant;
+                self.visit_span(span);
+                self.visit_mir_const(const_, location);
+            }
+
+            fn super_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) {
+                let MirConst { kind: _, ty, id: _ } = constant;
+                self.visit_ty(ty, location);
+            }
+
+            fn super_ty_const(&mut self, constant: &$($mutability)? TyConst) {
+                let _ = constant;
+            }
+
+            fn super_region(&mut self, region: &$($mutability)? Region) {
+                let _ = region;
+            }
+
+            fn super_args(&mut self, args: &$($mutability)? GenericArgs) {
+                let _ = args;
+            }
+
+            fn super_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) {
+                let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } =
+                    var_debug_info;
+                self.visit_span(&$($mutability)? source_info.span);
+                let location = Location(source_info.span);
+                if let Some(composite) = composite {
+                    self.visit_ty(&$($mutability)? composite.ty, location);
+                }
+                match value {
+                    VarDebugInfoContents::Place(place) => {
+                        self.visit_place(place, PlaceContext::NON_USE, location);
+                    }
+                    VarDebugInfoContents::Const(constant) => {
+                        self.visit_mir_const(&$($mutability)? constant.const_, location);
+                    }
+                }
+            }
+
+            fn super_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) {
+                match msg {
+                    AssertMessage::BoundsCheck { len, index } => {
+                        self.visit_operand(len, location);
+                        self.visit_operand(index, location);
+                    }
+                    AssertMessage::Overflow(_, left, right) => {
+                        self.visit_operand(left, location);
+                        self.visit_operand(right, location);
+                    }
+                    AssertMessage::OverflowNeg(op)
+                    | AssertMessage::DivisionByZero(op)
+                    | AssertMessage::RemainderByZero(op) => {
+                        self.visit_operand(op, location);
+                    }
+                    AssertMessage::ResumedAfterReturn(_)
+                    | AssertMessage::ResumedAfterPanic(_)
+                    | AssertMessage::NullPointerDereference => {
+                        //nothing to visit
+                    }
+                    AssertMessage::MisalignedPointerDereference { required, found } => {
+                        self.visit_operand(required, location);
+                        self.visit_operand(found, location);
+                    }
+                }
+            }
+        }
+    };
+}
+
+macro_rules! super_body {
+    ($self:ident, $body:ident, mut) => {
+        for bb in $body.blocks.iter_mut() {
+            $self.visit_basic_block(bb);
+        }
+
+        $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local_mut());
+
+        for (idx, arg) in $body.arg_locals_mut().iter_mut().enumerate() {
+            $self.visit_arg_decl(idx + 1, arg)
+        }
+
+        let local_start = $body.arg_count + 1;
+        for (idx, arg) in $body.inner_locals_mut().iter_mut().enumerate() {
+            $self.visit_local_decl(idx + local_start, arg)
+        }
+
+        for info in $body.var_debug_info.iter_mut() {
+            $self.visit_var_debug_info(info);
+        }
+
+        $self.visit_span(&mut $body.span)
+    };
+
+    ($self:ident, $body:ident, ) => {
+        let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = $body;
+
+        for bb in blocks {
+            $self.visit_basic_block(bb);
+        }
+
+        $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local());
+
+        for (idx, arg) in $body.arg_locals().iter().enumerate() {
+            $self.visit_arg_decl(idx + 1, arg)
+        }
+
+        let local_start = arg_count + 1;
+        for (idx, arg) in $body.inner_locals().iter().enumerate() {
+            $self.visit_local_decl(idx + local_start, arg)
+        }
+
+        for info in var_debug_info.iter() {
+            $self.visit_var_debug_info(info);
+        }
+
+        $self.visit_span(span)
+    };
+}
+
+macro_rules! visit_place_fns {
+    (mut) => {
+        fn super_place(&mut self, place: &mut Place, ptx: PlaceContext, location: Location) {
+            self.visit_local(&mut place.local, ptx, location);
+
+            for elem in place.projection.iter_mut() {
+                self.visit_projection_elem(elem, ptx, location);
+            }
+        }
+
+        // We don't have to replicate the `process_projection()` like we did in
+        // `rustc_middle::mir::visit.rs` here because the `projection` field in `Place`
+        // of Stable-MIR is not an immutable borrow, unlike in `Place` of MIR.
+        fn visit_projection_elem(
+            &mut self,
+            elem: &mut ProjectionElem,
+            ptx: PlaceContext,
+            location: Location,
+        ) {
+            self.super_projection_elem(elem, ptx, location)
+        }
+
+        fn super_projection_elem(
+            &mut self,
+            elem: &mut ProjectionElem,
+            ptx: PlaceContext,
+            location: Location,
+        ) {
+            match elem {
+                ProjectionElem::Deref => {}
+                ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
+                ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
+                ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {}
+                ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
+                ProjectionElem::Downcast(_idx) => {}
+                ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
+                ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
+            }
+        }
+    };
+
+    () => {
+        fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
+            self.visit_local(&place.local, ptx, location);
+
+            for (idx, elem) in place.projection.iter().enumerate() {
+                let place_ref =
+                    PlaceRef { local: place.local, projection: &place.projection[..idx] };
+                self.visit_projection_elem(place_ref, elem, ptx, location);
+            }
+        }
+
+        fn visit_projection_elem<'a>(
+            &mut self,
+            place_ref: PlaceRef<'a>,
+            elem: &ProjectionElem,
+            ptx: PlaceContext,
+            location: Location,
+        ) {
+            let _ = place_ref;
+            self.super_projection_elem(elem, ptx, location);
+        }
+
+        fn super_projection_elem(
+            &mut self,
+            elem: &ProjectionElem,
+            ptx: PlaceContext,
+            location: Location,
+        ) {
+            match elem {
+                ProjectionElem::Deref => {}
+                ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
+                ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
+                ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {}
+                ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
+                ProjectionElem::Downcast(_idx) => {}
+                ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
+                ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
+            }
+        }
+    };
+}
+
+make_mir_visitor!(MirVisitor,);
+make_mir_visitor!(MutMirVisitor, mut);
+
+/// This function is a no-op that gets used to ensure this visitor is kept up-to-date.
+///
+/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail
+/// when trying to invoke `visit_opaque`.
+///
+/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()`
+/// by a `visit_<CONSTRUCT>` for your construct.
+fn visit_opaque(_: &Opaque) {}
+
+/// The location of a statement / terminator in the code and the CFG.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct Location(Span);
+
+impl Location {
+    pub fn span(&self) -> Span {
+        self.0
+    }
+}
+
+/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx`
+/// and `bb_idx` are valid for a given body.
+pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location {
+    let bb = &body.blocks[*bb_idx];
+    let stmt = &bb.statements[stmt_idx];
+    Location(stmt.span)
+}
+
+/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given
+/// body.
+pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location {
+    let bb = &body.blocks[*bb_idx];
+    let terminator = &bb.terminator;
+    Location(terminator.span)
+}
+
+/// Reference to a place used to represent a partial projection.
+pub struct PlaceRef<'a> {
+    pub local: Local,
+    pub projection: &'a [ProjectionElem],
+}
+
+impl PlaceRef<'_> {
+    /// Get the type of this place.
+    pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
+        self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty))
+    }
+}
+
+/// Information about a place's usage.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct PlaceContext {
+    /// Whether the access is mutable or not. Keep this private so we can increment the type in a
+    /// backward compatible manner.
+    is_mut: bool,
+}
+
+impl PlaceContext {
+    const MUTATING: Self = PlaceContext { is_mut: true };
+    const NON_MUTATING: Self = PlaceContext { is_mut: false };
+    const NON_USE: Self = PlaceContext { is_mut: false };
+
+    pub fn is_mutating(&self) -> bool {
+        self.is_mut
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
new file mode 100644
index 00000000000..c59758d4ad3
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -0,0 +1,239 @@
+//! Module that is temporarily parasitic on the `rustc_smir` crate,
+//!
+//! This module is designed to resolve circular dependency that would happen
+//! if we gradually invert the dependency order between `rustc_smir` and `stable_mir`.
+//!
+//! Once refactoring is complete, we will migrate it back to the `stable_mir` crate.
+
+//! The WIP stable interface to rustc internals.
+//!
+//! For more information see <https://github.com/rust-lang/project-stable-mir>
+//!
+//! # Note
+//!
+//! This API is still completely unstable and subject to change.
+
+// #![doc(
+//     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
+//     test(attr(allow(unused_variables), deny(warnings)))
+// )]
+//!
+//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to
+//! interact with the compiler.
+//!
+//! The goal is to eventually be published on
+//! [crates.io](https://crates.io).
+
+use std::fmt::Debug;
+use std::{fmt, io};
+
+use serde::Serialize;
+use stable_mir::compiler_interface::with;
+pub use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId};
+pub use stable_mir::error::*;
+use stable_mir::mir::mono::StaticDef;
+use stable_mir::mir::{Body, Mutability};
+use stable_mir::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
+
+use crate::stable_mir;
+
+pub mod abi;
+#[macro_use]
+pub mod crate_def;
+pub mod compiler_interface;
+#[macro_use]
+pub mod error;
+pub mod mir;
+pub mod target;
+pub mod ty;
+pub mod visitor;
+
+/// Use String for now but we should replace it.
+pub type Symbol = String;
+
+/// The number that identifies a crate.
+pub type CrateNum = usize;
+
+impl Debug for DefId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish()
+    }
+}
+
+impl IndexedVal for DefId {
+    fn to_val(index: usize) -> Self {
+        DefId(index)
+    }
+
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
+/// A list of crate items.
+pub type CrateItems = Vec<CrateItem>;
+
+/// A list of trait decls.
+pub type TraitDecls = Vec<TraitDef>;
+
+/// A list of impl trait decls.
+pub type ImplTraitDecls = Vec<ImplDef>;
+
+/// A list of associated items.
+pub type AssocItems = Vec<AssocItem>;
+
+/// Holds information about a crate.
+#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
+pub struct Crate {
+    pub id: CrateNum,
+    pub name: Symbol,
+    pub is_local: bool,
+}
+
+impl Crate {
+    /// The list of foreign modules in this crate.
+    pub fn foreign_modules(&self) -> Vec<ForeignModuleDef> {
+        with(|cx| cx.foreign_modules(self.id))
+    }
+
+    /// The list of traits declared in this crate.
+    pub fn trait_decls(&self) -> TraitDecls {
+        with(|cx| cx.trait_decls(self.id))
+    }
+
+    /// The list of trait implementations in this crate.
+    pub fn trait_impls(&self) -> ImplTraitDecls {
+        with(|cx| cx.trait_impls(self.id))
+    }
+
+    /// Return a list of function definitions from this crate independent on their visibility.
+    pub fn fn_defs(&self) -> Vec<FnDef> {
+        with(|cx| cx.crate_functions(self.id))
+    }
+
+    /// Return a list of static items defined in this crate independent on their visibility.
+    pub fn statics(&self) -> Vec<StaticDef> {
+        with(|cx| cx.crate_statics(self.id))
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum ItemKind {
+    Fn,
+    Static,
+    Const,
+    Ctor(CtorKind),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum CtorKind {
+    Const,
+    Fn,
+}
+
+pub type Filename = String;
+
+crate_def_with_ty! {
+    /// Holds information about an item in a crate.
+    #[derive(Serialize)]
+    pub CrateItem;
+}
+
+impl CrateItem {
+    /// This will return the body of an item or panic if it's not available.
+    pub fn expect_body(&self) -> mir::Body {
+        with(|cx| cx.mir_body(self.0))
+    }
+
+    /// Return the body of an item if available.
+    pub fn body(&self) -> Option<mir::Body> {
+        with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0)))
+    }
+
+    /// Check if a body is available for this item.
+    pub fn has_body(&self) -> bool {
+        with(|cx| cx.has_body(self.0))
+    }
+
+    pub fn span(&self) -> Span {
+        with(|cx| cx.span_of_an_item(self.0))
+    }
+
+    pub fn kind(&self) -> ItemKind {
+        with(|cx| cx.item_kind(*self))
+    }
+
+    pub fn requires_monomorphization(&self) -> bool {
+        with(|cx| cx.requires_monomorphization(self.0))
+    }
+
+    pub fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.0))
+    }
+
+    pub fn is_foreign_item(&self) -> bool {
+        with(|cx| cx.is_foreign_item(self.0))
+    }
+
+    /// Emit MIR for this item body.
+    pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
+        self.body()
+            .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))?
+            .dump(w, &self.name())
+    }
+}
+
+/// Return the function where execution starts if the current
+/// crate defines that. This is usually `main`, but could be
+/// `start` if the crate is a no-std crate.
+pub fn entry_fn() -> Option<CrateItem> {
+    with(|cx| cx.entry_fn())
+}
+
+/// Access to the local crate.
+pub fn local_crate() -> Crate {
+    with(|cx| cx.local_crate())
+}
+
+/// Try to find a crate or crates if multiple crates exist from given name.
+pub fn find_crates(name: &str) -> Vec<Crate> {
+    with(|cx| cx.find_crates(name))
+}
+
+/// Try to find a crate with the given name.
+pub fn external_crates() -> Vec<Crate> {
+    with(|cx| cx.external_crates())
+}
+
+/// Retrieve all items in the local crate that have a MIR associated with them.
+pub fn all_local_items() -> CrateItems {
+    with(|cx| cx.all_local_items())
+}
+
+pub fn all_trait_decls() -> TraitDecls {
+    with(|cx| cx.all_trait_decls())
+}
+
+pub fn all_trait_impls() -> ImplTraitDecls {
+    with(|cx| cx.all_trait_impls())
+}
+
+/// A type that provides internal information but that can still be used for debug purpose.
+#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
+pub struct Opaque(String);
+
+impl std::fmt::Display for Opaque {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+impl std::fmt::Debug for Opaque {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+pub fn opaque<T: Debug>(value: &T) -> Opaque {
+    Opaque(format!("{value:?}"))
+}
diff --git a/compiler/rustc_smir/src/stable_mir/target.rs b/compiler/rustc_smir/src/stable_mir/target.rs
new file mode 100644
index 00000000000..6cf1e9feb01
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/target.rs
@@ -0,0 +1,61 @@
+//! Provide information about the machine that this is being compiled into.
+
+use serde::Serialize;
+use stable_mir::compiler_interface::with;
+
+use crate::stable_mir;
+
+/// The properties of the target machine being compiled into.
+#[derive(Clone, PartialEq, Eq, Serialize)]
+pub struct MachineInfo {
+    pub endian: Endian,
+    pub pointer_width: MachineSize,
+}
+
+impl MachineInfo {
+    pub fn target() -> MachineInfo {
+        with(|cx| cx.target_info())
+    }
+
+    pub fn target_endianness() -> Endian {
+        with(|cx| cx.target_info().endian)
+    }
+
+    pub fn target_pointer_width() -> MachineSize {
+        with(|cx| cx.target_info().pointer_width)
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Serialize)]
+pub enum Endian {
+    Little,
+    Big,
+}
+
+/// Represent the size of a component.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+pub struct MachineSize {
+    num_bits: usize,
+}
+
+impl MachineSize {
+    #[inline(always)]
+    pub fn bytes(self) -> usize {
+        self.num_bits / 8
+    }
+
+    #[inline(always)]
+    pub fn bits(self) -> usize {
+        self.num_bits
+    }
+
+    #[inline(always)]
+    pub fn from_bits(num_bits: usize) -> MachineSize {
+        MachineSize { num_bits }
+    }
+
+    #[inline]
+    pub fn unsigned_int_max(self) -> Option<u128> {
+        (self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits()))
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
new file mode 100644
index 00000000000..3fcbbb0e138
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -0,0 +1,1622 @@
+use std::fmt::{self, Debug, Display, Formatter};
+use std::ops::Range;
+
+use serde::Serialize;
+use stable_mir::abi::{FnAbi, Layout};
+use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType};
+use stable_mir::mir::alloc::{AllocId, read_target_int, read_target_uint};
+use stable_mir::mir::mono::StaticDef;
+use stable_mir::target::MachineInfo;
+use stable_mir::{Filename, Opaque};
+
+use super::mir::{Body, Mutability, Safety};
+use super::{DefId, Error, Symbol, with};
+use crate::stable_mir;
+
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
+pub struct Ty(usize);
+
+impl Debug for Ty {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Ty").field("id", &self.0).field("kind", &self.kind()).finish()
+    }
+}
+
+/// Constructors for `Ty`.
+impl Ty {
+    /// Create a new type from a given kind.
+    pub fn from_rigid_kind(kind: RigidTy) -> Ty {
+        with(|cx| cx.new_rigid_ty(kind))
+    }
+
+    /// Create a new array type.
+    pub fn try_new_array(elem_ty: Ty, size: u64) -> Result<Ty, Error> {
+        Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, TyConst::try_from_target_usize(size)?)))
+    }
+
+    /// Create a new array type from Const length.
+    pub fn new_array_with_const_len(elem_ty: Ty, len: TyConst) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Array(elem_ty, len))
+    }
+
+    /// Create a new pointer type.
+    pub fn new_ptr(pointee_ty: Ty, mutability: Mutability) -> Ty {
+        Ty::from_rigid_kind(RigidTy::RawPtr(pointee_ty, mutability))
+    }
+
+    /// Create a new reference type.
+    pub fn new_ref(reg: Region, pointee_ty: Ty, mutability: Mutability) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Ref(reg, pointee_ty, mutability))
+    }
+
+    /// Create a new pointer type.
+    pub fn new_tuple(tys: &[Ty]) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Tuple(Vec::from(tys)))
+    }
+
+    /// Create a new closure type.
+    pub fn new_closure(def: ClosureDef, args: GenericArgs) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Closure(def, args))
+    }
+
+    /// Create a new coroutine type.
+    pub fn new_coroutine(def: CoroutineDef, args: GenericArgs, mov: Movability) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Coroutine(def, args, mov))
+    }
+
+    /// Create a new closure type.
+    pub fn new_coroutine_closure(def: CoroutineClosureDef, args: GenericArgs) -> Ty {
+        Ty::from_rigid_kind(RigidTy::CoroutineClosure(def, args))
+    }
+
+    /// Create a new box type that represents `Box<T>`, for the given inner type `T`.
+    pub fn new_box(inner_ty: Ty) -> Ty {
+        with(|cx| cx.new_box_ty(inner_ty))
+    }
+
+    /// Create a type representing `usize`.
+    pub fn usize_ty() -> Ty {
+        Ty::from_rigid_kind(RigidTy::Uint(UintTy::Usize))
+    }
+
+    /// Create a type representing `bool`.
+    pub fn bool_ty() -> Ty {
+        Ty::from_rigid_kind(RigidTy::Bool)
+    }
+
+    /// Create a type representing a signed integer.
+    pub fn signed_ty(inner: IntTy) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Int(inner))
+    }
+
+    /// Create a type representing an unsigned integer.
+    pub fn unsigned_ty(inner: UintTy) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Uint(inner))
+    }
+
+    /// Get a type layout.
+    pub fn layout(self) -> Result<Layout, Error> {
+        with(|cx| cx.ty_layout(self))
+    }
+}
+
+impl Ty {
+    pub fn kind(&self) -> TyKind {
+        with(|context| context.ty_kind(*self))
+    }
+}
+
+/// Represents a pattern in the type system
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum Pattern {
+    Range { start: Option<TyConst>, end: Option<TyConst>, include_end: bool },
+}
+
+/// Represents a constant in the type system
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct TyConst {
+    pub(crate) kind: TyConstKind,
+    pub id: TyConstId,
+}
+
+impl TyConst {
+    pub fn new(kind: TyConstKind, id: TyConstId) -> TyConst {
+        Self { kind, id }
+    }
+
+    /// Retrieve the constant kind.
+    pub fn kind(&self) -> &TyConstKind {
+        &self.kind
+    }
+
+    /// Creates an interned usize constant.
+    pub fn try_from_target_usize(val: u64) -> Result<Self, Error> {
+        with(|cx| cx.try_new_ty_const_uint(val.into(), UintTy::Usize))
+    }
+
+    /// Try to evaluate to a target `usize`.
+    pub fn eval_target_usize(&self) -> Result<u64, Error> {
+        with(|cx| cx.eval_target_usize_ty(self))
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum TyConstKind {
+    Param(ParamConst),
+    Bound(DebruijnIndex, BoundVar),
+    Unevaluated(ConstDef, GenericArgs),
+
+    // FIXME: These should be a valtree
+    Value(Ty, Allocation),
+    ZSTValue(Ty),
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct TyConstId(usize);
+
+/// Represents a constant in MIR
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct MirConst {
+    /// The constant kind.
+    pub(crate) kind: ConstantKind,
+    /// The constant type.
+    pub(crate) ty: Ty,
+    /// Used for internal tracking of the internal constant.
+    pub id: MirConstId,
+}
+
+impl MirConst {
+    /// Build a constant. Note that this should only be used by the compiler.
+    pub fn new(kind: ConstantKind, ty: Ty, id: MirConstId) -> MirConst {
+        MirConst { kind, ty, id }
+    }
+
+    /// Retrieve the constant kind.
+    pub fn kind(&self) -> &ConstantKind {
+        &self.kind
+    }
+
+    /// Get the constant type.
+    pub fn ty(&self) -> Ty {
+        self.ty
+    }
+
+    /// Try to evaluate to a target `usize`.
+    pub fn eval_target_usize(&self) -> Result<u64, Error> {
+        with(|cx| cx.eval_target_usize(self))
+    }
+
+    /// Create a constant that represents a new zero-sized constant of type T.
+    /// Fails if the type is not a ZST or if it doesn't have a known size.
+    pub fn try_new_zero_sized(ty: Ty) -> Result<MirConst, Error> {
+        with(|cx| cx.try_new_const_zst(ty))
+    }
+
+    /// Build a new constant that represents the given string.
+    ///
+    /// Note that there is no guarantee today about duplication of the same constant.
+    /// I.e.: Calling this function multiple times with the same argument may or may not return
+    /// the same allocation.
+    pub fn from_str(value: &str) -> MirConst {
+        with(|cx| cx.new_const_str(value))
+    }
+
+    /// Build a new constant that represents the given boolean value.
+    pub fn from_bool(value: bool) -> MirConst {
+        with(|cx| cx.new_const_bool(value))
+    }
+
+    /// Build a new constant that represents the given unsigned integer.
+    pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result<MirConst, Error> {
+        with(|cx| cx.try_new_const_uint(value, uint_ty))
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
+pub struct MirConstId(usize);
+
+type Ident = Opaque;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct Region {
+    pub kind: RegionKind,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum RegionKind {
+    ReEarlyParam(EarlyParamRegion),
+    ReBound(DebruijnIndex, BoundRegion),
+    ReStatic,
+    RePlaceholder(Placeholder<BoundRegion>),
+    ReErased,
+}
+
+pub(crate) type DebruijnIndex = u32;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct EarlyParamRegion {
+    pub index: u32,
+    pub name: Symbol,
+}
+
+pub(crate) type BoundVar = u32;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct BoundRegion {
+    pub var: BoundVar,
+    pub kind: BoundRegionKind,
+}
+
+pub(crate) type UniverseIndex = u32;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct Placeholder<T> {
+    pub universe: UniverseIndex,
+    pub bound: T,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Serialize)]
+pub struct Span(usize);
+
+impl Debug for Span {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Span")
+            .field("id", &self.0)
+            .field("repr", &with(|cx| cx.span_to_string(*self)))
+            .finish()
+    }
+}
+
+impl Span {
+    /// Return filename for diagnostic purposes
+    pub fn get_filename(&self) -> Filename {
+        with(|c| c.get_filename(self))
+    }
+
+    /// Return lines that correspond to this `Span`
+    pub fn get_lines(&self) -> LineInfo {
+        with(|c| c.get_lines(self))
+    }
+
+    /// Return the span location to be printed in diagnostic messages.
+    ///
+    /// This may leak local file paths and should not be used to build artifacts that may be
+    /// distributed.
+    pub fn diagnostic(&self) -> String {
+        with(|c| c.span_to_string(*self))
+    }
+}
+
+#[derive(Clone, Copy, Debug, Serialize)]
+/// Information you get from `Span` in a struct form.
+/// Line and col start from 1.
+pub struct LineInfo {
+    pub start_line: usize,
+    pub start_col: usize,
+    pub end_line: usize,
+    pub end_col: usize,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum TyKind {
+    RigidTy(RigidTy),
+    Alias(AliasKind, AliasTy),
+    Param(ParamTy),
+    Bound(usize, BoundTy),
+}
+
+impl TyKind {
+    pub fn rigid(&self) -> Option<&RigidTy> {
+        if let TyKind::RigidTy(inner) = self { Some(inner) } else { None }
+    }
+
+    #[inline]
+    pub fn is_unit(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Tuple(data)) if data.is_empty())
+    }
+
+    #[inline]
+    pub fn is_bool(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Bool))
+    }
+
+    #[inline]
+    pub fn is_char(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Char))
+    }
+
+    #[inline]
+    pub fn is_trait(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn)))
+    }
+
+    #[inline]
+    pub fn is_enum(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Enum)
+    }
+
+    #[inline]
+    pub fn is_struct(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Struct)
+    }
+
+    #[inline]
+    pub fn is_union(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Union)
+    }
+
+    #[inline]
+    pub fn is_adt(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Adt(..)))
+    }
+
+    #[inline]
+    pub fn is_ref(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Ref(..)))
+    }
+
+    #[inline]
+    pub fn is_fn(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::FnDef(..)))
+    }
+
+    #[inline]
+    pub fn is_fn_ptr(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::FnPtr(..)))
+    }
+
+    #[inline]
+    pub fn is_primitive(&self) -> bool {
+        matches!(
+            self,
+            TyKind::RigidTy(
+                RigidTy::Bool
+                    | RigidTy::Char
+                    | RigidTy::Int(_)
+                    | RigidTy::Uint(_)
+                    | RigidTy::Float(_)
+            )
+        )
+    }
+
+    #[inline]
+    pub fn is_float(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Float(_)))
+    }
+
+    #[inline]
+    pub fn is_integral(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Int(_) | RigidTy::Uint(_)))
+    }
+
+    #[inline]
+    pub fn is_numeric(&self) -> bool {
+        self.is_integral() || self.is_float()
+    }
+
+    #[inline]
+    pub fn is_signed(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Int(_)))
+    }
+
+    #[inline]
+    pub fn is_str(&self) -> bool {
+        *self == TyKind::RigidTy(RigidTy::Str)
+    }
+
+    #[inline]
+    pub fn is_cstr(&self) -> bool {
+        let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else {
+            return false;
+        };
+        with(|cx| cx.adt_is_cstr(*def))
+    }
+
+    #[inline]
+    pub fn is_slice(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Slice(_)))
+    }
+
+    #[inline]
+    pub fn is_array(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Array(..)))
+    }
+
+    #[inline]
+    pub fn is_mutable_ptr(&self) -> bool {
+        matches!(
+            self,
+            TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut))
+                | TyKind::RigidTy(RigidTy::Ref(_, _, Mutability::Mut))
+        )
+    }
+
+    #[inline]
+    pub fn is_raw_ptr(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::RawPtr(..)))
+    }
+
+    /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer).
+    #[inline]
+    pub fn is_any_ptr(&self) -> bool {
+        self.is_ref() || self.is_raw_ptr() || self.is_fn_ptr()
+    }
+
+    #[inline]
+    pub fn is_coroutine(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Coroutine(..)))
+    }
+
+    #[inline]
+    pub fn is_closure(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Closure(..)))
+    }
+
+    #[inline]
+    pub fn is_box(&self) -> bool {
+        match self {
+            TyKind::RigidTy(RigidTy::Adt(def, _)) => def.is_box(),
+            _ => false,
+        }
+    }
+
+    #[inline]
+    pub fn is_simd(&self) -> bool {
+        matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.is_simd())
+    }
+
+    pub fn trait_principal(&self) -> Option<Binder<ExistentialTraitRef>> {
+        if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self {
+            if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) =
+                predicates.first()
+            {
+                Some(Binder { value: trait_ref.clone(), bound_vars: bound_vars.clone() })
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    }
+
+    /// Returns the type of `ty[i]` for builtin types.
+    pub fn builtin_index(&self) -> Option<Ty> {
+        match self.rigid()? {
+            RigidTy::Array(ty, _) | RigidTy::Slice(ty) => Some(*ty),
+            _ => None,
+        }
+    }
+
+    /// Returns the type and mutability of `*ty` for builtin types.
+    ///
+    /// The parameter `explicit` indicates if this is an *explicit* dereference.
+    /// Some types -- notably raw ptrs -- can only be dereferenced explicitly.
+    pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut> {
+        match self.rigid()? {
+            RigidTy::Adt(def, args) if def.is_box() => {
+                Some(TypeAndMut { ty: *args.0.first()?.ty()?, mutability: Mutability::Not })
+            }
+            RigidTy::Ref(_, ty, mutability) => {
+                Some(TypeAndMut { ty: *ty, mutability: *mutability })
+            }
+            RigidTy::RawPtr(ty, mutability) if explicit => {
+                Some(TypeAndMut { ty: *ty, mutability: *mutability })
+            }
+            _ => None,
+        }
+    }
+
+    /// Get the function signature for function like types (Fn, FnPtr, and Closure)
+    pub fn fn_sig(&self) -> Option<PolyFnSig> {
+        match self {
+            TyKind::RigidTy(RigidTy::FnDef(def, args)) => Some(with(|cx| cx.fn_sig(*def, args))),
+            TyKind::RigidTy(RigidTy::FnPtr(sig)) => Some(sig.clone()),
+            TyKind::RigidTy(RigidTy::Closure(_def, args)) => Some(with(|cx| cx.closure_sig(args))),
+            _ => None,
+        }
+    }
+
+    /// Get the discriminant type for this type.
+    pub fn discriminant_ty(&self) -> Option<Ty> {
+        self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty)))
+    }
+
+    /// Deconstruct a function type if this is one.
+    pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> {
+        if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self {
+            Some((*def, args))
+        } else {
+            None
+        }
+    }
+}
+
+pub struct TypeAndMut {
+    pub ty: Ty,
+    pub mutability: Mutability,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum RigidTy {
+    Bool,
+    Char,
+    Int(IntTy),
+    Uint(UintTy),
+    Float(FloatTy),
+    Adt(AdtDef, GenericArgs),
+    Foreign(ForeignDef),
+    Str,
+    Array(Ty, TyConst),
+    Pat(Ty, Pattern),
+    Slice(Ty),
+    RawPtr(Ty, Mutability),
+    Ref(Region, Ty, Mutability),
+    FnDef(FnDef, GenericArgs),
+    FnPtr(PolyFnSig),
+    Closure(ClosureDef, GenericArgs),
+    // FIXME(stable_mir): Movability here is redundant
+    Coroutine(CoroutineDef, GenericArgs, Movability),
+    CoroutineClosure(CoroutineClosureDef, GenericArgs),
+    Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind),
+    Never,
+    Tuple(Vec<Ty>),
+    CoroutineWitness(CoroutineWitnessDef, GenericArgs),
+}
+
+impl RigidTy {
+    /// Get the discriminant type for this type.
+    pub fn discriminant_ty(&self) -> Ty {
+        with(|cx| cx.rigid_ty_discriminant_ty(self))
+    }
+}
+
+impl From<RigidTy> for TyKind {
+    fn from(value: RigidTy) -> Self {
+        TyKind::RigidTy(value)
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
+pub enum IntTy {
+    Isize,
+    I8,
+    I16,
+    I32,
+    I64,
+    I128,
+}
+
+impl IntTy {
+    pub fn num_bytes(self) -> usize {
+        match self {
+            IntTy::Isize => MachineInfo::target_pointer_width().bytes(),
+            IntTy::I8 => 1,
+            IntTy::I16 => 2,
+            IntTy::I32 => 4,
+            IntTy::I64 => 8,
+            IntTy::I128 => 16,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
+pub enum UintTy {
+    Usize,
+    U8,
+    U16,
+    U32,
+    U64,
+    U128,
+}
+
+impl UintTy {
+    pub fn num_bytes(self) -> usize {
+        match self {
+            UintTy::Usize => MachineInfo::target_pointer_width().bytes(),
+            UintTy::U8 => 1,
+            UintTy::U16 => 2,
+            UintTy::U32 => 4,
+            UintTy::U64 => 8,
+            UintTy::U128 => 16,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
+pub enum FloatTy {
+    F16,
+    F32,
+    F64,
+    F128,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
+pub enum Movability {
+    Static,
+    Movable,
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub ForeignModuleDef;
+}
+
+impl ForeignModuleDef {
+    pub fn module(&self) -> ForeignModule {
+        with(|cx| cx.foreign_module(*self))
+    }
+}
+
+pub struct ForeignModule {
+    pub def_id: ForeignModuleDef,
+    pub abi: Abi,
+}
+
+impl ForeignModule {
+    pub fn items(&self) -> Vec<ForeignDef> {
+        with(|cx| cx.foreign_items(self.def_id))
+    }
+}
+
+crate_def_with_ty! {
+    /// Hold information about a ForeignItem in a crate.
+    #[derive(Serialize)]
+    pub ForeignDef;
+}
+
+impl ForeignDef {
+    pub fn kind(&self) -> ForeignItemKind {
+        with(|cx| cx.foreign_item_kind(*self))
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum ForeignItemKind {
+    Fn(FnDef),
+    Static(StaticDef),
+    Type(Ty),
+}
+
+crate_def_with_ty! {
+    /// Hold information about a function definition in a crate.
+    #[derive(Serialize)]
+    pub FnDef;
+}
+
+impl FnDef {
+    // Get the function body if available.
+    pub fn body(&self) -> Option<Body> {
+        with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0)))
+    }
+
+    // Check if the function body is available.
+    pub fn has_body(&self) -> bool {
+        with(|ctx| ctx.has_body(self.0))
+    }
+
+    /// Get the information of the intrinsic if this function is a definition of one.
+    pub fn as_intrinsic(&self) -> Option<IntrinsicDef> {
+        with(|cx| cx.intrinsic(self.def_id()))
+    }
+
+    /// Check if the function is an intrinsic.
+    #[inline]
+    pub fn is_intrinsic(&self) -> bool {
+        self.as_intrinsic().is_some()
+    }
+
+    /// Get the function signature for this function definition.
+    pub fn fn_sig(&self) -> PolyFnSig {
+        let kind = self.ty().kind();
+        kind.fn_sig().unwrap()
+    }
+}
+
+crate_def_with_ty! {
+    #[derive(Serialize)]
+    pub IntrinsicDef;
+}
+
+impl IntrinsicDef {
+    /// Returns the plain name of the intrinsic.
+    /// e.g., `transmute` for `core::intrinsics::transmute`.
+    pub fn fn_name(&self) -> Symbol {
+        with(|cx| cx.intrinsic_name(*self))
+    }
+
+    /// Returns whether the intrinsic has no meaningful body and all backends
+    /// need to shim all calls to it.
+    pub fn must_be_overridden(&self) -> bool {
+        with(|cx| !cx.has_body(self.0))
+    }
+}
+
+impl From<IntrinsicDef> for FnDef {
+    fn from(def: IntrinsicDef) -> Self {
+        FnDef(def.0)
+    }
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub ClosureDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub CoroutineDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub CoroutineClosureDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub ParamDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub BrNamedDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub AdtDef;
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum AdtKind {
+    Enum,
+    Union,
+    Struct,
+}
+
+impl AdtDef {
+    pub fn kind(&self) -> AdtKind {
+        with(|cx| cx.adt_kind(*self))
+    }
+
+    /// Retrieve the type of this Adt.
+    pub fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.0))
+    }
+
+    /// Retrieve the type of this Adt by instantiating and normalizing it with the given arguments.
+    ///
+    /// This will assume the type can be instantiated with these arguments.
+    pub fn ty_with_args(&self, args: &GenericArgs) -> Ty {
+        with(|cx| cx.def_ty_with_args(self.0, args))
+    }
+
+    pub fn is_box(&self) -> bool {
+        with(|cx| cx.adt_is_box(*self))
+    }
+
+    pub fn is_simd(&self) -> bool {
+        with(|cx| cx.adt_is_simd(*self))
+    }
+
+    /// The number of variants in this ADT.
+    pub fn num_variants(&self) -> usize {
+        with(|cx| cx.adt_variants_len(*self))
+    }
+
+    /// Retrieve the variants in this ADT.
+    pub fn variants(&self) -> Vec<VariantDef> {
+        self.variants_iter().collect()
+    }
+
+    /// Iterate over the variants in this ADT.
+    pub fn variants_iter(&self) -> impl Iterator<Item = VariantDef> {
+        (0..self.num_variants())
+            .map(|idx| VariantDef { idx: VariantIdx::to_val(idx), adt_def: *self })
+    }
+
+    pub fn variant(&self, idx: VariantIdx) -> Option<VariantDef> {
+        (idx.to_index() < self.num_variants()).then_some(VariantDef { idx, adt_def: *self })
+    }
+}
+
+/// Definition of a variant, which can be either a struct / union field or an enum variant.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct VariantDef {
+    /// The variant index.
+    ///
+    /// ## Warning
+    /// Do not access this field directly!
+    pub idx: VariantIdx,
+    /// The data type where this variant comes from.
+    /// For now, we use this to retrieve information about the variant itself so we don't need to
+    /// cache more information.
+    ///
+    /// ## Warning
+    /// Do not access this field directly!
+    pub adt_def: AdtDef,
+}
+
+impl VariantDef {
+    pub fn name(&self) -> Symbol {
+        with(|cx| cx.variant_name(*self))
+    }
+
+    /// Retrieve all the fields in this variant.
+    // We expect user to cache this and use it directly since today it is expensive to generate all
+    // fields name.
+    pub fn fields(&self) -> Vec<FieldDef> {
+        with(|cx| cx.variant_fields(*self))
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct FieldDef {
+    /// The field definition.
+    ///
+    /// ## Warning
+    /// Do not access this field directly! This is public for the compiler to have access to it.
+    pub def: DefId,
+
+    /// The field name.
+    pub name: Symbol,
+}
+
+impl FieldDef {
+    /// Retrieve the type of this field instantiating and normalizing it with the given arguments.
+    ///
+    /// This will assume the type can be instantiated with these arguments.
+    pub fn ty_with_args(&self, args: &GenericArgs) -> Ty {
+        with(|cx| cx.def_ty_with_args(self.def, args))
+    }
+
+    /// Retrieve the type of this field.
+    pub fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.def))
+    }
+}
+
+impl Display for AdtKind {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_str(match self {
+            AdtKind::Enum => "enum",
+            AdtKind::Union => "union",
+            AdtKind::Struct => "struct",
+        })
+    }
+}
+
+impl AdtKind {
+    pub fn is_enum(&self) -> bool {
+        matches!(self, AdtKind::Enum)
+    }
+
+    pub fn is_struct(&self) -> bool {
+        matches!(self, AdtKind::Struct)
+    }
+
+    pub fn is_union(&self) -> bool {
+        matches!(self, AdtKind::Union)
+    }
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub AliasDef;
+}
+
+crate_def! {
+    /// A trait's definition.
+    #[derive(Serialize)]
+    pub TraitDef;
+}
+
+impl_crate_def_items! {
+    TraitDef;
+}
+
+impl TraitDef {
+    pub fn declaration(trait_def: &TraitDef) -> TraitDecl {
+        with(|cx| cx.trait_decl(trait_def))
+    }
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub GenericDef;
+}
+
+crate_def_with_ty! {
+    #[derive(Serialize)]
+    pub ConstDef;
+}
+
+crate_def! {
+    /// A trait impl definition.
+    #[derive(Serialize)]
+    pub ImplDef;
+}
+
+impl_crate_def_items! {
+    ImplDef;
+}
+
+impl ImplDef {
+    /// Retrieve information about this implementation.
+    pub fn trait_impl(&self) -> ImplTrait {
+        with(|cx| cx.trait_impl(self))
+    }
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub RegionDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub CoroutineWitnessDef;
+}
+
+/// A list of generic arguments.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct GenericArgs(pub Vec<GenericArgKind>);
+
+impl std::ops::Index<ParamTy> for GenericArgs {
+    type Output = Ty;
+
+    fn index(&self, index: ParamTy) -> &Self::Output {
+        self.0[index.index as usize].expect_ty()
+    }
+}
+
+impl std::ops::Index<ParamConst> for GenericArgs {
+    type Output = TyConst;
+
+    fn index(&self, index: ParamConst) -> &Self::Output {
+        self.0[index.index as usize].expect_const()
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum GenericArgKind {
+    Lifetime(Region),
+    Type(Ty),
+    Const(TyConst),
+}
+
+impl GenericArgKind {
+    /// Panic if this generic argument is not a type, otherwise
+    /// return the type.
+    #[track_caller]
+    pub fn expect_ty(&self) -> &Ty {
+        match self {
+            GenericArgKind::Type(ty) => ty,
+            _ => panic!("{self:?}"),
+        }
+    }
+
+    /// Panic if this generic argument is not a const, otherwise
+    /// return the const.
+    #[track_caller]
+    pub fn expect_const(&self) -> &TyConst {
+        match self {
+            GenericArgKind::Const(c) => c,
+            _ => panic!("{self:?}"),
+        }
+    }
+
+    /// Return the generic argument type if applicable, otherwise return `None`.
+    pub fn ty(&self) -> Option<&Ty> {
+        match self {
+            GenericArgKind::Type(ty) => Some(ty),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum TermKind {
+    Type(Ty),
+    Const(TyConst),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AliasKind {
+    Projection,
+    Inherent,
+    Opaque,
+    Weak,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct AliasTy {
+    pub def_id: AliasDef,
+    pub args: GenericArgs,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct AliasTerm {
+    pub def_id: AliasDef,
+    pub args: GenericArgs,
+}
+
+pub type PolyFnSig = Binder<FnSig>;
+
+impl PolyFnSig {
+    /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
+    ///
+    /// NB: this doesn't handle virtual calls - those should use `Instance::fn_abi`
+    /// instead, where the instance is an `InstanceKind::Virtual`.
+    pub fn fn_ptr_abi(self) -> Result<FnAbi, Error> {
+        with(|cx| cx.fn_ptr_abi(self))
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct FnSig {
+    pub inputs_and_output: Vec<Ty>,
+    pub c_variadic: bool,
+    pub safety: Safety,
+    pub abi: Abi,
+}
+
+impl FnSig {
+    pub fn output(&self) -> Ty {
+        self.inputs_and_output[self.inputs_and_output.len() - 1]
+    }
+
+    pub fn inputs(&self) -> &[Ty] {
+        &self.inputs_and_output[..self.inputs_and_output.len() - 1]
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
+pub enum Abi {
+    Rust,
+    C { unwind: bool },
+    Cdecl { unwind: bool },
+    Stdcall { unwind: bool },
+    Fastcall { unwind: bool },
+    Vectorcall { unwind: bool },
+    Thiscall { unwind: bool },
+    Aapcs { unwind: bool },
+    Win64 { unwind: bool },
+    SysV64 { unwind: bool },
+    PtxKernel,
+    Msp430Interrupt,
+    X86Interrupt,
+    GpuKernel,
+    EfiApi,
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+    CCmseNonSecureCall,
+    CCmseNonSecureEntry,
+    System { unwind: bool },
+    RustCall,
+    Unadjusted,
+    RustCold,
+    RiscvInterruptM,
+    RiscvInterruptS,
+}
+
+/// A binder represents a possibly generic type and its bound vars.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct Binder<T> {
+    pub value: T,
+    pub bound_vars: Vec<BoundVariableKind>,
+}
+
+impl<T> Binder<T> {
+    /// Create a new binder with the given bound vars.
+    pub fn bind_with_vars(value: T, bound_vars: Vec<BoundVariableKind>) -> Self {
+        Binder { value, bound_vars }
+    }
+
+    /// Create a new binder with no bounded variable.
+    pub fn dummy(value: T) -> Self {
+        Binder { value, bound_vars: vec![] }
+    }
+
+    pub fn skip_binder(self) -> T {
+        self.value
+    }
+
+    pub fn map_bound_ref<F, U>(&self, f: F) -> Binder<U>
+    where
+        F: FnOnce(&T) -> U,
+    {
+        let Binder { value, bound_vars } = self;
+        let new_value = f(value);
+        Binder { value: new_value, bound_vars: bound_vars.clone() }
+    }
+
+    pub fn map_bound<F, U>(self, f: F) -> Binder<U>
+    where
+        F: FnOnce(T) -> U,
+    {
+        let Binder { value, bound_vars } = self;
+        let new_value = f(value);
+        Binder { value: new_value, bound_vars }
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct EarlyBinder<T> {
+    pub value: T,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum BoundVariableKind {
+    Ty(BoundTyKind),
+    Region(BoundRegionKind),
+    Const,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
+pub enum BoundTyKind {
+    Anon,
+    Param(ParamDef, String),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum BoundRegionKind {
+    BrAnon,
+    BrNamed(BrNamedDef, String),
+    BrEnv,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum DynKind {
+    Dyn,
+    DynStar,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum ExistentialPredicate {
+    Trait(ExistentialTraitRef),
+    Projection(ExistentialProjection),
+    AutoTrait(TraitDef),
+}
+
+/// An existential reference to a trait where `Self` is not included.
+///
+/// The `generic_args` will include any other known argument.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct ExistentialTraitRef {
+    pub def_id: TraitDef,
+    pub generic_args: GenericArgs,
+}
+
+impl Binder<ExistentialTraitRef> {
+    pub fn with_self_ty(&self, self_ty: Ty) -> Binder<TraitRef> {
+        self.map_bound_ref(|trait_ref| trait_ref.with_self_ty(self_ty))
+    }
+}
+
+impl ExistentialTraitRef {
+    pub fn with_self_ty(&self, self_ty: Ty) -> TraitRef {
+        TraitRef::new(self.def_id, self_ty, &self.generic_args)
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct ExistentialProjection {
+    pub def_id: TraitDef,
+    pub generic_args: GenericArgs,
+    pub term: TermKind,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct ParamTy {
+    pub index: u32,
+    pub name: String,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct BoundTy {
+    pub var: usize,
+    pub kind: BoundTyKind,
+}
+
+pub type Bytes = Vec<Option<u8>>;
+
+/// Size in bytes.
+pub type Size = usize;
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)]
+pub struct Prov(pub AllocId);
+
+pub type Align = u64;
+pub type Promoted = u32;
+pub type InitMaskMaterialized = Vec<u64>;
+
+/// Stores the provenance information of pointers stored in memory.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
+pub struct ProvenanceMap {
+    /// Provenance in this map applies from the given offset for an entire pointer-size worth of
+    /// bytes. Two entries in this map are always at least a pointer size apart.
+    pub ptrs: Vec<(Size, Prov)>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
+pub struct Allocation {
+    pub bytes: Bytes,
+    pub provenance: ProvenanceMap,
+    pub align: Align,
+    pub mutability: Mutability,
+}
+
+impl Allocation {
+    /// Get a vector of bytes for an Allocation that has been fully initialized
+    pub fn raw_bytes(&self) -> Result<Vec<u8>, Error> {
+        self.bytes
+            .iter()
+            .copied()
+            .collect::<Option<Vec<_>>>()
+            .ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes))
+    }
+
+    /// Read a uint value from the specified range.
+    pub fn read_partial_uint(&self, range: Range<usize>) -> Result<u128, Error> {
+        if range.end - range.start > 16 {
+            return Err(error!("Allocation is bigger than largest integer"));
+        }
+        if range.end > self.bytes.len() {
+            return Err(error!(
+                "Range is out of bounds. Allocation length is `{}`, but requested range `{:?}`",
+                self.bytes.len(),
+                range
+            ));
+        }
+        let raw = self.bytes[range]
+            .iter()
+            .copied()
+            .collect::<Option<Vec<_>>>()
+            .ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes))?;
+        read_target_uint(&raw)
+    }
+
+    /// Read this allocation and try to convert it to an unassigned integer.
+    pub fn read_uint(&self) -> Result<u128, Error> {
+        if self.bytes.len() > 16 {
+            return Err(error!("Allocation is bigger than largest integer"));
+        }
+        let raw = self.raw_bytes()?;
+        read_target_uint(&raw)
+    }
+
+    /// Read this allocation and try to convert it to a signed integer.
+    pub fn read_int(&self) -> Result<i128, Error> {
+        if self.bytes.len() > 16 {
+            return Err(error!("Allocation is bigger than largest integer"));
+        }
+        let raw = self.raw_bytes()?;
+        read_target_int(&raw)
+    }
+
+    /// Read this allocation and try to convert it to a boolean.
+    pub fn read_bool(&self) -> Result<bool, Error> {
+        match self.read_int()? {
+            0 => Ok(false),
+            1 => Ok(true),
+            val => Err(error!("Unexpected value for bool: `{val}`")),
+        }
+    }
+
+    /// Read this allocation as a pointer and return whether it represents a `null` pointer.
+    pub fn is_null(&self) -> Result<bool, Error> {
+        let len = self.bytes.len();
+        let ptr_len = MachineInfo::target_pointer_width().bytes();
+        if len != ptr_len {
+            return Err(error!("Expected width of pointer (`{ptr_len}`), but found: `{len}`"));
+        }
+        Ok(self.read_uint()? == 0 && self.provenance.ptrs.is_empty())
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum ConstantKind {
+    Ty(TyConst),
+    Allocated(Allocation),
+    Unevaluated(UnevaluatedConst),
+    Param(ParamConst),
+    /// Store ZST constants.
+    /// We have to special handle these constants since its type might be generic.
+    ZeroSized,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct ParamConst {
+    pub index: u32,
+    pub name: String,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct UnevaluatedConst {
+    pub def: ConstDef,
+    pub args: GenericArgs,
+    pub promoted: Option<Promoted>,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
+pub enum TraitSpecializationKind {
+    None,
+    Marker,
+    AlwaysApplicable,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct TraitDecl {
+    pub def_id: TraitDef,
+    pub safety: Safety,
+    pub paren_sugar: bool,
+    pub has_auto_impl: bool,
+    pub is_marker: bool,
+    pub is_coinductive: bool,
+    pub skip_array_during_method_dispatch: bool,
+    pub skip_boxed_slice_during_method_dispatch: bool,
+    pub specialization_kind: TraitSpecializationKind,
+    pub must_implement_one_of: Option<Vec<Ident>>,
+    pub implement_via_object: bool,
+    pub deny_explicit_impl: bool,
+}
+
+impl TraitDecl {
+    pub fn generics_of(&self) -> Generics {
+        with(|cx| cx.generics_of(self.def_id.0))
+    }
+
+    pub fn predicates_of(&self) -> GenericPredicates {
+        with(|cx| cx.predicates_of(self.def_id.0))
+    }
+
+    pub fn explicit_predicates_of(&self) -> GenericPredicates {
+        with(|cx| cx.explicit_predicates_of(self.def_id.0))
+    }
+}
+
+pub type ImplTrait = EarlyBinder<TraitRef>;
+
+/// A complete reference to a trait, i.e., one where `Self` is known.
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct TraitRef {
+    pub def_id: TraitDef,
+    /// The generic arguments for this definition.
+    /// The first element must always be type, and it represents `Self`.
+    args: GenericArgs,
+}
+
+impl TraitRef {
+    pub fn new(def_id: TraitDef, self_ty: Ty, gen_args: &GenericArgs) -> TraitRef {
+        let mut args = vec![GenericArgKind::Type(self_ty)];
+        args.extend_from_slice(&gen_args.0);
+        TraitRef { def_id, args: GenericArgs(args) }
+    }
+
+    pub fn try_new(def_id: TraitDef, args: GenericArgs) -> Result<TraitRef, ()> {
+        match &args.0[..] {
+            [GenericArgKind::Type(_), ..] => Ok(TraitRef { def_id, args }),
+            _ => Err(()),
+        }
+    }
+
+    pub fn args(&self) -> &GenericArgs {
+        &self.args
+    }
+
+    pub fn self_ty(&self) -> Ty {
+        let GenericArgKind::Type(self_ty) = self.args.0[0] else {
+            panic!("Self must be a type, but found: {:?}", self.args.0[0])
+        };
+        self_ty
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct Generics {
+    pub parent: Option<GenericDef>,
+    pub parent_count: usize,
+    pub params: Vec<GenericParamDef>,
+    pub param_def_id_to_index: Vec<(GenericDef, u32)>,
+    pub has_self: bool,
+    pub has_late_bound_regions: Option<Span>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum GenericParamDefKind {
+    Lifetime,
+    Type { has_default: bool, synthetic: bool },
+    Const { has_default: bool },
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct GenericParamDef {
+    pub name: super::Symbol,
+    pub def_id: GenericDef,
+    pub index: u32,
+    pub pure_wrt_drop: bool,
+    pub kind: GenericParamDefKind,
+}
+
+pub struct GenericPredicates {
+    pub parent: Option<TraitDef>,
+    pub predicates: Vec<(PredicateKind, Span)>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum PredicateKind {
+    Clause(ClauseKind),
+    DynCompatible(TraitDef),
+    SubType(SubtypePredicate),
+    Coerce(CoercePredicate),
+    ConstEquate(TyConst, TyConst),
+    Ambiguous,
+    AliasRelate(TermKind, TermKind, AliasRelationDirection),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum ClauseKind {
+    Trait(TraitPredicate),
+    RegionOutlives(RegionOutlivesPredicate),
+    TypeOutlives(TypeOutlivesPredicate),
+    Projection(ProjectionPredicate),
+    ConstArgHasType(TyConst, Ty),
+    WellFormed(GenericArgKind),
+    ConstEvaluatable(TyConst),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum ClosureKind {
+    Fn,
+    FnMut,
+    FnOnce,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct SubtypePredicate {
+    pub a: Ty,
+    pub b: Ty,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct CoercePredicate {
+    pub a: Ty,
+    pub b: Ty,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AliasRelationDirection {
+    Equate,
+    Subtype,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct TraitPredicate {
+    pub trait_ref: TraitRef,
+    pub polarity: PredicatePolarity,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct OutlivesPredicate<A, B>(pub A, pub B);
+
+pub type RegionOutlivesPredicate = OutlivesPredicate<Region, Region>;
+pub type TypeOutlivesPredicate = OutlivesPredicate<Ty, Region>;
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct ProjectionPredicate {
+    pub projection_term: AliasTerm,
+    pub term: TermKind,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum ImplPolarity {
+    Positive,
+    Negative,
+    Reservation,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum PredicatePolarity {
+    Positive,
+    Negative,
+}
+
+pub trait IndexedVal {
+    fn to_val(index: usize) -> Self;
+
+    fn to_index(&self) -> usize;
+}
+
+macro_rules! index_impl {
+    ($name:ident) => {
+        impl IndexedVal for $name {
+            fn to_val(index: usize) -> Self {
+                $name(index)
+            }
+            fn to_index(&self) -> usize {
+                self.0
+            }
+        }
+    };
+}
+
+index_impl!(TyConstId);
+index_impl!(MirConstId);
+index_impl!(Ty);
+index_impl!(Span);
+
+/// The source-order index of a variant in a type.
+///
+/// For example, in the following types,
+/// ```ignore(illustrative)
+/// enum Demo1 {
+///    Variant0 { a: bool, b: i32 },
+///    Variant1 { c: u8, d: u64 },
+/// }
+/// struct Demo2 { e: u8, f: u16, g: u8 }
+/// ```
+/// `a` is in the variant with the `VariantIdx` of `0`,
+/// `c` is in the variant with the `VariantIdx` of `1`, and
+/// `g` is in the variant with the `VariantIdx` of `0`.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct VariantIdx(usize);
+
+index_impl!(VariantIdx);
+
+crate_def! {
+    /// Hold infomation about an Opaque definition, particularly useful in `RPITIT`.
+    #[derive(Serialize)]
+    pub OpaqueDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub AssocDef;
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct AssocItem {
+    pub def_id: AssocDef,
+    pub name: Symbol,
+    pub kind: AssocKind,
+    pub container: AssocItemContainer,
+
+    /// If this is an item in an impl of a trait then this is the `DefId` of
+    /// the associated item on the trait that this implements.
+    pub trait_item_def_id: Option<AssocDef>,
+
+    /// Whether this is a method with an explicit self
+    /// as its first parameter, allowing method calls.
+    pub fn_has_self_parameter: bool,
+
+    /// `Some` if the associated item (an associated type) comes from the
+    /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData`
+    /// provides additional information about its source.
+    pub opt_rpitit_info: Option<ImplTraitInTraitData>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AssocKind {
+    Const,
+    Fn,
+    Type,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AssocItemContainer {
+    Trait,
+    Impl,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum ImplTraitInTraitData {
+    Trait { fn_def_id: FnDef, opaque_def_id: OpaqueDef },
+    Impl { fn_def_id: FnDef },
+}
+
+impl AssocItem {
+    pub fn is_impl_trait_in_trait(&self) -> bool {
+        self.opt_rpitit_info.is_some()
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/visitor.rs b/compiler/rustc_smir/src/stable_mir/visitor.rs
new file mode 100644
index 00000000000..31a53d1b19d
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/visitor.rs
@@ -0,0 +1,226 @@
+use std::ops::ControlFlow;
+
+use stable_mir::Opaque;
+use stable_mir::ty::TyConst;
+
+use super::ty::{
+    Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs,
+    MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst,
+};
+use crate::stable_mir;
+
+pub trait Visitor: Sized {
+    type Break;
+    fn visit_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break> {
+        ty.super_visit(self)
+    }
+    fn visit_const(&mut self, c: &TyConst) -> ControlFlow<Self::Break> {
+        c.super_visit(self)
+    }
+    fn visit_reg(&mut self, reg: &Region) -> ControlFlow<Self::Break> {
+        reg.super_visit(self)
+    }
+}
+
+pub trait Visitable {
+    fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        self.super_visit(visitor)
+    }
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break>;
+}
+
+impl Visitable for Ty {
+    fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        visitor.visit_ty(self)
+    }
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match self.kind() {
+            super::ty::TyKind::RigidTy(ty) => ty.visit(visitor)?,
+            super::ty::TyKind::Alias(_, alias) => alias.args.visit(visitor)?,
+            super::ty::TyKind::Param(_) | super::ty::TyKind::Bound(_, _) => {}
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+impl Visitable for TyConst {
+    fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        visitor.visit_const(self)
+    }
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match &self.kind {
+            super::ty::TyConstKind::Param(_) | super::ty::TyConstKind::Bound(_, _) => {}
+            super::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?,
+            super::ty::TyConstKind::Value(ty, alloc) => {
+                alloc.visit(visitor)?;
+                ty.visit(visitor)?;
+            }
+            super::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?,
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+impl Visitable for MirConst {
+    fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        self.super_visit(visitor)
+    }
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match &self.kind() {
+            super::ty::ConstantKind::Ty(ct) => ct.visit(visitor)?,
+            super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?,
+            super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?,
+            super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {}
+        }
+        self.ty().visit(visitor)
+    }
+}
+
+impl Visitable for Opaque {
+    fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+        ControlFlow::Continue(())
+    }
+}
+
+impl Visitable for Allocation {
+    fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+        ControlFlow::Continue(())
+    }
+}
+
+impl Visitable for UnevaluatedConst {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        let UnevaluatedConst { def, args, promoted } = self;
+        def.visit(visitor)?;
+        args.visit(visitor)?;
+        promoted.visit(visitor)
+    }
+}
+
+impl Visitable for ConstDef {
+    fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+        ControlFlow::Continue(())
+    }
+}
+
+impl<T: Visitable> Visitable for Option<T> {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match self {
+            Some(val) => val.visit(visitor),
+            None => ControlFlow::Continue(()),
+        }
+    }
+}
+
+impl Visitable for Promoted {
+    fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+        ControlFlow::Continue(())
+    }
+}
+
+impl Visitable for GenericArgs {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        self.0.visit(visitor)
+    }
+}
+
+impl Visitable for Region {
+    fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        visitor.visit_reg(self)
+    }
+
+    fn super_visit<V: Visitor>(&self, _: &mut V) -> ControlFlow<V::Break> {
+        ControlFlow::Continue(())
+    }
+}
+
+impl Visitable for GenericArgKind {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match self {
+            GenericArgKind::Lifetime(lt) => lt.visit(visitor),
+            GenericArgKind::Type(t) => t.visit(visitor),
+            GenericArgKind::Const(c) => c.visit(visitor),
+        }
+    }
+}
+
+impl Visitable for RigidTy {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match self {
+            RigidTy::Bool
+            | RigidTy::Char
+            | RigidTy::Int(_)
+            | RigidTy::Uint(_)
+            | RigidTy::Float(_)
+            | RigidTy::Never
+            | RigidTy::Foreign(_)
+            | RigidTy::Str => ControlFlow::Continue(()),
+            RigidTy::Array(t, c) => {
+                t.visit(visitor)?;
+                c.visit(visitor)
+            }
+            RigidTy::Pat(t, _p) => t.visit(visitor),
+            RigidTy::Slice(inner) => inner.visit(visitor),
+            RigidTy::RawPtr(ty, _) => ty.visit(visitor),
+            RigidTy::Ref(reg, ty, _) => {
+                reg.visit(visitor)?;
+                ty.visit(visitor)
+            }
+            RigidTy::Adt(_, args)
+            | RigidTy::Closure(_, args)
+            | RigidTy::Coroutine(_, args, _)
+            | RigidTy::CoroutineWitness(_, args)
+            | RigidTy::CoroutineClosure(_, args)
+            | RigidTy::FnDef(_, args) => args.visit(visitor),
+            RigidTy::FnPtr(sig) => sig.visit(visitor),
+            RigidTy::Dynamic(pred, r, _) => {
+                pred.visit(visitor)?;
+                r.visit(visitor)
+            }
+            RigidTy::Tuple(fields) => fields.visit(visitor),
+        }
+    }
+}
+
+impl<T: Visitable> Visitable for Vec<T> {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        for arg in self {
+            arg.visit(visitor)?;
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+impl<T: Visitable> Visitable for Binder<T> {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        self.value.visit(visitor)
+    }
+}
+
+impl Visitable for ExistentialPredicate {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match self {
+            ExistentialPredicate::Trait(tr) => tr.generic_args.visit(visitor),
+            ExistentialPredicate::Projection(p) => {
+                p.term.visit(visitor)?;
+                p.generic_args.visit(visitor)
+            }
+            ExistentialPredicate::AutoTrait(_) => ControlFlow::Continue(()),
+        }
+    }
+}
+
+impl Visitable for TermKind {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        match self {
+            TermKind::Type(t) => t.visit(visitor),
+            TermKind::Const(c) => c.visit(visitor),
+        }
+    }
+}
+
+impl Visitable for FnSig {
+    fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        self.inputs_and_output.visit(visitor)
+    }
+}