about summary refs log tree commit diff
path: root/compiler/rustc_public
diff options
context:
space:
mode:
authorMakai <m4kai410@gmail.com>2025-07-12 15:28:50 +0000
committerMakai <m4kai410@gmail.com>2025-07-14 09:25:54 +0000
commit6598c61725abb0e8179b8a5fab7a0ed1f7fcaf5f (patch)
tree65d506113ce17311d0346fa4089500192dd0f3ed /compiler/rustc_public
parentad635e5d0696076b4412dd7db7b7e8c0867d6e0c (diff)
downloadrust-6598c61725abb0e8179b8a5fab7a0ed1f7fcaf5f.tar.gz
rust-6598c61725abb0e8179b8a5fab7a0ed1f7fcaf5f.zip
rename `stable_mir` to `rustc_public`, and `rustc_smir` to `rustc_public_bridge`
Diffstat (limited to 'compiler/rustc_public')
-rw-r--r--compiler/rustc_public/Cargo.toml24
-rw-r--r--compiler/rustc_public/README.md46
-rw-r--r--compiler/rustc_public/rust-toolchain.toml3
-rw-r--r--compiler/rustc_public/src/abi.rs493
-rw-r--r--compiler/rustc_public/src/alloc.rs76
-rw-r--r--compiler/rustc_public/src/compiler_interface.rs1099
-rw-r--r--compiler/rustc_public/src/crate_def.rs174
-rw-r--r--compiler/rustc_public/src/error.rs91
-rw-r--r--compiler/rustc_public/src/lib.rs302
-rw-r--r--compiler/rustc_public/src/mir.rs8
-rw-r--r--compiler/rustc_public/src/mir/alloc.rs90
-rw-r--r--compiler/rustc_public/src/mir/body.rs1127
-rw-r--r--compiler/rustc_public/src/mir/mono.rs305
-rw-r--r--compiler/rustc_public/src/mir/pretty.rs469
-rw-r--r--compiler/rustc_public/src/mir/visit.rs588
-rw-r--r--compiler/rustc_public/src/rustc_internal/mod.rs270
-rw-r--r--compiler/rustc_public/src/rustc_internal/pretty.rs21
-rw-r--r--compiler/rustc_public/src/target.rs60
-rw-r--r--compiler/rustc_public/src/ty.rs1655
-rw-r--r--compiler/rustc_public/src/unstable/convert/internal.rs823
-rw-r--r--compiler/rustc_public/src/unstable/convert/mod.rs110
-rw-r--r--compiler/rustc_public/src/unstable/convert/stable/abi.rs410
-rw-r--r--compiler/rustc_public/src/unstable/convert/stable/mir.rs936
-rw-r--r--compiler/rustc_public/src/unstable/convert/stable/mod.rs95
-rw-r--r--compiler/rustc_public/src/unstable/convert/stable/ty.rs1139
-rw-r--r--compiler/rustc_public/src/unstable/internal_cx/mod.rs93
-rw-r--r--compiler/rustc_public/src/unstable/internal_cx/traits.rs31
-rw-r--r--compiler/rustc_public/src/unstable/mod.rs124
-rw-r--r--compiler/rustc_public/src/visitor.rs224
29 files changed, 10886 insertions, 0 deletions
diff --git a/compiler/rustc_public/Cargo.toml b/compiler/rustc_public/Cargo.toml
new file mode 100644
index 00000000000..fa782166e4f
--- /dev/null
+++ b/compiler/rustc_public/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "rustc_public"
+version = "0.1.0-preview"
+edition = "2024"
+
+[dependencies]
+# tidy-alphabetical-start
+rustc_abi = { path = "../rustc_abi" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_middle = { path = "../rustc_middle" }
+rustc_public_bridge = { path = "../rustc_public_bridge" }
+rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+scoped-tls = "1.0"
+serde = { version = "1.0.125", features = [ "derive" ] }
+tracing = "0.1"
+# tidy-alphabetical-end
+
+[features]
+# Provides access to APIs that expose internals of the rust compiler.
+# APIs enabled by this feature are unstable. They can be removed or modified
+# at any point and they are not included in the crate's semantic versioning.
+rustc_internal = []
diff --git a/compiler/rustc_public/README.md b/compiler/rustc_public/README.md
new file mode 100644
index 00000000000..ab2546e377a
--- /dev/null
+++ b/compiler/rustc_public/README.md
@@ -0,0 +1,46 @@
+This crate is currently developed in-tree together with the compiler.
+
+Our goal is to start publishing `stable_mir` into crates.io.
+Until then, users will use this as any other rustc crate, by installing
+the rustup component `rustc-dev`, and declaring `stable-mir` as an external crate.
+
+See the StableMIR ["Getting Started"](https://rust-lang.github.io/project-stable-mir/getting-started.html)
+guide for more information.
+
+## Stable MIR Design
+
+The stable-mir will follow a similar approach to proc-macro2. Its
+implementation is split between two main crates:
+
+- `stable_mir`: Public crate, to be published on crates.io, which will contain
+the stable data structure as well as calls to `rustc_smir` APIs. The
+translation between stable and internal constructs will also be done in this crate,
+however, this is currently implemented in the `rustc_smir` crate.[^translation].
+- `rustc_smir`: This crate implements the public APIs to the compiler.
+It is responsible for gathering all the information requested, and providing
+the data in its unstable form.
+
+[^translation]: This is currently implemented in the `rustc_smir` crate,
+but we are working to change that.
+
+I.e.,
+tools will depend on `stable_mir` crate,
+which will invoke the compiler using APIs defined in `rustc_smir`.
+
+I.e.:
+
+```
+    ┌──────────────────────────────────┐           ┌──────────────────────────────────┐
+    │   External Tool     ┌──────────┐ │           │ ┌──────────┐   Rust Compiler     │
+    │                     │          │ │           │ │          │                     │
+    │                     │stable_mir| │           │ │rustc_smir│                     │
+    │                     │          │ ├──────────►| │          │                     │
+    │                     │          │ │◄──────────┤ │          │                     │
+    │                     │          │ │           │ │          │                     │
+    │                     │          │ │           │ │          │                     │
+    │                     └──────────┘ │           │ └──────────┘                     │
+    └──────────────────────────────────┘           └──────────────────────────────────┘
+```
+
+More details can be found here:
+https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view
diff --git a/compiler/rustc_public/rust-toolchain.toml b/compiler/rustc_public/rust-toolchain.toml
new file mode 100644
index 00000000000..d75e8e33b1c
--- /dev/null
+++ b/compiler/rustc_public/rust-toolchain.toml
@@ -0,0 +1,3 @@
+[toolchain]
+channel = "nightly-2023-06-14"
+components = [ "rustfmt", "rustc-dev" ]
diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs
new file mode 100644
index 00000000000..7b0882caf1b
--- /dev/null
+++ b/compiler/rustc_public/src/abi.rs
@@ -0,0 +1,493 @@
+use std::fmt::{self, Debug};
+use std::num::NonZero;
+use std::ops::RangeInclusive;
+
+use serde::Serialize;
+
+use crate::compiler_interface::with;
+use crate::mir::FieldIdx;
+use crate::target::{MachineInfo, MachineSize as Size};
+use crate::ty::{Align, Ty, VariantIdx};
+use crate::{Error, Opaque, error};
+
+/// 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 crate::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,
+
+    Custom,
+
+    // Target-specific calling conventions.
+    ArmAapcs,
+    CCmseNonSecureCall,
+    CCmseNonSecureEntry,
+
+    Msp430Intr,
+
+    PtxKernel,
+
+    GpuKernel,
+
+    X86Fastcall,
+    X86Intr,
+    X86Stdcall,
+    X86ThisCall,
+    X86VectorCall,
+
+    X86_64SysV,
+    X86_64Win64,
+
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+
+    RiscvInterrupt,
+}
+
+#[non_exhaustive]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+pub struct ReprFlags {
+    pub is_simd: bool,
+    pub is_c: bool,
+    pub is_transparent: bool,
+    pub is_linear: bool,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+pub enum IntegerType {
+    /// Pointer-sized integer type, i.e. `isize` and `usize`.
+    Pointer {
+        /// Signedness. e.g. `true` for `isize`
+        is_signed: bool,
+    },
+    /// Fixed-sized integer type, e.g. `i8`, `u32`, `i128`.
+    Fixed {
+        /// Length of this integer type. e.g. `IntegerLength::I8` for `u8`.
+        length: IntegerLength,
+        /// Signedness. e.g. `false` for `u8`
+        is_signed: bool,
+    },
+}
+
+/// Representation options provided by the user
+#[non_exhaustive]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+pub struct ReprOptions {
+    pub int: Option<IntegerType>,
+    pub align: Option<Align>,
+    pub pack: Option<Align>,
+    pub flags: ReprFlags,
+}
diff --git a/compiler/rustc_public/src/alloc.rs b/compiler/rustc_public/src/alloc.rs
new file mode 100644
index 00000000000..d2db6c08bbc
--- /dev/null
+++ b/compiler/rustc_public/src/alloc.rs
@@ -0,0 +1,76 @@
+//! Memory allocation implementation for StableMIR.
+//!
+//! This module is responsible for constructing stable components.
+//! All operations requiring rustc queries must be delegated
+//! to `rustc_public_bridge::alloc` to maintain stability guarantees.
+
+use rustc_abi::Align;
+use rustc_middle::mir::ConstValue;
+use rustc_middle::mir::interpret::AllocRange;
+use rustc_public_bridge::bridge::SmirError;
+use rustc_public_bridge::context::SmirCtxt;
+use rustc_public_bridge::{Tables, alloc};
+
+use super::Error;
+use super::compiler_interface::BridgeTys;
+use super::mir::Mutability;
+use super::ty::{Allocation, ProvenanceMap};
+use super::unstable::Stable;
+
+/// Creates new empty `Allocation` from given `Align`.
+fn new_empty_allocation(align: Align) -> Allocation {
+    Allocation {
+        bytes: Vec::new(),
+        provenance: ProvenanceMap { ptrs: Vec::new() },
+        align: align.bytes(),
+        mutability: Mutability::Not,
+    }
+}
+
+// We need this method instead of a Stable implementation
+// because we need to get `Ty` of the const we are trying to create, to do that
+// we need to have access to `ConstantKind` but we can't access that inside Stable impl.
+#[allow(rustc::usage_of_qualified_ty)]
+pub(crate) fn new_allocation<'tcx>(
+    ty: rustc_middle::ty::Ty<'tcx>,
+    const_value: ConstValue<'tcx>,
+    tables: &mut Tables<'tcx, BridgeTys>,
+    cx: &SmirCtxt<'tcx, BridgeTys>,
+) -> Allocation {
+    try_new_allocation(ty, const_value, tables, cx)
+        .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}"))
+}
+
+#[allow(rustc::usage_of_qualified_ty)]
+pub(crate) fn try_new_allocation<'tcx>(
+    ty: rustc_middle::ty::Ty<'tcx>,
+    const_value: ConstValue<'tcx>,
+    tables: &mut Tables<'tcx, BridgeTys>,
+    cx: &SmirCtxt<'tcx, BridgeTys>,
+) -> Result<Allocation, Error> {
+    let layout = alloc::create_ty_and_layout(cx, ty).map_err(|e| Error::from_internal(e))?;
+    match const_value {
+        ConstValue::Scalar(scalar) => {
+            alloc::try_new_scalar(layout, scalar, cx).map(|alloc| alloc.stable(tables, cx))
+        }
+        ConstValue::ZeroSized => Ok(new_empty_allocation(layout.align.abi)),
+        ConstValue::Slice { data, meta } => {
+            alloc::try_new_slice(layout, data, meta, cx).map(|alloc| alloc.stable(tables, cx))
+        }
+        ConstValue::Indirect { alloc_id, offset } => {
+            let alloc = alloc::try_new_indirect(alloc_id, cx);
+            use rustc_public_bridge::context::SmirAllocRange;
+            Ok(allocation_filter(&alloc.0, cx.alloc_range(offset, layout.size), tables, cx))
+        }
+    }
+}
+
+/// Creates an `Allocation` only from information within the `AllocRange`.
+pub(super) fn allocation_filter<'tcx>(
+    alloc: &rustc_middle::mir::interpret::Allocation,
+    alloc_range: AllocRange,
+    tables: &mut Tables<'tcx, BridgeTys>,
+    cx: &SmirCtxt<'tcx, BridgeTys>,
+) -> Allocation {
+    alloc::allocation_filter(alloc, alloc_range, tables, cx)
+}
diff --git a/compiler/rustc_public/src/compiler_interface.rs b/compiler/rustc_public/src/compiler_interface.rs
new file mode 100644
index 00000000000..d15438c2b80
--- /dev/null
+++ b/compiler/rustc_public/src/compiler_interface.rs
@@ -0,0 +1,1099 @@
+//! 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 rustc_hir::def::DefKind;
+use rustc_public_bridge::context::SmirCtxt;
+use rustc_public_bridge::{Bridge, SmirContainer};
+use tracing::debug;
+
+use crate::abi::{FnAbi, Layout, LayoutShape, ReprOptions};
+use crate::crate_def::Attribute;
+use crate::mir::alloc::{AllocId, GlobalAlloc};
+use crate::mir::mono::{Instance, InstanceDef, StaticDef};
+use crate::mir::{BinOp, Body, Place, UnOp};
+use crate::target::{MachineInfo, MachineSize};
+use crate::ty::{
+    AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, 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, VariantIdx,
+};
+use crate::unstable::{RustcInternal, Stable, new_item_kind};
+use crate::{
+    AssocItems, Crate, CrateDef, CrateItem, CrateItems, CrateNum, DefId, Error, Filename,
+    ImplTraitDecls, ItemKind, Symbol, TraitDecls, alloc, mir,
+};
+
+pub struct BridgeTys;
+
+impl Bridge for BridgeTys {
+    type DefId = crate::DefId;
+    type AllocId = crate::mir::alloc::AllocId;
+    type Span = crate::ty::Span;
+    type Ty = crate::ty::Ty;
+    type InstanceDef = crate::mir::mono::InstanceDef;
+    type TyConstId = crate::ty::TyConstId;
+    type MirConstId = crate::ty::MirConstId;
+    type Layout = crate::abi::Layout;
+
+    type Error = crate::Error;
+    type CrateItem = crate::CrateItem;
+    type AdtDef = crate::ty::AdtDef;
+    type ForeignModuleDef = crate::ty::ForeignModuleDef;
+    type ForeignDef = crate::ty::ForeignDef;
+    type FnDef = crate::ty::FnDef;
+    type ClosureDef = crate::ty::ClosureDef;
+    type CoroutineDef = crate::ty::CoroutineDef;
+    type CoroutineClosureDef = crate::ty::CoroutineClosureDef;
+    type AliasDef = crate::ty::AliasDef;
+    type ParamDef = crate::ty::ParamDef;
+    type BrNamedDef = crate::ty::BrNamedDef;
+    type TraitDef = crate::ty::TraitDef;
+    type GenericDef = crate::ty::GenericDef;
+    type ConstDef = crate::ty::ConstDef;
+    type ImplDef = crate::ty::ImplDef;
+    type RegionDef = crate::ty::RegionDef;
+    type CoroutineWitnessDef = crate::ty::CoroutineWitnessDef;
+    type AssocDef = crate::ty::AssocDef;
+    type OpaqueDef = crate::ty::OpaqueDef;
+    type Prov = crate::ty::Prov;
+    type StaticDef = crate::mir::mono::StaticDef;
+
+    type Allocation = crate::ty::Allocation;
+}
+
+/// Stable public API for querying compiler information.
+///
+/// All queries are delegated to [`rustc_public_bridge::context::SmirCtxt`] that provides
+/// similar APIs but based on internal rustc constructs.
+///
+/// Do not use this directly. This is currently used in the macro expansion.
+pub(crate) trait SmirInterface {
+    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;
+
+    /// Returns the representation options for this ADT.
+    fn adt_repr(&self, def: AdtDef) -> ReprOptions;
+
+    /// 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;
+
+    /// Discriminant for a given variant index of AdtDef.
+    fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr;
+
+    /// Discriminant for a given variand index and args of a coroutine.
+    fn coroutine_discr_for_variant(
+        &self,
+        coroutine: CoroutineDef,
+        args: &GenericArgs,
+        variant: VariantIdx,
+    ) -> Discr;
+
+    /// 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 kind 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;
+
+    /// 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;
+}
+
+impl<'tcx> SmirInterface for SmirContainer<'tcx, BridgeTys> {
+    fn entry_fn(&self) -> Option<CrateItem> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = cx.entry_fn();
+        Some(tables.crate_item(did?))
+    }
+
+    /// Retrieve all items of the local crate that have a MIR associated with them.
+    fn all_local_items(&self) -> CrateItems {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.all_local_items().iter().map(|did| tables.crate_item(*did)).collect()
+    }
+
+    /// Retrieve the body of a function.
+    /// This function will panic if the body is not available.
+    fn mir_body(&self, item: DefId) -> mir::Body {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[item];
+        cx.mir_body(did).stable(&mut *tables, cx)
+    }
+
+    /// Check whether the body of a function is available.
+    fn has_body(&self, item: DefId) -> bool {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def = item.internal(&mut *tables, cx.tcx);
+        cx.has_body(def)
+    }
+
+    fn foreign_modules(&self, crate_num: CrateNum) -> Vec<ForeignModuleDef> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.foreign_modules(crate_num.internal(&mut *tables, cx.tcx))
+            .iter()
+            .map(|did| tables.foreign_module_def(*did))
+            .collect()
+    }
+
+    /// Retrieve all functions defined in this crate.
+    fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let krate = crate_num.internal(&mut *tables, cx.tcx);
+        cx.crate_functions(krate).iter().map(|did| tables.fn_def(*did)).collect()
+    }
+
+    /// Retrieve all static items defined in this crate.
+    fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let krate = crate_num.internal(&mut *tables, cx.tcx);
+        cx.crate_statics(krate).iter().map(|did| tables.static_def(*did)).collect()
+    }
+
+    fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[mod_def.def_id()];
+        cx.foreign_module(did).stable(&mut *tables, cx)
+    }
+
+    fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec<ForeignDef> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[mod_def.def_id()];
+        cx.foreign_items(did).iter().map(|did| tables.foreign_def(*did)).collect()
+    }
+
+    fn all_trait_decls(&self) -> TraitDecls {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.all_trait_decls().map(|did| tables.trait_def(did)).collect()
+    }
+
+    fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let krate = crate_num.internal(&mut *tables, cx.tcx);
+        cx.trait_decls(krate).iter().map(|did| tables.trait_def(*did)).collect()
+    }
+
+    fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[trait_def.0];
+        cx.trait_decl(did).stable(&mut *tables, cx)
+    }
+
+    fn all_trait_impls(&self) -> ImplTraitDecls {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.all_trait_impls().iter().map(|did| tables.impl_def(*did)).collect()
+    }
+
+    fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let krate = crate_num.internal(&mut *tables, cx.tcx);
+        cx.trait_impls(krate).iter().map(|did| tables.impl_def(*did)).collect()
+    }
+
+    fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[trait_impl.0];
+        cx.trait_impl(did).stable(&mut *tables, cx)
+    }
+
+    fn generics_of(&self, def_id: DefId) -> Generics {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.generics_of(did).stable(&mut *tables, cx)
+    }
+
+    fn predicates_of(&self, def_id: DefId) -> GenericPredicates {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        let (parent, kinds) = cx.predicates_of(did);
+        crate::ty::GenericPredicates {
+            parent: parent.map(|did| tables.trait_def(did)),
+            predicates: kinds
+                .iter()
+                .map(|(kind, span)| (kind.stable(&mut *tables, cx), span.stable(&mut *tables, cx)))
+                .collect(),
+        }
+    }
+
+    fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        let (parent, kinds) = cx.explicit_predicates_of(did);
+        crate::ty::GenericPredicates {
+            parent: parent.map(|did| tables.trait_def(did)),
+            predicates: kinds
+                .iter()
+                .map(|(kind, span)| (kind.stable(&mut *tables, cx), span.stable(&mut *tables, cx)))
+                .collect(),
+        }
+    }
+
+    /// Get information about the local crate.
+    fn local_crate(&self) -> Crate {
+        let cx = &*self.cx.borrow();
+        smir_crate(cx, cx.local_crate_num())
+    }
+
+    /// Retrieve a list of all external crates.
+    fn external_crates(&self) -> Vec<Crate> {
+        let cx = &*self.cx.borrow();
+        cx.external_crates().iter().map(|crate_num| smir_crate(cx, *crate_num)).collect()
+    }
+
+    /// Find a crate with the given name.
+    fn find_crates(&self, name: &str) -> Vec<Crate> {
+        let cx = &*self.cx.borrow();
+        cx.find_crates(name).iter().map(|crate_num| smir_crate(cx, *crate_num)).collect()
+    }
+
+    /// Returns the name of given `DefId`.
+    fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol {
+        let tables = self.tables.borrow();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.def_name(did, trimmed)
+    }
+
+    /// 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> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.tool_attrs(did, attr)
+            .into_iter()
+            .map(|(attr_str, span)| Attribute::new(attr_str, span.stable(&mut *tables, cx)))
+            .collect()
+    }
+
+    /// Get all tool attributes of a definition.
+    fn all_tool_attrs(&self, def_id: DefId) -> Vec<Attribute> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.all_tool_attrs(did)
+            .into_iter()
+            .map(|(attr_str, span)| Attribute::new(attr_str, span.stable(&mut *tables, cx)))
+            .collect()
+    }
+
+    /// Returns printable, human readable form of `Span`.
+    fn span_to_string(&self, span: Span) -> String {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let sp = tables.spans[span];
+        cx.span_to_string(sp)
+    }
+
+    /// Return filename from given `Span`, for diagnostic purposes.
+    fn get_filename(&self, span: &Span) -> Filename {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let sp = tables.spans[*span];
+        cx.get_filename(sp)
+    }
+
+    /// Return lines corresponding to this `Span`.
+    fn get_lines(&self, span: &Span) -> LineInfo {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let sp = tables.spans[*span];
+        let lines = cx.get_lines(sp);
+        LineInfo::from(lines)
+    }
+
+    /// Returns the `kind` of given `DefId`.
+    fn item_kind(&self, item: CrateItem) -> ItemKind {
+        let tables = self.tables.borrow();
+        let cx = &*self.cx.borrow();
+        let did = tables[item.0];
+        new_item_kind(cx.def_kind(did))
+    }
+
+    /// Returns whether this is a foreign item.
+    fn is_foreign_item(&self, item: DefId) -> bool {
+        let tables = self.tables.borrow();
+        let cx = &*self.cx.borrow();
+        let did = tables[item];
+        cx.is_foreign_item(did)
+    }
+
+    /// Returns the kind of a given foreign item.
+    fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = tables[def.def_id()];
+        let def_kind = cx.foreign_item_kind(def_id);
+        match def_kind {
+            DefKind::Fn => ForeignItemKind::Fn(tables.fn_def(def_id)),
+            DefKind::Static { .. } => ForeignItemKind::Static(tables.static_def(def_id)),
+            DefKind::ForeignTy => {
+                use rustc_public_bridge::context::SmirTy;
+                ForeignItemKind::Type(tables.intern_ty(cx.new_foreign(def_id)))
+            }
+            def_kind => unreachable!("Unexpected kind for a foreign item: {:?}", def_kind),
+        }
+    }
+
+    /// Returns the kind of a given algebraic data type.
+    fn adt_kind(&self, def: AdtDef) -> AdtKind {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_kind(def.internal(&mut *tables, cx.tcx)).stable(&mut *tables, cx)
+    }
+
+    /// Returns if the ADT is a box.
+    fn adt_is_box(&self, def: AdtDef) -> bool {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_is_box(def.internal(&mut *tables, cx.tcx))
+    }
+
+    /// Returns whether this ADT is simd.
+    fn adt_is_simd(&self, def: AdtDef) -> bool {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_is_simd(def.internal(&mut *tables, cx.tcx))
+    }
+
+    /// Returns whether this definition is a C string.
+    fn adt_is_cstr(&self, def: AdtDef) -> bool {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_is_cstr(def.0.internal(&mut *tables, cx.tcx))
+    }
+
+    /// Returns the representation options for this ADT
+    fn adt_repr(&self, def: AdtDef) -> ReprOptions {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_repr(def.internal(&mut *tables, cx.tcx)).stable(&mut *tables, cx)
+    }
+
+    /// Retrieve the function signature for the given generic arguments.
+    fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = def.0.internal(&mut *tables, cx.tcx);
+        let args_ref = args.internal(&mut *tables, cx.tcx);
+        cx.fn_sig(def_id, args_ref).stable(&mut *tables, cx)
+    }
+
+    /// Retrieve the intrinsic definition if the item corresponds one.
+    fn intrinsic(&self, item: DefId) -> Option<IntrinsicDef> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = item.internal(&mut *tables, cx.tcx);
+        cx.intrinsic(def_id).map(|_| IntrinsicDef(item))
+    }
+
+    /// Retrieve the plain function name of an intrinsic.
+    fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = def.0.internal(&mut *tables, cx.tcx);
+        cx.intrinsic_name(def_id)
+    }
+
+    /// Retrieve the closure signature for the given generic arguments.
+    fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let args_ref = args.internal(&mut *tables, cx.tcx);
+        cx.closure_sig(args_ref).stable(&mut *tables, cx)
+    }
+
+    /// The number of variants in this ADT.
+    fn adt_variants_len(&self, def: AdtDef) -> usize {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_variants_len(def.internal(&mut *tables, cx.tcx))
+    }
+
+    /// Discriminant for a given variant index of AdtDef.
+    fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.adt_discr_for_variant(
+            adt.internal(&mut *tables, cx.tcx),
+            variant.internal(&mut *tables, cx.tcx),
+        )
+        .stable(&mut *tables, cx)
+    }
+
+    /// Discriminant for a given variand index and args of a coroutine.
+    fn coroutine_discr_for_variant(
+        &self,
+        coroutine: CoroutineDef,
+        args: &GenericArgs,
+        variant: VariantIdx,
+    ) -> Discr {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let tcx = cx.tcx;
+        let def = coroutine.def_id().internal(&mut *tables, tcx);
+        let args_ref = args.internal(&mut *tables, tcx);
+        cx.coroutine_discr_for_variant(def, args_ref, variant.internal(&mut *tables, tcx))
+            .stable(&mut *tables, cx)
+    }
+
+    /// The name of a variant.
+    fn variant_name(&self, def: VariantDef) -> Symbol {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.variant_name(def.internal(&mut *tables, cx.tcx))
+    }
+
+    fn variant_fields(&self, def: VariantDef) -> Vec<FieldDef> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        def.internal(&mut *tables, cx.tcx)
+            .fields
+            .iter()
+            .map(|f| f.stable(&mut *tables, cx))
+            .collect()
+    }
+
+    /// Evaluate constant as a target usize.
+    fn eval_target_usize(&self, mir_const: &MirConst) -> Result<u64, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let cnst = mir_const.internal(&mut *tables, cx.tcx);
+        cx.eval_target_usize(cnst)
+    }
+
+    fn eval_target_usize_ty(&self, ty_const: &TyConst) -> Result<u64, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let cnst = ty_const.internal(&mut *tables, cx.tcx);
+        cx.eval_target_usize_ty(cnst)
+    }
+
+    /// Create a new zero-sized constant.
+    fn try_new_const_zst(&self, ty: Ty) -> Result<MirConst, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let ty_internal = ty.internal(&mut *tables, cx.tcx);
+        cx.try_new_const_zst(ty_internal).map(|cnst| cnst.stable(&mut *tables, cx))
+    }
+
+    /// Create a new constant that represents the given string value.
+    fn new_const_str(&self, value: &str) -> MirConst {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.new_const_str(value).stable(&mut *tables, cx)
+    }
+
+    /// Create a new constant that represents the given boolean value.
+    fn new_const_bool(&self, value: bool) -> MirConst {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.new_const_bool(value).stable(&mut *tables, cx)
+    }
+
+    /// Create a new constant that represents the given value.
+    fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<MirConst, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let ty = cx.ty_new_uint(uint_ty.internal(&mut *tables, cx.tcx));
+        cx.try_new_const_uint(value, ty).map(|cnst| cnst.stable(&mut *tables, cx))
+    }
+
+    fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<TyConst, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let ty = cx.ty_new_uint(uint_ty.internal(&mut *tables, cx.tcx));
+        cx.try_new_ty_const_uint(value, ty).map(|cnst| cnst.stable(&mut *tables, cx))
+    }
+
+    /// Create a new type from the given kind.
+    fn new_rigid_ty(&self, kind: RigidTy) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let internal_kind = kind.internal(&mut *tables, cx.tcx);
+        cx.new_rigid_ty(internal_kind).stable(&mut *tables, cx)
+    }
+
+    /// Create a new box type, `Box<T>`, for the given inner type `T`.
+    fn new_box_ty(&self, ty: Ty) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let inner = ty.internal(&mut *tables, cx.tcx);
+        cx.new_box_ty(inner).stable(&mut *tables, cx)
+    }
+
+    /// Returns the type of given crate item.
+    fn def_ty(&self, item: DefId) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let inner = item.internal(&mut *tables, cx.tcx);
+        cx.def_ty(inner).stable(&mut *tables, cx)
+    }
+
+    /// Returns the type of given definition instantiated with the given arguments.
+    fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let inner = item.internal(&mut *tables, cx.tcx);
+        let args_ref = args.internal(&mut *tables, cx.tcx);
+        cx.def_ty_with_args(inner, args_ref).stable(&mut *tables, cx)
+    }
+
+    /// Returns literal value of a const as a string.
+    fn mir_const_pretty(&self, cnst: &MirConst) -> String {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cnst.internal(&mut *tables, cx.tcx).to_string()
+    }
+
+    /// `Span` of an item.
+    fn span_of_an_item(&self, def_id: DefId) -> Span {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.span_of_an_item(did).stable(&mut *tables, cx)
+    }
+
+    fn ty_const_pretty(&self, ct: TyConstId) -> String {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.ty_const_pretty(tables.ty_consts[ct])
+    }
+
+    /// Obtain the representation of a type.
+    fn ty_pretty(&self, ty: Ty) -> String {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.ty_pretty(tables.types[ty])
+    }
+
+    /// Obtain the kind of a type.
+    fn ty_kind(&self, ty: Ty) -> TyKind {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        cx.ty_kind(tables.types[ty]).stable(&mut *tables, cx)
+    }
+
+    /// Get the discriminant Ty for this Ty if there's one.
+    fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let internal_kind = ty.internal(&mut *tables, cx.tcx);
+        cx.rigid_ty_discriminant_ty(internal_kind).stable(&mut *tables, cx)
+    }
+
+    /// Get the body of an Instance which is already monomorphized.
+    fn instance_body(&self, instance: InstanceDef) -> Option<Body> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[instance];
+        cx.instance_body(instance).map(|body| body.stable(&mut *tables, cx))
+    }
+
+    /// Get the instance type with generic instantiations applied and lifetimes erased.
+    fn instance_ty(&self, instance: InstanceDef) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[instance];
+        cx.instance_ty(instance).stable(&mut *tables, cx)
+    }
+
+    /// Get the instantiation types.
+    fn instance_args(&self, def: InstanceDef) -> GenericArgs {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[def];
+        cx.instance_args(instance).stable(&mut *tables, cx)
+    }
+
+    /// Get the instance.
+    fn instance_def_id(&self, instance: InstanceDef) -> DefId {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[instance];
+        cx.instance_def_id(instance, &mut *tables)
+    }
+
+    /// Get the instance mangled name.
+    fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[instance];
+        cx.instance_mangled_name(instance)
+    }
+
+    /// Check if this is an empty DropGlue shim.
+    fn is_empty_drop_shim(&self, def: InstanceDef) -> bool {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[def];
+        cx.is_empty_drop_shim(instance)
+    }
+
+    /// 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 {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.mono_instance(did).stable(&mut *tables, cx)
+    }
+
+    /// Item requires monomorphization.
+    fn requires_monomorphization(&self, def_id: DefId) -> bool {
+        let tables = self.tables.borrow();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.requires_monomorphization(did)
+    }
+
+    /// Resolve an instance from the given function definition and generic arguments.
+    fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option<Instance> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = def.0.internal(&mut *tables, cx.tcx);
+        let args_ref = args.internal(&mut *tables, cx.tcx);
+        cx.resolve_instance(def_id, args_ref).map(|inst| inst.stable(&mut *tables, cx))
+    }
+
+    /// Resolve an instance for drop_in_place for the given type.
+    fn resolve_drop_in_place(&self, ty: Ty) -> Instance {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let internal_ty = ty.internal(&mut *tables, cx.tcx);
+
+        cx.resolve_drop_in_place(internal_ty).stable(&mut *tables, cx)
+    }
+
+    /// Resolve instance for a function pointer.
+    fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option<Instance> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = def.0.internal(&mut *tables, cx.tcx);
+        let args_ref = args.internal(&mut *tables, cx.tcx);
+        cx.resolve_for_fn_ptr(def_id, args_ref).stable(&mut *tables, cx)
+    }
+
+    /// Resolve instance for a closure with the requested type.
+    fn resolve_closure(
+        &self,
+        def: ClosureDef,
+        args: &GenericArgs,
+        kind: ClosureKind,
+    ) -> Option<Instance> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = def.0.internal(&mut *tables, cx.tcx);
+        let args_ref = args.internal(&mut *tables, cx.tcx);
+        let closure_kind = kind.internal(&mut *tables, cx.tcx);
+        cx.resolve_closure(def_id, args_ref, closure_kind).map(|inst| inst.stable(&mut *tables, cx))
+    }
+
+    /// Evaluate a static's initializer.
+    fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let def_id = def.0.internal(&mut *tables, cx.tcx);
+
+        cx.eval_static_initializer(def_id).stable(&mut *tables, cx)
+    }
+
+    /// Try to evaluate an instance into a constant.
+    fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let instance = tables.instances[def];
+        let cx = &*self.cx.borrow();
+        let const_ty = const_ty.internal(&mut *tables, cx.tcx);
+        cx.eval_instance(instance)
+            .map(|const_val| alloc::try_new_allocation(const_ty, const_val, &mut *tables, cx))
+            .map_err(|e| e.stable(&mut *tables, cx))?
+    }
+
+    /// Retrieve global allocation for the given allocation ID.
+    fn global_alloc(&self, id: AllocId) -> GlobalAlloc {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let alloc_id = id.internal(&mut *tables, cx.tcx);
+        cx.global_alloc(alloc_id).stable(&mut *tables, cx)
+    }
+
+    /// Retrieve the id for the virtual table.
+    fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option<AllocId> {
+        let mut tables = self.tables.borrow_mut();
+        let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else {
+            return None;
+        };
+        let cx = &*self.cx.borrow();
+        let ty = ty.internal(&mut *tables, cx.tcx);
+        let trait_ref = trait_ref.internal(&mut *tables, cx.tcx);
+        let alloc_id = cx.vtable_allocation(ty, trait_ref);
+        Some(alloc_id.stable(&mut *tables, cx))
+    }
+
+    fn krate(&self, def_id: DefId) -> Crate {
+        let tables = self.tables.borrow();
+        let cx = &*self.cx.borrow();
+        smir_crate(cx, tables[def_id].krate)
+    }
+
+    fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol {
+        let tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[def];
+        cx.instance_name(instance, trimmed)
+    }
+
+    /// Return information about the target machine.
+    fn target_info(&self) -> MachineInfo {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        MachineInfo {
+            endian: cx.target_endian().stable(&mut *tables, cx),
+            pointer_width: MachineSize::from_bits(cx.target_pointer_size()),
+        }
+    }
+
+    /// Get an instance ABI.
+    fn instance_abi(&self, def: InstanceDef) -> Result<FnAbi, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let instance = tables.instances[def];
+        cx.instance_abi(instance).map(|fn_abi| fn_abi.stable(&mut *tables, cx))
+    }
+
+    /// Get the ABI of a function pointer.
+    fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result<FnAbi, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let sig = fn_ptr.internal(&mut *tables, cx.tcx);
+        cx.fn_ptr_abi(sig).map(|fn_abi| fn_abi.stable(&mut *tables, cx))
+    }
+
+    /// Get the layout of a type.
+    fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let internal_ty = ty.internal(&mut *tables, cx.tcx);
+        cx.ty_layout(internal_ty).map(|layout| layout.stable(&mut *tables, cx))
+    }
+
+    /// Get the layout shape.
+    fn layout_shape(&self, id: Layout) -> LayoutShape {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        id.internal(&mut *tables, cx.tcx).0.stable(&mut *tables, cx)
+    }
+
+    /// Get a debug string representation of a place.
+    fn place_pretty(&self, place: &Place) -> String {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+
+        format!("{:?}", place.internal(&mut *tables, cx.tcx))
+    }
+
+    /// Get the resulting type of binary operation.
+    fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let rhs_internal = rhs.internal(&mut *tables, cx.tcx);
+        let lhs_internal = lhs.internal(&mut *tables, cx.tcx);
+        let bin_op_internal = bin_op.internal(&mut *tables, cx.tcx);
+        cx.binop_ty(bin_op_internal, rhs_internal, lhs_internal).stable(&mut *tables, cx)
+    }
+
+    /// Get the resulting type of unary operation.
+    fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let un_op = un_op.internal(&mut *tables, cx.tcx);
+        let arg = arg.internal(&mut *tables, cx.tcx);
+        cx.unop_ty(un_op, arg).stable(&mut *tables, cx)
+    }
+
+    /// Get all associated items of a definition.
+    fn associated_items(&self, def_id: DefId) -> AssocItems {
+        let mut tables = self.tables.borrow_mut();
+        let cx = &*self.cx.borrow();
+        let did = tables[def_id];
+        cx.associated_items(did).iter().map(|assoc| assoc.stable(&mut *tables, cx)).collect()
+    }
+}
+
+// A thread local variable that stores a pointer to [`SmirInterface`].
+scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>);
+
+pub(crate) fn run<F, T>(interface: &dyn SmirInterface, 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 interface) as _;
+        TLV.set(&Cell::new(ptr), || Ok(f()))
+    }
+}
+
+/// Execute the given function with access the [`SmirInterface`].
+///
+/// I.e., This function will load the current interface and calls a function with it.
+/// Do not nest these, as that will ICE.
+pub(crate) fn with<R>(f: impl FnOnce(&dyn SmirInterface) -> R) -> R {
+    assert!(TLV.is_set());
+    TLV.with(|tlv| {
+        let ptr = tlv.get();
+        assert!(!ptr.is_null());
+        f(unsafe { *(ptr as *const &dyn SmirInterface) })
+    })
+}
+
+fn smir_crate<'tcx>(
+    cx: &SmirCtxt<'tcx, BridgeTys>,
+    crate_num: rustc_span::def_id::CrateNum,
+) -> Crate {
+    let name = cx.crate_name(crate_num);
+    let is_local = cx.crate_is_local(crate_num);
+    let id = cx.crate_num_id(crate_num);
+    debug!(?name, ?crate_num, "smir_crate");
+    Crate { id, name, is_local }
+}
diff --git a/compiler/rustc_public/src/crate_def.rs b/compiler/rustc_public/src/crate_def.rs
new file mode 100644
index 00000000000..75228135e4c
--- /dev/null
+++ b/compiler/rustc_public/src/crate_def.rs
@@ -0,0 +1,174 @@
+//! 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 crate::ty::{GenericArgs, Span, Ty};
+use crate::{AssocItems, Crate, Symbol, with};
+
+/// 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_public/src/error.rs b/compiler/rustc_public/src/error.rs
new file mode 100644
index 00000000000..bc2124d1716
--- /dev/null
+++ b/compiler/rustc_public/src/error.rs
@@ -0,0 +1,91 @@
+//! 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};
+
+use rustc_public_bridge::bridge::SmirError;
+
+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 SmirError for Error {
+    fn new(msg: String) -> Self {
+        Self(msg)
+    }
+
+    fn from_internal<T: Debug>(err: T) -> Self {
+        Self(format!("{err:?}"))
+    }
+}
+
+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_public/src/lib.rs b/compiler/rustc_public/src/lib.rs
new file mode 100644
index 00000000000..e320c4eca71
--- /dev/null
+++ b/compiler/rustc_public/src/lib.rs
@@ -0,0 +1,302 @@
+//! The WIP public 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.
+
+#![allow(rustc::usage_of_ty_tykind)]
+#![doc(
+    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
+    test(attr(allow(unused_variables), deny(warnings)))
+)]
+#![feature(sized_hierarchy)]
+//!
+//! 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};
+
+pub(crate) use rustc_public_bridge::IndexedVal;
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::context::SmirCtxt;
+/// Export the rustc_internal APIs. Note that this module has no stability
+/// guarantees and it is not taken into account for semver.
+#[cfg(feature = "rustc_internal")]
+pub mod rustc_internal;
+use serde::Serialize;
+
+use crate::compiler_interface::with;
+pub use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId};
+pub use crate::error::*;
+use crate::mir::mono::StaticDef;
+use crate::mir::{Body, Mutability};
+use crate::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, ProvenanceMap, Span, TraitDef, Ty};
+use crate::unstable::Stable;
+
+pub mod abi;
+mod alloc;
+pub(crate) mod unstable;
+#[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:?}"))
+}
+
+macro_rules! bridge_impl {
+    ($name: ident, $ty: ty) => {
+        impl rustc_public_bridge::bridge::$name<compiler_interface::BridgeTys> for $ty {
+            fn new(def: crate::DefId) -> Self {
+                Self(def)
+            }
+        }
+    };
+}
+
+bridge_impl!(CrateItem, crate::CrateItem);
+bridge_impl!(AdtDef, crate::ty::AdtDef);
+bridge_impl!(ForeignModuleDef, crate::ty::ForeignModuleDef);
+bridge_impl!(ForeignDef, crate::ty::ForeignDef);
+bridge_impl!(FnDef, crate::ty::FnDef);
+bridge_impl!(ClosureDef, crate::ty::ClosureDef);
+bridge_impl!(CoroutineDef, crate::ty::CoroutineDef);
+bridge_impl!(CoroutineClosureDef, crate::ty::CoroutineClosureDef);
+bridge_impl!(AliasDef, crate::ty::AliasDef);
+bridge_impl!(ParamDef, crate::ty::ParamDef);
+bridge_impl!(BrNamedDef, crate::ty::BrNamedDef);
+bridge_impl!(TraitDef, crate::ty::TraitDef);
+bridge_impl!(GenericDef, crate::ty::GenericDef);
+bridge_impl!(ConstDef, crate::ty::ConstDef);
+bridge_impl!(ImplDef, crate::ty::ImplDef);
+bridge_impl!(RegionDef, crate::ty::RegionDef);
+bridge_impl!(CoroutineWitnessDef, crate::ty::CoroutineWitnessDef);
+bridge_impl!(AssocDef, crate::ty::AssocDef);
+bridge_impl!(OpaqueDef, crate::ty::OpaqueDef);
+bridge_impl!(StaticDef, crate::mir::mono::StaticDef);
+
+impl rustc_public_bridge::bridge::Prov<compiler_interface::BridgeTys> for crate::ty::Prov {
+    fn new(aid: crate::mir::alloc::AllocId) -> Self {
+        Self(aid)
+    }
+}
+
+impl rustc_public_bridge::bridge::Allocation<compiler_interface::BridgeTys>
+    for crate::ty::Allocation
+{
+    fn new<'tcx>(
+        bytes: Vec<Option<u8>>,
+        ptrs: Vec<(usize, rustc_middle::mir::interpret::AllocId)>,
+        align: u64,
+        mutability: rustc_middle::mir::Mutability,
+        tables: &mut Tables<'tcx, compiler_interface::BridgeTys>,
+        cx: &SmirCtxt<'tcx, compiler_interface::BridgeTys>,
+    ) -> Self {
+        Self {
+            bytes,
+            provenance: ProvenanceMap {
+                ptrs: ptrs.iter().map(|(i, aid)| (*i, tables.prov(*aid))).collect(),
+            },
+            align,
+            mutability: mutability.stable(tables, cx),
+        }
+    }
+}
diff --git a/compiler/rustc_public/src/mir.rs b/compiler/rustc_public/src/mir.rs
new file mode 100644
index 00000000000..413b5152bb3
--- /dev/null
+++ b/compiler/rustc_public/src/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_public/src/mir/alloc.rs b/compiler/rustc_public/src/mir/alloc.rs
new file mode 100644
index 00000000000..9a94551f3ec
--- /dev/null
+++ b/compiler/rustc_public/src/mir/alloc.rs
@@ -0,0 +1,90 @@
+//! This module provides methods to retrieve allocation information, such as static variables.
+
+use std::io::Read;
+
+use serde::Serialize;
+
+use crate::mir::mono::{Instance, StaticDef};
+use crate::target::{Endian, MachineInfo};
+use crate::ty::{Allocation, Binder, ExistentialTraitRef, Ty};
+use crate::{Error, IndexedVal, with};
+
+/// 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),
+    /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id
+    /// is split into two segments, on 32 bit systems there are 4 segments, and so on.
+    TypeId { ty: Ty },
+}
+
+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_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs
new file mode 100644
index 00000000000..28a7aa6e758
--- /dev/null
+++ b/compiler/rustc_public/src/mir/body.rs
@@ -0,0 +1,1127 @@
+use std::io;
+
+use serde::Serialize;
+
+use crate::compiler_interface::with;
+use crate::mir::pretty::function_body;
+use crate::ty::{
+    AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability,
+    Region, RigidTy, Ty, TyConst, TyKind, VariantIdx,
+};
+use crate::{Error, Opaque, Span, Symbol};
+
+/// 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),
+    ResumedAfterDrop(CoroutineKind),
+    MisalignedPointerDereference { required: Operand, found: Operand },
+    NullPointerDereference,
+    InvalidEnumConstruction(Operand),
+}
+
+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::ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
+                Ok("coroutine resumed after async drop")
+            }
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                _,
+            )) => Ok("`async fn` resumed after async drop"),
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+                CoroutineDesugaring::Gen,
+                _,
+            )) => Ok("`async gen fn` resumed after async drop"),
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+                CoroutineDesugaring::AsyncGen,
+                _,
+            )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after async drop"),
+
+            AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"),
+            AssertMessage::MisalignedPointerDereference { .. } => {
+                Ok("misaligned pointer dereference")
+            }
+            AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"),
+            AssertMessage::InvalidEnumConstruction(_) => {
+                Ok("trying to construct an enum from an invalid value")
+            }
+        }
+    }
+}
+
+#[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(crate::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(rustc_public): 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),
+    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_public/src/mir/mono.rs b/compiler/rustc_public/src/mir/mono.rs
new file mode 100644
index 00000000000..c85f0fa36f7
--- /dev/null
+++ b/compiler/rustc_public/src/mir/mono.rs
@@ -0,0 +1,305 @@
+use std::fmt::{Debug, Formatter};
+use std::io;
+
+use rustc_public_bridge::bridge::SmirError;
+use serde::Serialize;
+
+use crate::abi::FnAbi;
+use crate::crate_def::CrateDef;
+use crate::mir::Body;
+use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, Ty};
+use crate::{CrateItem, DefId, Error, IndexedVal, ItemKind, Opaque, Symbol, with};
+
+#[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))
+    }
+
+    /// 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 = crate::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 = crate::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 = crate::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 = crate::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_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs
new file mode 100644
index 00000000000..f496d80053e
--- /dev/null
+++ b/compiler/rustc_public/src/mir/pretty.rs
@@ -0,0 +1,469 @@
+//! 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 super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
+use crate::mir::{
+    Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents,
+};
+use crate::ty::{AdtKind, AssocKind, MirConst, Ty, TyConst};
+use crate::{Body, CrateDef, IndexedVal, Mutability, with};
+
+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 { has_self: true, .. } => write!(f, "method"),
+            AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"),
+            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::InvalidEnumConstruction(op) => {
+            let pretty_op = pretty_operand(op);
+            write!(writer, "\"trying to construct an enum from an invalid value {{}}\",{pretty_op}")
+        }
+        AssertMessage::ResumedAfterReturn(_)
+        | AssertMessage::ResumedAfterPanic(_)
+        | AssertMessage::ResumedAfterDrop(_) => {
+            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_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs
new file mode 100644
index 00000000000..7dc99d1d1e1
--- /dev/null
+++ b/compiler/rustc_public/src/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 crate::mir::*;
+use crate::ty::{GenericArgs, MirConst, Region, Ty, TyConst};
+use crate::{Error, Opaque, Span};
+
+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)
+                    | AssertMessage::InvalidEnumConstruction(op) => {
+                        self.visit_operand(op, location);
+                    }
+                    AssertMessage::ResumedAfterReturn(_)
+                    | AssertMessage::ResumedAfterPanic(_)
+                    | AssertMessage::NullPointerDereference
+                    | AssertMessage::ResumedAfterDrop(_) => {
+                        //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_public/src/rustc_internal/mod.rs b/compiler/rustc_public/src/rustc_internal/mod.rs
new file mode 100644
index 00000000000..5d7c8256d5b
--- /dev/null
+++ b/compiler/rustc_public/src/rustc_internal/mod.rs
@@ -0,0 +1,270 @@
+//! Module that implements the bridge between Stable MIR and internal compiler MIR.
+//!
+//! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
+//! until stable MIR is complete.
+
+use std::cell::{Cell, RefCell};
+
+use rustc_middle::ty::TyCtxt;
+use rustc_public_bridge::context::SmirCtxt;
+use rustc_public_bridge::{Bridge, SmirContainer, Tables};
+use rustc_span::def_id::CrateNum;
+use scoped_tls::scoped_thread_local;
+
+use crate::Error;
+use crate::unstable::{RustcInternal, Stable};
+
+pub mod pretty;
+
+/// Convert an internal Rust compiler item into its stable counterpart, if one exists.
+///
+/// # Warning
+///
+/// This function is unstable, and its behavior may change at any point.
+/// E.g.: Items that were previously supported, may no longer be supported, or its translation may
+/// change.
+///
+/// # Panics
+///
+/// This function will panic if StableMIR has not been properly initialized.
+pub fn stable<'tcx, S: Stable<'tcx>>(item: S) -> S::T {
+    with_container(|tables, cx| item.stable(tables, cx))
+}
+
+/// Convert a stable item into its internal Rust compiler counterpart, if one exists.
+///
+/// # Warning
+///
+/// This function is unstable, and it's behavior may change at any point.
+/// Not every stable item can be converted to an internal one.
+/// Furthermore, items that were previously supported, may no longer be supported in newer versions.
+///
+/// # Panics
+///
+/// This function will panic if StableMIR has not been properly initialized.
+pub fn internal<'tcx, S>(tcx: TyCtxt<'tcx>, item: S) -> S::T<'tcx>
+where
+    S: RustcInternal,
+{
+    // The tcx argument ensures that the item won't outlive the type context.
+    // See https://github.com/rust-lang/rust/pull/120128/commits/9aace6723572438a94378451793ca37deb768e72
+    // for more details.
+    with_container(|tables, _| item.internal(tables, tcx))
+}
+
+pub fn crate_num(item: &crate::Crate) -> CrateNum {
+    item.id.into()
+}
+
+// A thread local variable that stores a pointer to the tables mapping between TyCtxt
+// datastructures and stable MIR datastructures
+scoped_thread_local! (static TLV: Cell<*const ()>);
+
+pub(crate) fn init<'tcx, F, T, B: Bridge>(container: &SmirContainer<'tcx, B>, f: F) -> T
+where
+    F: FnOnce() -> T,
+{
+    assert!(!TLV.is_set());
+    let ptr = container as *const _ as *const ();
+    TLV.set(&Cell::new(ptr), || f())
+}
+
+/// Loads the current context and calls a function with it.
+/// Do not nest these, as that will ICE.
+pub(crate) fn with_container<R, B: Bridge>(
+    f: impl for<'tcx> FnOnce(&mut Tables<'tcx, B>, &SmirCtxt<'tcx, B>) -> R,
+) -> R {
+    assert!(TLV.is_set());
+    TLV.with(|tlv| {
+        let ptr = tlv.get();
+        assert!(!ptr.is_null());
+        let container = ptr as *const SmirContainer<'_, B>;
+        let mut tables = unsafe { (*container).tables.borrow_mut() };
+        let cx = unsafe { (*container).cx.borrow() };
+        f(&mut *tables, &*cx)
+    })
+}
+
+pub fn run<F, T>(tcx: TyCtxt<'_>, f: F) -> Result<T, Error>
+where
+    F: FnOnce() -> T,
+{
+    let smir_cx = RefCell::new(SmirCtxt::new(tcx));
+    let container = SmirContainer { tables: RefCell::new(Tables::default()), cx: smir_cx };
+
+    crate::compiler_interface::run(&container, || init(&container, f))
+}
+
+/// Instantiate and run the compiler with the provided arguments and callback.
+///
+/// The callback will be invoked after the compiler ran all its analyses, but before code generation.
+/// Note that this macro accepts two different formats for the callback:
+/// 1. An ident that resolves to a function that accepts no argument and returns `ControlFlow<B, C>`
+/// ```ignore(needs-extern-crate)
+/// # extern crate rustc_driver;
+/// # extern crate rustc_interface;
+/// # extern crate rustc_middle;
+/// # #[macro_use]
+/// # extern crate rustc_public;
+/// #
+/// # fn main() {
+/// #   use std::ops::ControlFlow;
+/// #   use rustc_public::CompilerError;
+///     fn analyze_code() -> ControlFlow<(), ()> {
+///         // Your code goes in here.
+/// #       ControlFlow::Continue(())
+///     }
+/// #   let args = &["--verbose".to_string()];
+///     let result = run!(args, analyze_code);
+/// #   assert_eq!(result, Err(CompilerError::Skipped))
+/// # }
+/// ```
+/// 2. A closure expression:
+/// ```ignore(needs-extern-crate)
+/// # extern crate rustc_driver;
+/// # extern crate rustc_interface;
+/// # extern crate rustc_middle;
+/// # #[macro_use]
+/// # extern crate rustc_public;
+/// #
+/// # fn main() {
+/// #   use std::ops::ControlFlow;
+/// #   use rustc_public::CompilerError;
+///     fn analyze_code(extra_args: Vec<String>) -> ControlFlow<(), ()> {
+/// #       let _ = extra_args;
+///         // Your code goes in here.
+/// #       ControlFlow::Continue(())
+///     }
+/// #   let args = &["--verbose".to_string()];
+/// #   let extra_args = vec![];
+///     let result = run!(args, || analyze_code(extra_args));
+/// #   assert_eq!(result, Err(CompilerError::Skipped))
+/// # }
+/// ```
+#[macro_export]
+macro_rules! run {
+    ($args:expr, $callback_fn:ident) => {
+        run_driver!($args, || $callback_fn())
+    };
+    ($args:expr, $callback:expr) => {
+        run_driver!($args, $callback)
+    };
+}
+
+/// Instantiate and run the compiler with the provided arguments and callback.
+///
+/// This is similar to `run` but it invokes the callback with the compiler's `TyCtxt`,
+/// which can be used to invoke internal APIs.
+#[macro_export]
+macro_rules! run_with_tcx {
+    ($args:expr, $callback_fn:ident) => {
+        run_driver!($args, |tcx| $callback_fn(tcx), with_tcx)
+    };
+    ($args:expr, $callback:expr) => {
+        run_driver!($args, $callback, with_tcx)
+    };
+}
+
+/// Optionally include an ident. This is needed due to macro hygiene.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! optional {
+    (with_tcx $ident:ident) => {
+        $ident
+    };
+}
+
+/// Prefer using [run!] and [run_with_tcx] instead.
+///
+/// This macro implements the instantiation of a StableMIR driver, and it will invoke
+/// the given callback after the compiler analyses.
+///
+/// The third argument determines whether the callback requires `tcx` as an argument.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! run_driver {
+    ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{
+        use rustc_driver::{Callbacks, Compilation, run_compiler};
+        use rustc_middle::ty::TyCtxt;
+        use rustc_interface::interface;
+        use rustc_public::rustc_internal;
+        use rustc_public::CompilerError;
+        use std::ops::ControlFlow;
+
+        pub struct StableMir<B = (), C = (), F = fn($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C>>
+        where
+            B: Send,
+            C: Send,
+            F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+        {
+            callback: Option<F>,
+            result: Option<ControlFlow<B, C>>,
+        }
+
+        impl<B, C, F> StableMir<B, C, F>
+        where
+            B: Send,
+            C: Send,
+            F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+        {
+            /// Creates a new `StableMir` instance, with given test_function and arguments.
+            pub fn new(callback: F) -> Self {
+                StableMir { callback: Some(callback), result: None }
+            }
+
+            /// Runs the compiler against given target and tests it with `test_function`
+            pub fn run(&mut self, args: &[String]) -> Result<C, CompilerError<B>> {
+                let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> {
+                    run_compiler(&args, self);
+                    Ok(())
+                });
+                match (compiler_result, self.result.take()) {
+                    (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value),
+                    (Ok(Ok(())), Some(ControlFlow::Break(value))) => {
+                        Err(CompilerError::Interrupted(value))
+                    }
+                    (Ok(Ok(_)), None) => Err(CompilerError::Skipped),
+                    // Two cases here:
+                    // - `run` finished normally and returned `Err`
+                    // - `run` panicked with `FatalErr`
+                    // You might think that normal compile errors cause the former, and
+                    // ICEs cause the latter. But some normal compiler errors also cause
+                    // the latter. So we can't meaningfully distinguish them, and group
+                    // them together.
+                    (Ok(Err(_)), _) | (Err(_), _) => Err(CompilerError::Failed),
+                }
+            }
+        }
+
+        impl<B, C, F> Callbacks for StableMir<B, C, F>
+        where
+            B: Send,
+            C: Send,
+            F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+        {
+            /// Called after analysis. Return value instructs the compiler whether to
+            /// continue the compilation afterwards (defaults to `Compilation::Continue`)
+            fn after_analysis<'tcx>(
+                &mut self,
+                _compiler: &interface::Compiler,
+                tcx: TyCtxt<'tcx>,
+            ) -> Compilation {
+                if let Some(callback) = self.callback.take() {
+                    rustc_internal::run(tcx, || {
+                        self.result = Some(callback($(optional!($with_tcx tcx))?));
+                    })
+                    .unwrap();
+                    if self.result.as_ref().is_some_and(|val| val.is_continue()) {
+                        Compilation::Continue
+                    } else {
+                        Compilation::Stop
+                    }
+                } else {
+                    Compilation::Continue
+                }
+            }
+        }
+
+        StableMir::new($callback).run($args)
+    }};
+}
diff --git a/compiler/rustc_public/src/rustc_internal/pretty.rs b/compiler/rustc_public/src/rustc_internal/pretty.rs
new file mode 100644
index 00000000000..28c5280fe04
--- /dev/null
+++ b/compiler/rustc_public/src/rustc_internal/pretty.rs
@@ -0,0 +1,21 @@
+use std::io;
+
+use rustc_middle::ty::TyCtxt;
+
+use super::run;
+
+pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> {
+    writeln!(
+        w,
+        "// WARNING: This is highly experimental output it's intended for stable-mir developers only."
+    )?;
+    writeln!(
+        w,
+        "// If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir."
+    )?;
+    let _ = run(tcx, || {
+        let items = crate::all_local_items();
+        let _ = items.iter().map(|item| -> io::Result<()> { item.emit_mir(w) }).collect::<Vec<_>>();
+    });
+    Ok(())
+}
diff --git a/compiler/rustc_public/src/target.rs b/compiler/rustc_public/src/target.rs
new file mode 100644
index 00000000000..32c3a2a9122
--- /dev/null
+++ b/compiler/rustc_public/src/target.rs
@@ -0,0 +1,60 @@
+//! Provide information about the machine that this is being compiled into.
+
+use serde::Serialize;
+
+use crate::compiler_interface::with;
+
+/// 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_public/src/ty.rs b/compiler/rustc_public/src/ty.rs
new file mode 100644
index 00000000000..bc67a2f987d
--- /dev/null
+++ b/compiler/rustc_public/src/ty.rs
@@ -0,0 +1,1655 @@
+use std::fmt::{self, Debug, Display, Formatter};
+use std::ops::Range;
+
+use serde::Serialize;
+
+use super::abi::ReprOptions;
+use super::mir::{Body, Mutability, Safety};
+use super::{DefId, Error, Symbol, with};
+use crate::abi::{FnAbi, Layout};
+use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType};
+use crate::mir::alloc::{AllocId, read_target_int, read_target_uint};
+use crate::mir::mono::StaticDef;
+use crate::target::MachineInfo;
+use crate::{Filename, IndexedVal, Opaque};
+
+#[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,
+}
+
+impl LineInfo {
+    pub fn from(lines: (usize, usize, usize, usize)) -> Self {
+        LineInfo { start_line: lines.0, start_col: lines.1, end_line: lines.2, end_col: lines.3 }
+    }
+}
+
+#[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(rustc_public): 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;
+}
+
+impl ClosureDef {
+    /// Retrieves the body of the closure definition. Returns None if the body
+    /// isn't available.
+    pub fn body(&self) -> Option<Body> {
+        with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0)))
+    }
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub CoroutineDef;
+}
+
+impl CoroutineDef {
+    /// Retrieves the body of the coroutine definition. Returns None if the body
+    /// isn't available.
+    pub fn body(&self) -> Option<Body> {
+        with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0)))
+    }
+
+    pub fn discriminant_for_variant(&self, args: &GenericArgs, idx: VariantIdx) -> Discr {
+        with(|cx| cx.coroutine_discr_for_variant(*self, args, idx))
+    }
+}
+
+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 })
+    }
+
+    pub fn repr(&self) -> ReprOptions {
+        with(|cx| cx.adt_repr(*self))
+    }
+
+    pub fn discriminant_for_variant(&self, idx: VariantIdx) -> Discr {
+        with(|cx| cx.adt_discr_for_variant(*self, idx))
+    }
+}
+
+pub struct Discr {
+    pub val: u128,
+    pub ty: Ty,
+}
+
+/// 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,
+    Free,
+}
+
+#[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,
+    RustInvalid,
+    Custom,
+}
+
+/// 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,
+}
+
+#[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(TermKind),
+    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,
+}
+
+macro_rules! index_impl {
+    ($name:ident) => {
+        impl crate::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 information 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 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>,
+}
+
+#[derive(Clone, PartialEq, Debug, Eq, Serialize)]
+pub enum AssocTypeData {
+    Normal(Symbol),
+    /// The associated type comes from an RPITIT. It has no name, and the
+    /// `ImplTraitInTraitData` provides additional information about its
+    /// source.
+    Rpitit(ImplTraitInTraitData),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AssocKind {
+    Const { name: Symbol },
+    Fn { name: Symbol, has_self: bool },
+    Type { data: AssocTypeData },
+}
+
+#[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 {
+        matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) })
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs
new file mode 100644
index 00000000000..8a6238413b0
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/convert/internal.rs
@@ -0,0 +1,823 @@
+//! Module containing the translation from stable mir constructs to the rustc counterpart.
+//!
+//! This module will only include a few constructs to allow users to invoke internal rustc APIs
+//! due to incomplete stable coverage.
+
+// Prefer importing rustc_public over internal rustc constructs to make this file more readable.
+
+use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy};
+use rustc_public_bridge::Tables;
+
+use crate::abi::Layout;
+use crate::compiler_interface::BridgeTys;
+use crate::mir::alloc::AllocId;
+use crate::mir::mono::{Instance, MonoItem, StaticDef};
+use crate::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp};
+use crate::ty::{
+    Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind,
+    ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig,
+    GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span,
+    TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx,
+};
+use crate::unstable::{InternalCx, RustcInternal};
+use crate::{CrateItem, CrateNum, DefId, IndexedVal};
+
+impl RustcInternal for CrateItem {
+    type T<'tcx> = rustc_span::def_id::DefId;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        self.0.internal(tables, tcx)
+    }
+}
+
+impl RustcInternal for CrateNum {
+    type T<'tcx> = rustc_span::def_id::CrateNum;
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        rustc_span::def_id::CrateNum::from_usize(*self)
+    }
+}
+
+impl RustcInternal for DefId {
+    type T<'tcx> = rustc_span::def_id::DefId;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(tables.def_ids[*self]).unwrap()
+    }
+}
+
+impl RustcInternal for GenericArgs {
+    type T<'tcx> = rustc_ty::GenericArgsRef<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        InternalCx::mk_args_from_iter(tcx, self.0.iter().map(|arg| arg.internal(tables, tcx)))
+    }
+}
+
+impl RustcInternal for GenericArgKind {
+    type T<'tcx> = rustc_ty::GenericArg<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        let arg: rustc_ty::GenericArg<'tcx> = match self {
+            GenericArgKind::Lifetime(reg) => reg.internal(tables, tcx).into(),
+            GenericArgKind::Type(ty) => ty.internal(tables, tcx).into(),
+            GenericArgKind::Const(cnst) => cnst.internal(tables, tcx).into(),
+        };
+        tcx.lift(arg).unwrap()
+    }
+}
+
+impl RustcInternal for Region {
+    type T<'tcx> = rustc_ty::Region<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        // Cannot recover region. Use erased for now.
+        tcx.lifetimes_re_erased()
+    }
+}
+
+impl RustcInternal for Ty {
+    type T<'tcx> = InternalTy<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(tables.types[*self]).unwrap()
+    }
+}
+
+impl RustcInternal for TyConst {
+    type T<'tcx> = InternalConst<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(tables.ty_consts[self.id]).unwrap()
+    }
+}
+
+impl RustcInternal for Pattern {
+    type T<'tcx> = rustc_ty::Pattern<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.mk_pat(match self {
+            Pattern::Range { start, end, include_end: _ } => rustc_ty::PatternKind::Range {
+                start: start.as_ref().unwrap().internal(tables, tcx),
+                end: end.as_ref().unwrap().internal(tables, tcx),
+            },
+        })
+    }
+}
+
+impl RustcInternal for RigidTy {
+    type T<'tcx> = rustc_ty::TyKind<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            RigidTy::Bool => rustc_ty::TyKind::Bool,
+            RigidTy::Char => rustc_ty::TyKind::Char,
+            RigidTy::Int(int_ty) => rustc_ty::TyKind::Int(int_ty.internal(tables, tcx)),
+            RigidTy::Uint(uint_ty) => rustc_ty::TyKind::Uint(uint_ty.internal(tables, tcx)),
+            RigidTy::Float(float_ty) => rustc_ty::TyKind::Float(float_ty.internal(tables, tcx)),
+            RigidTy::Never => rustc_ty::TyKind::Never,
+            RigidTy::Array(ty, cnst) => {
+                rustc_ty::TyKind::Array(ty.internal(tables, tcx), cnst.internal(tables, tcx))
+            }
+            RigidTy::Pat(ty, pat) => {
+                rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx))
+            }
+            RigidTy::Adt(def, args) => {
+                rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx))
+            }
+            RigidTy::Str => rustc_ty::TyKind::Str,
+            RigidTy::Slice(ty) => rustc_ty::TyKind::Slice(ty.internal(tables, tcx)),
+            RigidTy::RawPtr(ty, mutability) => {
+                rustc_ty::TyKind::RawPtr(ty.internal(tables, tcx), mutability.internal(tables, tcx))
+            }
+            RigidTy::Ref(region, ty, mutability) => rustc_ty::TyKind::Ref(
+                region.internal(tables, tcx),
+                ty.internal(tables, tcx),
+                mutability.internal(tables, tcx),
+            ),
+            RigidTy::Foreign(def) => rustc_ty::TyKind::Foreign(def.0.internal(tables, tcx)),
+            RigidTy::FnDef(def, args) => {
+                rustc_ty::TyKind::FnDef(def.0.internal(tables, tcx), args.internal(tables, tcx))
+            }
+            RigidTy::FnPtr(sig) => {
+                let (sig_tys, hdr) = sig.internal(tables, tcx).split();
+                rustc_ty::TyKind::FnPtr(sig_tys, hdr)
+            }
+            RigidTy::Closure(def, args) => {
+                rustc_ty::TyKind::Closure(def.0.internal(tables, tcx), args.internal(tables, tcx))
+            }
+            RigidTy::Coroutine(def, args, _mov) => {
+                rustc_ty::TyKind::Coroutine(def.0.internal(tables, tcx), args.internal(tables, tcx))
+            }
+            RigidTy::CoroutineClosure(def, args) => rustc_ty::TyKind::CoroutineClosure(
+                def.0.internal(tables, tcx),
+                args.internal(tables, tcx),
+            ),
+            RigidTy::CoroutineWitness(def, args) => rustc_ty::TyKind::CoroutineWitness(
+                def.0.internal(tables, tcx),
+                args.internal(tables, tcx),
+            ),
+            RigidTy::Dynamic(predicate, region, dyn_kind) => rustc_ty::TyKind::Dynamic(
+                tcx.mk_poly_existential_predicates(&predicate.internal(tables, tcx)),
+                region.internal(tables, tcx),
+                dyn_kind.internal(tables, tcx),
+            ),
+            RigidTy::Tuple(tys) => {
+                rustc_ty::TyKind::Tuple(tcx.mk_type_list(&tys.internal(tables, tcx)))
+            }
+        }
+    }
+}
+
+impl RustcInternal for IntTy {
+    type T<'tcx> = rustc_ty::IntTy;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            IntTy::Isize => rustc_ty::IntTy::Isize,
+            IntTy::I8 => rustc_ty::IntTy::I8,
+            IntTy::I16 => rustc_ty::IntTy::I16,
+            IntTy::I32 => rustc_ty::IntTy::I32,
+            IntTy::I64 => rustc_ty::IntTy::I64,
+            IntTy::I128 => rustc_ty::IntTy::I128,
+        }
+    }
+}
+
+impl RustcInternal for UintTy {
+    type T<'tcx> = rustc_ty::UintTy;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            UintTy::Usize => rustc_ty::UintTy::Usize,
+            UintTy::U8 => rustc_ty::UintTy::U8,
+            UintTy::U16 => rustc_ty::UintTy::U16,
+            UintTy::U32 => rustc_ty::UintTy::U32,
+            UintTy::U64 => rustc_ty::UintTy::U64,
+            UintTy::U128 => rustc_ty::UintTy::U128,
+        }
+    }
+}
+
+impl RustcInternal for FloatTy {
+    type T<'tcx> = rustc_ty::FloatTy;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            FloatTy::F16 => rustc_ty::FloatTy::F16,
+            FloatTy::F32 => rustc_ty::FloatTy::F32,
+            FloatTy::F64 => rustc_ty::FloatTy::F64,
+            FloatTy::F128 => rustc_ty::FloatTy::F128,
+        }
+    }
+}
+
+impl RustcInternal for Mutability {
+    type T<'tcx> = rustc_ty::Mutability;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            Mutability::Not => rustc_ty::Mutability::Not,
+            Mutability::Mut => rustc_ty::Mutability::Mut,
+        }
+    }
+}
+
+impl RustcInternal for Movability {
+    type T<'tcx> = rustc_ty::Movability;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            Movability::Static => rustc_ty::Movability::Static,
+            Movability::Movable => rustc_ty::Movability::Movable,
+        }
+    }
+}
+
+impl RustcInternal for RawPtrKind {
+    type T<'tcx> = rustc_middle::mir::RawPtrKind;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            RawPtrKind::Mut => rustc_middle::mir::RawPtrKind::Mut,
+            RawPtrKind::Const => rustc_middle::mir::RawPtrKind::Const,
+            RawPtrKind::FakeForPtrMetadata => rustc_middle::mir::RawPtrKind::FakeForPtrMetadata,
+        }
+    }
+}
+
+impl RustcInternal for FnSig {
+    type T<'tcx> = rustc_ty::FnSig<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(rustc_ty::FnSig {
+            inputs_and_output: tcx.mk_type_list(&self.inputs_and_output.internal(tables, tcx)),
+            c_variadic: self.c_variadic,
+            safety: self.safety.internal(tables, tcx),
+            abi: self.abi.internal(tables, tcx),
+        })
+        .unwrap()
+    }
+}
+
+impl RustcInternal for VariantIdx {
+    type T<'tcx> = rustc_abi::VariantIdx;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        rustc_abi::VariantIdx::from(self.to_index())
+    }
+}
+
+impl RustcInternal for VariantDef {
+    type T<'tcx> = &'tcx rustc_ty::VariantDef;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        self.adt_def.internal(tables, tcx).variant(self.idx.internal(tables, tcx))
+    }
+}
+
+impl RustcInternal for MirConst {
+    type T<'tcx> = rustc_middle::mir::Const<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        let constant = tables.mir_consts[self.id];
+        match constant {
+            rustc_middle::mir::Const::Ty(ty, ct) => {
+                rustc_middle::mir::Const::Ty(tcx.lift(ty).unwrap(), tcx.lift(ct).unwrap())
+            }
+            rustc_middle::mir::Const::Unevaluated(uneval, ty) => {
+                rustc_middle::mir::Const::Unevaluated(
+                    tcx.lift(uneval).unwrap(),
+                    tcx.lift(ty).unwrap(),
+                )
+            }
+            rustc_middle::mir::Const::Val(const_val, ty) => {
+                rustc_middle::mir::Const::Val(tcx.lift(const_val).unwrap(), tcx.lift(ty).unwrap())
+            }
+        }
+    }
+}
+
+impl RustcInternal for MonoItem {
+    type T<'tcx> = rustc_middle::mir::mono::MonoItem<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use rustc_middle::mir::mono as rustc_mono;
+        match self {
+            MonoItem::Fn(instance) => rustc_mono::MonoItem::Fn(instance.internal(tables, tcx)),
+            MonoItem::Static(def) => rustc_mono::MonoItem::Static(def.internal(tables, tcx)),
+            MonoItem::GlobalAsm(_) => {
+                unimplemented!()
+            }
+        }
+    }
+}
+
+impl RustcInternal for Instance {
+    type T<'tcx> = rustc_ty::Instance<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(tables.instances[self.def]).unwrap()
+    }
+}
+
+impl RustcInternal for StaticDef {
+    type T<'tcx> = rustc_span::def_id::DefId;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        self.0.internal(tables, tcx)
+    }
+}
+
+#[allow(rustc::usage_of_qualified_ty)]
+impl<T> RustcInternal for Binder<T>
+where
+    T: RustcInternal,
+    for<'tcx> T::T<'tcx>: rustc_ty::TypeVisitable<rustc_ty::TyCtxt<'tcx>>,
+{
+    type T<'tcx> = rustc_ty::Binder<'tcx, T::T<'tcx>>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        rustc_ty::Binder::bind_with_vars(
+            self.value.internal(tables, tcx),
+            tcx.mk_bound_variable_kinds_from_iter(
+                self.bound_vars.iter().map(|bound| bound.internal(tables, tcx)),
+            ),
+        )
+    }
+}
+
+impl RustcInternal for BoundVariableKind {
+    type T<'tcx> = rustc_ty::BoundVariableKind;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            BoundVariableKind::Ty(kind) => rustc_ty::BoundVariableKind::Ty(match kind {
+                BoundTyKind::Anon => rustc_ty::BoundTyKind::Anon,
+                BoundTyKind::Param(def, _symbol) => {
+                    rustc_ty::BoundTyKind::Param(def.0.internal(tables, tcx))
+                }
+            }),
+            BoundVariableKind::Region(kind) => rustc_ty::BoundVariableKind::Region(match kind {
+                BoundRegionKind::BrAnon => rustc_ty::BoundRegionKind::Anon,
+                BoundRegionKind::BrNamed(def, _symbol) => {
+                    rustc_ty::BoundRegionKind::Named(def.0.internal(tables, tcx))
+                }
+                BoundRegionKind::BrEnv => rustc_ty::BoundRegionKind::ClosureEnv,
+            }),
+            BoundVariableKind::Const => rustc_ty::BoundVariableKind::Const,
+        }
+    }
+}
+
+impl RustcInternal for DynKind {
+    type T<'tcx> = rustc_ty::DynKind;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            DynKind::Dyn => rustc_ty::DynKind::Dyn,
+        }
+    }
+}
+
+impl RustcInternal for ExistentialPredicate {
+    type T<'tcx> = rustc_ty::ExistentialPredicate<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            ExistentialPredicate::Trait(trait_ref) => {
+                rustc_ty::ExistentialPredicate::Trait(trait_ref.internal(tables, tcx))
+            }
+            ExistentialPredicate::Projection(proj) => {
+                rustc_ty::ExistentialPredicate::Projection(proj.internal(tables, tcx))
+            }
+            ExistentialPredicate::AutoTrait(trait_def) => {
+                rustc_ty::ExistentialPredicate::AutoTrait(trait_def.0.internal(tables, tcx))
+            }
+        }
+    }
+}
+
+impl RustcInternal for ExistentialProjection {
+    type T<'tcx> = rustc_ty::ExistentialProjection<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use crate::unstable::internal_cx::SmirExistentialProjection;
+        tcx.new_from_args(
+            self.def_id.0.internal(tables, tcx),
+            self.generic_args.internal(tables, tcx),
+            self.term.internal(tables, tcx),
+        )
+    }
+}
+
+impl RustcInternal for TermKind {
+    type T<'tcx> = rustc_ty::Term<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            TermKind::Type(ty) => ty.internal(tables, tcx).into(),
+            TermKind::Const(cnst) => cnst.internal(tables, tcx).into(),
+        }
+    }
+}
+
+impl RustcInternal for ExistentialTraitRef {
+    type T<'tcx> = rustc_ty::ExistentialTraitRef<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use crate::unstable::internal_cx::SmirExistentialTraitRef;
+        tcx.new_from_args(
+            self.def_id.0.internal(tables, tcx),
+            self.generic_args.internal(tables, tcx),
+        )
+    }
+}
+
+impl RustcInternal for TraitRef {
+    type T<'tcx> = rustc_ty::TraitRef<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use crate::unstable::internal_cx::SmirTraitRef;
+        tcx.new_from_args(self.def_id.0.internal(tables, tcx), self.args().internal(tables, tcx))
+    }
+}
+
+impl RustcInternal for AllocId {
+    type T<'tcx> = rustc_middle::mir::interpret::AllocId;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(tables.alloc_ids[*self]).unwrap()
+    }
+}
+
+impl RustcInternal for ClosureKind {
+    type T<'tcx> = rustc_ty::ClosureKind;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            ClosureKind::Fn => rustc_ty::ClosureKind::Fn,
+            ClosureKind::FnMut => rustc_ty::ClosureKind::FnMut,
+            ClosureKind::FnOnce => rustc_ty::ClosureKind::FnOnce,
+        }
+    }
+}
+
+impl RustcInternal for AdtDef {
+    type T<'tcx> = rustc_ty::AdtDef<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        InternalCx::adt_def(tcx, self.0.internal(tables, tcx))
+    }
+}
+
+impl RustcInternal for Abi {
+    type T<'tcx> = rustc_abi::ExternAbi;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match *self {
+            Abi::Rust => rustc_abi::ExternAbi::Rust,
+            Abi::C { unwind } => rustc_abi::ExternAbi::C { unwind },
+            Abi::Cdecl { unwind } => rustc_abi::ExternAbi::Cdecl { unwind },
+            Abi::Stdcall { unwind } => rustc_abi::ExternAbi::Stdcall { unwind },
+            Abi::Fastcall { unwind } => rustc_abi::ExternAbi::Fastcall { unwind },
+            Abi::Vectorcall { unwind } => rustc_abi::ExternAbi::Vectorcall { unwind },
+            Abi::Thiscall { unwind } => rustc_abi::ExternAbi::Thiscall { unwind },
+            Abi::Aapcs { unwind } => rustc_abi::ExternAbi::Aapcs { unwind },
+            Abi::CCmseNonSecureCall => rustc_abi::ExternAbi::CmseNonSecureCall,
+            Abi::CCmseNonSecureEntry => rustc_abi::ExternAbi::CmseNonSecureEntry,
+            Abi::Win64 { unwind } => rustc_abi::ExternAbi::Win64 { unwind },
+            Abi::SysV64 { unwind } => rustc_abi::ExternAbi::SysV64 { unwind },
+            Abi::PtxKernel => rustc_abi::ExternAbi::PtxKernel,
+            Abi::Msp430Interrupt => rustc_abi::ExternAbi::Msp430Interrupt,
+            Abi::X86Interrupt => rustc_abi::ExternAbi::X86Interrupt,
+            Abi::GpuKernel => rustc_abi::ExternAbi::GpuKernel,
+            Abi::EfiApi => rustc_abi::ExternAbi::EfiApi,
+            Abi::AvrInterrupt => rustc_abi::ExternAbi::AvrInterrupt,
+            Abi::AvrNonBlockingInterrupt => rustc_abi::ExternAbi::AvrNonBlockingInterrupt,
+            Abi::System { unwind } => rustc_abi::ExternAbi::System { unwind },
+            Abi::RustCall => rustc_abi::ExternAbi::RustCall,
+            Abi::Unadjusted => rustc_abi::ExternAbi::Unadjusted,
+            Abi::RustCold => rustc_abi::ExternAbi::RustCold,
+            Abi::RustInvalid => rustc_abi::ExternAbi::RustInvalid,
+            Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
+            Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
+            Abi::Custom => rustc_abi::ExternAbi::Custom,
+        }
+    }
+}
+
+impl RustcInternal for Safety {
+    type T<'tcx> = rustc_hir::Safety;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            Safety::Unsafe => rustc_hir::Safety::Unsafe,
+            Safety::Safe => rustc_hir::Safety::Safe,
+        }
+    }
+}
+impl RustcInternal for Span {
+    type T<'tcx> = rustc_span::Span;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tables.spans[*self]
+    }
+}
+
+impl RustcInternal for Layout {
+    type T<'tcx> = rustc_abi::Layout<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        tcx.lift(tables.layouts[*self]).unwrap()
+    }
+}
+
+impl RustcInternal for Place {
+    type T<'tcx> = rustc_middle::mir::Place<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        rustc_middle::mir::Place {
+            local: rustc_middle::mir::Local::from_usize(self.local),
+            projection: tcx.mk_place_elems(&self.projection.internal(tables, tcx)),
+        }
+    }
+}
+
+impl RustcInternal for ProjectionElem {
+    type T<'tcx> = rustc_middle::mir::PlaceElem<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            ProjectionElem::Deref => rustc_middle::mir::PlaceElem::Deref,
+            ProjectionElem::Field(idx, ty) => {
+                rustc_middle::mir::PlaceElem::Field((*idx).into(), ty.internal(tables, tcx))
+            }
+            ProjectionElem::Index(idx) => rustc_middle::mir::PlaceElem::Index((*idx).into()),
+            ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
+                rustc_middle::mir::PlaceElem::ConstantIndex {
+                    offset: *offset,
+                    min_length: *min_length,
+                    from_end: *from_end,
+                }
+            }
+            ProjectionElem::Subslice { from, to, from_end } => {
+                rustc_middle::mir::PlaceElem::Subslice { from: *from, to: *to, from_end: *from_end }
+            }
+            ProjectionElem::Downcast(idx) => {
+                rustc_middle::mir::PlaceElem::Downcast(None, idx.internal(tables, tcx))
+            }
+            ProjectionElem::OpaqueCast(ty) => {
+                rustc_middle::mir::PlaceElem::OpaqueCast(ty.internal(tables, tcx))
+            }
+            ProjectionElem::Subtype(ty) => {
+                rustc_middle::mir::PlaceElem::Subtype(ty.internal(tables, tcx))
+            }
+        }
+    }
+}
+
+impl RustcInternal for BinOp {
+    type T<'tcx> = rustc_middle::mir::BinOp;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            BinOp::Add => rustc_middle::mir::BinOp::Add,
+            BinOp::AddUnchecked => rustc_middle::mir::BinOp::AddUnchecked,
+            BinOp::Sub => rustc_middle::mir::BinOp::Sub,
+            BinOp::SubUnchecked => rustc_middle::mir::BinOp::SubUnchecked,
+            BinOp::Mul => rustc_middle::mir::BinOp::Mul,
+            BinOp::MulUnchecked => rustc_middle::mir::BinOp::MulUnchecked,
+            BinOp::Div => rustc_middle::mir::BinOp::Div,
+            BinOp::Rem => rustc_middle::mir::BinOp::Rem,
+            BinOp::BitXor => rustc_middle::mir::BinOp::BitXor,
+            BinOp::BitAnd => rustc_middle::mir::BinOp::BitAnd,
+            BinOp::BitOr => rustc_middle::mir::BinOp::BitOr,
+            BinOp::Shl => rustc_middle::mir::BinOp::Shl,
+            BinOp::ShlUnchecked => rustc_middle::mir::BinOp::ShlUnchecked,
+            BinOp::Shr => rustc_middle::mir::BinOp::Shr,
+            BinOp::ShrUnchecked => rustc_middle::mir::BinOp::ShrUnchecked,
+            BinOp::Eq => rustc_middle::mir::BinOp::Eq,
+            BinOp::Lt => rustc_middle::mir::BinOp::Lt,
+            BinOp::Le => rustc_middle::mir::BinOp::Le,
+            BinOp::Ne => rustc_middle::mir::BinOp::Ne,
+            BinOp::Ge => rustc_middle::mir::BinOp::Ge,
+            BinOp::Gt => rustc_middle::mir::BinOp::Gt,
+            BinOp::Cmp => rustc_middle::mir::BinOp::Cmp,
+            BinOp::Offset => rustc_middle::mir::BinOp::Offset,
+        }
+    }
+}
+
+impl RustcInternal for UnOp {
+    type T<'tcx> = rustc_middle::mir::UnOp;
+
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        match self {
+            UnOp::Not => rustc_middle::mir::UnOp::Not,
+            UnOp::Neg => rustc_middle::mir::UnOp::Neg,
+            UnOp::PtrMetadata => rustc_middle::mir::UnOp::PtrMetadata,
+        }
+    }
+}
+
+impl<T> RustcInternal for &T
+where
+    T: RustcInternal,
+{
+    type T<'tcx> = T::T<'tcx>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        (*self).internal(tables, tcx)
+    }
+}
+
+impl<T> RustcInternal for Option<T>
+where
+    T: RustcInternal,
+{
+    type T<'tcx> = Option<T::T<'tcx>>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        self.as_ref().map(|inner| inner.internal(tables, tcx))
+    }
+}
+
+impl<T> RustcInternal for Vec<T>
+where
+    T: RustcInternal,
+{
+    type T<'tcx> = Vec<T::T<'tcx>>;
+
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        self.iter().map(|e| e.internal(tables, tcx)).collect()
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/convert/mod.rs b/compiler/rustc_public/src/unstable/convert/mod.rs
new file mode 100644
index 00000000000..85a71e09c3e
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/convert/mod.rs
@@ -0,0 +1,110 @@
+//! This module holds the logic to convert rustc internal ADTs into stable mir ADTs.
+//!
+//! The conversion from stable to internal is not meant to be complete,
+//! and it should be added as when needed to be passed as input to rustc_public_bridge functions.
+//!
+//! For contributors, please make sure to avoid calling rustc's internal functions and queries.
+//! These should be done via `rustc_public_bridge` APIs, but it's possible to access ADT fields directly.
+
+use std::ops::RangeInclusive;
+
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::context::SmirCtxt;
+
+use super::Stable;
+use crate::compiler_interface::BridgeTys;
+
+mod internal;
+mod stable;
+
+impl<'tcx, T> Stable<'tcx> for &T
+where
+    T: Stable<'tcx>,
+{
+    type T = T::T;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        (*self).stable(tables, cx)
+    }
+}
+
+impl<'tcx, T> Stable<'tcx> for Option<T>
+where
+    T: Stable<'tcx>,
+{
+    type T = Option<T::T>;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        self.as_ref().map(|value| value.stable(tables, cx))
+    }
+}
+
+impl<'tcx, T, E> Stable<'tcx> for Result<T, E>
+where
+    T: Stable<'tcx>,
+    E: Stable<'tcx>,
+{
+    type T = Result<T::T, E::T>;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            Ok(val) => Ok(val.stable(tables, cx)),
+            Err(error) => Err(error.stable(tables, cx)),
+        }
+    }
+}
+
+impl<'tcx, T> Stable<'tcx> for &[T]
+where
+    T: Stable<'tcx>,
+{
+    type T = Vec<T::T>;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        self.iter().map(|e| e.stable(tables, cx)).collect()
+    }
+}
+
+impl<'tcx, T, U> Stable<'tcx> for (T, U)
+where
+    T: Stable<'tcx>,
+    U: Stable<'tcx>,
+{
+    type T = (T::T, U::T);
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        (self.0.stable(tables, cx), self.1.stable(tables, cx))
+    }
+}
+
+impl<'tcx, T> Stable<'tcx> for RangeInclusive<T>
+where
+    T: Stable<'tcx>,
+{
+    type T = RangeInclusive<T::T>;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        RangeInclusive::new(self.start().stable(tables, cx), self.end().stable(tables, cx))
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs
new file mode 100644
index 00000000000..40a8bf614e1
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs
@@ -0,0 +1,410 @@
+//! Conversion of internal Rust compiler `rustc_target` and `rustc_abi` items to stable ones.
+
+#![allow(rustc::usage_of_qualified_ty)]
+
+use rustc_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
+use rustc_middle::ty;
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::context::SmirCtxt;
+use rustc_target::callconv;
+
+use crate::abi::{
+    AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength,
+    IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar,
+    TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange,
+};
+use crate::compiler_interface::BridgeTys;
+use crate::target::MachineSize as Size;
+use crate::ty::{Align, VariantIdx};
+use crate::unstable::Stable;
+use crate::{IndexedVal, opaque};
+
+impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx {
+    type T = VariantIdx;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        VariantIdx::to_val(self.as_usize())
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
+    type T = crate::target::Endian;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            rustc_abi::Endian::Little => crate::target::Endian::Little,
+            rustc_abi::Endian::Big => crate::target::Endian::Big,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::TyAndLayout<'tcx, ty::Ty<'tcx>> {
+    type T = TyAndLayout;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        TyAndLayout { ty: self.ty.stable(tables, cx), layout: self.layout.stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Layout<'tcx> {
+    type T = Layout;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        tables.layout_id(cx.lift(*self).unwrap())
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::LayoutData<rustc_abi::FieldIdx, rustc_abi::VariantIdx> {
+    type T = LayoutShape;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        LayoutShape {
+            fields: self.fields.stable(tables, cx),
+            variants: self.variants.stable(tables, cx),
+            abi: self.backend_repr.stable(tables, cx),
+            abi_align: self.align.abi.stable(tables, cx),
+            size: self.size.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for callconv::FnAbi<'tcx, ty::Ty<'tcx>> {
+    type T = FnAbi;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        assert!(self.args.len() >= self.fixed_count as usize);
+        assert!(!self.c_variadic || matches!(self.conv, CanonAbi::C));
+        FnAbi {
+            args: self.args.as_ref().stable(tables, cx),
+            ret: self.ret.stable(tables, cx),
+            fixed_count: self.fixed_count,
+            conv: self.conv.stable(tables, cx),
+            c_variadic: self.c_variadic,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for callconv::ArgAbi<'tcx, ty::Ty<'tcx>> {
+    type T = ArgAbi;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        ArgAbi {
+            ty: self.layout.ty.stable(tables, cx),
+            layout: self.layout.layout.stable(tables, cx),
+            mode: self.mode.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for CanonAbi {
+    type T = CallConvention;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            CanonAbi::C => CallConvention::C,
+            CanonAbi::Rust => CallConvention::Rust,
+            CanonAbi::RustCold => CallConvention::Cold,
+            CanonAbi::Custom => CallConvention::Custom,
+            CanonAbi::Arm(arm_call) => match arm_call {
+                ArmCall::Aapcs => CallConvention::ArmAapcs,
+                ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,
+                ArmCall::CCmseNonSecureEntry => CallConvention::CCmseNonSecureEntry,
+            },
+            CanonAbi::GpuKernel => CallConvention::GpuKernel,
+            CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
+                InterruptKind::Avr => CallConvention::AvrInterrupt,
+                InterruptKind::AvrNonBlocking => CallConvention::AvrNonBlockingInterrupt,
+                InterruptKind::Msp430 => CallConvention::Msp430Intr,
+                InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => {
+                    CallConvention::RiscvInterrupt
+                }
+                InterruptKind::X86 => CallConvention::X86Intr,
+            },
+            CanonAbi::X86(x86_call) => match x86_call {
+                X86Call::Fastcall => CallConvention::X86Fastcall,
+                X86Call::Stdcall => CallConvention::X86Stdcall,
+                X86Call::SysV64 => CallConvention::X86_64SysV,
+                X86Call::Thiscall => CallConvention::X86ThisCall,
+                X86Call::Vectorcall => CallConvention::X86VectorCall,
+                X86Call::Win64 => CallConvention::X86_64Win64,
+            },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for callconv::PassMode {
+    type T = PassMode;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            callconv::PassMode::Ignore => PassMode::Ignore,
+            callconv::PassMode::Direct(attr) => PassMode::Direct(opaque(attr)),
+            callconv::PassMode::Pair(first, second) => {
+                PassMode::Pair(opaque(first), opaque(second))
+            }
+            callconv::PassMode::Cast { pad_i32, cast } => {
+                PassMode::Cast { pad_i32: *pad_i32, cast: opaque(cast) }
+            }
+            callconv::PassMode::Indirect { attrs, meta_attrs, on_stack } => PassMode::Indirect {
+                attrs: opaque(attrs),
+                meta_attrs: opaque(meta_attrs),
+                on_stack: *on_stack,
+            },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape<rustc_abi::FieldIdx> {
+    type T = FieldsShape;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            rustc_abi::FieldsShape::Primitive => FieldsShape::Primitive,
+            rustc_abi::FieldsShape::Union(count) => FieldsShape::Union(*count),
+            rustc_abi::FieldsShape::Array { stride, count } => {
+                FieldsShape::Array { stride: stride.stable(tables, cx), count: *count }
+            }
+            rustc_abi::FieldsShape::Arbitrary { offsets, .. } => {
+                FieldsShape::Arbitrary { offsets: offsets.iter().as_slice().stable(tables, cx) }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi::VariantIdx> {
+    type T = VariantsShape;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            rustc_abi::Variants::Single { index } => {
+                VariantsShape::Single { index: index.stable(tables, cx) }
+            }
+            rustc_abi::Variants::Empty => VariantsShape::Empty,
+            rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => {
+                VariantsShape::Multiple {
+                    tag: tag.stable(tables, cx),
+                    tag_encoding: tag_encoding.stable(tables, cx),
+                    tag_field: tag_field.stable(tables, cx),
+                    variants: variants.iter().as_slice().stable(tables, cx),
+                }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding<rustc_abi::VariantIdx> {
+    type T = TagEncoding;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            rustc_abi::TagEncoding::Direct => TagEncoding::Direct,
+            rustc_abi::TagEncoding::Niche { untagged_variant, niche_variants, niche_start } => {
+                TagEncoding::Niche {
+                    untagged_variant: untagged_variant.stable(tables, cx),
+                    niche_variants: niche_variants.stable(tables, cx),
+                    niche_start: *niche_start,
+                }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr {
+    type T = ValueAbi;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match *self {
+            rustc_abi::BackendRepr::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables, cx)),
+            rustc_abi::BackendRepr::ScalarPair(first, second) => {
+                ValueAbi::ScalarPair(first.stable(tables, cx), second.stable(tables, cx))
+            }
+            rustc_abi::BackendRepr::SimdVector { element, count } => {
+                ValueAbi::Vector { element: element.stable(tables, cx), count }
+            }
+            rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Size {
+    type T = Size;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        Size::from_bits(self.bits_usize())
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Align {
+    type T = Align;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        self.bytes()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
+    type T = Scalar;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            rustc_abi::Scalar::Initialized { value, valid_range } => Scalar::Initialized {
+                value: value.stable(tables, cx),
+                valid_range: valid_range.stable(tables, cx),
+            },
+            rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables, cx) },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Primitive {
+    type T = Primitive;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            rustc_abi::Primitive::Int(length, signed) => {
+                Primitive::Int { length: length.stable(tables, cx), signed: *signed }
+            }
+            rustc_abi::Primitive::Float(length) => {
+                Primitive::Float { length: length.stable(tables, cx) }
+            }
+            rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables, cx)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace {
+    type T = AddressSpace;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        AddressSpace(self.0)
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Integer {
+    type T = IntegerLength;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            rustc_abi::Integer::I8 => IntegerLength::I8,
+            rustc_abi::Integer::I16 => IntegerLength::I16,
+            rustc_abi::Integer::I32 => IntegerLength::I32,
+            rustc_abi::Integer::I64 => IntegerLength::I64,
+            rustc_abi::Integer::I128 => IntegerLength::I128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Float {
+    type T = FloatLength;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            rustc_abi::Float::F16 => FloatLength::F16,
+            rustc_abi::Float::F32 => FloatLength::F32,
+            rustc_abi::Float::F64 => FloatLength::F64,
+            rustc_abi::Float::F128 => FloatLength::F128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange {
+    type T = WrappingRange;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        WrappingRange { start: self.start, end: self.end }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::ReprFlags {
+    type T = ReprFlags;
+
+    fn stable<'cx>(
+        &self,
+        _tables: &mut Tables<'cx, BridgeTys>,
+        _cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        ReprFlags {
+            is_simd: self.intersects(Self::IS_SIMD),
+            is_c: self.intersects(Self::IS_C),
+            is_transparent: self.intersects(Self::IS_TRANSPARENT),
+            is_linear: self.intersects(Self::IS_LINEAR),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::IntegerType {
+    type T = IntegerType;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            rustc_abi::IntegerType::Pointer(signed) => IntegerType::Pointer { is_signed: *signed },
+            rustc_abi::IntegerType::Fixed(integer, signed) => {
+                IntegerType::Fixed { length: integer.stable(tables, cx), is_signed: *signed }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::ReprOptions {
+    type T = ReprOptions;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        ReprOptions {
+            int: self.int.map(|int| int.stable(tables, cx)),
+            align: self.align.map(|align| align.stable(tables, cx)),
+            pack: self.pack.map(|pack| pack.stable(tables, cx)),
+            flags: self.flags.stable(tables, cx),
+        }
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
new file mode 100644
index 00000000000..bd7d4807152
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
@@ -0,0 +1,936 @@
+//! Conversion of internal Rust compiler `mir` items to stable ones.
+
+use rustc_middle::mir::mono::MonoItem;
+use rustc_middle::{bug, mir};
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::bridge::SmirError;
+use rustc_public_bridge::context::SmirCtxt;
+
+use crate::compiler_interface::BridgeTys;
+use crate::mir::alloc::GlobalAlloc;
+use crate::mir::{ConstOperand, Statement, UserTypeProjection, VarDebugInfoFragment};
+use crate::ty::{Allocation, ConstantKind, MirConst};
+use crate::unstable::Stable;
+use crate::{Error, alloc, opaque};
+
+impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
+    type T = crate::mir::Body;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::mir::Body::new(
+            self.basic_blocks
+                .iter()
+                .map(|block| crate::mir::BasicBlock {
+                    terminator: block.terminator().stable(tables, cx),
+                    statements: block
+                        .statements
+                        .iter()
+                        .map(|statement| statement.stable(tables, cx))
+                        .collect(),
+                })
+                .collect(),
+            self.local_decls
+                .iter()
+                .map(|decl| crate::mir::LocalDecl {
+                    ty: decl.ty.stable(tables, cx),
+                    span: decl.source_info.span.stable(tables, cx),
+                    mutability: decl.mutability.stable(tables, cx),
+                })
+                .collect(),
+            self.arg_count,
+            self.var_debug_info.iter().map(|info| info.stable(tables, cx)).collect(),
+            self.spread_arg.stable(tables, cx),
+            self.span.stable(tables, cx),
+        )
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::VarDebugInfo<'tcx> {
+    type T = crate::mir::VarDebugInfo;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::mir::VarDebugInfo {
+            name: self.name.to_string(),
+            source_info: self.source_info.stable(tables, cx),
+            composite: self.composite.as_ref().map(|composite| composite.stable(tables, cx)),
+            value: self.value.stable(tables, cx),
+            argument_index: self.argument_index,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> {
+    type T = crate::mir::Statement;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        Statement {
+            kind: self.kind.stable(tables, cx),
+            span: self.source_info.span.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::SourceInfo {
+    type T = crate::mir::SourceInfo;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::mir::SourceInfo { span: self.span.stable(tables, cx), scope: self.scope.into() }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::VarDebugInfoFragment<'tcx> {
+    type T = crate::mir::VarDebugInfoFragment;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        VarDebugInfoFragment {
+            ty: self.ty.stable(tables, cx),
+            projection: self.projection.iter().map(|e| e.stable(tables, cx)).collect(),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::VarDebugInfoContents<'tcx> {
+    type T = crate::mir::VarDebugInfoContents;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            mir::VarDebugInfoContents::Place(place) => {
+                crate::mir::VarDebugInfoContents::Place(place.stable(tables, cx))
+            }
+            mir::VarDebugInfoContents::Const(const_operand) => {
+                let op = ConstOperand {
+                    span: const_operand.span.stable(tables, cx),
+                    user_ty: const_operand.user_ty.map(|index| index.as_usize()),
+                    const_: const_operand.const_.stable(tables, cx),
+                };
+                crate::mir::VarDebugInfoContents::Const(op)
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::StatementKind<'tcx> {
+    type T = crate::mir::StatementKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            mir::StatementKind::Assign(assign) => crate::mir::StatementKind::Assign(
+                assign.0.stable(tables, cx),
+                assign.1.stable(tables, cx),
+            ),
+            mir::StatementKind::FakeRead(fake_read_place) => crate::mir::StatementKind::FakeRead(
+                fake_read_place.0.stable(tables, cx),
+                fake_read_place.1.stable(tables, cx),
+            ),
+            mir::StatementKind::SetDiscriminant { place, variant_index } => {
+                crate::mir::StatementKind::SetDiscriminant {
+                    place: place.as_ref().stable(tables, cx),
+                    variant_index: variant_index.stable(tables, cx),
+                }
+            }
+            mir::StatementKind::Deinit(place) => {
+                crate::mir::StatementKind::Deinit(place.stable(tables, cx))
+            }
+
+            mir::StatementKind::StorageLive(place) => {
+                crate::mir::StatementKind::StorageLive(place.stable(tables, cx))
+            }
+
+            mir::StatementKind::StorageDead(place) => {
+                crate::mir::StatementKind::StorageDead(place.stable(tables, cx))
+            }
+            mir::StatementKind::Retag(retag, place) => {
+                crate::mir::StatementKind::Retag(retag.stable(tables, cx), place.stable(tables, cx))
+            }
+            mir::StatementKind::PlaceMention(place) => {
+                crate::mir::StatementKind::PlaceMention(place.stable(tables, cx))
+            }
+            mir::StatementKind::AscribeUserType(place_projection, variance) => {
+                crate::mir::StatementKind::AscribeUserType {
+                    place: place_projection.as_ref().0.stable(tables, cx),
+                    projections: place_projection.as_ref().1.stable(tables, cx),
+                    variance: variance.stable(tables, cx),
+                }
+            }
+            mir::StatementKind::Coverage(coverage) => {
+                crate::mir::StatementKind::Coverage(opaque(coverage))
+            }
+            mir::StatementKind::Intrinsic(intrinstic) => {
+                crate::mir::StatementKind::Intrinsic(intrinstic.stable(tables, cx))
+            }
+            mir::StatementKind::ConstEvalCounter => crate::mir::StatementKind::ConstEvalCounter,
+            // BackwardIncompatibleDropHint has no semantics, so it is translated to Nop.
+            mir::StatementKind::BackwardIncompatibleDropHint { .. } => {
+                crate::mir::StatementKind::Nop
+            }
+            mir::StatementKind::Nop => crate::mir::StatementKind::Nop,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
+    type T = crate::mir::Rvalue;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::Rvalue::*;
+        match self {
+            Use(op) => crate::mir::Rvalue::Use(op.stable(tables, cx)),
+            Repeat(op, len) => {
+                let len = len.stable(tables, cx);
+                crate::mir::Rvalue::Repeat(op.stable(tables, cx), len)
+            }
+            Ref(region, kind, place) => crate::mir::Rvalue::Ref(
+                region.stable(tables, cx),
+                kind.stable(tables, cx),
+                place.stable(tables, cx),
+            ),
+            ThreadLocalRef(def_id) => {
+                crate::mir::Rvalue::ThreadLocalRef(tables.crate_item(*def_id))
+            }
+            RawPtr(mutability, place) => crate::mir::Rvalue::AddressOf(
+                mutability.stable(tables, cx),
+                place.stable(tables, cx),
+            ),
+            Len(place) => crate::mir::Rvalue::Len(place.stable(tables, cx)),
+            Cast(cast_kind, op, ty) => crate::mir::Rvalue::Cast(
+                cast_kind.stable(tables, cx),
+                op.stable(tables, cx),
+                ty.stable(tables, cx),
+            ),
+            BinaryOp(bin_op, ops) => {
+                if let Some(bin_op) = bin_op.overflowing_to_wrapping() {
+                    crate::mir::Rvalue::CheckedBinaryOp(
+                        bin_op.stable(tables, cx),
+                        ops.0.stable(tables, cx),
+                        ops.1.stable(tables, cx),
+                    )
+                } else {
+                    crate::mir::Rvalue::BinaryOp(
+                        bin_op.stable(tables, cx),
+                        ops.0.stable(tables, cx),
+                        ops.1.stable(tables, cx),
+                    )
+                }
+            }
+            NullaryOp(null_op, ty) => {
+                crate::mir::Rvalue::NullaryOp(null_op.stable(tables, cx), ty.stable(tables, cx))
+            }
+            UnaryOp(un_op, op) => {
+                crate::mir::Rvalue::UnaryOp(un_op.stable(tables, cx), op.stable(tables, cx))
+            }
+            Discriminant(place) => crate::mir::Rvalue::Discriminant(place.stable(tables, cx)),
+            Aggregate(agg_kind, operands) => {
+                let operands = operands.iter().map(|op| op.stable(tables, cx)).collect();
+                crate::mir::Rvalue::Aggregate(agg_kind.stable(tables, cx), operands)
+            }
+            ShallowInitBox(op, ty) => {
+                crate::mir::Rvalue::ShallowInitBox(op.stable(tables, cx), ty.stable(tables, cx))
+            }
+            CopyForDeref(place) => crate::mir::Rvalue::CopyForDeref(place.stable(tables, cx)),
+            WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Mutability {
+    type T = crate::mir::Mutability;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_hir::Mutability::*;
+        match *self {
+            Not => crate::mir::Mutability::Not,
+            Mut => crate::mir::Mutability::Mut,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::RawPtrKind {
+    type T = crate::mir::RawPtrKind;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use mir::RawPtrKind::*;
+        match *self {
+            Const => crate::mir::RawPtrKind::Const,
+            Mut => crate::mir::RawPtrKind::Mut,
+            FakeForPtrMetadata => crate::mir::RawPtrKind::FakeForPtrMetadata,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::BorrowKind {
+    type T = crate::mir::BorrowKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::BorrowKind::*;
+        match *self {
+            Shared => crate::mir::BorrowKind::Shared,
+            Fake(kind) => crate::mir::BorrowKind::Fake(kind.stable(tables, cx)),
+            Mut { kind } => crate::mir::BorrowKind::Mut { kind: kind.stable(tables, cx) },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::MutBorrowKind {
+    type T = crate::mir::MutBorrowKind;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::MutBorrowKind::*;
+        match *self {
+            Default => crate::mir::MutBorrowKind::Default,
+            TwoPhaseBorrow => crate::mir::MutBorrowKind::TwoPhaseBorrow,
+            ClosureCapture => crate::mir::MutBorrowKind::ClosureCapture,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
+    type T = crate::mir::FakeBorrowKind;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::FakeBorrowKind::*;
+        match *self {
+            Deep => crate::mir::FakeBorrowKind::Deep,
+            Shallow => crate::mir::FakeBorrowKind::Shallow,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
+    type T = crate::mir::NullOp;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::NullOp::*;
+        match self {
+            SizeOf => crate::mir::NullOp::SizeOf,
+            AlignOf => crate::mir::NullOp::AlignOf,
+            OffsetOf(indices) => crate::mir::NullOp::OffsetOf(
+                indices.iter().map(|idx| idx.stable(tables, cx)).collect(),
+            ),
+            UbChecks => crate::mir::NullOp::UbChecks,
+            ContractChecks => crate::mir::NullOp::ContractChecks,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::CastKind {
+    type T = crate::mir::CastKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::CastKind::*;
+        match self {
+            PointerExposeProvenance => crate::mir::CastKind::PointerExposeAddress,
+            PointerWithExposedProvenance => crate::mir::CastKind::PointerWithExposedProvenance,
+            PointerCoercion(c, _) => crate::mir::CastKind::PointerCoercion(c.stable(tables, cx)),
+            IntToInt => crate::mir::CastKind::IntToInt,
+            FloatToInt => crate::mir::CastKind::FloatToInt,
+            FloatToFloat => crate::mir::CastKind::FloatToFloat,
+            IntToFloat => crate::mir::CastKind::IntToFloat,
+            PtrToPtr => crate::mir::CastKind::PtrToPtr,
+            FnPtrToPtr => crate::mir::CastKind::FnPtrToPtr,
+            Transmute => crate::mir::CastKind::Transmute,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::FakeReadCause {
+    type T = crate::mir::FakeReadCause;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::FakeReadCause::*;
+        match self {
+            ForMatchGuard => crate::mir::FakeReadCause::ForMatchGuard,
+            ForMatchedPlace(local_def_id) => {
+                crate::mir::FakeReadCause::ForMatchedPlace(opaque(local_def_id))
+            }
+            ForGuardBinding => crate::mir::FakeReadCause::ForGuardBinding,
+            ForLet(local_def_id) => crate::mir::FakeReadCause::ForLet(opaque(local_def_id)),
+            ForIndex => crate::mir::FakeReadCause::ForIndex,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
+    type T = crate::mir::Operand;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::Operand::*;
+        match self {
+            Copy(place) => crate::mir::Operand::Copy(place.stable(tables, cx)),
+            Move(place) => crate::mir::Operand::Move(place.stable(tables, cx)),
+            Constant(c) => crate::mir::Operand::Constant(c.stable(tables, cx)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::ConstOperand<'tcx> {
+    type T = crate::mir::ConstOperand;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::mir::ConstOperand {
+            span: self.span.stable(tables, cx),
+            user_ty: self.user_ty.map(|u| u.as_usize()).or(None),
+            const_: self.const_.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Place<'tcx> {
+    type T = crate::mir::Place;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::mir::Place {
+            local: self.local.as_usize(),
+            projection: self.projection.iter().map(|e| e.stable(tables, cx)).collect(),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
+    type T = crate::mir::ProjectionElem;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::ProjectionElem::*;
+        match self {
+            Deref => crate::mir::ProjectionElem::Deref,
+            Field(idx, ty) => {
+                crate::mir::ProjectionElem::Field(idx.stable(tables, cx), ty.stable(tables, cx))
+            }
+            Index(local) => crate::mir::ProjectionElem::Index(local.stable(tables, cx)),
+            ConstantIndex { offset, min_length, from_end } => {
+                crate::mir::ProjectionElem::ConstantIndex {
+                    offset: *offset,
+                    min_length: *min_length,
+                    from_end: *from_end,
+                }
+            }
+            Subslice { from, to, from_end } => {
+                crate::mir::ProjectionElem::Subslice { from: *from, to: *to, from_end: *from_end }
+            }
+            // MIR includes an `Option<Symbol>` argument for `Downcast` that is the name of the
+            // variant, used for printing MIR. However this information should also be accessible
+            // via a lookup using the `VariantIdx`. The `Option<Symbol>` argument is therefore
+            // dropped when converting to Stable MIR. A brief justification for this decision can be
+            // found at https://github.com/rust-lang/rust/pull/117517#issuecomment-1811683486
+            Downcast(_, idx) => crate::mir::ProjectionElem::Downcast(idx.stable(tables, cx)),
+            OpaqueCast(ty) => crate::mir::ProjectionElem::OpaqueCast(ty.stable(tables, cx)),
+            Subtype(ty) => crate::mir::ProjectionElem::Subtype(ty.stable(tables, cx)),
+            UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::UserTypeProjection {
+    type T = crate::mir::UserTypeProjection;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        UserTypeProjection { base: self.base.as_usize(), projection: opaque(&self.projs) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Local {
+    type T = crate::mir::Local;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        self.as_usize()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::RetagKind {
+    type T = crate::mir::RetagKind;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::RetagKind;
+        match self {
+            RetagKind::FnEntry => crate::mir::RetagKind::FnEntry,
+            RetagKind::TwoPhase => crate::mir::RetagKind::TwoPhase,
+            RetagKind::Raw => crate::mir::RetagKind::Raw,
+            RetagKind::Default => crate::mir::RetagKind::Default,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::UnwindAction {
+    type T = crate::mir::UnwindAction;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::UnwindAction;
+        match self {
+            UnwindAction::Continue => crate::mir::UnwindAction::Continue,
+            UnwindAction::Unreachable => crate::mir::UnwindAction::Unreachable,
+            UnwindAction::Terminate(_) => crate::mir::UnwindAction::Terminate,
+            UnwindAction::Cleanup(bb) => crate::mir::UnwindAction::Cleanup(bb.as_usize()),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> {
+    type T = crate::mir::NonDivergingIntrinsic;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::NonDivergingIntrinsic;
+
+        use crate::mir::CopyNonOverlapping;
+        match self {
+            NonDivergingIntrinsic::Assume(op) => {
+                crate::mir::NonDivergingIntrinsic::Assume(op.stable(tables, cx))
+            }
+            NonDivergingIntrinsic::CopyNonOverlapping(copy_non_overlapping) => {
+                crate::mir::NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
+                    src: copy_non_overlapping.src.stable(tables, cx),
+                    dst: copy_non_overlapping.dst.stable(tables, cx),
+                    count: copy_non_overlapping.count.stable(tables, cx),
+                })
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
+    type T = crate::mir::AssertMessage;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::AssertKind;
+        match self {
+            AssertKind::BoundsCheck { len, index } => crate::mir::AssertMessage::BoundsCheck {
+                len: len.stable(tables, cx),
+                index: index.stable(tables, cx),
+            },
+            AssertKind::Overflow(bin_op, op1, op2) => crate::mir::AssertMessage::Overflow(
+                bin_op.stable(tables, cx),
+                op1.stable(tables, cx),
+                op2.stable(tables, cx),
+            ),
+            AssertKind::OverflowNeg(op) => {
+                crate::mir::AssertMessage::OverflowNeg(op.stable(tables, cx))
+            }
+            AssertKind::DivisionByZero(op) => {
+                crate::mir::AssertMessage::DivisionByZero(op.stable(tables, cx))
+            }
+            AssertKind::RemainderByZero(op) => {
+                crate::mir::AssertMessage::RemainderByZero(op.stable(tables, cx))
+            }
+            AssertKind::ResumedAfterReturn(coroutine) => {
+                crate::mir::AssertMessage::ResumedAfterReturn(coroutine.stable(tables, cx))
+            }
+            AssertKind::ResumedAfterPanic(coroutine) => {
+                crate::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables, cx))
+            }
+            AssertKind::ResumedAfterDrop(coroutine) => {
+                crate::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables, cx))
+            }
+            AssertKind::MisalignedPointerDereference { required, found } => {
+                crate::mir::AssertMessage::MisalignedPointerDereference {
+                    required: required.stable(tables, cx),
+                    found: found.stable(tables, cx),
+                }
+            }
+            AssertKind::NullPointerDereference => crate::mir::AssertMessage::NullPointerDereference,
+            AssertKind::InvalidEnumConstruction(source) => {
+                crate::mir::AssertMessage::InvalidEnumConstruction(source.stable(tables, cx))
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::BinOp {
+    type T = crate::mir::BinOp;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::BinOp;
+        match self {
+            BinOp::Add => crate::mir::BinOp::Add,
+            BinOp::AddUnchecked => crate::mir::BinOp::AddUnchecked,
+            BinOp::AddWithOverflow => bug!("AddWithOverflow should have been translated already"),
+            BinOp::Sub => crate::mir::BinOp::Sub,
+            BinOp::SubUnchecked => crate::mir::BinOp::SubUnchecked,
+            BinOp::SubWithOverflow => bug!("AddWithOverflow should have been translated already"),
+            BinOp::Mul => crate::mir::BinOp::Mul,
+            BinOp::MulUnchecked => crate::mir::BinOp::MulUnchecked,
+            BinOp::MulWithOverflow => bug!("AddWithOverflow should have been translated already"),
+            BinOp::Div => crate::mir::BinOp::Div,
+            BinOp::Rem => crate::mir::BinOp::Rem,
+            BinOp::BitXor => crate::mir::BinOp::BitXor,
+            BinOp::BitAnd => crate::mir::BinOp::BitAnd,
+            BinOp::BitOr => crate::mir::BinOp::BitOr,
+            BinOp::Shl => crate::mir::BinOp::Shl,
+            BinOp::ShlUnchecked => crate::mir::BinOp::ShlUnchecked,
+            BinOp::Shr => crate::mir::BinOp::Shr,
+            BinOp::ShrUnchecked => crate::mir::BinOp::ShrUnchecked,
+            BinOp::Eq => crate::mir::BinOp::Eq,
+            BinOp::Lt => crate::mir::BinOp::Lt,
+            BinOp::Le => crate::mir::BinOp::Le,
+            BinOp::Ne => crate::mir::BinOp::Ne,
+            BinOp::Ge => crate::mir::BinOp::Ge,
+            BinOp::Gt => crate::mir::BinOp::Gt,
+            BinOp::Cmp => crate::mir::BinOp::Cmp,
+            BinOp::Offset => crate::mir::BinOp::Offset,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::UnOp {
+    type T = crate::mir::UnOp;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::mir::UnOp;
+        match self {
+            UnOp::Not => crate::mir::UnOp::Not,
+            UnOp::Neg => crate::mir::UnOp::Neg,
+            UnOp::PtrMetadata => crate::mir::UnOp::PtrMetadata,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
+    type T = crate::mir::AggregateKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            mir::AggregateKind::Array(ty) => {
+                crate::mir::AggregateKind::Array(ty.stable(tables, cx))
+            }
+            mir::AggregateKind::Tuple => crate::mir::AggregateKind::Tuple,
+            mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => {
+                crate::mir::AggregateKind::Adt(
+                    tables.adt_def(*def_id),
+                    var_idx.stable(tables, cx),
+                    generic_arg.stable(tables, cx),
+                    user_ty_index.map(|idx| idx.index()),
+                    field_idx.map(|idx| idx.index()),
+                )
+            }
+            mir::AggregateKind::Closure(def_id, generic_arg) => crate::mir::AggregateKind::Closure(
+                tables.closure_def(*def_id),
+                generic_arg.stable(tables, cx),
+            ),
+            mir::AggregateKind::Coroutine(def_id, generic_arg) => {
+                crate::mir::AggregateKind::Coroutine(
+                    tables.coroutine_def(*def_id),
+                    generic_arg.stable(tables, cx),
+                    cx.coroutine_movability(*def_id).stable(tables, cx),
+                )
+            }
+            mir::AggregateKind::CoroutineClosure(def_id, generic_args) => {
+                crate::mir::AggregateKind::CoroutineClosure(
+                    tables.coroutine_closure_def(*def_id),
+                    generic_args.stable(tables, cx),
+                )
+            }
+            mir::AggregateKind::RawPtr(ty, mutability) => crate::mir::AggregateKind::RawPtr(
+                ty.stable(tables, cx),
+                mutability.stable(tables, cx),
+            ),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> {
+    type T = crate::mir::InlineAsmOperand;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::mir::InlineAsmOperand;
+
+        let (in_value, out_place) = match self {
+            InlineAsmOperand::In { value, .. } => (Some(value.stable(tables, cx)), None),
+            InlineAsmOperand::Out { place, .. } => {
+                (None, place.map(|place| place.stable(tables, cx)))
+            }
+            InlineAsmOperand::InOut { in_value, out_place, .. } => {
+                (Some(in_value.stable(tables, cx)), out_place.map(|place| place.stable(tables, cx)))
+            }
+            InlineAsmOperand::Const { .. }
+            | InlineAsmOperand::SymFn { .. }
+            | InlineAsmOperand::SymStatic { .. }
+            | InlineAsmOperand::Label { .. } => (None, None),
+        };
+
+        crate::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{self:?}") }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> {
+    type T = crate::mir::Terminator;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::mir::Terminator;
+        Terminator {
+            kind: self.kind.stable(tables, cx),
+            span: self.source_info.span.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
+    type T = crate::mir::TerminatorKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::mir::TerminatorKind;
+        match self {
+            mir::TerminatorKind::Goto { target } => {
+                TerminatorKind::Goto { target: target.as_usize() }
+            }
+            mir::TerminatorKind::SwitchInt { discr, targets } => TerminatorKind::SwitchInt {
+                discr: discr.stable(tables, cx),
+                targets: {
+                    let branches = targets.iter().map(|(val, target)| (val, target.as_usize()));
+                    crate::mir::SwitchTargets::new(
+                        branches.collect(),
+                        targets.otherwise().as_usize(),
+                    )
+                },
+            },
+            mir::TerminatorKind::UnwindResume => TerminatorKind::Resume,
+            mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort,
+            mir::TerminatorKind::Return => TerminatorKind::Return,
+            mir::TerminatorKind::Unreachable => TerminatorKind::Unreachable,
+            mir::TerminatorKind::Drop {
+                place,
+                target,
+                unwind,
+                replace: _,
+                drop: _,
+                async_fut: _,
+            } => TerminatorKind::Drop {
+                place: place.stable(tables, cx),
+                target: target.as_usize(),
+                unwind: unwind.stable(tables, cx),
+            },
+            mir::TerminatorKind::Call {
+                func,
+                args,
+                destination,
+                target,
+                unwind,
+                call_source: _,
+                fn_span: _,
+            } => TerminatorKind::Call {
+                func: func.stable(tables, cx),
+                args: args.iter().map(|arg| arg.node.stable(tables, cx)).collect(),
+                destination: destination.stable(tables, cx),
+                target: target.map(|t| t.as_usize()),
+                unwind: unwind.stable(tables, cx),
+            },
+            mir::TerminatorKind::TailCall { func: _, args: _, fn_span: _ } => todo!(),
+            mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => {
+                TerminatorKind::Assert {
+                    cond: cond.stable(tables, cx),
+                    expected: *expected,
+                    msg: msg.stable(tables, cx),
+                    target: target.as_usize(),
+                    unwind: unwind.stable(tables, cx),
+                }
+            }
+            mir::TerminatorKind::InlineAsm {
+                asm_macro: _,
+                template,
+                operands,
+                options,
+                line_spans,
+                targets,
+                unwind,
+            } => TerminatorKind::InlineAsm {
+                template: format!("{template:?}"),
+                operands: operands.iter().map(|operand| operand.stable(tables, cx)).collect(),
+                options: format!("{options:?}"),
+                line_spans: format!("{line_spans:?}"),
+                // FIXME: Figure out how to do labels in SMIR
+                destination: targets.first().map(|d| d.as_usize()),
+                unwind: unwind.stable(tables, cx),
+            },
+            mir::TerminatorKind::Yield { .. }
+            | mir::TerminatorKind::CoroutineDrop
+            | mir::TerminatorKind::FalseEdge { .. }
+            | mir::TerminatorKind::FalseUnwind { .. } => unreachable!(),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::interpret::ConstAllocation<'tcx> {
+    type T = Allocation;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        self.inner().stable(tables, cx)
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::interpret::Allocation {
+    type T = crate::ty::Allocation;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_public_bridge::context::SmirAllocRange;
+        alloc::allocation_filter(
+            self,
+            cx.alloc_range(rustc_abi::Size::ZERO, self.size()),
+            tables,
+            cx,
+        )
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::interpret::AllocId {
+    type T = crate::mir::alloc::AllocId;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        tables.create_alloc_id(*self)
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> {
+    type T = GlobalAlloc;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            mir::interpret::GlobalAlloc::Function { instance, .. } => {
+                GlobalAlloc::Function(instance.stable(tables, cx))
+            }
+            mir::interpret::GlobalAlloc::VTable(ty, dyn_ty) => {
+                // FIXME: Should we record the whole vtable?
+                GlobalAlloc::VTable(ty.stable(tables, cx), dyn_ty.principal().stable(tables, cx))
+            }
+            mir::interpret::GlobalAlloc::Static(def) => {
+                GlobalAlloc::Static(tables.static_def(*def))
+            }
+            mir::interpret::GlobalAlloc::Memory(alloc) => {
+                GlobalAlloc::Memory(alloc.stable(tables, cx))
+            }
+            mir::interpret::GlobalAlloc::TypeId { ty } => {
+                GlobalAlloc::TypeId { ty: ty.stable(tables, cx) }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> {
+    type T = crate::ty::MirConst;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let id = tables.intern_mir_const(cx.lift(*self).unwrap());
+        match *self {
+            mir::Const::Ty(ty, c) => MirConst::new(
+                crate::ty::ConstantKind::Ty(c.stable(tables, cx)),
+                ty.stable(tables, cx),
+                id,
+            ),
+            mir::Const::Unevaluated(unev_const, ty) => {
+                let kind = crate::ty::ConstantKind::Unevaluated(crate::ty::UnevaluatedConst {
+                    def: tables.const_def(unev_const.def),
+                    args: unev_const.args.stable(tables, cx),
+                    promoted: unev_const.promoted.map(|u| u.as_u32()),
+                });
+                let ty = ty.stable(tables, cx);
+                MirConst::new(kind, ty, id)
+            }
+            mir::Const::Val(mir::ConstValue::ZeroSized, ty) => {
+                let ty = ty.stable(tables, cx);
+                MirConst::new(ConstantKind::ZeroSized, ty, id)
+            }
+            mir::Const::Val(val, ty) => {
+                let ty = cx.lift(ty).unwrap();
+                let val = cx.lift(val).unwrap();
+                let kind = ConstantKind::Allocated(alloc::new_allocation(ty, val, tables, cx));
+                let ty = ty.stable(tables, cx);
+                MirConst::new(kind, ty, id)
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for mir::interpret::ErrorHandled {
+    type T = Error;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        Error::new(format!("{self:?}"))
+    }
+}
+
+impl<'tcx> Stable<'tcx> for MonoItem<'tcx> {
+    type T = crate::mir::mono::MonoItem;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::mir::mono::MonoItem as StableMonoItem;
+        match self {
+            MonoItem::Fn(instance) => StableMonoItem::Fn(instance.stable(tables, cx)),
+            MonoItem::Static(def_id) => StableMonoItem::Static(tables.static_def(*def_id)),
+            MonoItem::GlobalAsm(item_id) => StableMonoItem::GlobalAsm(opaque(item_id)),
+        }
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/convert/stable/mod.rs b/compiler/rustc_public/src/unstable/convert/stable/mod.rs
new file mode 100644
index 00000000000..ea78ca50eb3
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/convert/stable/mod.rs
@@ -0,0 +1,95 @@
+//! Conversion of internal Rust compiler items to stable ones.
+
+use rustc_abi::FieldIdx;
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::context::SmirCtxt;
+
+use super::Stable;
+use crate::compiler_interface::BridgeTys;
+
+mod abi;
+mod mir;
+mod ty;
+
+impl<'tcx> Stable<'tcx> for rustc_hir::Safety {
+    type T = crate::mir::Safety;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            rustc_hir::Safety::Unsafe => crate::mir::Safety::Unsafe,
+            rustc_hir::Safety::Safe => crate::mir::Safety::Safe,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for FieldIdx {
+    type T = usize;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        self.as_usize()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
+    type T = crate::mir::CoroutineSource;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_hir::CoroutineSource;
+        match self {
+            CoroutineSource::Block => crate::mir::CoroutineSource::Block,
+            CoroutineSource::Closure => crate::mir::CoroutineSource::Closure,
+            CoroutineSource::Fn => crate::mir::CoroutineSource::Fn,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
+    type T = crate::mir::CoroutineKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_hir::{CoroutineDesugaring, CoroutineKind};
+        match *self {
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, source) => {
+                crate::mir::CoroutineKind::Desugared(
+                    crate::mir::CoroutineDesugaring::Async,
+                    source.stable(tables, cx),
+                )
+            }
+            CoroutineKind::Desugared(CoroutineDesugaring::Gen, source) => {
+                crate::mir::CoroutineKind::Desugared(
+                    crate::mir::CoroutineDesugaring::Gen,
+                    source.stable(tables, cx),
+                )
+            }
+            CoroutineKind::Coroutine(movability) => {
+                crate::mir::CoroutineKind::Coroutine(movability.stable(tables, cx))
+            }
+            CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, source) => {
+                crate::mir::CoroutineKind::Desugared(
+                    crate::mir::CoroutineDesugaring::AsyncGen,
+                    source.stable(tables, cx),
+                )
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_span::Symbol {
+    type T = crate::Symbol;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        self.to_string()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_span::Span {
+    type T = crate::ty::Span;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        tables.create_span(*self)
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
new file mode 100644
index 00000000000..75c29788787
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
@@ -0,0 +1,1139 @@
+//! Conversion of internal Rust compiler `ty` items to stable ones.
+
+use rustc_middle::ty::Ty;
+use rustc_middle::{bug, mir, ty};
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::context::SmirCtxt;
+
+use crate::alloc;
+use crate::compiler_interface::BridgeTys;
+use crate::ty::{
+    AdtKind, FloatTy, GenericArgs, GenericParamDef, IntTy, Region, RigidTy, TyKind, UintTy,
+};
+use crate::unstable::Stable;
+
+impl<'tcx> Stable<'tcx> for ty::AliasTyKind {
+    type T = crate::ty::AliasKind;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::Projection => crate::ty::AliasKind::Projection,
+            ty::Inherent => crate::ty::AliasKind::Inherent,
+            ty::Opaque => crate::ty::AliasKind::Opaque,
+            ty::Free => crate::ty::AliasKind::Free,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> {
+    type T = crate::ty::AliasTy;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::AliasTy { args, def_id, .. } = self;
+        crate::ty::AliasTy { def_id: tables.alias_def(*def_id), args: args.stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> {
+    type T = crate::ty::AliasTerm;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::AliasTerm { args, def_id, .. } = self;
+        crate::ty::AliasTerm { def_id: tables.alias_def(*def_id), args: args.stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::DynKind {
+    type T = crate::ty::DynKind;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::Dyn => crate::ty::DynKind::Dyn,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> {
+    type T = crate::ty::ExistentialPredicate;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::ExistentialPredicate::*;
+        match self {
+            ty::ExistentialPredicate::Trait(existential_trait_ref) => {
+                Trait(existential_trait_ref.stable(tables, cx))
+            }
+            ty::ExistentialPredicate::Projection(existential_projection) => {
+                Projection(existential_projection.stable(tables, cx))
+            }
+            ty::ExistentialPredicate::AutoTrait(def_id) => AutoTrait(tables.trait_def(*def_id)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> {
+    type T = crate::ty::ExistentialTraitRef;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::ExistentialTraitRef { def_id, args, .. } = self;
+        crate::ty::ExistentialTraitRef {
+            def_id: tables.trait_def(*def_id),
+            generic_args: args.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> {
+    type T = crate::ty::TermKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::TermKind;
+        match self {
+            ty::TermKind::Ty(ty) => TermKind::Type(ty.stable(tables, cx)),
+            ty::TermKind::Const(cnst) => {
+                let cnst = cnst.stable(tables, cx);
+                TermKind::Const(cnst)
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> {
+    type T = crate::ty::ExistentialProjection;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::ExistentialProjection { def_id, args, term, .. } = self;
+        crate::ty::ExistentialProjection {
+            def_id: tables.trait_def(*def_id),
+            generic_args: args.stable(tables, cx),
+            term: term.kind().stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion {
+    type T = crate::mir::PointerCoercion;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::ty::adjustment::PointerCoercion;
+        match self {
+            PointerCoercion::ReifyFnPointer => crate::mir::PointerCoercion::ReifyFnPointer,
+            PointerCoercion::UnsafeFnPointer => crate::mir::PointerCoercion::UnsafeFnPointer,
+            PointerCoercion::ClosureFnPointer(safety) => {
+                crate::mir::PointerCoercion::ClosureFnPointer(safety.stable(tables, cx))
+            }
+            PointerCoercion::MutToConstPointer => crate::mir::PointerCoercion::MutToConstPointer,
+            PointerCoercion::ArrayToPointer => crate::mir::PointerCoercion::ArrayToPointer,
+            PointerCoercion::Unsize => crate::mir::PointerCoercion::Unsize,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::UserTypeAnnotationIndex {
+    type T = usize;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        self.as_usize()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AdtKind {
+    type T = AdtKind;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::AdtKind::Struct => AdtKind::Struct,
+            ty::AdtKind::Union => AdtKind::Union,
+            ty::AdtKind::Enum => AdtKind::Enum,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::FieldDef {
+    type T = crate::ty::FieldDef;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::ty::FieldDef {
+            def: tables.create_def_id(self.did),
+            name: self.name.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> {
+    type T = crate::ty::GenericArgs;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        GenericArgs(self.iter().map(|arg| arg.kind().stable(tables, cx)).collect())
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::GenericArgKind<'tcx> {
+    type T = crate::ty::GenericArgKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::GenericArgKind;
+        match self {
+            ty::GenericArgKind::Lifetime(region) => {
+                GenericArgKind::Lifetime(region.stable(tables, cx))
+            }
+            ty::GenericArgKind::Type(ty) => GenericArgKind::Type(ty.stable(tables, cx)),
+            ty::GenericArgKind::Const(cnst) => GenericArgKind::Const(cnst.stable(tables, cx)),
+        }
+    }
+}
+
+impl<'tcx, S, V> Stable<'tcx> for ty::Binder<'tcx, S>
+where
+    S: Stable<'tcx, T = V>,
+{
+    type T = crate::ty::Binder<V>;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::Binder;
+
+        Binder {
+            value: self.as_ref().skip_binder().stable(tables, cx),
+            bound_vars: self
+                .bound_vars()
+                .iter()
+                .map(|bound_var| bound_var.stable(tables, cx))
+                .collect(),
+        }
+    }
+}
+
+impl<'tcx, S, V> Stable<'tcx> for ty::EarlyBinder<'tcx, S>
+where
+    S: Stable<'tcx, T = V>,
+{
+    type T = crate::ty::EarlyBinder<V>;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::EarlyBinder;
+
+        EarlyBinder { value: self.as_ref().skip_binder().stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
+    type T = crate::ty::FnSig;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::FnSig;
+
+        FnSig {
+            inputs_and_output: self
+                .inputs_and_output
+                .iter()
+                .map(|ty| ty.stable(tables, cx))
+                .collect(),
+            c_variadic: self.c_variadic,
+            safety: self.safety.stable(tables, cx),
+            abi: self.abi.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
+    type T = crate::ty::BoundTyKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::BoundTyKind;
+
+        match self {
+            ty::BoundTyKind::Anon => BoundTyKind::Anon,
+            ty::BoundTyKind::Param(def_id) => {
+                BoundTyKind::Param(tables.param_def(*def_id), cx.tcx.item_name(*def_id).to_string())
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
+    type T = crate::ty::BoundRegionKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::BoundRegionKind;
+
+        match self {
+            ty::BoundRegionKind::Anon => BoundRegionKind::BrAnon,
+            ty::BoundRegionKind::Named(def_id) => BoundRegionKind::BrNamed(
+                tables.br_named_def(*def_id),
+                cx.tcx.item_name(*def_id).to_string(),
+            ),
+            ty::BoundRegionKind::ClosureEnv => BoundRegionKind::BrEnv,
+            ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::BoundVariableKind {
+    type T = crate::ty::BoundVariableKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::BoundVariableKind;
+
+        match self {
+            ty::BoundVariableKind::Ty(bound_ty_kind) => {
+                BoundVariableKind::Ty(bound_ty_kind.stable(tables, cx))
+            }
+            ty::BoundVariableKind::Region(bound_region_kind) => {
+                BoundVariableKind::Region(bound_region_kind.stable(tables, cx))
+            }
+            ty::BoundVariableKind::Const => BoundVariableKind::Const,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::IntTy {
+    type T = IntTy;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::IntTy::Isize => IntTy::Isize,
+            ty::IntTy::I8 => IntTy::I8,
+            ty::IntTy::I16 => IntTy::I16,
+            ty::IntTy::I32 => IntTy::I32,
+            ty::IntTy::I64 => IntTy::I64,
+            ty::IntTy::I128 => IntTy::I128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::UintTy {
+    type T = UintTy;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::UintTy::Usize => UintTy::Usize,
+            ty::UintTy::U8 => UintTy::U8,
+            ty::UintTy::U16 => UintTy::U16,
+            ty::UintTy::U32 => UintTy::U32,
+            ty::UintTy::U64 => UintTy::U64,
+            ty::UintTy::U128 => UintTy::U128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::FloatTy {
+    type T = FloatTy;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::FloatTy::F16 => FloatTy::F16,
+            ty::FloatTy::F32 => FloatTy::F32,
+            ty::FloatTy::F64 => FloatTy::F64,
+            ty::FloatTy::F128 => FloatTy::F128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for Ty<'tcx> {
+    type T = crate::ty::Ty;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        tables.intern_ty(cx.lift(*self).unwrap())
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
+    type T = crate::ty::TyKind;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match self {
+            ty::Bool => TyKind::RigidTy(RigidTy::Bool),
+            ty::Char => TyKind::RigidTy(RigidTy::Char),
+            ty::Int(int_ty) => TyKind::RigidTy(RigidTy::Int(int_ty.stable(tables, cx))),
+            ty::Uint(uint_ty) => TyKind::RigidTy(RigidTy::Uint(uint_ty.stable(tables, cx))),
+            ty::Float(float_ty) => TyKind::RigidTy(RigidTy::Float(float_ty.stable(tables, cx))),
+            ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt(
+                tables.adt_def(adt_def.did()),
+                generic_args.stable(tables, cx),
+            )),
+            ty::Foreign(def_id) => TyKind::RigidTy(RigidTy::Foreign(tables.foreign_def(*def_id))),
+            ty::Str => TyKind::RigidTy(RigidTy::Str),
+            ty::Array(ty, constant) => {
+                TyKind::RigidTy(RigidTy::Array(ty.stable(tables, cx), constant.stable(tables, cx)))
+            }
+            ty::Pat(ty, pat) => {
+                TyKind::RigidTy(RigidTy::Pat(ty.stable(tables, cx), pat.stable(tables, cx)))
+            }
+            ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables, cx))),
+            ty::RawPtr(ty, mutbl) => {
+                TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables, cx), mutbl.stable(tables, cx)))
+            }
+            ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref(
+                region.stable(tables, cx),
+                ty.stable(tables, cx),
+                mutbl.stable(tables, cx),
+            )),
+            ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef(
+                tables.fn_def(*def_id),
+                generic_args.stable(tables, cx),
+            )),
+            ty::FnPtr(sig_tys, hdr) => {
+                TyKind::RigidTy(RigidTy::FnPtr(sig_tys.with(*hdr).stable(tables, cx)))
+            }
+            // FIXME(unsafe_binders):
+            ty::UnsafeBinder(_) => todo!(),
+            ty::Dynamic(existential_predicates, region, dyn_kind) => {
+                TyKind::RigidTy(RigidTy::Dynamic(
+                    existential_predicates
+                        .iter()
+                        .map(|existential_predicate| existential_predicate.stable(tables, cx))
+                        .collect(),
+                    region.stable(tables, cx),
+                    dyn_kind.stable(tables, cx),
+                ))
+            }
+            ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure(
+                tables.closure_def(*def_id),
+                generic_args.stable(tables, cx),
+            )),
+            ty::CoroutineClosure(..) => todo!("FIXME(async_closures): Lower these to SMIR"),
+            ty::Coroutine(def_id, generic_args) => TyKind::RigidTy(RigidTy::Coroutine(
+                tables.coroutine_def(*def_id),
+                generic_args.stable(tables, cx),
+                cx.coroutine_movability(*def_id).stable(tables, cx),
+            )),
+            ty::Never => TyKind::RigidTy(RigidTy::Never),
+            ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
+                fields.iter().map(|ty| ty.stable(tables, cx)).collect(),
+            )),
+            ty::Alias(alias_kind, alias_ty) => {
+                TyKind::Alias(alias_kind.stable(tables, cx), alias_ty.stable(tables, cx))
+            }
+            ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables, cx)),
+            ty::Bound(debruijn_idx, bound_ty) => {
+                TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables, cx))
+            }
+            ty::CoroutineWitness(def_id, args) => TyKind::RigidTy(RigidTy::CoroutineWitness(
+                tables.coroutine_witness_def(*def_id),
+                args.stable(tables, cx),
+            )),
+            ty::Placeholder(..) | ty::Infer(_) | ty::Error(_) => {
+                unreachable!();
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
+    type T = crate::ty::Pattern;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        match **self {
+            ty::PatternKind::Range { start, end } => crate::ty::Pattern::Range {
+                // FIXME(SMIR): update data structures to not have an Option here anymore
+                start: Some(start.stable(tables, cx)),
+                end: Some(end.stable(tables, cx)),
+                include_end: true,
+            },
+            ty::PatternKind::Or(_) => todo!(),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
+    type T = crate::ty::TyConst;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ct = cx.lift(*self).unwrap();
+        let kind = match ct.kind() {
+            ty::ConstKind::Value(cv) => {
+                let const_val = cx.valtree_to_const_val(cv);
+                if matches!(const_val, mir::ConstValue::ZeroSized) {
+                    crate::ty::TyConstKind::ZSTValue(cv.ty.stable(tables, cx))
+                } else {
+                    crate::ty::TyConstKind::Value(
+                        cv.ty.stable(tables, cx),
+                        alloc::new_allocation(cv.ty, const_val, tables, cx),
+                    )
+                }
+            }
+            ty::ConstKind::Param(param) => crate::ty::TyConstKind::Param(param.stable(tables, cx)),
+            ty::ConstKind::Unevaluated(uv) => crate::ty::TyConstKind::Unevaluated(
+                tables.const_def(uv.def),
+                uv.args.stable(tables, cx),
+            ),
+            ty::ConstKind::Error(_) => unreachable!(),
+            ty::ConstKind::Infer(_) => unreachable!(),
+            ty::ConstKind::Bound(_, _) => unimplemented!(),
+            ty::ConstKind::Placeholder(_) => unimplemented!(),
+            ty::ConstKind::Expr(_) => unimplemented!(),
+        };
+        let id = tables.intern_ty_const(ct);
+        crate::ty::TyConst::new(kind, id)
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ParamConst {
+    type T = crate::ty::ParamConst;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use crate::ty::ParamConst;
+        ParamConst { index: self.index, name: self.name.to_string() }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ParamTy {
+    type T = crate::ty::ParamTy;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use crate::ty::ParamTy;
+        ParamTy { index: self.index, name: self.name.to_string() }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::BoundTy {
+    type T = crate::ty::BoundTy;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::BoundTy;
+        BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::trait_def::TraitSpecializationKind {
+    type T = crate::ty::TraitSpecializationKind;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use crate::ty::TraitSpecializationKind;
+
+        match self {
+            ty::trait_def::TraitSpecializationKind::None => TraitSpecializationKind::None,
+            ty::trait_def::TraitSpecializationKind::Marker => TraitSpecializationKind::Marker,
+            ty::trait_def::TraitSpecializationKind::AlwaysApplicable => {
+                TraitSpecializationKind::AlwaysApplicable
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::TraitDef {
+    type T = crate::ty::TraitDecl;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::opaque;
+        use crate::ty::TraitDecl;
+
+        TraitDecl {
+            def_id: tables.trait_def(self.def_id),
+            safety: self.safety.stable(tables, cx),
+            paren_sugar: self.paren_sugar,
+            has_auto_impl: self.has_auto_impl,
+            is_marker: self.is_marker,
+            is_coinductive: self.is_coinductive,
+            skip_array_during_method_dispatch: self.skip_array_during_method_dispatch,
+            skip_boxed_slice_during_method_dispatch: self.skip_boxed_slice_during_method_dispatch,
+            specialization_kind: self.specialization_kind.stable(tables, cx),
+            must_implement_one_of: self
+                .must_implement_one_of
+                .as_ref()
+                .map(|idents| idents.iter().map(|ident| opaque(ident)).collect()),
+            implement_via_object: self.implement_via_object,
+            deny_explicit_impl: self.deny_explicit_impl,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> {
+    type T = crate::ty::TraitRef;
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::TraitRef;
+
+        TraitRef::try_new(tables.trait_def(self.def_id), self.args.stable(tables, cx)).unwrap()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Generics {
+    type T = crate::ty::Generics;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::Generics;
+
+        let params: Vec<_> = self.own_params.iter().map(|param| param.stable(tables, cx)).collect();
+        let param_def_id_to_index =
+            params.iter().map(|param| (param.def_id, param.index)).collect();
+
+        Generics {
+            parent: self.parent.map(|did| tables.generic_def(did)),
+            parent_count: self.parent_count,
+            params,
+            param_def_id_to_index,
+            has_self: self.has_self,
+            has_late_bound_regions: self
+                .has_late_bound_regions
+                .as_ref()
+                .map(|late_bound_regions| late_bound_regions.stable(tables, cx)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
+    type T = crate::ty::GenericParamDefKind;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use crate::ty::GenericParamDefKind;
+        match self {
+            ty::GenericParamDefKind::Lifetime => GenericParamDefKind::Lifetime,
+            ty::GenericParamDefKind::Type { has_default, synthetic } => {
+                GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic }
+            }
+            ty::GenericParamDefKind::Const { has_default, synthetic: _ } => {
+                GenericParamDefKind::Const { has_default: *has_default }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDef {
+    type T = crate::ty::GenericParamDef;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        GenericParamDef {
+            name: self.name.to_string(),
+            def_id: tables.generic_def(self.def_id),
+            index: self.index,
+            pure_wrt_drop: self.pure_wrt_drop,
+            kind: self.kind.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> {
+    type T = crate::ty::PredicateKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::ty::PredicateKind;
+        match self {
+            PredicateKind::Clause(clause_kind) => {
+                crate::ty::PredicateKind::Clause(clause_kind.stable(tables, cx))
+            }
+            PredicateKind::DynCompatible(did) => {
+                crate::ty::PredicateKind::DynCompatible(tables.trait_def(*did))
+            }
+            PredicateKind::Subtype(subtype_predicate) => {
+                crate::ty::PredicateKind::SubType(subtype_predicate.stable(tables, cx))
+            }
+            PredicateKind::Coerce(coerce_predicate) => {
+                crate::ty::PredicateKind::Coerce(coerce_predicate.stable(tables, cx))
+            }
+            PredicateKind::ConstEquate(a, b) => {
+                crate::ty::PredicateKind::ConstEquate(a.stable(tables, cx), b.stable(tables, cx))
+            }
+            PredicateKind::Ambiguous => crate::ty::PredicateKind::Ambiguous,
+            PredicateKind::NormalizesTo(_pred) => unimplemented!(),
+            PredicateKind::AliasRelate(a, b, alias_relation_direction) => {
+                crate::ty::PredicateKind::AliasRelate(
+                    a.kind().stable(tables, cx),
+                    b.kind().stable(tables, cx),
+                    alias_relation_direction.stable(tables, cx),
+                )
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
+    type T = crate::ty::ClauseKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_middle::ty::ClauseKind;
+        match *self {
+            ClauseKind::Trait(trait_object) => {
+                crate::ty::ClauseKind::Trait(trait_object.stable(tables, cx))
+            }
+            ClauseKind::RegionOutlives(region_outlives) => {
+                crate::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables, cx))
+            }
+            ClauseKind::TypeOutlives(type_outlives) => {
+                let ty::OutlivesPredicate::<_, _>(a, b) = type_outlives;
+                crate::ty::ClauseKind::TypeOutlives(crate::ty::OutlivesPredicate(
+                    a.stable(tables, cx),
+                    b.stable(tables, cx),
+                ))
+            }
+            ClauseKind::Projection(projection_predicate) => {
+                crate::ty::ClauseKind::Projection(projection_predicate.stable(tables, cx))
+            }
+            ClauseKind::ConstArgHasType(const_, ty) => crate::ty::ClauseKind::ConstArgHasType(
+                const_.stable(tables, cx),
+                ty.stable(tables, cx),
+            ),
+            ClauseKind::WellFormed(term) => {
+                crate::ty::ClauseKind::WellFormed(term.kind().stable(tables, cx))
+            }
+            ClauseKind::ConstEvaluatable(const_) => {
+                crate::ty::ClauseKind::ConstEvaluatable(const_.stable(tables, cx))
+            }
+            ClauseKind::HostEffect(..) => {
+                todo!()
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ClosureKind {
+    type T = crate::ty::ClosureKind;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::ty::ClosureKind::*;
+        match self {
+            Fn => crate::ty::ClosureKind::Fn,
+            FnMut => crate::ty::ClosureKind::FnMut,
+            FnOnce => crate::ty::ClosureKind::FnOnce,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::SubtypePredicate<'tcx> {
+    type T = crate::ty::SubtypePredicate;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::SubtypePredicate { a, b, a_is_expected: _ } = self;
+        crate::ty::SubtypePredicate { a: a.stable(tables, cx), b: b.stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::CoercePredicate<'tcx> {
+    type T = crate::ty::CoercePredicate;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::CoercePredicate { a, b } = self;
+        crate::ty::CoercePredicate { a: a.stable(tables, cx), b: b.stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AliasRelationDirection {
+    type T = crate::ty::AliasRelationDirection;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::ty::AliasRelationDirection::*;
+        match self {
+            Equate => crate::ty::AliasRelationDirection::Equate,
+            Subtype => crate::ty::AliasRelationDirection::Subtype,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::TraitPredicate<'tcx> {
+    type T = crate::ty::TraitPredicate;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::TraitPredicate { trait_ref, polarity } = self;
+        crate::ty::TraitPredicate {
+            trait_ref: trait_ref.stable(tables, cx),
+            polarity: polarity.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx, T> Stable<'tcx> for ty::OutlivesPredicate<'tcx, T>
+where
+    T: Stable<'tcx>,
+{
+    type T = crate::ty::OutlivesPredicate<T::T, Region>;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::OutlivesPredicate(a, b) = self;
+        crate::ty::OutlivesPredicate(a.stable(tables, cx), b.stable(tables, cx))
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ProjectionPredicate<'tcx> {
+    type T = crate::ty::ProjectionPredicate;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let ty::ProjectionPredicate { projection_term, term } = self;
+        crate::ty::ProjectionPredicate {
+            projection_term: projection_term.stable(tables, cx),
+            term: term.kind().stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ImplPolarity {
+    type T = crate::ty::ImplPolarity;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::ty::ImplPolarity::*;
+        match self {
+            Positive => crate::ty::ImplPolarity::Positive,
+            Negative => crate::ty::ImplPolarity::Negative,
+            Reservation => crate::ty::ImplPolarity::Reservation,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::PredicatePolarity {
+    type T = crate::ty::PredicatePolarity;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_middle::ty::PredicatePolarity::*;
+        match self {
+            Positive => crate::ty::PredicatePolarity::Positive,
+            Negative => crate::ty::PredicatePolarity::Negative,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Region<'tcx> {
+    type T = crate::ty::Region;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        Region { kind: self.kind().stable(tables, cx) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> {
+    type T = crate::ty::RegionKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::{BoundRegion, EarlyParamRegion, RegionKind};
+        match self {
+            ty::ReEarlyParam(early_reg) => RegionKind::ReEarlyParam(EarlyParamRegion {
+                index: early_reg.index,
+                name: early_reg.name.to_string(),
+            }),
+            ty::ReBound(db_index, bound_reg) => RegionKind::ReBound(
+                db_index.as_u32(),
+                BoundRegion {
+                    var: bound_reg.var.as_u32(),
+                    kind: bound_reg.kind.stable(tables, cx),
+                },
+            ),
+            ty::ReStatic => RegionKind::ReStatic,
+            ty::RePlaceholder(place_holder) => RegionKind::RePlaceholder(crate::ty::Placeholder {
+                universe: place_holder.universe.as_u32(),
+                bound: BoundRegion {
+                    var: place_holder.bound.var.as_u32(),
+                    kind: place_holder.bound.kind.stable(tables, cx),
+                },
+            }),
+            ty::ReErased => RegionKind::ReErased,
+            _ => unreachable!("{self:?}"),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
+    type T = crate::mir::mono::Instance;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        let def = tables.instance_def(cx.lift(*self).unwrap());
+        let kind = match self.def {
+            ty::InstanceKind::Item(..) => crate::mir::mono::InstanceKind::Item,
+            ty::InstanceKind::Intrinsic(..) => crate::mir::mono::InstanceKind::Intrinsic,
+            ty::InstanceKind::Virtual(_def_id, idx) => {
+                crate::mir::mono::InstanceKind::Virtual { idx }
+            }
+            ty::InstanceKind::VTableShim(..)
+            | ty::InstanceKind::ReifyShim(..)
+            | ty::InstanceKind::FnPtrAddrShim(..)
+            | ty::InstanceKind::ClosureOnceShim { .. }
+            | ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
+            | ty::InstanceKind::ThreadLocalShim(..)
+            | ty::InstanceKind::DropGlue(..)
+            | ty::InstanceKind::CloneShim(..)
+            | ty::InstanceKind::FnPtrShim(..)
+            | ty::InstanceKind::FutureDropPollShim(..)
+            | ty::InstanceKind::AsyncDropGlue(..)
+            | ty::InstanceKind::AsyncDropGlueCtorShim(..) => crate::mir::mono::InstanceKind::Shim,
+        };
+        crate::mir::mono::Instance { def, kind }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Variance {
+    type T = crate::mir::Variance;
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::Bivariant => crate::mir::Variance::Bivariant,
+            ty::Contravariant => crate::mir::Variance::Contravariant,
+            ty::Covariant => crate::mir::Variance::Covariant,
+            ty::Invariant => crate::mir::Variance::Invariant,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::Movability {
+    type T = crate::ty::Movability;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        match self {
+            ty::Movability::Static => crate::ty::Movability::Static,
+            ty::Movability::Movable => crate::ty::Movability::Movable,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
+    type T = crate::ty::Abi;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use rustc_abi::ExternAbi;
+
+        use crate::ty::Abi;
+        match *self {
+            ExternAbi::Rust => Abi::Rust,
+            ExternAbi::C { unwind } => Abi::C { unwind },
+            ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
+            ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
+            ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
+            ExternAbi::Vectorcall { unwind } => Abi::Vectorcall { unwind },
+            ExternAbi::Thiscall { unwind } => Abi::Thiscall { unwind },
+            ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
+            ExternAbi::Win64 { unwind } => Abi::Win64 { unwind },
+            ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
+            ExternAbi::PtxKernel => Abi::PtxKernel,
+            ExternAbi::GpuKernel => Abi::GpuKernel,
+            ExternAbi::Msp430Interrupt => Abi::Msp430Interrupt,
+            ExternAbi::X86Interrupt => Abi::X86Interrupt,
+            ExternAbi::EfiApi => Abi::EfiApi,
+            ExternAbi::AvrInterrupt => Abi::AvrInterrupt,
+            ExternAbi::AvrNonBlockingInterrupt => Abi::AvrNonBlockingInterrupt,
+            ExternAbi::CmseNonSecureCall => Abi::CCmseNonSecureCall,
+            ExternAbi::CmseNonSecureEntry => Abi::CCmseNonSecureEntry,
+            ExternAbi::System { unwind } => Abi::System { unwind },
+            ExternAbi::RustCall => Abi::RustCall,
+            ExternAbi::Unadjusted => Abi::Unadjusted,
+            ExternAbi::RustCold => Abi::RustCold,
+            ExternAbi::RustInvalid => Abi::RustInvalid,
+            ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
+            ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
+            ExternAbi::Custom => Abi::Custom,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_session::cstore::ForeignModule {
+    type T = crate::ty::ForeignModule;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::ty::ForeignModule {
+            def_id: tables.foreign_module_def(self.def_id),
+            abi: self.abi.stable(tables, cx),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocKind {
+    type T = crate::ty::AssocKind;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::ty::{AssocKind, AssocTypeData};
+        match *self {
+            ty::AssocKind::Const { name } => AssocKind::Const { name: name.to_string() },
+            ty::AssocKind::Fn { name, has_self } => {
+                AssocKind::Fn { name: name.to_string(), has_self }
+            }
+            ty::AssocKind::Type { data } => AssocKind::Type {
+                data: match data {
+                    ty::AssocTypeData::Normal(name) => AssocTypeData::Normal(name.to_string()),
+                    ty::AssocTypeData::Rpitit(rpitit) => {
+                        AssocTypeData::Rpitit(rpitit.stable(tables, cx))
+                    }
+                },
+            },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocItemContainer {
+    type T = crate::ty::AssocItemContainer;
+
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
+        use crate::ty::AssocItemContainer;
+        match self {
+            ty::AssocItemContainer::Trait => AssocItemContainer::Trait,
+            ty::AssocItemContainer::Impl => AssocItemContainer::Impl,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocItem {
+    type T = crate::ty::AssocItem;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::ty::AssocItem {
+            def_id: tables.assoc_def(self.def_id),
+            kind: self.kind.stable(tables, cx),
+            container: self.container.stable(tables, cx),
+            trait_item_def_id: self.trait_item_def_id.map(|did| tables.assoc_def(did)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData {
+    type T = crate::ty::ImplTraitInTraitData;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use crate::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) }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_middle::ty::util::Discr<'tcx> {
+    type T = crate::ty::Discr;
+
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        crate::ty::Discr { val: self.val, ty: self.ty.stable(tables, cx) }
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/internal_cx/mod.rs b/compiler/rustc_public/src/unstable/internal_cx/mod.rs
new file mode 100644
index 00000000000..6b0a06e304c
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/internal_cx/mod.rs
@@ -0,0 +1,93 @@
+//! Implementation of InternalCx.
+
+use rustc_middle::ty::{List, Ty, TyCtxt};
+use rustc_middle::{mir, ty};
+pub(crate) use traits::*;
+
+use super::InternalCx;
+
+pub(crate) mod traits;
+
+impl<'tcx, T: InternalCx<'tcx>> SmirExistentialProjection<'tcx> for T {
+    fn new_from_args(
+        &self,
+        def_id: rustc_span::def_id::DefId,
+        args: ty::GenericArgsRef<'tcx>,
+        term: ty::Term<'tcx>,
+    ) -> ty::ExistentialProjection<'tcx> {
+        ty::ExistentialProjection::new_from_args(self.tcx(), def_id, args, term)
+    }
+}
+
+impl<'tcx, T: InternalCx<'tcx>> SmirExistentialTraitRef<'tcx> for T {
+    fn new_from_args(
+        &self,
+        trait_def_id: rustc_span::def_id::DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> ty::ExistentialTraitRef<'tcx> {
+        ty::ExistentialTraitRef::new_from_args(self.tcx(), trait_def_id, args)
+    }
+}
+
+impl<'tcx, T: InternalCx<'tcx>> SmirTraitRef<'tcx> for T {
+    fn new_from_args(
+        &self,
+        trait_def_id: rustc_span::def_id::DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> ty::TraitRef<'tcx> {
+        ty::TraitRef::new_from_args(self.tcx(), trait_def_id, args)
+    }
+}
+
+impl<'tcx> InternalCx<'tcx> for TyCtxt<'tcx> {
+    fn tcx(self) -> TyCtxt<'tcx> {
+        self
+    }
+
+    fn lift<T: ty::Lift<TyCtxt<'tcx>>>(self, value: T) -> Option<T::Lifted> {
+        TyCtxt::lift(self, value)
+    }
+
+    fn mk_args_from_iter<I, T>(self, iter: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: ty::CollectAndApply<ty::GenericArg<'tcx>, ty::GenericArgsRef<'tcx>>,
+    {
+        TyCtxt::mk_args_from_iter(self, iter)
+    }
+
+    fn mk_pat(self, v: ty::PatternKind<'tcx>) -> ty::Pattern<'tcx> {
+        TyCtxt::mk_pat(self, v)
+    }
+
+    fn mk_poly_existential_predicates(
+        self,
+        eps: &[ty::PolyExistentialPredicate<'tcx>],
+    ) -> &'tcx List<ty::PolyExistentialPredicate<'tcx>> {
+        TyCtxt::mk_poly_existential_predicates(self, eps)
+    }
+
+    fn mk_type_list(self, v: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> {
+        TyCtxt::mk_type_list(self, v)
+    }
+
+    fn lifetimes_re_erased(self) -> ty::Region<'tcx> {
+        self.lifetimes.re_erased
+    }
+
+    fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: ty::CollectAndApply<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>,
+    {
+        TyCtxt::mk_bound_variable_kinds_from_iter(self, iter)
+    }
+
+    fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List<mir::PlaceElem<'tcx>> {
+        TyCtxt::mk_place_elems(self, v)
+    }
+
+    fn adt_def(self, def_id: rustc_hir::def_id::DefId) -> ty::AdtDef<'tcx> {
+        self.adt_def(def_id)
+    }
+}
diff --git a/compiler/rustc_public/src/unstable/internal_cx/traits.rs b/compiler/rustc_public/src/unstable/internal_cx/traits.rs
new file mode 100644
index 00000000000..da443cd78f1
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/internal_cx/traits.rs
@@ -0,0 +1,31 @@
+//! A set of traits that define a stable interface to rustc's internals.
+//!
+//! These traits are primarily used to clarify the behavior of different
+//! functions that share the same name across various contexts.
+
+use rustc_middle::ty;
+
+pub(crate) trait SmirExistentialProjection<'tcx> {
+    fn new_from_args(
+        &self,
+        def_id: rustc_span::def_id::DefId,
+        args: ty::GenericArgsRef<'tcx>,
+        term: ty::Term<'tcx>,
+    ) -> ty::ExistentialProjection<'tcx>;
+}
+
+pub(crate) trait SmirExistentialTraitRef<'tcx> {
+    fn new_from_args(
+        &self,
+        trait_def_id: rustc_span::def_id::DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> ty::ExistentialTraitRef<'tcx>;
+}
+
+pub(crate) trait SmirTraitRef<'tcx> {
+    fn new_from_args(
+        &self,
+        trait_def_id: rustc_span::def_id::DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> ty::TraitRef<'tcx>;
+}
diff --git a/compiler/rustc_public/src/unstable/mod.rs b/compiler/rustc_public/src/unstable/mod.rs
new file mode 100644
index 00000000000..ce7c41a64fa
--- /dev/null
+++ b/compiler/rustc_public/src/unstable/mod.rs
@@ -0,0 +1,124 @@
+//! Module that collects the things that have no stability guarantees.
+//!
+//! We want to keep StableMIR definitions and logic separate from
+//! any sort of conversion and usage of internal rustc code. So we
+//! restrict the usage of internal items to be inside this module.
+
+use std::marker::PointeeSized;
+
+use rustc_hir::def::DefKind;
+use rustc_middle::ty::{List, Ty, TyCtxt};
+use rustc_middle::{mir, ty};
+use rustc_public_bridge::Tables;
+use rustc_public_bridge::context::SmirCtxt;
+
+use super::compiler_interface::BridgeTys;
+use crate::{CtorKind, ItemKind};
+
+pub(crate) mod convert;
+mod internal_cx;
+
+/// Trait that defines the methods that are fine to call from [`RustcInternal`].
+///
+/// This trait is only for [`RustcInternal`]. Any other other access to rustc's internals
+/// should go through [`rustc_public_bridge::context::SmirCtxt`].
+pub trait InternalCx<'tcx>: Copy + Clone {
+    fn tcx(self) -> TyCtxt<'tcx>;
+
+    fn lift<T: ty::Lift<TyCtxt<'tcx>>>(self, value: T) -> Option<T::Lifted>;
+
+    fn mk_args_from_iter<I, T>(self, iter: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: ty::CollectAndApply<ty::GenericArg<'tcx>, ty::GenericArgsRef<'tcx>>;
+
+    fn mk_pat(self, v: ty::PatternKind<'tcx>) -> ty::Pattern<'tcx>;
+
+    fn mk_poly_existential_predicates(
+        self,
+        eps: &[ty::PolyExistentialPredicate<'tcx>],
+    ) -> &'tcx List<ty::PolyExistentialPredicate<'tcx>>;
+
+    fn mk_type_list(self, v: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>>;
+
+    fn lifetimes_re_erased(self) -> ty::Region<'tcx>;
+
+    fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: ty::CollectAndApply<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>;
+
+    fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List<mir::PlaceElem<'tcx>>;
+
+    fn adt_def(self, def_id: rustc_hir::def_id::DefId) -> ty::AdtDef<'tcx>;
+}
+
+/// Trait used to convert between an internal MIR type to a Stable MIR type.
+///
+/// This trait is currently exposed to users so they can have interoperability between internal MIR
+/// and StableMIR constructs. However, they should be used seldom and they have no influence
+/// in this crate semver.
+#[doc(hidden)]
+pub trait Stable<'tcx>: PointeeSized {
+    /// The stable representation of the type implementing Stable.
+    type T;
+    /// Converts an object to the equivalent Stable MIR representation.
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T;
+}
+
+/// Trait used to translate a stable construct to its rustc counterpart.
+///
+/// This is basically a mirror of [Stable].
+///
+/// This trait is currently exposed to users so they can have interoperability between internal MIR
+/// and StableMIR constructs. They should be used seldom as they have no stability guarantees.
+#[doc(hidden)]
+pub trait RustcInternal {
+    type T<'tcx>;
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx>;
+}
+
+pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind {
+    match kind {
+        DefKind::Mod
+        | DefKind::Struct
+        | DefKind::Union
+        | DefKind::Enum
+        | DefKind::Variant
+        | DefKind::Trait
+        | DefKind::TyAlias
+        | DefKind::ForeignTy
+        | DefKind::TraitAlias
+        | DefKind::AssocTy
+        | DefKind::TyParam
+        | DefKind::ConstParam
+        | DefKind::Macro(_)
+        | DefKind::ExternCrate
+        | DefKind::Use
+        | DefKind::ForeignMod
+        | DefKind::OpaqueTy
+        | DefKind::Field
+        | DefKind::LifetimeParam
+        | DefKind::Impl { .. }
+        | DefKind::GlobalAsm => {
+            unreachable!("Not a valid item kind: {kind:?}");
+        }
+        DefKind::Closure | DefKind::AssocFn | DefKind::Fn | DefKind::SyntheticCoroutineBody => {
+            ItemKind::Fn
+        }
+        DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => {
+            ItemKind::Const
+        }
+        DefKind::Static { .. } => ItemKind::Static,
+        DefKind::Ctor(_, rustc_hir::def::CtorKind::Const) => ItemKind::Ctor(CtorKind::Const),
+        DefKind::Ctor(_, rustc_hir::def::CtorKind::Fn) => ItemKind::Ctor(CtorKind::Fn),
+    }
+}
diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs
new file mode 100644
index 00000000000..45e2a815470
--- /dev/null
+++ b/compiler/rustc_public/src/visitor.rs
@@ -0,0 +1,224 @@
+use std::ops::ControlFlow;
+
+use super::ty::{
+    Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs,
+    MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst,
+};
+use crate::Opaque;
+use crate::ty::TyConst;
+
+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)
+    }
+}