diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 2982a920b03d1..dcdc77b76c216 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -4,28 +4,17 @@
 //! until stable MIR is complete.
 
 use std::cell::{Cell, RefCell};
-use std::fmt::Debug;
-use std::hash::Hash;
-use std::ops::Index;
 
-use rustc_data_structures::fx;
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_middle::mir::interpret::AllocId;
-use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
-use rustc_span::Span;
-use rustc_span::def_id::{CrateNum, DefId};
+use rustc_span::def_id::CrateNum;
 use scoped_tls::scoped_thread_local;
 use stable_mir::Error;
-use stable_mir::abi::Layout;
-use stable_mir::compiler_interface::SmirInterface;
-use stable_mir::ty::IndexedVal;
+use stable_mir::unstable::{RustcInternal, Stable};
 
 use crate::rustc_smir::context::SmirCtxt;
-use crate::rustc_smir::{Stable, Tables};
+use crate::rustc_smir::{Bridge, SmirContainer, Tables};
 use crate::stable_mir;
 
-mod internal;
 pub mod pretty;
 
 /// Convert an internal Rust compiler item into its stable counterpart, if one exists.
@@ -40,7 +29,7 @@ pub mod pretty;
 ///
 /// This function will panic if StableMIR has not been properly initialized.
 pub fn stable<'tcx, S: Stable<'tcx>>(item: S) -> S::T {
-    with_tables(|tables| item.stable(tables))
+    with_container(|tables, cx| item.stable(tables, cx))
 }
 
 /// Convert a stable item into its internal Rust compiler counterpart, if one exists.
@@ -59,134 +48,9 @@ where
     S: RustcInternal,
 {
     // The tcx argument ensures that the item won't outlive the type context.
-    with_tables(|tables| item.internal(tables, tcx))
-}
-
-impl<'tcx> Index<stable_mir::DefId> for Tables<'tcx> {
-    type Output = DefId;
-
-    #[inline(always)]
-    fn index(&self, index: stable_mir::DefId) -> &Self::Output {
-        &self.def_ids[index]
-    }
-}
-
-impl<'tcx> Index<stable_mir::ty::Span> for Tables<'tcx> {
-    type Output = Span;
-
-    #[inline(always)]
-    fn index(&self, index: stable_mir::ty::Span) -> &Self::Output {
-        &self.spans[index]
-    }
-}
-
-impl<'tcx> Tables<'tcx> {
-    pub fn crate_item(&mut self, did: DefId) -> stable_mir::CrateItem {
-        stable_mir::CrateItem(self.create_def_id(did))
-    }
-
-    pub fn adt_def(&mut self, did: DefId) -> stable_mir::ty::AdtDef {
-        stable_mir::ty::AdtDef(self.create_def_id(did))
-    }
-
-    pub fn foreign_module_def(&mut self, did: DefId) -> stable_mir::ty::ForeignModuleDef {
-        stable_mir::ty::ForeignModuleDef(self.create_def_id(did))
-    }
-
-    pub fn foreign_def(&mut self, did: DefId) -> stable_mir::ty::ForeignDef {
-        stable_mir::ty::ForeignDef(self.create_def_id(did))
-    }
-
-    pub fn fn_def(&mut self, did: DefId) -> stable_mir::ty::FnDef {
-        stable_mir::ty::FnDef(self.create_def_id(did))
-    }
-
-    pub fn closure_def(&mut self, did: DefId) -> stable_mir::ty::ClosureDef {
-        stable_mir::ty::ClosureDef(self.create_def_id(did))
-    }
-
-    pub fn coroutine_def(&mut self, did: DefId) -> stable_mir::ty::CoroutineDef {
-        stable_mir::ty::CoroutineDef(self.create_def_id(did))
-    }
-
-    pub fn coroutine_closure_def(&mut self, did: DefId) -> stable_mir::ty::CoroutineClosureDef {
-        stable_mir::ty::CoroutineClosureDef(self.create_def_id(did))
-    }
-
-    pub fn alias_def(&mut self, did: DefId) -> stable_mir::ty::AliasDef {
-        stable_mir::ty::AliasDef(self.create_def_id(did))
-    }
-
-    pub fn param_def(&mut self, did: DefId) -> stable_mir::ty::ParamDef {
-        stable_mir::ty::ParamDef(self.create_def_id(did))
-    }
-
-    pub fn br_named_def(&mut self, did: DefId) -> stable_mir::ty::BrNamedDef {
-        stable_mir::ty::BrNamedDef(self.create_def_id(did))
-    }
-
-    pub fn trait_def(&mut self, did: DefId) -> stable_mir::ty::TraitDef {
-        stable_mir::ty::TraitDef(self.create_def_id(did))
-    }
-
-    pub fn generic_def(&mut self, did: DefId) -> stable_mir::ty::GenericDef {
-        stable_mir::ty::GenericDef(self.create_def_id(did))
-    }
-
-    pub fn const_def(&mut self, did: DefId) -> stable_mir::ty::ConstDef {
-        stable_mir::ty::ConstDef(self.create_def_id(did))
-    }
-
-    pub fn impl_def(&mut self, did: DefId) -> stable_mir::ty::ImplDef {
-        stable_mir::ty::ImplDef(self.create_def_id(did))
-    }
-
-    pub fn region_def(&mut self, did: DefId) -> stable_mir::ty::RegionDef {
-        stable_mir::ty::RegionDef(self.create_def_id(did))
-    }
-
-    pub fn coroutine_witness_def(&mut self, did: DefId) -> stable_mir::ty::CoroutineWitnessDef {
-        stable_mir::ty::CoroutineWitnessDef(self.create_def_id(did))
-    }
-
-    pub fn assoc_def(&mut self, did: DefId) -> stable_mir::ty::AssocDef {
-        stable_mir::ty::AssocDef(self.create_def_id(did))
-    }
-
-    pub fn opaque_def(&mut self, did: DefId) -> stable_mir::ty::OpaqueDef {
-        stable_mir::ty::OpaqueDef(self.create_def_id(did))
-    }
-
-    pub fn prov(&mut self, aid: AllocId) -> stable_mir::ty::Prov {
-        stable_mir::ty::Prov(self.create_alloc_id(aid))
-    }
-
-    pub(crate) fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
-        self.def_ids.create_or_fetch(did)
-    }
-
-    pub(crate) fn create_alloc_id(&mut self, aid: AllocId) -> stable_mir::mir::alloc::AllocId {
-        self.alloc_ids.create_or_fetch(aid)
-    }
-
-    pub(crate) fn create_span(&mut self, span: Span) -> stable_mir::ty::Span {
-        self.spans.create_or_fetch(span)
-    }
-
-    pub(crate) fn instance_def(
-        &mut self,
-        instance: ty::Instance<'tcx>,
-    ) -> stable_mir::mir::mono::InstanceDef {
-        self.instances.create_or_fetch(instance)
-    }
-
-    pub(crate) fn static_def(&mut self, did: DefId) -> stable_mir::mir::mono::StaticDef {
-        stable_mir::mir::mono::StaticDef(self.create_def_id(did))
-    }
-
-    pub(crate) fn layout_id(&mut self, layout: rustc_abi::Layout<'tcx>) -> Layout {
-        self.layouts.create_or_fetch(layout)
-    }
+    // 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: &stable_mir::Crate) -> CrateNum {
@@ -197,25 +61,28 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
 // datastructures and stable MIR datastructures
 scoped_thread_local! (static TLV: Cell<*const ()>);
 
-pub(crate) fn init<'tcx, F, T>(cx: &SmirCtxt<'tcx>, f: F) -> T
+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 = cx as *const _ as *const ();
+    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_tables<R>(f: impl for<'tcx> FnOnce(&mut Tables<'tcx>) -> R) -> R {
+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 context = ptr as *const SmirCtxt<'_>;
-        let mut tables = unsafe { (*context).0.borrow_mut() };
-        f(&mut *tables)
+        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)
     })
 }
 
@@ -223,23 +90,10 @@ pub fn run<F, T>(tcx: TyCtxt<'_>, f: F) -> Result<T, Error>
 where
     F: FnOnce() -> T,
 {
-    let tables = SmirCtxt(RefCell::new(Tables {
-        tcx,
-        def_ids: IndexMap::default(),
-        alloc_ids: IndexMap::default(),
-        spans: IndexMap::default(),
-        types: IndexMap::default(),
-        instances: IndexMap::default(),
-        ty_consts: IndexMap::default(),
-        mir_consts: IndexMap::default(),
-        layouts: IndexMap::default(),
-    }));
+    let smir_cx = RefCell::new(SmirCtxt::new(tcx));
+    let container = SmirContainer { tables: RefCell::new(Tables::default()), cx: smir_cx };
 
-    let interface = SmirInterface { cx: tables };
-
-    // Pass the `SmirInterface` to compiler_interface::run
-    // and initialize the rustc-specific TLS with tables.
-    stable_mir::compiler_interface::run(&interface, || init(&interface.cx, f))
+    stable_mir::compiler_interface::run(&container, || init(&container, f))
 }
 
 /// Instantiate and run the compiler with the provided arguments and callback.
@@ -417,43 +271,3 @@ macro_rules! run_driver {
         StableMir::new($callback).run($args)
     }};
 }
-
-/// Similar to rustc's `FxIndexMap`, `IndexMap` with extra
-/// safety features added.
-pub struct IndexMap<K, V> {
-    index_map: fx::FxIndexMap<K, V>,
-}
-
-impl<K, V> Default for IndexMap<K, V> {
-    fn default() -> Self {
-        Self { index_map: FxIndexMap::default() }
-    }
-}
-
-impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> IndexMap<K, V> {
-    pub fn create_or_fetch(&mut self, key: K) -> V {
-        let len = self.index_map.len();
-        let v = self.index_map.entry(key).or_insert(V::to_val(len));
-        *v
-    }
-}
-
-impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> Index<V>
-    for IndexMap<K, V>
-{
-    type Output = K;
-
-    fn index(&self, index: V) -> &Self::Output {
-        let (k, v) = self.index_map.get_index(index.to_index()).unwrap();
-        assert_eq!(*v, index, "Provided value doesn't match with indexed value");
-        k
-    }
-}
-
-/// Trait used to translate a stable construct to its rustc counterpart.
-///
-/// This is basically a mirror of [crate::rustc_smir::Stable].
-pub trait RustcInternal {
-    type T<'tcx>;
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx>;
-}
diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs
index a6d31ac4e13dd..ecaf3571896ea 100644
--- a/compiler/rustc_smir/src/rustc_smir/alloc.rs
+++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs
@@ -1,102 +1,82 @@
-use rustc_abi::{Align, Size};
-use rustc_middle::mir::ConstValue;
-use rustc_middle::mir::interpret::{AllocInit, AllocRange, Pointer, alloc_range};
-use stable_mir::Error;
-use stable_mir::mir::Mutability;
-use stable_mir::ty::{Allocation, ProvenanceMap};
+//! Internal memory allocator implementation for StableMIR.
+//!
+//! This module handles all direct interactions with rustc queries and performs
+//! the actual memory allocations. The stable interface in `stable_mir::alloc`
+//! delegates all query-related operations to this implementation.
 
-use crate::rustc_smir::{Stable, Tables};
-use crate::stable_mir;
+use rustc_abi::{Size, TyAndLayout};
+use rustc_middle::mir::interpret::{
+    AllocId, AllocInit, AllocRange, Allocation, ConstAllocation, Pointer, Scalar, alloc_range,
+};
+use rustc_middle::ty::{Ty, layout};
 
-/// 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,
-    }
+use super::{SmirCtxt, Tables};
+use crate::rustc_smir::bridge::Allocation as _;
+use crate::rustc_smir::{Bridge, SmirError};
+
+pub fn create_ty_and_layout<'tcx, B: Bridge>(
+    cx: &SmirCtxt<'tcx, B>,
+    ty: Ty<'tcx>,
+) -> Result<TyAndLayout<'tcx, Ty<'tcx>>, &'tcx layout::LayoutError<'tcx>> {
+    use crate::rustc_smir::context::SmirTypingEnv;
+    cx.tcx.layout_of(cx.fully_monomorphized().as_query_input(ty))
 }
 
-// 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>,
-) -> Allocation {
-    try_new_allocation(ty, const_value, tables)
-        .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}"))
+pub fn try_new_scalar<'tcx, B: Bridge>(
+    layout: TyAndLayout<'tcx, Ty<'tcx>>,
+    scalar: Scalar,
+    cx: &SmirCtxt<'tcx, B>,
+) -> Result<Allocation, B::Error> {
+    let size = scalar.size();
+    let mut allocation = Allocation::new(size, layout.align.abi, AllocInit::Uninit, ());
+    allocation
+        .write_scalar(&cx.tcx, alloc_range(Size::ZERO, size), scalar)
+        .map_err(|e| B::Error::from_internal(e))?;
+
+    Ok(allocation)
 }
 
-#[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>,
-) -> Result<Allocation, Error> {
-    let layout = tables
-        .tcx
-        .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-        .map_err(|e| e.stable(tables))?;
-    Ok(match const_value {
-        ConstValue::Scalar(scalar) => {
-            let size = scalar.size();
-            let mut allocation = rustc_middle::mir::interpret::Allocation::new(
-                size,
-                layout.align.abi,
-                AllocInit::Uninit,
-                (),
-            );
-            allocation
-                .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar)
-                .map_err(|e| e.stable(tables))?;
-            allocation.stable(tables)
-        }
-        ConstValue::ZeroSized => new_empty_allocation(layout.align.abi),
-        ConstValue::Slice { data, meta } => {
-            let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
-            let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
-            let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
-            let scalar_meta =
-                rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx);
-            let mut allocation = rustc_middle::mir::interpret::Allocation::new(
-                layout.size,
-                layout.align.abi,
-                AllocInit::Uninit,
-                (),
-            );
-            allocation
-                .write_scalar(
-                    &tables.tcx,
-                    alloc_range(Size::ZERO, tables.tcx.data_layout.pointer_size),
-                    scalar_ptr,
-                )
-                .map_err(|e| e.stable(tables))?;
-            allocation
-                .write_scalar(
-                    &tables.tcx,
-                    alloc_range(tables.tcx.data_layout.pointer_size, scalar_meta.size()),
-                    scalar_meta,
-                )
-                .map_err(|e| e.stable(tables))?;
-            allocation.stable(tables)
-        }
-        ConstValue::Indirect { alloc_id, offset } => {
-            let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
-            allocation_filter(&alloc.0, alloc_range(offset, layout.size), tables)
-        }
-    })
+pub fn try_new_slice<'tcx, B: Bridge>(
+    layout: TyAndLayout<'tcx, Ty<'tcx>>,
+    data: ConstAllocation<'tcx>,
+    meta: u64,
+    cx: &SmirCtxt<'tcx, B>,
+) -> Result<Allocation, B::Error> {
+    let alloc_id = cx.tcx.reserve_and_set_memory_alloc(data);
+    let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
+    let scalar_ptr = Scalar::from_pointer(ptr, &cx.tcx);
+    let scalar_meta: Scalar = Scalar::from_target_usize(meta, &cx.tcx);
+    let mut allocation = Allocation::new(layout.size, layout.align.abi, AllocInit::Uninit, ());
+    allocation
+        .write_scalar(&cx.tcx, alloc_range(Size::ZERO, cx.tcx.data_layout.pointer_size), scalar_ptr)
+        .map_err(|e| B::Error::from_internal(e))?;
+    allocation
+        .write_scalar(
+            &cx.tcx,
+            alloc_range(cx.tcx.data_layout.pointer_size, scalar_meta.size()),
+            scalar_meta,
+        )
+        .map_err(|e| B::Error::from_internal(e))?;
+
+    Ok(allocation)
+}
+
+pub fn try_new_indirect<'tcx, B: Bridge>(
+    alloc_id: AllocId,
+    cx: &SmirCtxt<'tcx, B>,
+) -> ConstAllocation<'tcx> {
+    let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory();
+
+    alloc
 }
 
 /// Creates an `Allocation` only from information within the `AllocRange`.
-pub(super) fn allocation_filter<'tcx>(
+pub fn allocation_filter<'tcx, B: Bridge>(
     alloc: &rustc_middle::mir::interpret::Allocation,
     alloc_range: AllocRange,
-    tables: &mut Tables<'tcx>,
-) -> Allocation {
+    tables: &mut Tables<'tcx, B>,
+    cx: &SmirCtxt<'tcx, B>,
+) -> B::Allocation {
     let mut bytes: Vec<Option<u8>> = alloc
         .inspect_with_uninit_and_ptr_outside_interpreter(
             alloc_range.start.bytes_usize()..alloc_range.end().bytes_usize(),
@@ -117,15 +97,8 @@ pub(super) fn allocation_filter<'tcx>(
         .iter()
         .filter(|a| a.0 >= alloc_range.start && a.0 <= alloc_range.end())
     {
-        ptrs.push((
-            offset.bytes_usize() - alloc_range.start.bytes_usize(),
-            tables.prov(prov.alloc_id()),
-        ));
-    }
-    Allocation {
-        bytes,
-        provenance: ProvenanceMap { ptrs },
-        align: alloc.align.bytes(),
-        mutability: alloc.mutability.stable(tables),
+        ptrs.push((offset.bytes_usize() - alloc_range.start.bytes_usize(), prov.alloc_id()));
     }
+
+    B::Allocation::new(bytes, ptrs, alloc.align.bytes(), alloc.mutability, tables, cx)
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/bridge.rs b/compiler/rustc_smir/src/rustc_smir/bridge.rs
new file mode 100644
index 0000000000000..a31eb93d0e80d
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_smir/bridge.rs
@@ -0,0 +1,59 @@
+//! Defines a set of traits that is used for abstracting
+//! stable_mir's components that are needed in rustc_smir.
+//!
+//! These traits are really useful when programming
+//! in stable_mir-agnostic settings.
+
+use std::fmt::Debug;
+
+use super::context::SmirCtxt;
+use super::{Bridge, Tables};
+
+pub trait SmirError {
+    fn new(msg: String) -> Self;
+    fn from_internal<T: Debug>(err: T) -> Self;
+}
+
+pub trait Prov<B: Bridge> {
+    fn new(aid: B::AllocId) -> Self;
+}
+
+pub trait Allocation<B: Bridge> {
+    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, B>,
+        cx: &SmirCtxt<'tcx, B>,
+    ) -> Self;
+}
+
+macro_rules! make_bridge_trait {
+    ($name:ident) => {
+        pub trait $name<B: Bridge> {
+            fn new(did: B::DefId) -> Self;
+        }
+    };
+}
+
+make_bridge_trait!(CrateItem);
+make_bridge_trait!(AdtDef);
+make_bridge_trait!(ForeignModuleDef);
+make_bridge_trait!(ForeignDef);
+make_bridge_trait!(FnDef);
+make_bridge_trait!(ClosureDef);
+make_bridge_trait!(CoroutineDef);
+make_bridge_trait!(CoroutineClosureDef);
+make_bridge_trait!(AliasDef);
+make_bridge_trait!(ParamDef);
+make_bridge_trait!(BrNamedDef);
+make_bridge_trait!(TraitDef);
+make_bridge_trait!(GenericDef);
+make_bridge_trait!(ConstDef);
+make_bridge_trait!(ImplDef);
+make_bridge_trait!(RegionDef);
+make_bridge_trait!(CoroutineWitnessDef);
+make_bridge_trait!(AssocDef);
+make_bridge_trait!(OpaqueDef);
+make_bridge_trait!(StaticDef);
diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs
index 40e6d21c06378..2141053d09af0 100644
--- a/compiler/rustc_smir/src/rustc_smir/builder.rs
+++ b/compiler/rustc_smir/src/rustc_smir/builder.rs
@@ -9,9 +9,6 @@ use rustc_middle::mir;
 use rustc_middle::mir::visit::MutVisitor;
 use rustc_middle::ty::{self, TyCtxt};
 
-use crate::rustc_smir::{Stable, Tables};
-use crate::stable_mir;
-
 /// Builds a monomorphic body for a given instance.
 pub(crate) struct BodyBuilder<'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -31,8 +28,8 @@ impl<'tcx> BodyBuilder<'tcx> {
     /// Build a stable monomorphic body for a given instance based on the MIR body.
     ///
     /// All constants are also evaluated.
-    pub(crate) fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body {
-        let body = tables.tcx.instance_mir(self.instance.def).clone();
+    pub(crate) fn build(mut self) -> mir::Body<'tcx> {
+        let body = self.tcx.instance_mir(self.instance.def).clone();
         let mono_body = if !self.instance.args.is_empty()
             // Without the `generic_const_exprs` feature gate, anon consts in signatures do not
             // get generic parameters. Which is wrong, but also not a problem without
@@ -40,7 +37,7 @@ impl<'tcx> BodyBuilder<'tcx> {
             || self.tcx.def_kind(self.instance.def_id()) != DefKind::AnonConst
         {
             let mut mono_body = self.instance.instantiate_mir_and_normalize_erasing_regions(
-                tables.tcx,
+                self.tcx,
                 ty::TypingEnv::fully_monomorphized(),
                 ty::EarlyBinder::bind(body),
             );
@@ -50,7 +47,8 @@ impl<'tcx> BodyBuilder<'tcx> {
             // Already monomorphic.
             body
         };
-        mono_body.stable(tables)
+
+        mono_body
     }
 }
 
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
deleted file mode 100644
index 3fa83cfc6a08c..0000000000000
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ /dev/null
@@ -1,1000 +0,0 @@
-//! Implementation of StableMIR Context.
-
-#![allow(rustc::usage_of_qualified_ty)]
-
-use std::cell::RefCell;
-use std::iter;
-
-use rustc_abi::HasDataLayout;
-use rustc_hir::{Attribute, LangItem};
-use rustc_middle::ty::layout::{
-    FnAbiOf, FnAbiOfHelpers, HasTyCtxt, HasTypingEnv, LayoutOf, LayoutOfHelpers,
-};
-use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
-use rustc_middle::ty::{
-    CoroutineArgsExt, GenericPredicates, Instance, List, ScalarInt, TyCtxt, TypeVisitableExt,
-    ValTree,
-};
-use rustc_middle::{mir, ty};
-use rustc_span::def_id::LOCAL_CRATE;
-use stable_mir::abi::{FnAbi, Layout, LayoutShape, ReprOptions};
-use stable_mir::mir::alloc::GlobalAlloc;
-use stable_mir::mir::mono::{InstanceDef, StaticDef};
-use stable_mir::mir::{BinOp, Body, Place, UnOp};
-use stable_mir::target::{MachineInfo, MachineSize};
-use stable_mir::ty::{
-    AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef,
-    ForeignDef, ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy,
-    Span, Ty, TyConst, TyKind, UintTy, VariantDef, VariantIdx,
-};
-use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol};
-
-use crate::rustc_internal::RustcInternal;
-use crate::rustc_smir::builder::BodyBuilder;
-use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate};
-use crate::stable_mir;
-
-/// Provides direct access to rustc's internal queries.
-///
-/// The [`crate::stable_mir::compiler_interface::SmirInterface`] must go through
-/// this context to obtain rustc-level information.
-pub struct SmirCtxt<'tcx>(pub RefCell<Tables<'tcx>>);
-
-impl<'tcx> SmirCtxt<'tcx> {
-    pub fn target_info(&self) -> MachineInfo {
-        let mut tables = self.0.borrow_mut();
-        MachineInfo {
-            endian: tables.tcx.data_layout.endian.stable(&mut *tables),
-            pointer_width: MachineSize::from_bits(
-                tables.tcx.data_layout.pointer_size.bits().try_into().unwrap(),
-            ),
-        }
-    }
-
-    pub fn entry_fn(&self) -> Option<stable_mir::CrateItem> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        Some(tables.crate_item(tcx.entry_fn(())?.0))
-    }
-
-    /// Retrieve all items of the local crate that have a MIR associated with them.
-    pub fn all_local_items(&self) -> stable_mir::CrateItems {
-        let mut tables = self.0.borrow_mut();
-        tables.tcx.mir_keys(()).iter().map(|item| tables.crate_item(item.to_def_id())).collect()
-    }
-
-    /// Retrieve the body of a function.
-    /// This function will panic if the body is not available.
-    pub fn mir_body(&self, item: stable_mir::DefId) -> stable_mir::mir::Body {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[item];
-        tables.tcx.instance_mir(rustc_middle::ty::InstanceKind::Item(def_id)).stable(&mut tables)
-    }
-
-    /// Check whether the body of a function is available.
-    pub fn has_body(&self, def: DefId) -> bool {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.internal(&mut *tables, tcx);
-        tables.item_has_body(def_id)
-    }
-
-    pub fn foreign_modules(&self, crate_num: CrateNum) -> Vec<stable_mir::ty::ForeignModuleDef> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        tcx.foreign_modules(crate_num.internal(&mut *tables, tcx))
-            .keys()
-            .map(|mod_def_id| tables.foreign_module_def(*mod_def_id))
-            .collect()
-    }
-
-    /// Retrieve all functions defined in this crate.
-    pub fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let krate = crate_num.internal(&mut *tables, tcx);
-        filter_def_ids(tcx, krate, |def_id| tables.to_fn_def(def_id))
-    }
-
-    /// Retrieve all static items defined in this crate.
-    pub fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let krate = crate_num.internal(&mut *tables, tcx);
-        filter_def_ids(tcx, krate, |def_id| tables.to_static(def_id))
-    }
-
-    pub fn foreign_module(
-        &self,
-        mod_def: stable_mir::ty::ForeignModuleDef,
-    ) -> stable_mir::ty::ForeignModule {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[mod_def.def_id()];
-        let mod_def = tables.tcx.foreign_modules(def_id.krate).get(&def_id).unwrap();
-        mod_def.stable(&mut *tables)
-    }
-
-    pub fn foreign_items(&self, mod_def: stable_mir::ty::ForeignModuleDef) -> Vec<ForeignDef> {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[mod_def.def_id()];
-        tables
-            .tcx
-            .foreign_modules(def_id.krate)
-            .get(&def_id)
-            .unwrap()
-            .foreign_items
-            .iter()
-            .map(|item_def| tables.foreign_def(*item_def))
-            .collect()
-    }
-
-    pub fn all_trait_decls(&self) -> stable_mir::TraitDecls {
-        let mut tables = self.0.borrow_mut();
-        tables
-            .tcx
-            .all_traits_including_private()
-            .map(|trait_def_id| tables.trait_def(trait_def_id))
-            .collect()
-    }
-
-    pub fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        tcx.traits(crate_num.internal(&mut *tables, tcx))
-            .iter()
-            .map(|trait_def_id| tables.trait_def(*trait_def_id))
-            .collect()
-    }
-
-    pub fn trait_decl(&self, trait_def: &stable_mir::ty::TraitDef) -> stable_mir::ty::TraitDecl {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[trait_def.0];
-        let trait_def = tables.tcx.trait_def(def_id);
-        trait_def.stable(&mut *tables)
-    }
-
-    pub fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        iter::once(LOCAL_CRATE)
-            .chain(tables.tcx.crates(()).iter().copied())
-            .flat_map(|cnum| tcx.trait_impls_in_crate(cnum).iter())
-            .map(|impl_def_id| tables.impl_def(*impl_def_id))
-            .collect()
-    }
-
-    pub fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        tcx.trait_impls_in_crate(crate_num.internal(&mut *tables, tcx))
-            .iter()
-            .map(|impl_def_id| tables.impl_def(*impl_def_id))
-            .collect()
-    }
-
-    pub fn trait_impl(&self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[impl_def.0];
-        let impl_trait = tables.tcx.impl_trait_ref(def_id).unwrap();
-        impl_trait.stable(&mut *tables)
-    }
-
-    pub fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[def_id];
-        let generics = tables.tcx.generics_of(def_id);
-        generics.stable(&mut *tables)
-    }
-
-    pub fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[def_id];
-        let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id);
-        stable_mir::ty::GenericPredicates {
-            parent: parent.map(|did| tables.trait_def(did)),
-            predicates: predicates
-                .iter()
-                .map(|(clause, span)| {
-                    (
-                        clause.as_predicate().kind().skip_binder().stable(&mut *tables),
-                        span.stable(&mut *tables),
-                    )
-                })
-                .collect(),
-        }
-    }
-
-    pub fn explicit_predicates_of(
-        &self,
-        def_id: stable_mir::DefId,
-    ) -> stable_mir::ty::GenericPredicates {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[def_id];
-        let GenericPredicates { parent, predicates } = tables.tcx.explicit_predicates_of(def_id);
-        stable_mir::ty::GenericPredicates {
-            parent: parent.map(|did| tables.trait_def(did)),
-            predicates: predicates
-                .iter()
-                .map(|(clause, span)| {
-                    (
-                        clause.as_predicate().kind().skip_binder().stable(&mut *tables),
-                        span.stable(&mut *tables),
-                    )
-                })
-                .collect(),
-        }
-    }
-
-    /// Get information about the local crate.
-    pub fn local_crate(&self) -> stable_mir::Crate {
-        let tables = self.0.borrow();
-        smir_crate(tables.tcx, LOCAL_CRATE)
-    }
-
-    /// Retrieve a list of all external crates.
-    pub fn external_crates(&self) -> Vec<stable_mir::Crate> {
-        let tables = self.0.borrow();
-        tables.tcx.crates(()).iter().map(|crate_num| smir_crate(tables.tcx, *crate_num)).collect()
-    }
-
-    /// Find a crate with the given name.
-    pub fn find_crates(&self, name: &str) -> Vec<stable_mir::Crate> {
-        let tables = self.0.borrow();
-        let crates: Vec<stable_mir::Crate> = [LOCAL_CRATE]
-            .iter()
-            .chain(tables.tcx.crates(()).iter())
-            .filter_map(|crate_num| {
-                let crate_name = tables.tcx.crate_name(*crate_num).to_string();
-                (name == crate_name).then(|| smir_crate(tables.tcx, *crate_num))
-            })
-            .collect();
-        crates
-    }
-
-    /// Returns the name of given `DefId`.
-    pub fn def_name(&self, def_id: stable_mir::DefId, trimmed: bool) -> Symbol {
-        let tables = self.0.borrow();
-        if trimmed {
-            with_forced_trimmed_paths!(tables.tcx.def_path_str(tables[def_id]))
-        } else {
-            with_no_trimmed_paths!(tables.tcx.def_path_str(tables[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()]`.
-    pub fn tool_attrs(
-        &self,
-        def_id: stable_mir::DefId,
-        attr: &[stable_mir::Symbol],
-    ) -> Vec<stable_mir::crate_def::Attribute> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let did = tables[def_id];
-        let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect();
-        tcx.get_attrs_by_path(did, &attr_name)
-            .filter_map(|attribute| {
-                if let Attribute::Unparsed(u) = attribute {
-                    let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
-                    Some(stable_mir::crate_def::Attribute::new(
-                        attr_str,
-                        u.span.stable(&mut *tables),
-                    ))
-                } else {
-                    None
-                }
-            })
-            .collect()
-    }
-
-    /// Get all tool attributes of a definition.
-    pub fn all_tool_attrs(
-        &self,
-        def_id: stable_mir::DefId,
-    ) -> Vec<stable_mir::crate_def::Attribute> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let did = tables[def_id];
-        let attrs_iter = if let Some(did) = did.as_local() {
-            tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)).iter()
-        } else {
-            tcx.attrs_for_def(did).iter()
-        };
-        attrs_iter
-            .filter_map(|attribute| {
-                if let Attribute::Unparsed(u) = attribute {
-                    let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
-                    Some(stable_mir::crate_def::Attribute::new(
-                        attr_str,
-                        u.span.stable(&mut *tables),
-                    ))
-                } else {
-                    None
-                }
-            })
-            .collect()
-    }
-
-    /// Returns printable, human readable form of `Span`.
-    pub fn span_to_string(&self, span: stable_mir::ty::Span) -> String {
-        let tables = self.0.borrow();
-        tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span])
-    }
-
-    /// Return filename from given `Span`, for diagnostic purposes.
-    pub fn get_filename(&self, span: &Span) -> Filename {
-        let tables = self.0.borrow();
-        tables
-            .tcx
-            .sess
-            .source_map()
-            .span_to_filename(tables[*span])
-            .display(rustc_span::FileNameDisplayPreference::Local)
-            .to_string()
-    }
-
-    /// Return lines corresponding to this `Span`.
-    pub fn get_lines(&self, span: &Span) -> LineInfo {
-        let tables = self.0.borrow();
-        let lines = &tables.tcx.sess.source_map().span_to_location_info(tables[*span]);
-        LineInfo { start_line: lines.1, start_col: lines.2, end_line: lines.3, end_col: lines.4 }
-    }
-
-    /// Returns the `kind` of given `DefId`.
-    pub fn item_kind(&self, item: CrateItem) -> ItemKind {
-        let tables = self.0.borrow();
-        new_item_kind(tables.tcx.def_kind(tables[item.0]))
-    }
-
-    /// Returns whether this is a foreign item.
-    pub fn is_foreign_item(&self, item: DefId) -> bool {
-        let tables = self.0.borrow();
-        tables.tcx.is_foreign_item(tables[item])
-    }
-
-    /// Returns the kind of a given foreign item.
-    pub fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[def.def_id()];
-        let tcx = tables.tcx;
-        use rustc_hir::def::DefKind;
-        match tcx.def_kind(def_id) {
-            DefKind::Fn => ForeignItemKind::Fn(tables.fn_def(def_id)),
-            DefKind::Static { .. } => ForeignItemKind::Static(tables.static_def(def_id)),
-            DefKind::ForeignTy => ForeignItemKind::Type(
-                tables.intern_ty(rustc_middle::ty::Ty::new_foreign(tcx, def_id)),
-            ),
-            def_kind => unreachable!("Unexpected kind for a foreign item: {:?}", def_kind),
-        }
-    }
-
-    /// Returns the kind of a given algebraic data type.
-    pub fn adt_kind(&self, def: AdtDef) -> AdtKind {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).adt_kind().stable(&mut *tables)
-    }
-
-    /// Returns if the ADT is a box.
-    pub fn adt_is_box(&self, def: AdtDef) -> bool {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).is_box()
-    }
-
-    /// Returns whether this ADT is simd.
-    pub fn adt_is_simd(&self, def: AdtDef) -> bool {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).repr().simd()
-    }
-
-    /// Returns whether this definition is a C string.
-    pub fn adt_is_cstr(&self, def: AdtDef) -> bool {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        tables.tcx.is_lang_item(def_id, LangItem::CStr)
-    }
-
-    /// Returns the representation options for this ADT
-    pub fn adt_repr(&self, def: AdtDef) -> ReprOptions {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).repr().stable(&mut *tables)
-    }
-
-    /// Retrieve the function signature for the given generic arguments.
-    pub fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        let sig =
-            tables.tcx.fn_sig(def_id).instantiate(tables.tcx, args.internal(&mut *tables, tcx));
-        sig.stable(&mut *tables)
-    }
-
-    /// Retrieve the intrinsic definition if the item corresponds one.
-    pub fn intrinsic(&self, def: DefId) -> Option<IntrinsicDef> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.internal(&mut *tables, tcx);
-        let intrinsic = tcx.intrinsic_raw(def_id);
-        intrinsic.map(|_| IntrinsicDef(def))
-    }
-
-    /// Retrieve the plain function name of an intrinsic.
-    pub fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        tcx.intrinsic(def_id).unwrap().name.to_string()
-    }
-
-    /// Retrieve the closure signature for the given generic arguments.
-    pub fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let args_ref = args.internal(&mut *tables, tcx);
-        let sig = args_ref.as_closure().sig();
-        sig.stable(&mut *tables)
-    }
-
-    /// The number of variants in this ADT.
-    pub fn adt_variants_len(&self, def: AdtDef) -> usize {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).variants().len()
-    }
-
-    /// Discriminant for a given variant index of AdtDef
-    pub fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let adt = adt.internal(&mut *tables, tcx);
-        let variant = variant.internal(&mut *tables, tcx);
-        adt.discriminant_for_variant(tcx, variant).stable(&mut *tables)
-    }
-
-    /// Discriminant for a given variand index and args of a coroutine
-    pub fn coroutine_discr_for_variant(
-        &self,
-        coroutine: CoroutineDef,
-        args: &GenericArgs,
-        variant: VariantIdx,
-    ) -> Discr {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let coroutine = coroutine.def_id().internal(&mut *tables, tcx);
-        let args = args.internal(&mut *tables, tcx);
-        let variant = variant.internal(&mut *tables, tcx);
-        args.as_coroutine().discriminant_for_variant(coroutine, tcx, variant).stable(&mut *tables)
-    }
-
-    /// The name of a variant.
-    pub fn variant_name(&self, def: VariantDef) -> Symbol {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).name.to_string()
-    }
-
-    pub fn variant_fields(&self, def: VariantDef) -> Vec<FieldDef> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        def.internal(&mut *tables, tcx).fields.iter().map(|f| f.stable(&mut *tables)).collect()
-    }
-
-    /// Evaluate constant as a target usize.
-    pub fn eval_target_usize(&self, cnst: &MirConst) -> Result<u64, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let mir_const = cnst.internal(&mut *tables, tcx);
-        mir_const
-            .try_eval_target_usize(tables.tcx, ty::TypingEnv::fully_monomorphized())
-            .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
-    }
-    pub fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result<u64, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let mir_const = cnst.internal(&mut *tables, tcx);
-        mir_const
-            .try_to_target_usize(tables.tcx)
-            .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
-    }
-
-    /// Create a new zero-sized constant.
-    pub fn try_new_const_zst(&self, ty: Ty) -> Result<MirConst, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let ty_internal = ty.internal(&mut *tables, tcx);
-        let size = tables
-            .tcx
-            .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty_internal))
-            .map_err(|err| {
-                Error::new(format!(
-                    "Cannot create a zero-sized constant for type `{ty_internal}`: {err}"
-                ))
-            })?
-            .size;
-        if size.bytes() != 0 {
-            return Err(Error::new(format!(
-                "Cannot create a zero-sized constant for type `{ty_internal}`: \
-                 Type `{ty_internal}` has {} bytes",
-                size.bytes()
-            )));
-        }
-
-        Ok(mir::Const::Ty(ty_internal, ty::Const::zero_sized(tables.tcx, ty_internal))
-            .stable(&mut *tables))
-    }
-
-    /// Create a new constant that represents the given string value.
-    pub fn new_const_str(&self, value: &str) -> MirConst {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let ty = ty::Ty::new_static_str(tcx);
-        let bytes = value.as_bytes();
-        let valtree = ty::ValTree::from_raw_bytes(tcx, bytes);
-        let cv = ty::Value { ty, valtree };
-        let val = tcx.valtree_to_const_val(cv);
-        mir::Const::from_value(val, ty).stable(&mut tables)
-    }
-
-    /// Create a new constant that represents the given boolean value.
-    pub fn new_const_bool(&self, value: bool) -> MirConst {
-        let mut tables = self.0.borrow_mut();
-        mir::Const::from_bool(tables.tcx, value).stable(&mut tables)
-    }
-
-    /// Create a new constant that represents the given value.
-    pub fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<MirConst, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx));
-        let size = tables
-            .tcx
-            .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-            .unwrap()
-            .size;
-        let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| {
-            Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`."))
-        })?;
-        Ok(mir::Const::from_scalar(tcx, mir::interpret::Scalar::Int(scalar), ty)
-            .stable(&mut tables))
-    }
-    pub fn try_new_ty_const_uint(
-        &self,
-        value: u128,
-        uint_ty: UintTy,
-    ) -> Result<stable_mir::ty::TyConst, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx));
-        let size = tables
-            .tcx
-            .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-            .unwrap()
-            .size;
-
-        // We don't use Const::from_bits since it doesn't have any error checking.
-        let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| {
-            Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`."))
-        })?;
-        Ok(ty::Const::new_value(tcx, ValTree::from_scalar_int(tcx, scalar), ty)
-            .stable(&mut *tables))
-    }
-
-    /// Create a new type from the given kind.
-    pub fn new_rigid_ty(&self, kind: RigidTy) -> stable_mir::ty::Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let internal_kind = kind.internal(&mut *tables, tcx);
-        tables.tcx.mk_ty_from_kind(internal_kind).stable(&mut *tables)
-    }
-
-    /// Create a new box type, `Box<T>`, for the given inner type `T`.
-    pub fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let inner = ty.internal(&mut *tables, tcx);
-        ty::Ty::new_box(tables.tcx, inner).stable(&mut *tables)
-    }
-
-    /// Returns the type of given crate item.
-    pub fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        tcx.type_of(item.internal(&mut *tables, tcx)).instantiate_identity().stable(&mut *tables)
-    }
-
-    /// Returns the type of given definition instantiated with the given arguments.
-    pub fn def_ty_with_args(
-        &self,
-        item: stable_mir::DefId,
-        args: &GenericArgs,
-    ) -> stable_mir::ty::Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let args = args.internal(&mut *tables, tcx);
-        let def_ty = tables.tcx.type_of(item.internal(&mut *tables, tcx));
-        tables
-            .tcx
-            .instantiate_and_normalize_erasing_regions(
-                args,
-                ty::TypingEnv::fully_monomorphized(),
-                def_ty,
-            )
-            .stable(&mut *tables)
-    }
-
-    /// Returns literal value of a const as a string.
-    pub fn mir_const_pretty(&self, cnst: &stable_mir::ty::MirConst) -> String {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        cnst.internal(&mut *tables, tcx).to_string()
-    }
-
-    /// `Span` of an item.
-    pub fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span {
-        let mut tables = self.0.borrow_mut();
-        tables.tcx.def_span(tables[def_id]).stable(&mut *tables)
-    }
-
-    /// Obtain the representation of a type.
-    pub fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String {
-        let tables = self.0.borrow_mut();
-        tables.types[ty].to_string()
-    }
-
-    /// Obtain the representation of a type.
-    pub fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind {
-        let mut tables = self.0.borrow_mut();
-        tables.types[ty].kind().stable(&mut *tables)
-    }
-
-    pub fn ty_const_pretty(&self, ct: stable_mir::ty::TyConstId) -> String {
-        let tables = self.0.borrow_mut();
-        tables.ty_consts[ct].to_string()
-    }
-
-    /// Get the discriminant Ty for this Ty if there's one.
-    pub fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> stable_mir::ty::Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let internal_kind = ty.internal(&mut *tables, tcx);
-        let internal_ty = tables.tcx.mk_ty_from_kind(internal_kind);
-        internal_ty.discriminant_ty(tables.tcx).stable(&mut *tables)
-    }
-
-    /// Get the body of an Instance which is already monomorphized.
-    pub fn instance_body(&self, def: InstanceDef) -> Option<Body> {
-        let mut tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        tables
-            .instance_has_body(instance)
-            .then(|| BodyBuilder::new(tables.tcx, instance).build(&mut *tables))
-    }
-
-    /// Get the instance type with generic instantiations applied and lifetimes erased.
-    pub fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty {
-        let mut tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        assert!(!instance.has_non_region_param(), "{instance:?} needs further instantiation");
-        instance.ty(tables.tcx, ty::TypingEnv::fully_monomorphized()).stable(&mut *tables)
-    }
-
-    /// Get the instantiation types.
-    pub fn instance_args(&self, def: InstanceDef) -> GenericArgs {
-        let mut tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        instance.args.stable(&mut *tables)
-    }
-
-    /// Get an instance ABI.
-    pub fn instance_abi(&self, def: InstanceDef) -> Result<FnAbi, Error> {
-        let mut tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        Ok(tables.fn_abi_of_instance(instance, List::empty())?.stable(&mut *tables))
-    }
-
-    /// Get the ABI of a function pointer.
-    pub fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result<FnAbi, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let sig = fn_ptr.internal(&mut *tables, tcx);
-        Ok(tables.fn_abi_of_fn_ptr(sig, List::empty())?.stable(&mut *tables))
-    }
-
-    /// Get the instance.
-    pub fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables.instances[def].def_id();
-        tables.create_def_id(def_id)
-    }
-
-    /// Get the instance mangled name.
-    pub fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol {
-        let tables = self.0.borrow_mut();
-        let instance = tables.instances[instance];
-        tables.tcx.symbol_name(instance).name.to_string()
-    }
-
-    /// Check if this is an empty DropGlue shim.
-    pub fn is_empty_drop_shim(&self, def: InstanceDef) -> bool {
-        let tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        matches!(instance.def, ty::InstanceKind::DropGlue(_, None))
-    }
-
-    /// Convert a non-generic crate item into an instance.
-    /// This function will panic if the item is generic.
-    pub fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance {
-        let mut tables = self.0.borrow_mut();
-        let def_id = tables[def_id];
-        Instance::mono(tables.tcx, def_id).stable(&mut *tables)
-    }
-
-    /// Item requires monomorphization.
-    pub fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool {
-        let tables = self.0.borrow();
-        let def_id = tables[def_id];
-        let generics = tables.tcx.generics_of(def_id);
-        let result = generics.requires_monomorphization(tables.tcx);
-        result
-    }
-
-    /// Resolve an instance from the given function definition and generic arguments.
-    pub fn resolve_instance(
-        &self,
-        def: stable_mir::ty::FnDef,
-        args: &stable_mir::ty::GenericArgs,
-    ) -> Option<stable_mir::mir::mono::Instance> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        let args_ref = args.internal(&mut *tables, tcx);
-        match Instance::try_resolve(
-            tables.tcx,
-            ty::TypingEnv::fully_monomorphized(),
-            def_id,
-            args_ref,
-        ) {
-            Ok(Some(instance)) => Some(instance.stable(&mut *tables)),
-            Ok(None) | Err(_) => None,
-        }
-    }
-
-    /// Resolve an instance for drop_in_place for the given type.
-    pub fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let internal_ty = ty.internal(&mut *tables, tcx);
-        let instance = Instance::resolve_drop_in_place(tables.tcx, internal_ty);
-        instance.stable(&mut *tables)
-    }
-
-    /// Resolve instance for a function pointer.
-    pub fn resolve_for_fn_ptr(
-        &self,
-        def: FnDef,
-        args: &GenericArgs,
-    ) -> Option<stable_mir::mir::mono::Instance> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        let args_ref = args.internal(&mut *tables, tcx);
-        Instance::resolve_for_fn_ptr(
-            tables.tcx,
-            ty::TypingEnv::fully_monomorphized(),
-            def_id,
-            args_ref,
-        )
-        .stable(&mut *tables)
-    }
-
-    /// Resolve instance for a closure with the requested type.
-    pub fn resolve_closure(
-        &self,
-        def: ClosureDef,
-        args: &GenericArgs,
-        kind: ClosureKind,
-    ) -> Option<stable_mir::mir::mono::Instance> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        let args_ref = args.internal(&mut *tables, tcx);
-        let closure_kind = kind.internal(&mut *tables, tcx);
-        Some(
-            Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind)
-                .stable(&mut *tables),
-        )
-    }
-
-    /// Try to evaluate an instance into a constant.
-    pub fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error> {
-        let mut tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        let tcx = tables.tcx;
-        let result = tcx.const_eval_instance(
-            ty::TypingEnv::fully_monomorphized(),
-            instance,
-            tcx.def_span(instance.def_id()),
-        );
-        result
-            .map(|const_val| {
-                alloc::try_new_allocation(
-                    const_ty.internal(&mut *tables, tcx),
-                    const_val,
-                    &mut *tables,
-                )
-            })
-            .map_err(|e| e.stable(&mut *tables))?
-    }
-
-    /// Evaluate a static's initializer.
-    pub fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        tables.tcx.eval_static_initializer(def_id).stable(&mut *tables)
-    }
-
-    /// Retrieve global allocation for the given allocation ID.
-    pub fn global_alloc(&self, alloc: stable_mir::mir::alloc::AllocId) -> GlobalAlloc {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let alloc_id = alloc.internal(&mut *tables, tcx);
-        tables.tcx.global_alloc(alloc_id).stable(&mut *tables)
-    }
-
-    /// Retrieve the id for the virtual table.
-    pub fn vtable_allocation(
-        &self,
-        global_alloc: &GlobalAlloc,
-    ) -> Option<stable_mir::mir::alloc::AllocId> {
-        let mut tables = self.0.borrow_mut();
-        let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else {
-            return None;
-        };
-        let tcx = tables.tcx;
-        let alloc_id = tables.tcx.vtable_allocation((
-            ty.internal(&mut *tables, tcx),
-            trait_ref
-                .internal(&mut *tables, tcx)
-                .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
-        ));
-        Some(alloc_id.stable(&mut *tables))
-    }
-
-    pub fn krate(&self, def_id: stable_mir::DefId) -> Crate {
-        let tables = self.0.borrow();
-        smir_crate(tables.tcx, tables[def_id].krate)
-    }
-
-    /// Retrieve the instance name for diagnostic messages.
-    ///
-    /// This will return the specialized name, e.g., `Vec<char>::new`.
-    pub fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol {
-        let tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        if trimmed {
-            with_forced_trimmed_paths!(
-                tables.tcx.def_path_str_with_args(instance.def_id(), instance.args)
-            )
-        } else {
-            with_no_trimmed_paths!(
-                tables.tcx.def_path_str_with_args(instance.def_id(), instance.args)
-            )
-        }
-    }
-
-    /// Get the layout of a type.
-    pub fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let ty = ty.internal(&mut *tables, tcx);
-        let layout = tables.layout_of(ty)?.layout;
-        Ok(layout.stable(&mut *tables))
-    }
-
-    /// Get the layout shape.
-    pub fn layout_shape(&self, id: Layout) -> LayoutShape {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        id.internal(&mut *tables, tcx).0.stable(&mut *tables)
-    }
-
-    /// Get a debug string representation of a place.
-    pub fn place_pretty(&self, place: &Place) -> String {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        format!("{:?}", place.internal(&mut *tables, tcx))
-    }
-
-    /// Get the resulting type of binary operation.
-    pub fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let rhs_internal = rhs.internal(&mut *tables, tcx);
-        let lhs_internal = lhs.internal(&mut *tables, tcx);
-        let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal);
-        ty.stable(&mut *tables)
-    }
-
-    /// Get the resulting type of unary operation.
-    pub fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let arg_internal = arg.internal(&mut *tables, tcx);
-        let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal);
-        ty.stable(&mut *tables)
-    }
-
-    /// Get all associated items of a definition.
-    pub fn associated_items(&self, def_id: stable_mir::DefId) -> stable_mir::AssocItems {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = tables[def_id];
-        let assoc_items = if tcx.is_trait_alias(def_id) {
-            Vec::new()
-        } else {
-            tcx.associated_item_def_ids(def_id)
-                .iter()
-                .map(|did| tcx.associated_item(*did).stable(&mut *tables))
-                .collect()
-        };
-        assoc_items
-    }
-}
-
-/// Implement error handling for extracting function ABI information.
-impl<'tcx> FnAbiOfHelpers<'tcx> for Tables<'tcx> {
-    type FnAbiOfResult = Result<&'tcx rustc_target::callconv::FnAbi<'tcx, ty::Ty<'tcx>>, Error>;
-
-    #[inline]
-    fn handle_fn_abi_err(
-        &self,
-        err: ty::layout::FnAbiError<'tcx>,
-        _span: rustc_span::Span,
-        fn_abi_request: ty::layout::FnAbiRequest<'tcx>,
-    ) -> Error {
-        Error::new(format!("Failed to get ABI for `{fn_abi_request:?}`: {err:?}"))
-    }
-}
-
-impl<'tcx> LayoutOfHelpers<'tcx> for Tables<'tcx> {
-    type LayoutOfResult = Result<ty::layout::TyAndLayout<'tcx>, Error>;
-
-    #[inline]
-    fn handle_layout_err(
-        &self,
-        err: ty::layout::LayoutError<'tcx>,
-        _span: rustc_span::Span,
-        ty: ty::Ty<'tcx>,
-    ) -> Error {
-        Error::new(format!("Failed to get layout for `{ty}`: {err}"))
-    }
-}
-
-impl<'tcx> HasTypingEnv<'tcx> for Tables<'tcx> {
-    fn typing_env(&self) -> ty::TypingEnv<'tcx> {
-        ty::TypingEnv::fully_monomorphized()
-    }
-}
-
-impl<'tcx> HasTyCtxt<'tcx> for Tables<'tcx> {
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-}
-
-impl<'tcx> HasDataLayout for Tables<'tcx> {
-    fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
-        self.tcx.data_layout()
-    }
-}
diff --git a/compiler/rustc_smir/src/rustc_smir/context/impls.rs b/compiler/rustc_smir/src/rustc_smir/context/impls.rs
new file mode 100644
index 0000000000000..89ae47143ef6d
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_smir/context/impls.rs
@@ -0,0 +1,762 @@
+//! Implementation of StableMIR Context.
+
+#![allow(rustc::usage_of_qualified_ty)]
+
+use std::iter;
+
+use rustc_abi::{Endian, Layout, ReprOptions};
+use rustc_hir::def::DefKind;
+use rustc_hir::{Attribute, LangItem};
+use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar};
+use rustc_middle::mir::{BinOp, Body, Const as MirConst, ConstValue, UnOp};
+use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
+use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
+use rustc_middle::ty::util::Discr;
+use rustc_middle::ty::{
+    AdtDef, AdtKind, AssocItem, Binder, ClosureKind, CoroutineArgsExt, EarlyBinder,
+    ExistentialTraitRef, FnSig, GenericArgsRef, Instance, InstanceKind, IntrinsicDef, List,
+    PolyFnSig, ScalarInt, TraitDef, TraitRef, Ty, TyCtxt, TyKind, TypeVisitableExt, UintTy,
+    ValTree, VariantDef,
+};
+use rustc_middle::{mir, ty};
+use rustc_session::cstore::ForeignModule;
+use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use rustc_span::{FileNameDisplayPreference, Span, Symbol};
+use rustc_target::callconv::FnAbi;
+
+use super::{SmirAllocRange, SmirCtxt, SmirTy, SmirTypingEnv};
+use crate::rustc_smir::builder::BodyBuilder;
+use crate::rustc_smir::{Bridge, SmirError, Tables, filter_def_ids};
+
+impl<'tcx, B: Bridge> SmirTy<'tcx> for SmirCtxt<'tcx, B> {
+    fn new_foreign(&self, def_id: DefId) -> ty::Ty<'tcx> {
+        ty::Ty::new_foreign(self.tcx, def_id)
+    }
+}
+
+impl<'tcx, B: Bridge> SmirTypingEnv<'tcx> for SmirCtxt<'tcx, B> {
+    fn fully_monomorphized(&self) -> ty::TypingEnv<'tcx> {
+        ty::TypingEnv::fully_monomorphized()
+    }
+}
+
+impl<'tcx, B: Bridge> SmirAllocRange<'tcx> for SmirCtxt<'tcx, B> {
+    fn alloc_range(
+        &self,
+        offset: rustc_abi::Size,
+        size: rustc_abi::Size,
+    ) -> mir::interpret::AllocRange {
+        rustc_middle::mir::interpret::alloc_range(offset, size)
+    }
+}
+
+impl<'tcx, B: Bridge> SmirCtxt<'tcx, B> {
+    pub fn lift<T: ty::Lift<TyCtxt<'tcx>>>(&self, value: T) -> Option<T::Lifted> {
+        self.tcx.lift(value)
+    }
+
+    pub fn adt_def(&self, def_id: DefId) -> AdtDef<'tcx> {
+        self.tcx.adt_def(def_id)
+    }
+
+    pub fn coroutine_movability(&self, def_id: DefId) -> ty::Movability {
+        self.tcx.coroutine_movability(def_id)
+    }
+
+    pub fn valtree_to_const_val(&self, key: ty::Value<'tcx>) -> ConstValue<'tcx> {
+        self.tcx.valtree_to_const_val(key)
+    }
+
+    /// Return whether the instance as a body available.
+    ///
+    /// Items and intrinsics may have a body available from its definition.
+    /// Shims body may be generated depending on their type.
+    pub(crate) fn instance_has_body(&self, instance: Instance<'tcx>) -> bool {
+        let def_id = instance.def_id();
+        self.item_has_body(def_id)
+            || !matches!(
+                instance.def,
+                ty::InstanceKind::Virtual(..)
+                    | ty::InstanceKind::Intrinsic(..)
+                    | ty::InstanceKind::Item(..)
+            )
+    }
+
+    /// Return whether the item has a body defined by the user.
+    ///
+    /// Note that intrinsics may have a placeholder body that shouldn't be used in practice.
+    /// In StableMIR, we handle this case as if the body is not available.
+    pub(crate) fn item_has_body(&self, def_id: DefId) -> bool {
+        let must_override = if let Some(intrinsic) = self.tcx.intrinsic(def_id) {
+            intrinsic.must_be_overridden
+        } else {
+            false
+        };
+        !must_override && self.tcx.is_mir_available(def_id)
+    }
+
+    fn filter_fn_def(&self, def_id: DefId) -> Option<DefId> {
+        if matches!(self.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
+            Some(def_id)
+        } else {
+            None
+        }
+    }
+
+    fn filter_static_def(&self, def_id: DefId) -> Option<DefId> {
+        matches!(self.tcx.def_kind(def_id), DefKind::Static { .. }).then(|| def_id)
+    }
+
+    pub fn target_endian(&self) -> Endian {
+        self.tcx.data_layout.endian
+    }
+
+    pub fn target_pointer_size(&self) -> usize {
+        self.tcx.data_layout.pointer_size.bits().try_into().unwrap()
+    }
+
+    pub fn entry_fn(&self) -> Option<DefId> {
+        Some(self.tcx.entry_fn(())?.0)
+    }
+
+    /// Retrieve all items of the local crate that have a MIR associated with them.
+    pub fn all_local_items(&self) -> Vec<DefId> {
+        self.tcx.mir_keys(()).iter().map(|item| item.to_def_id()).collect()
+    }
+
+    /// Retrieve the body of a function.
+    /// This function will panic if the body is not available.
+    pub fn mir_body(&self, item: DefId) -> &'tcx Body<'tcx> {
+        self.tcx.instance_mir(InstanceKind::Item(item))
+    }
+
+    /// Check whether the body of a function is available.
+    pub fn has_body(&self, def: DefId) -> bool {
+        self.item_has_body(def)
+    }
+
+    pub fn foreign_modules(&self, crate_num: CrateNum) -> Vec<DefId> {
+        self.tcx.foreign_modules(crate_num).keys().map(|mod_def_id| *mod_def_id).collect()
+    }
+
+    /// Retrieve all functions defined in this crate.
+    pub fn crate_functions(&self, crate_num: CrateNum) -> Vec<DefId> {
+        filter_def_ids(self.tcx, crate_num, |def_id| self.filter_fn_def(def_id))
+    }
+
+    /// Retrieve all static items defined in this crate.
+    pub fn crate_statics(&self, crate_num: CrateNum) -> Vec<DefId> {
+        filter_def_ids(self.tcx, crate_num, |def_id| self.filter_static_def(def_id))
+    }
+
+    pub fn foreign_module(&self, mod_def: DefId) -> &ForeignModule {
+        self.tcx.foreign_modules(mod_def.krate).get(&mod_def).unwrap()
+    }
+
+    pub fn foreign_items(&self, mod_def: DefId) -> Vec<DefId> {
+        self.tcx
+            .foreign_modules(mod_def.krate)
+            .get(&mod_def)
+            .unwrap()
+            .foreign_items
+            .iter()
+            .map(|item_def| *item_def)
+            .collect()
+    }
+
+    pub fn all_trait_decls(&self) -> impl Iterator<Item = DefId> {
+        self.tcx.all_traits_including_private()
+    }
+
+    pub fn trait_decls(&self, crate_num: CrateNum) -> Vec<DefId> {
+        self.tcx.traits(crate_num).iter().map(|trait_def_id| *trait_def_id).collect()
+    }
+
+    pub fn trait_decl(&self, trait_def: DefId) -> &'tcx TraitDef {
+        self.tcx.trait_def(trait_def)
+    }
+
+    pub fn all_trait_impls(&self) -> Vec<DefId> {
+        iter::once(LOCAL_CRATE)
+            .chain(self.tcx.crates(()).iter().copied())
+            .flat_map(|cnum| self.tcx.trait_impls_in_crate(cnum).iter())
+            .map(|impl_def_id| *impl_def_id)
+            .collect()
+    }
+
+    pub fn trait_impls(&self, crate_num: CrateNum) -> Vec<DefId> {
+        self.tcx.trait_impls_in_crate(crate_num).iter().map(|impl_def_id| *impl_def_id).collect()
+    }
+
+    pub fn trait_impl(&self, impl_def: DefId) -> EarlyBinder<'tcx, TraitRef<'tcx>> {
+        self.tcx.impl_trait_ref(impl_def).unwrap()
+    }
+
+    pub fn generics_of(&self, def_id: DefId) -> &'tcx ty::Generics {
+        self.tcx.generics_of(def_id)
+    }
+
+    pub fn predicates_of(
+        &self,
+        def_id: DefId,
+    ) -> (Option<DefId>, Vec<(ty::PredicateKind<'tcx>, Span)>) {
+        let ty::GenericPredicates { parent, predicates } = self.tcx.predicates_of(def_id);
+        (
+            parent,
+            predicates
+                .iter()
+                .map(|(clause, span)| (clause.as_predicate().kind().skip_binder(), *span))
+                .collect(),
+        )
+    }
+
+    pub fn explicit_predicates_of(
+        &self,
+        def_id: DefId,
+    ) -> (Option<DefId>, Vec<(ty::PredicateKind<'tcx>, Span)>) {
+        let ty::GenericPredicates { parent, predicates } = self.tcx.explicit_predicates_of(def_id);
+        (
+            parent,
+            predicates
+                .iter()
+                .map(|(clause, span)| (clause.as_predicate().kind().skip_binder(), *span))
+                .collect(),
+        )
+    }
+
+    pub fn crate_name(&self, crate_num: CrateNum) -> String {
+        self.tcx.crate_name(crate_num).to_string()
+    }
+
+    pub fn crate_is_local(&self, crate_num: CrateNum) -> bool {
+        crate_num == LOCAL_CRATE
+    }
+
+    pub fn crate_num_id(&self, crate_num: CrateNum) -> usize {
+        crate_num.into()
+    }
+
+    pub fn local_crate_num(&self) -> CrateNum {
+        LOCAL_CRATE
+    }
+
+    /// Retrieve a list of all external crates.
+    pub fn external_crates(&self) -> Vec<CrateNum> {
+        self.tcx.crates(()).iter().map(|crate_num| *crate_num).collect()
+    }
+
+    /// Find a crate with the given name.
+    pub fn find_crates(&self, name: &str) -> Vec<CrateNum> {
+        let crates: Vec<CrateNum> = [LOCAL_CRATE]
+            .iter()
+            .chain(self.tcx.crates(()).iter())
+            .filter_map(|crate_num| {
+                let crate_name = self.tcx.crate_name(*crate_num).to_string();
+                (name == crate_name).then(|| *crate_num)
+            })
+            .collect();
+        crates
+    }
+
+    /// Returns the name of given `DefId`.
+    pub fn def_name(&self, def_id: DefId, trimmed: bool) -> String {
+        if trimmed {
+            with_forced_trimmed_paths!(self.tcx.def_path_str(def_id))
+        } else {
+            with_no_trimmed_paths!(self.tcx.def_path_str(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()]`.
+    pub fn tool_attrs(&self, def_id: DefId, attr: &[String]) -> Vec<(String, Span)> {
+        let attr_name: Vec<_> = attr.iter().map(|seg| Symbol::intern(&seg)).collect();
+        self.tcx
+            .get_attrs_by_path(def_id, &attr_name)
+            .filter_map(|attribute| {
+                if let Attribute::Unparsed(u) = attribute {
+                    let attr_str = rustc_hir_pretty::attribute_to_string(&self.tcx, attribute);
+                    Some((attr_str, u.span))
+                } else {
+                    None
+                }
+            })
+            .collect()
+    }
+
+    /// Get all tool attributes of a definition.
+    pub fn all_tool_attrs(&self, did: DefId) -> Vec<(String, Span)> {
+        let attrs_iter = if let Some(did) = did.as_local() {
+            self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(did)).iter()
+        } else {
+            self.tcx.attrs_for_def(did).iter()
+        };
+        attrs_iter
+            .filter_map(|attribute| {
+                if let Attribute::Unparsed(u) = attribute {
+                    let attr_str = rustc_hir_pretty::attribute_to_string(&self.tcx, attribute);
+                    Some((attr_str, u.span))
+                } else {
+                    None
+                }
+            })
+            .collect()
+    }
+
+    /// Returns printable, human readable form of `Span`.
+    pub fn span_to_string(&self, span: Span) -> String {
+        self.tcx.sess.source_map().span_to_diagnostic_string(span)
+    }
+
+    /// Return filename from given `Span`, for diagnostic purposes.
+    pub fn get_filename(&self, span: Span) -> String {
+        self.tcx
+            .sess
+            .source_map()
+            .span_to_filename(span)
+            .display(FileNameDisplayPreference::Local)
+            .to_string()
+    }
+
+    /// Return lines corresponding to this `Span`.
+    pub fn get_lines(&self, span: Span) -> (usize, usize, usize, usize) {
+        let lines = &self.tcx.sess.source_map().span_to_location_info(span);
+        (lines.1, lines.2, lines.3, lines.4)
+    }
+
+    /// Returns the `kind` of given `DefId`.
+    pub fn def_kind(&self, item: DefId) -> DefKind {
+        self.tcx.def_kind(item)
+    }
+
+    /// Returns whether this is a foreign item.
+    pub fn is_foreign_item(&self, item: DefId) -> bool {
+        self.tcx.is_foreign_item(item)
+    }
+
+    /// Returns the kind of a given foreign item.
+    pub fn foreign_item_kind(&self, def_id: DefId) -> DefKind {
+        self.tcx.def_kind(def_id)
+    }
+
+    /// Returns the kind of a given algebraic data type.
+    pub fn adt_kind(&self, def: AdtDef<'tcx>) -> AdtKind {
+        def.adt_kind()
+    }
+
+    /// Returns if the ADT is a box.
+    pub fn adt_is_box(&self, def: AdtDef<'tcx>) -> bool {
+        def.is_box()
+    }
+
+    /// Returns whether this ADT is simd.
+    pub fn adt_is_simd(&self, def: AdtDef<'tcx>) -> bool {
+        def.repr().simd()
+    }
+
+    /// Returns whether this definition is a C string.
+    pub fn adt_is_cstr(&self, def_id: DefId) -> bool {
+        self.tcx.is_lang_item(def_id, LangItem::CStr)
+    }
+
+    /// Returns the representation options for this ADT.
+    pub fn adt_repr(&self, def: AdtDef<'tcx>) -> ReprOptions {
+        def.repr()
+    }
+
+    /// Retrieve the function signature for the given generic arguments.
+    pub fn fn_sig(
+        &self,
+        def_id: DefId,
+        args_ref: GenericArgsRef<'tcx>,
+    ) -> Binder<'tcx, FnSig<'tcx>> {
+        let sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args_ref);
+        sig
+    }
+
+    /// Retrieve the intrinsic definition if the item corresponds one.
+    pub fn intrinsic(&self, def_id: DefId) -> Option<IntrinsicDef> {
+        let intrinsic = self.tcx.intrinsic_raw(def_id);
+        intrinsic
+    }
+
+    /// Retrieve the plain function name of an intrinsic.
+    pub fn intrinsic_name(&self, def_id: DefId) -> String {
+        self.tcx.intrinsic(def_id).unwrap().name.to_string()
+    }
+
+    /// Retrieve the closure signature for the given generic arguments.
+    pub fn closure_sig(&self, args_ref: GenericArgsRef<'tcx>) -> Binder<'tcx, FnSig<'tcx>> {
+        args_ref.as_closure().sig()
+    }
+
+    /// The number of variants in this ADT.
+    pub fn adt_variants_len(&self, def: AdtDef<'tcx>) -> usize {
+        def.variants().len()
+    }
+
+    /// Discriminant for a given variant index of AdtDef.
+    pub fn adt_discr_for_variant(
+        &self,
+        adt: AdtDef<'tcx>,
+        variant: rustc_abi::VariantIdx,
+    ) -> Discr<'tcx> {
+        adt.discriminant_for_variant(self.tcx, variant)
+    }
+
+    /// Discriminant for a given variand index and args of a coroutine.
+    pub fn coroutine_discr_for_variant(
+        &self,
+        coroutine: DefId,
+        args: GenericArgsRef<'tcx>,
+        variant: rustc_abi::VariantIdx,
+    ) -> Discr<'tcx> {
+        args.as_coroutine().discriminant_for_variant(coroutine, self.tcx, variant)
+    }
+
+    /// The name of a variant.
+    pub fn variant_name(&self, def: &'tcx VariantDef) -> String {
+        def.name.to_string()
+    }
+
+    /// Evaluate constant as a target usize.
+    pub fn eval_target_usize(&self, cnst: MirConst<'tcx>) -> Result<u64, B::Error> {
+        use crate::rustc_smir::context::SmirTypingEnv;
+        cnst.try_eval_target_usize(self.tcx, self.fully_monomorphized())
+            .ok_or_else(|| B::Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
+    }
+
+    pub fn eval_target_usize_ty(&self, cnst: ty::Const<'tcx>) -> Result<u64, B::Error> {
+        cnst.try_to_target_usize(self.tcx)
+            .ok_or_else(|| B::Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
+    }
+
+    pub(crate) fn try_new_const_zst(
+        &self,
+        ty_internal: Ty<'tcx>,
+    ) -> Result<MirConst<'tcx>, B::Error> {
+        let size = self
+            .tcx
+            .layout_of(self.fully_monomorphized().as_query_input(ty_internal))
+            .map_err(|err| {
+                B::Error::new(format!(
+                    "Cannot create a zero-sized constant for type `{ty_internal}`: {err}"
+                ))
+            })?
+            .size;
+        if size.bytes() != 0 {
+            return Err(B::Error::new(format!(
+                "Cannot create a zero-sized constant for type `{ty_internal}`: \
+                Type `{ty_internal}` has {} bytes",
+                size.bytes()
+            )));
+        }
+
+        Ok(MirConst::Ty(ty_internal, self.const_zero_sized(ty_internal)))
+    }
+
+    pub fn const_zero_sized(&self, ty_internal: Ty<'tcx>) -> ty::Const<'tcx> {
+        ty::Const::zero_sized(self.tcx, ty_internal)
+    }
+
+    /// Create a new constant that represents the given string value.
+    pub fn new_const_str(&self, value: &str) -> MirConst<'tcx> {
+        let ty = Ty::new_static_str(self.tcx);
+        let bytes = value.as_bytes();
+        let valtree = ValTree::from_raw_bytes(self.tcx, bytes);
+        let cv = ty::Value { ty, valtree };
+        let val = self.tcx.valtree_to_const_val(cv);
+        MirConst::from_value(val, ty)
+    }
+
+    /// Create a new constant that represents the given boolean value.
+    pub fn new_const_bool(&self, value: bool) -> MirConst<'tcx> {
+        MirConst::from_bool(self.tcx, value)
+    }
+
+    pub fn try_new_const_uint(
+        &self,
+        value: u128,
+        ty_internal: Ty<'tcx>,
+    ) -> Result<MirConst<'tcx>, B::Error> {
+        let size = self
+            .tcx
+            .layout_of(self.fully_monomorphized().as_query_input(ty_internal))
+            .unwrap()
+            .size;
+        let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| {
+            B::Error::new(format!("Value overflow: cannot convert `{value}` to `{ty_internal}`."))
+        })?;
+        Ok(self.mir_const_from_scalar(Scalar::Int(scalar), ty_internal))
+    }
+
+    pub fn try_new_ty_const_uint(
+        &self,
+        value: u128,
+        ty_internal: Ty<'tcx>,
+    ) -> Result<ty::Const<'tcx>, B::Error> {
+        let size = self
+            .tcx
+            .layout_of(self.fully_monomorphized().as_query_input(ty_internal))
+            .unwrap()
+            .size;
+        let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| {
+            B::Error::new(format!("Value overflow: cannot convert `{value}` to `{ty_internal}`."))
+        })?;
+
+        Ok(self.ty_const_new_value(ValTree::from_scalar_int(self.tcx, scalar), ty_internal))
+    }
+
+    pub fn ty_new_uint(&self, ty: UintTy) -> Ty<'tcx> {
+        Ty::new_uint(self.tcx, ty)
+    }
+
+    pub fn mir_const_from_scalar(&self, s: Scalar, ty: Ty<'tcx>) -> MirConst<'tcx> {
+        MirConst::from_scalar(self.tcx, s, ty)
+    }
+
+    pub fn ty_const_new_value(&self, valtree: ValTree<'tcx>, ty: Ty<'tcx>) -> ty::Const<'tcx> {
+        ty::Const::new_value(self.tcx, valtree, ty)
+    }
+
+    pub fn ty_valtree_from_scalar_int(&self, i: ScalarInt) -> ValTree<'tcx> {
+        ValTree::from_scalar_int(self.tcx, i)
+    }
+
+    /// Create a new type from the given kind.
+    pub fn new_rigid_ty(&self, internal_kind: TyKind<'tcx>) -> Ty<'tcx> {
+        self.tcx.mk_ty_from_kind(internal_kind)
+    }
+
+    /// Create a new box type, `Box<T>`, for the given inner type `T`.
+    pub fn new_box_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        ty::Ty::new_box(self.tcx, ty)
+    }
+
+    /// Returns the type of given crate item.
+    pub fn def_ty(&self, item: DefId) -> Ty<'tcx> {
+        self.tcx.type_of(item).instantiate_identity()
+    }
+
+    /// Returns the type of given definition instantiated with the given arguments.
+    pub fn def_ty_with_args(&self, item: DefId, args_ref: GenericArgsRef<'tcx>) -> Ty<'tcx> {
+        let def_ty = self.tcx.type_of(item);
+        self.tcx.instantiate_and_normalize_erasing_regions(
+            args_ref,
+            self.fully_monomorphized(),
+            def_ty,
+        )
+    }
+
+    /// `Span` of an item.
+    pub fn span_of_an_item(&self, def_id: DefId) -> Span {
+        self.tcx.def_span(def_id)
+    }
+
+    pub fn ty_const_pretty(&self, ct: ty::Const<'tcx>) -> String {
+        ct.to_string()
+    }
+
+    /// Obtain the representation of a type.
+    pub fn ty_pretty(&self, ty: Ty<'tcx>) -> String {
+        ty.to_string()
+    }
+
+    /// Obtain the kind of a type.
+    pub fn ty_kind(&self, ty: Ty<'tcx>) -> &'tcx TyKind<'tcx> {
+        ty.kind()
+    }
+
+    /// Get the discriminant Ty for this Ty if there's one.
+    pub fn rigid_ty_discriminant_ty(&self, internal_kind: TyKind<'tcx>) -> Ty<'tcx> {
+        let internal_ty = self.tcx.mk_ty_from_kind(internal_kind);
+        internal_ty.discriminant_ty(self.tcx)
+    }
+
+    /// Get the body of an Instance which is already monomorphized.
+    pub fn instance_body(&self, instance: ty::Instance<'tcx>) -> Option<Body<'tcx>> {
+        self.instance_has_body(instance).then(|| BodyBuilder::new(self.tcx, instance).build())
+    }
+
+    /// Get the instance type with generic instantiations applied and lifetimes erased.
+    pub fn instance_ty(&self, instance: ty::Instance<'tcx>) -> Ty<'tcx> {
+        assert!(!instance.has_non_region_param(), "{instance:?} needs further instantiation");
+        instance.ty(self.tcx, self.fully_monomorphized())
+    }
+
+    /// Get the instantiation types.
+    pub fn instance_args(&self, instance: ty::Instance<'tcx>) -> GenericArgsRef<'tcx> {
+        instance.args
+    }
+
+    /// Get an instance ABI.
+    pub fn instance_abi(
+        &self,
+        instance: ty::Instance<'tcx>,
+    ) -> Result<&FnAbi<'tcx, Ty<'tcx>>, B::Error> {
+        Ok(self.fn_abi_of_instance(instance, List::empty())?)
+    }
+
+    /// Get the ABI of a function pointer.
+    pub fn fn_ptr_abi(&self, sig: PolyFnSig<'tcx>) -> Result<&FnAbi<'tcx, Ty<'tcx>>, B::Error> {
+        Ok(self.fn_abi_of_fn_ptr(sig, List::empty())?)
+    }
+
+    /// Get the instance.
+    pub fn instance_def_id(
+        &self,
+        instances: ty::Instance<'tcx>,
+        tables: &mut Tables<'_, B>,
+    ) -> B::DefId {
+        let def_id = instances.def_id();
+        tables.create_def_id(def_id)
+    }
+
+    /// Get the instance mangled name.
+    pub fn instance_mangled_name(&self, instance: ty::Instance<'tcx>) -> String {
+        self.tcx.symbol_name(instance).name.to_string()
+    }
+
+    /// Check if this is an empty DropGlue shim.
+    pub fn is_empty_drop_shim(&self, instance: ty::Instance<'tcx>) -> bool {
+        matches!(instance.def, ty::InstanceKind::DropGlue(_, None))
+    }
+
+    /// Convert a non-generic crate item into an instance.
+    /// This function will panic if the item is generic.
+    pub fn mono_instance(&self, def_id: DefId) -> Instance<'tcx> {
+        Instance::mono(self.tcx, def_id)
+    }
+
+    /// Item requires monomorphization.
+    pub fn requires_monomorphization(&self, def_id: DefId) -> bool {
+        let generics = self.tcx.generics_of(def_id);
+        let result = generics.requires_monomorphization(self.tcx);
+        result
+    }
+
+    /// Resolve an instance from the given function definition and generic arguments.
+    pub fn resolve_instance(
+        &self,
+        def_id: DefId,
+        args_ref: GenericArgsRef<'tcx>,
+    ) -> Option<Instance<'tcx>> {
+        match Instance::try_resolve(self.tcx, self.fully_monomorphized(), def_id, args_ref) {
+            Ok(Some(instance)) => Some(instance),
+            Ok(None) | Err(_) => None,
+        }
+    }
+
+    /// Resolve an instance for drop_in_place for the given type.
+    pub fn resolve_drop_in_place(&self, internal_ty: Ty<'tcx>) -> Instance<'tcx> {
+        let instance = Instance::resolve_drop_in_place(self.tcx, internal_ty);
+        instance
+    }
+
+    /// Resolve instance for a function pointer.
+    pub fn resolve_for_fn_ptr(
+        &self,
+        def_id: DefId,
+        args_ref: GenericArgsRef<'tcx>,
+    ) -> Option<Instance<'tcx>> {
+        Instance::resolve_for_fn_ptr(self.tcx, self.fully_monomorphized(), def_id, args_ref)
+    }
+
+    /// Resolve instance for a closure with the requested type.
+    pub fn resolve_closure(
+        &self,
+        def_id: DefId,
+        args_ref: GenericArgsRef<'tcx>,
+        closure_kind: ClosureKind,
+    ) -> Option<Instance<'tcx>> {
+        Some(Instance::resolve_closure(self.tcx, def_id, args_ref, closure_kind))
+    }
+
+    /// Try to evaluate an instance into a constant.
+    pub fn eval_instance(
+        &self,
+        instance: ty::Instance<'tcx>,
+    ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+        self.tcx.const_eval_instance(
+            self.fully_monomorphized(),
+            instance,
+            self.tcx.def_span(instance.def_id()),
+        )
+    }
+
+    /// Evaluate a static's initializer.
+    pub fn eval_static_initializer(
+        &self,
+        def_id: DefId,
+    ) -> Result<ConstAllocation<'tcx>, ErrorHandled> {
+        self.tcx.eval_static_initializer(def_id)
+    }
+
+    /// Retrieve global allocation for the given allocation ID.
+    pub fn global_alloc(&self, alloc_id: AllocId) -> GlobalAlloc<'tcx> {
+        self.tcx.global_alloc(alloc_id)
+    }
+
+    /// Retrieve the id for the virtual table.
+    pub fn vtable_allocation(
+        &self,
+        ty: Ty<'tcx>,
+        trait_ref: Option<Binder<'tcx, ExistentialTraitRef<'tcx>>>,
+    ) -> AllocId {
+        let alloc_id = self.tcx.vtable_allocation((
+            ty,
+            trait_ref.map(|principal| self.tcx.instantiate_bound_regions_with_erased(principal)),
+        ));
+        alloc_id
+    }
+
+    /// Retrieve the instance name for diagnostic messages.
+    ///
+    /// This will return the specialized name, e.g., `Vec<char>::new`.
+    pub fn instance_name(&self, instance: ty::Instance<'tcx>, trimmed: bool) -> String {
+        if trimmed {
+            with_forced_trimmed_paths!(
+                self.tcx.def_path_str_with_args(instance.def_id(), instance.args)
+            )
+        } else {
+            with_no_trimmed_paths!(
+                self.tcx.def_path_str_with_args(instance.def_id(), instance.args)
+            )
+        }
+    }
+
+    /// Get the layout of a type.
+    pub fn ty_layout(&self, ty: Ty<'tcx>) -> Result<Layout<'tcx>, B::Error> {
+        let layout = self.layout_of(ty)?.layout;
+        Ok(layout)
+    }
+
+    /// Get the resulting type of binary operation.
+    pub fn binop_ty(&self, bin_op: BinOp, rhs: Ty<'tcx>, lhs: Ty<'tcx>) -> Ty<'tcx> {
+        bin_op.ty(self.tcx, rhs, lhs)
+    }
+
+    /// Get the resulting type of unary operation.
+    pub fn unop_ty(&self, un_op: UnOp, arg: Ty<'tcx>) -> Ty<'tcx> {
+        un_op.ty(self.tcx, arg)
+    }
+
+    /// Get all associated items of a definition.
+    pub fn associated_items(&self, def_id: DefId) -> Vec<AssocItem> {
+        let assoc_items = if self.tcx.is_trait_alias(def_id) {
+            Vec::new()
+        } else {
+            self.tcx
+                .associated_item_def_ids(def_id)
+                .iter()
+                .map(|did| self.tcx.associated_item(*did))
+                .collect()
+        };
+        assoc_items
+    }
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/context/mod.rs b/compiler/rustc_smir/src/rustc_smir/context/mod.rs
new file mode 100644
index 0000000000000..38743e5f7d372
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_smir/context/mod.rs
@@ -0,0 +1,79 @@
+//! Implementation of StableMIR Context.
+
+#![allow(rustc::usage_of_qualified_ty)]
+
+use std::marker::PhantomData;
+
+use rustc_abi::HasDataLayout;
+use rustc_middle::ty;
+use rustc_middle::ty::layout::{FnAbiOfHelpers, HasTyCtxt, HasTypingEnv, LayoutOfHelpers};
+use rustc_middle::ty::{Ty, TyCtxt};
+
+use crate::rustc_smir::{Bridge, SmirError};
+
+mod impls;
+mod traits;
+
+pub use traits::*;
+
+/// Provides direct access to rustc's internal queries.
+///
+/// The [`crate::stable_mir::compiler_interface::SmirInterface`] must go through
+/// this context to obtain rustc-level information.
+pub struct SmirCtxt<'tcx, B: Bridge> {
+    pub tcx: TyCtxt<'tcx>,
+    _marker: PhantomData<B>,
+}
+
+impl<'tcx, B: Bridge> SmirCtxt<'tcx, B> {
+    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+        Self { tcx, _marker: Default::default() }
+    }
+}
+
+/// Implement error handling for extracting function ABI information.
+impl<'tcx, B: Bridge> FnAbiOfHelpers<'tcx> for SmirCtxt<'tcx, B> {
+    type FnAbiOfResult = Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, B::Error>;
+
+    #[inline]
+    fn handle_fn_abi_err(
+        &self,
+        err: ty::layout::FnAbiError<'tcx>,
+        _span: rustc_span::Span,
+        fn_abi_request: ty::layout::FnAbiRequest<'tcx>,
+    ) -> B::Error {
+        B::Error::new(format!("Failed to get ABI for `{fn_abi_request:?}`: {err:?}"))
+    }
+}
+
+impl<'tcx, B: Bridge> LayoutOfHelpers<'tcx> for SmirCtxt<'tcx, B> {
+    type LayoutOfResult = Result<ty::layout::TyAndLayout<'tcx>, B::Error>;
+
+    #[inline]
+    fn handle_layout_err(
+        &self,
+        err: ty::layout::LayoutError<'tcx>,
+        _span: rustc_span::Span,
+        ty: Ty<'tcx>,
+    ) -> B::Error {
+        B::Error::new(format!("Failed to get layout for `{ty}`: {err}"))
+    }
+}
+
+impl<'tcx, B: Bridge> HasTypingEnv<'tcx> for SmirCtxt<'tcx, B> {
+    fn typing_env(&self) -> ty::TypingEnv<'tcx> {
+        ty::TypingEnv::fully_monomorphized()
+    }
+}
+
+impl<'tcx, B: Bridge> HasTyCtxt<'tcx> for SmirCtxt<'tcx, B> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+}
+
+impl<'tcx, B: Bridge> HasDataLayout for SmirCtxt<'tcx, B> {
+    fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
+        self.tcx.data_layout()
+    }
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/context/traits.rs b/compiler/rustc_smir/src/rustc_smir/context/traits.rs
new file mode 100644
index 0000000000000..19e09016cddef
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_smir/context/traits.rs
@@ -0,0 +1,46 @@
+//! A set of traits that define a stable interface to rustc's internals.
+//!
+//! These traits abstract rustc's internal APIs, allowing StableMIR to maintain a stable
+//! interface regardless of internal compiler changes.
+
+use rustc_middle::mir::interpret::AllocRange;
+use rustc_middle::ty;
+use rustc_middle::ty::Ty;
+use rustc_span::def_id::DefId;
+
+pub trait SmirExistentialProjection<'tcx> {
+    fn new_from_args(
+        &self,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+        term: ty::Term<'tcx>,
+    ) -> ty::ExistentialProjection<'tcx>;
+}
+
+pub trait SmirExistentialTraitRef<'tcx> {
+    fn new_from_args(
+        &self,
+        trait_def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> ty::ExistentialTraitRef<'tcx>;
+}
+
+pub trait SmirTraitRef<'tcx> {
+    fn new_from_args(
+        &self,
+        trait_def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> ty::TraitRef<'tcx>;
+}
+
+pub trait SmirTy<'tcx> {
+    fn new_foreign(&self, def_id: DefId) -> Ty<'tcx>;
+}
+
+pub trait SmirTypingEnv<'tcx> {
+    fn fully_monomorphized(&self) -> ty::TypingEnv<'tcx>;
+}
+
+pub trait SmirAllocRange<'tcx> {
+    fn alloc_range(&self, offset: rustc_abi::Size, size: rustc_abi::Size) -> AllocRange;
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/error.rs b/compiler/rustc_smir/src/rustc_smir/convert/error.rs
deleted file mode 100644
index 2cde5542483db..0000000000000
--- a/compiler/rustc_smir/src/rustc_smir/convert/error.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-//! Handle the conversion of different internal errors into a stable version.
-//!
-//! Currently we encode everything as [stable_mir::Error], which is represented as a string.
-
-use rustc_middle::mir::interpret::AllocError;
-use rustc_middle::ty::layout::LayoutError;
-
-use crate::rustc_smir::{Stable, Tables};
-use crate::stable_mir;
-
-impl<'tcx> Stable<'tcx> for LayoutError<'tcx> {
-    type T = stable_mir::Error;
-
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
-        stable_mir::Error::new(format!("{self:?}"))
-    }
-}
-
-impl<'tcx> Stable<'tcx> for AllocError {
-    type T = stable_mir::Error;
-
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
-        stable_mir::Error::new(format!("{self:?}"))
-    }
-}
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 26de9b0a4960f..e8b7a3fec0923 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -7,229 +7,280 @@
 //!
 //! For now, we are developing everything inside `rustc`, thus, we keep this module private.
 
-use std::marker::PointeeSized;
-use std::ops::RangeInclusive;
+use std::cell::RefCell;
+use std::fmt::Debug;
+use std::hash::Hash;
+use std::ops::Index;
 
-use rustc_hir::def::DefKind;
+use bridge::*;
+use context::SmirCtxt;
+use rustc_data_structures::fx::{self, FxIndexMap};
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::AllocId;
-use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
 use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use stable_mir::abi::Layout;
-use stable_mir::mir::mono::{InstanceDef, StaticDef};
-use stable_mir::ty::{FnDef, MirConstId, Span, TyConstId};
-use stable_mir::{CtorKind, ItemKind};
-use tracing::debug;
 
-use crate::rustc_internal::IndexMap;
-use crate::stable_mir;
-
-mod alloc;
+pub mod alloc;
+pub mod bridge;
 mod builder;
 pub mod context;
-mod convert;
-
-pub struct Tables<'tcx> {
-    pub(crate) tcx: TyCtxt<'tcx>,
-    pub(crate) def_ids: IndexMap<DefId, stable_mir::DefId>,
-    pub(crate) alloc_ids: IndexMap<AllocId, stable_mir::mir::alloc::AllocId>,
-    pub(crate) spans: IndexMap<rustc_span::Span, Span>,
-    pub(crate) types: IndexMap<Ty<'tcx>, stable_mir::ty::Ty>,
-    pub(crate) instances: IndexMap<ty::Instance<'tcx>, InstanceDef>,
-    pub(crate) ty_consts: IndexMap<ty::Const<'tcx>, TyConstId>,
-    pub(crate) mir_consts: IndexMap<mir::Const<'tcx>, MirConstId>,
-    pub(crate) layouts: IndexMap<rustc_abi::Layout<'tcx>, Layout>,
+
+/// A container which is used for TLS.
+pub struct SmirContainer<'tcx, B: Bridge> {
+    pub tables: RefCell<Tables<'tcx, B>>,
+    pub cx: RefCell<SmirCtxt<'tcx, B>>,
+}
+
+pub struct Tables<'tcx, B: Bridge> {
+    pub def_ids: IndexMap<DefId, B::DefId>,
+    pub alloc_ids: IndexMap<AllocId, B::AllocId>,
+    pub spans: IndexMap<rustc_span::Span, B::Span>,
+    pub types: IndexMap<Ty<'tcx>, B::Ty>,
+    pub instances: IndexMap<ty::Instance<'tcx>, B::InstanceDef>,
+    pub ty_consts: IndexMap<ty::Const<'tcx>, B::TyConstId>,
+    pub mir_consts: IndexMap<mir::Const<'tcx>, B::MirConstId>,
+    pub layouts: IndexMap<rustc_abi::Layout<'tcx>, B::Layout>,
+}
+
+impl<'tcx, B: Bridge> Default for Tables<'tcx, B> {
+    fn default() -> Self {
+        Self {
+            def_ids: IndexMap::default(),
+            alloc_ids: IndexMap::default(),
+            spans: IndexMap::default(),
+            types: IndexMap::default(),
+            instances: IndexMap::default(),
+            ty_consts: IndexMap::default(),
+            mir_consts: IndexMap::default(),
+            layouts: IndexMap::default(),
+        }
+    }
 }
 
-impl<'tcx> Tables<'tcx> {
-    pub(crate) fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty {
+impl<'tcx, B: Bridge> Index<B::DefId> for Tables<'tcx, B> {
+    type Output = DefId;
+
+    #[inline(always)]
+    fn index(&self, index: B::DefId) -> &Self::Output {
+        &self.def_ids[index]
+    }
+}
+
+impl<'tcx, B: Bridge> Tables<'tcx, B> {
+    pub fn intern_ty(&mut self, ty: Ty<'tcx>) -> B::Ty {
         self.types.create_or_fetch(ty)
     }
 
-    pub(crate) fn intern_ty_const(&mut self, ct: ty::Const<'tcx>) -> TyConstId {
+    pub fn intern_ty_const(&mut self, ct: ty::Const<'tcx>) -> B::TyConstId {
         self.ty_consts.create_or_fetch(ct)
     }
 
-    pub(crate) fn intern_mir_const(&mut self, constant: mir::Const<'tcx>) -> MirConstId {
+    pub fn intern_mir_const(&mut self, constant: mir::Const<'tcx>) -> B::MirConstId {
         self.mir_consts.create_or_fetch(constant)
     }
 
-    /// Return whether the instance as a body available.
-    ///
-    /// Items and intrinsics may have a body available from its definition.
-    /// Shims body may be generated depending on their type.
-    pub(crate) fn instance_has_body(&self, instance: Instance<'tcx>) -> bool {
-        let def_id = instance.def_id();
-        self.item_has_body(def_id)
-            || !matches!(
-                instance.def,
-                ty::InstanceKind::Virtual(..)
-                    | ty::InstanceKind::Intrinsic(..)
-                    | ty::InstanceKind::Item(..)
-            )
-    }
-
-    /// Return whether the item has a body defined by the user.
-    ///
-    /// Note that intrinsics may have a placeholder body that shouldn't be used in practice.
-    /// In StableMIR, we handle this case as if the body is not available.
-    pub(crate) fn item_has_body(&self, def_id: DefId) -> bool {
-        let must_override = if let Some(intrinsic) = self.tcx.intrinsic(def_id) {
-            intrinsic.must_be_overridden
-        } else {
-            false
-        };
-        !must_override && self.tcx.is_mir_available(def_id)
-    }
-
-    fn to_fn_def(&mut self, def_id: DefId) -> Option<FnDef> {
-        if matches!(self.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
-            Some(self.fn_def(def_id))
-        } else {
-            None
-        }
+    pub fn create_def_id(&mut self, did: DefId) -> B::DefId {
+        self.def_ids.create_or_fetch(did)
     }
 
-    fn to_static(&mut self, def_id: DefId) -> Option<StaticDef> {
-        matches!(self.tcx.def_kind(def_id), DefKind::Static { .. }).then(|| self.static_def(def_id))
+    pub fn create_alloc_id(&mut self, aid: AllocId) -> B::AllocId {
+        self.alloc_ids.create_or_fetch(aid)
     }
-}
 
-/// Iterate over the definitions of the given crate.
-pub(crate) fn filter_def_ids<F, T>(tcx: TyCtxt<'_>, krate: CrateNum, mut func: F) -> Vec<T>
-where
-    F: FnMut(DefId) -> Option<T>,
-{
-    if krate == LOCAL_CRATE {
-        tcx.iter_local_def_id().filter_map(|did| func(did.to_def_id())).collect()
-    } else {
-        let num_definitions = tcx.num_extern_def_ids(krate);
-        (0..num_definitions)
-            .filter_map(move |i| {
-                let def_id = DefId { krate, index: rustc_span::def_id::DefIndex::from_usize(i) };
-                func(def_id)
-            })
-            .collect()
+    pub fn create_span(&mut self, span: Span) -> B::Span {
+        self.spans.create_or_fetch(span)
     }
-}
 
-/// Build a stable mir crate from a given crate number.
-pub(crate) fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate {
-    let crate_name = tcx.crate_name(crate_num).to_string();
-    let is_local = crate_num == LOCAL_CRATE;
-    debug!(?crate_name, ?crate_num, "smir_crate");
-    stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local }
-}
+    pub fn instance_def(&mut self, instance: ty::Instance<'tcx>) -> B::InstanceDef {
+        self.instances.create_or_fetch(instance)
+    }
 
-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),
+    pub fn layout_id(&mut self, layout: rustc_abi::Layout<'tcx>) -> B::Layout {
+        self.layouts.create_or_fetch(layout)
     }
-}
 
-/// Trait used to convert between an internal MIR type to a Stable MIR type.
-pub trait Stable<'cx>: PointeeSized {
-    /// The stable representation of the type implementing Stable.
-    type T;
-    /// Converts an object to the equivalent Stable MIR representation.
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T;
-}
+    pub fn crate_item(&mut self, did: rustc_span::def_id::DefId) -> B::CrateItem {
+        B::CrateItem::new(self.create_def_id(did))
+    }
 
-impl<'tcx, T> Stable<'tcx> for &T
-where
-    T: Stable<'tcx>,
-{
-    type T = T::T;
+    pub fn adt_def(&mut self, did: rustc_span::def_id::DefId) -> B::AdtDef {
+        B::AdtDef::new(self.create_def_id(did))
+    }
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        (*self).stable(tables)
+    pub fn foreign_module_def(&mut self, did: rustc_span::def_id::DefId) -> B::ForeignModuleDef {
+        B::ForeignModuleDef::new(self.create_def_id(did))
     }
-}
 
-impl<'tcx, T> Stable<'tcx> for Option<T>
-where
-    T: Stable<'tcx>,
-{
-    type T = Option<T::T>;
+    pub fn foreign_def(&mut self, did: rustc_span::def_id::DefId) -> B::ForeignDef {
+        B::ForeignDef::new(self.create_def_id(did))
+    }
+
+    pub fn fn_def(&mut self, did: rustc_span::def_id::DefId) -> B::FnDef {
+        B::FnDef::new(self.create_def_id(did))
+    }
+
+    pub fn closure_def(&mut self, did: rustc_span::def_id::DefId) -> B::ClosureDef {
+        B::ClosureDef::new(self.create_def_id(did))
+    }
+
+    pub fn coroutine_def(&mut self, did: rustc_span::def_id::DefId) -> B::CoroutineDef {
+        B::CoroutineDef::new(self.create_def_id(did))
+    }
+
+    pub fn coroutine_closure_def(
+        &mut self,
+        did: rustc_span::def_id::DefId,
+    ) -> B::CoroutineClosureDef {
+        B::CoroutineClosureDef::new(self.create_def_id(did))
+    }
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        self.as_ref().map(|value| value.stable(tables))
+    pub fn alias_def(&mut self, did: rustc_span::def_id::DefId) -> B::AliasDef {
+        B::AliasDef::new(self.create_def_id(did))
+    }
+
+    pub fn param_def(&mut self, did: rustc_span::def_id::DefId) -> B::ParamDef {
+        B::ParamDef::new(self.create_def_id(did))
+    }
+
+    pub fn br_named_def(&mut self, did: rustc_span::def_id::DefId) -> B::BrNamedDef {
+        B::BrNamedDef::new(self.create_def_id(did))
+    }
+
+    pub fn trait_def(&mut self, did: rustc_span::def_id::DefId) -> B::TraitDef {
+        B::TraitDef::new(self.create_def_id(did))
+    }
+
+    pub fn generic_def(&mut self, did: rustc_span::def_id::DefId) -> B::GenericDef {
+        B::GenericDef::new(self.create_def_id(did))
+    }
+
+    pub fn const_def(&mut self, did: rustc_span::def_id::DefId) -> B::ConstDef {
+        B::ConstDef::new(self.create_def_id(did))
+    }
+
+    pub fn impl_def(&mut self, did: rustc_span::def_id::DefId) -> B::ImplDef {
+        B::ImplDef::new(self.create_def_id(did))
+    }
+
+    pub fn region_def(&mut self, did: rustc_span::def_id::DefId) -> B::RegionDef {
+        B::RegionDef::new(self.create_def_id(did))
+    }
+
+    pub fn coroutine_witness_def(
+        &mut self,
+        did: rustc_span::def_id::DefId,
+    ) -> B::CoroutineWitnessDef {
+        B::CoroutineWitnessDef::new(self.create_def_id(did))
+    }
+
+    pub fn assoc_def(&mut self, did: rustc_span::def_id::DefId) -> B::AssocDef {
+        B::AssocDef::new(self.create_def_id(did))
+    }
+
+    pub fn opaque_def(&mut self, did: rustc_span::def_id::DefId) -> B::OpaqueDef {
+        B::OpaqueDef::new(self.create_def_id(did))
+    }
+
+    pub fn prov(&mut self, aid: rustc_middle::mir::interpret::AllocId) -> B::Prov {
+        B::Prov::new(self.create_alloc_id(aid))
+    }
+
+    pub fn static_def(&mut self, did: rustc_span::def_id::DefId) -> B::StaticDef {
+        B::StaticDef::new(self.create_def_id(did))
     }
 }
 
-impl<'tcx, T, E> Stable<'tcx> for Result<T, E>
-where
-    T: Stable<'tcx>,
-    E: Stable<'tcx>,
-{
-    type T = Result<T::T, E::T>;
+/// A trait defining types that are used to emulate StableMIR components, which is really
+/// useful when programming in stable_mir-agnostic settings.
+pub trait Bridge: Sized {
+    type DefId: Copy + Debug + PartialEq + IndexedVal;
+    type AllocId: Copy + Debug + PartialEq + IndexedVal;
+    type Span: Copy + Debug + PartialEq + IndexedVal;
+    type Ty: Copy + Debug + PartialEq + IndexedVal;
+    type InstanceDef: Copy + Debug + PartialEq + IndexedVal;
+    type TyConstId: Copy + Debug + PartialEq + IndexedVal;
+    type MirConstId: Copy + Debug + PartialEq + IndexedVal;
+    type Layout: Copy + Debug + PartialEq + IndexedVal;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        match self {
-            Ok(val) => Ok(val.stable(tables)),
-            Err(error) => Err(error.stable(tables)),
-        }
+    type Error: SmirError;
+    type CrateItem: CrateItem<Self>;
+    type AdtDef: AdtDef<Self>;
+    type ForeignModuleDef: ForeignModuleDef<Self>;
+    type ForeignDef: ForeignDef<Self>;
+    type FnDef: FnDef<Self>;
+    type ClosureDef: ClosureDef<Self>;
+    type CoroutineDef: CoroutineDef<Self>;
+    type CoroutineClosureDef: CoroutineClosureDef<Self>;
+    type AliasDef: AliasDef<Self>;
+    type ParamDef: ParamDef<Self>;
+    type BrNamedDef: BrNamedDef<Self>;
+    type TraitDef: TraitDef<Self>;
+    type GenericDef: GenericDef<Self>;
+    type ConstDef: ConstDef<Self>;
+    type ImplDef: ImplDef<Self>;
+    type RegionDef: RegionDef<Self>;
+    type CoroutineWitnessDef: CoroutineWitnessDef<Self>;
+    type AssocDef: AssocDef<Self>;
+    type OpaqueDef: OpaqueDef<Self>;
+    type Prov: Prov<Self>;
+    type StaticDef: StaticDef<Self>;
+
+    type Allocation: Allocation<Self>;
+}
+
+pub trait IndexedVal {
+    fn to_val(index: usize) -> Self;
+
+    fn to_index(&self) -> usize;
+}
+
+/// Similar to rustc's `FxIndexMap`, `IndexMap` with extra
+/// safety features added.
+pub struct IndexMap<K, V> {
+    index_map: fx::FxIndexMap<K, V>,
+}
+
+impl<K, V> Default for IndexMap<K, V> {
+    fn default() -> Self {
+        Self { index_map: FxIndexMap::default() }
     }
 }
 
-impl<'tcx, T> Stable<'tcx> for &[T]
-where
-    T: Stable<'tcx>,
-{
-    type T = Vec<T::T>;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        self.iter().map(|e| e.stable(tables)).collect()
+impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> IndexMap<K, V> {
+    pub fn create_or_fetch(&mut self, key: K) -> V {
+        let len = self.index_map.len();
+        let v = self.index_map.entry(key).or_insert(V::to_val(len));
+        *v
     }
 }
 
-impl<'tcx, T, U> Stable<'tcx> for (T, U)
-where
-    T: Stable<'tcx>,
-    U: Stable<'tcx>,
+impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> Index<V>
+    for IndexMap<K, V>
 {
-    type T = (T::T, U::T);
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        (self.0.stable(tables), self.1.stable(tables))
+    type Output = K;
+
+    fn index(&self, index: V) -> &Self::Output {
+        let (k, v) = self.index_map.get_index(index.to_index()).unwrap();
+        assert_eq!(*v, index, "Provided value doesn't match with indexed value");
+        k
     }
 }
 
-impl<'tcx, T> Stable<'tcx> for RangeInclusive<T>
+/// Iterate over the definitions of the given crate.
+pub(crate) fn filter_def_ids<F, T>(tcx: TyCtxt<'_>, krate: CrateNum, mut func: F) -> Vec<T>
 where
-    T: Stable<'tcx>,
+    F: FnMut(DefId) -> Option<T>,
 {
-    type T = RangeInclusive<T::T>;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        RangeInclusive::new(self.start().stable(tables), self.end().stable(tables))
+    if krate == LOCAL_CRATE {
+        tcx.iter_local_def_id().filter_map(|did| func(did.to_def_id())).collect()
+    } else {
+        let num_definitions = tcx.num_extern_def_ids(krate);
+        (0..num_definitions)
+            .filter_map(move |i| {
+                let def_id = DefId { krate, index: rustc_span::def_id::DefIndex::from_usize(i) };
+                func(def_id)
+            })
+            .collect()
     }
 }
diff --git a/compiler/rustc_smir/src/stable_mir/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs
index d8a2b97662c45..369d08e444ec5 100644
--- a/compiler/rustc_smir/src/stable_mir/abi.rs
+++ b/compiler/rustc_smir/src/stable_mir/abi.rs
@@ -6,7 +6,7 @@ use serde::Serialize;
 use stable_mir::compiler_interface::with;
 use stable_mir::mir::FieldIdx;
 use stable_mir::target::{MachineInfo, MachineSize as Size};
-use stable_mir::ty::{Align, IndexedVal, Ty, VariantIdx};
+use stable_mir::ty::{Align, Ty, VariantIdx};
 use stable_mir::{Error, Opaque, error};
 
 use crate::stable_mir;
@@ -119,7 +119,7 @@ impl Layout {
     }
 }
 
-impl IndexedVal for Layout {
+impl stable_mir::IndexedVal for Layout {
     fn to_val(index: usize) -> Self {
         Layout(index)
     }
diff --git a/compiler/rustc_smir/src/stable_mir/alloc.rs b/compiler/rustc_smir/src/stable_mir/alloc.rs
new file mode 100644
index 0000000000000..120cb4404b96f
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/alloc.rs
@@ -0,0 +1,77 @@
+//! Memory allocation implementation for StableMIR.
+//!
+//! This module is responsible for constructing stable components.
+//! All operations requiring rustc queries must be delegated
+//! to `rustc_smir::alloc` to maintain stability guarantees.
+
+use rustc_abi::Align;
+use rustc_middle::mir::ConstValue;
+use rustc_middle::mir::interpret::AllocRange;
+use rustc_smir::bridge::SmirError;
+use rustc_smir::context::SmirCtxt;
+use rustc_smir::{Tables, alloc};
+
+use super::Error;
+use super::compiler_interface::BridgeTys;
+use super::mir::Mutability;
+use super::ty::{Allocation, ProvenanceMap};
+use super::unstable::Stable;
+use crate::rustc_smir;
+
+/// 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_smir::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_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
index 2668fba9f4f60..a19968d2ab77d 100644
--- a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
+++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
@@ -5,133 +5,488 @@
 
 use std::cell::Cell;
 
+use rustc_hir::def::DefKind;
 use rustc_smir::context::SmirCtxt;
+use rustc_smir::{Bridge, SmirContainer};
 use stable_mir::abi::{FnAbi, Layout, LayoutShape, ReprOptions};
 use stable_mir::crate_def::Attribute;
 use stable_mir::mir::alloc::{AllocId, GlobalAlloc};
 use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef};
 use stable_mir::mir::{BinOp, Body, Place, UnOp};
-use stable_mir::target::MachineInfo;
+use stable_mir::target::{MachineInfo, MachineSize};
 use stable_mir::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 stable_mir::unstable::{RustcInternal, Stable, new_item_kind};
 use stable_mir::{
-    AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls,
-    ItemKind, Symbol, TraitDecls, mir,
+    AssocItems, Crate, CrateDef, CrateItem, CrateItems, CrateNum, DefId, Error, Filename,
+    ImplTraitDecls, ItemKind, Symbol, TraitDecls, alloc, mir,
 };
+use tracing::debug;
 
 use crate::{rustc_smir, stable_mir};
 
+pub struct BridgeTys;
+
+impl Bridge for BridgeTys {
+    type DefId = stable_mir::DefId;
+    type AllocId = stable_mir::mir::alloc::AllocId;
+    type Span = stable_mir::ty::Span;
+    type Ty = stable_mir::ty::Ty;
+    type InstanceDef = stable_mir::mir::mono::InstanceDef;
+    type TyConstId = stable_mir::ty::TyConstId;
+    type MirConstId = stable_mir::ty::MirConstId;
+    type Layout = stable_mir::abi::Layout;
+
+    type Error = stable_mir::Error;
+    type CrateItem = stable_mir::CrateItem;
+    type AdtDef = stable_mir::ty::AdtDef;
+    type ForeignModuleDef = stable_mir::ty::ForeignModuleDef;
+    type ForeignDef = stable_mir::ty::ForeignDef;
+    type FnDef = stable_mir::ty::FnDef;
+    type ClosureDef = stable_mir::ty::ClosureDef;
+    type CoroutineDef = stable_mir::ty::CoroutineDef;
+    type CoroutineClosureDef = stable_mir::ty::CoroutineClosureDef;
+    type AliasDef = stable_mir::ty::AliasDef;
+    type ParamDef = stable_mir::ty::ParamDef;
+    type BrNamedDef = stable_mir::ty::BrNamedDef;
+    type TraitDef = stable_mir::ty::TraitDef;
+    type GenericDef = stable_mir::ty::GenericDef;
+    type ConstDef = stable_mir::ty::ConstDef;
+    type ImplDef = stable_mir::ty::ImplDef;
+    type RegionDef = stable_mir::ty::RegionDef;
+    type CoroutineWitnessDef = stable_mir::ty::CoroutineWitnessDef;
+    type AssocDef = stable_mir::ty::AssocDef;
+    type OpaqueDef = stable_mir::ty::OpaqueDef;
+    type Prov = stable_mir::ty::Prov;
+    type StaticDef = stable_mir::mir::mono::StaticDef;
+
+    type Allocation = stable_mir::ty::Allocation;
+}
+
 /// Stable public API for querying compiler information.
 ///
-/// All queries are delegated to an internal [`SmirCtxt`] that provides
+/// All queries are delegated to [`crate::rustc_smir::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) struct SmirInterface<'tcx> {
-    pub(crate) cx: SmirCtxt<'tcx>,
+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<'tcx> {
-    pub(crate) fn entry_fn(&self) -> Option<CrateItem> {
-        self.cx.entry_fn()
+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.
-    pub(crate) fn all_local_items(&self) -> CrateItems {
-        self.cx.all_local_items()
+    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.
-    pub(crate) fn mir_body(&self, item: DefId) -> mir::Body {
-        self.cx.mir_body(item)
+    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.
-    pub(crate) fn has_body(&self, item: DefId) -> bool {
-        self.cx.has_body(item)
+    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)
     }
 
-    pub(crate) fn foreign_modules(&self, crate_num: CrateNum) -> Vec<ForeignModuleDef> {
-        self.cx.foreign_modules(crate_num)
+    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.
-    pub(crate) fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef> {
-        self.cx.crate_functions(crate_num)
+    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.
-    pub(crate) fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef> {
-        self.cx.crate_statics(crate_num)
+    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()
     }
 
-    pub(crate) fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule {
-        self.cx.foreign_module(mod_def)
+    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)
     }
 
-    pub(crate) fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec<ForeignDef> {
-        self.cx.foreign_items(mod_def)
+    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()
     }
 
-    pub(crate) fn all_trait_decls(&self) -> TraitDecls {
-        self.cx.all_trait_decls()
+    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()
     }
 
-    pub(crate) fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls {
-        self.cx.trait_decls(crate_num)
+    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()
     }
 
-    pub(crate) fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl {
-        self.cx.trait_decl(trait_def)
+    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)
     }
 
-    pub(crate) fn all_trait_impls(&self) -> ImplTraitDecls {
-        self.cx.all_trait_impls()
+    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()
     }
 
-    pub(crate) fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls {
-        self.cx.trait_impls(crate_num)
+    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()
     }
 
-    pub(crate) fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait {
-        self.cx.trait_impl(trait_impl)
+    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)
     }
 
-    pub(crate) fn generics_of(&self, def_id: DefId) -> Generics {
-        self.cx.generics_of(def_id)
+    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)
     }
 
-    pub(crate) fn predicates_of(&self, def_id: DefId) -> GenericPredicates {
-        self.cx.predicates_of(def_id)
+    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);
+        stable_mir::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(),
+        }
     }
 
-    pub(crate) fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates {
-        self.cx.explicit_predicates_of(def_id)
+    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);
+        stable_mir::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.
-    pub(crate) fn local_crate(&self) -> Crate {
-        self.cx.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.
-    pub(crate) fn external_crates(&self) -> Vec<Crate> {
-        self.cx.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.
-    pub(crate) fn find_crates(&self, name: &str) -> Vec<Crate> {
-        self.cx.find_crates(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`.
-    pub(crate) fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol {
-        self.cx.def_name(def_id, trimmed)
+    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.
@@ -141,364 +496,581 @@ impl<'tcx> SmirInterface<'tcx> {
     ///
     /// 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()]`.
-    pub(crate) fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute> {
-        self.cx.tool_attrs(def_id, attr)
+    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.
-    pub(crate) fn all_tool_attrs(&self, def_id: DefId) -> Vec<Attribute> {
-        self.cx.all_tool_attrs(def_id)
+    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`.
-    pub(crate) fn span_to_string(&self, span: Span) -> String {
-        self.cx.span_to_string(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.
-    pub(crate) fn get_filename(&self, span: &Span) -> Filename {
-        self.cx.get_filename(span)
+    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`.
-    pub(crate) fn get_lines(&self, span: &Span) -> LineInfo {
-        self.cx.get_lines(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`.
-    pub(crate) fn item_kind(&self, item: CrateItem) -> ItemKind {
-        self.cx.item_kind(item)
+    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.
-    pub(crate) fn is_foreign_item(&self, item: DefId) -> bool {
-        self.cx.is_foreign_item(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.
-    pub(crate) fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind {
-        self.cx.foreign_item_kind(def)
+    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_smir::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.
-    pub(crate) fn adt_kind(&self, def: AdtDef) -> AdtKind {
-        self.cx.adt_kind(def)
+    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.
-    pub(crate) fn adt_is_box(&self, def: AdtDef) -> bool {
-        self.cx.adt_is_box(def)
+    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.
-    pub(crate) fn adt_is_simd(&self, def: AdtDef) -> bool {
-        self.cx.adt_is_simd(def)
+    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.
-    pub(crate) fn adt_is_cstr(&self, def: AdtDef) -> bool {
-        self.cx.adt_is_cstr(def)
+    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
-    pub(crate) fn adt_repr(&self, def: AdtDef) -> ReprOptions {
-        self.cx.adt_repr(def)
+    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.
-    pub(crate) fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig {
-        self.cx.fn_sig(def, args)
+    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.
-    pub(crate) fn intrinsic(&self, item: DefId) -> Option<IntrinsicDef> {
-        self.cx.intrinsic(item)
+    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.
-    pub(crate) fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol {
-        self.cx.intrinsic_name(def)
+    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.
-    pub(crate) fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig {
-        self.cx.closure_sig(args)
+    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.
-    pub(crate) fn adt_variants_len(&self, def: AdtDef) -> usize {
-        self.cx.adt_variants_len(def)
+    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
-    pub(crate) fn adt_discr_for_variant(&self, adt: AdtDef, variant: VariantIdx) -> Discr {
-        self.cx.adt_discr_for_variant(adt, variant)
+    /// 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
-    pub(crate) fn coroutine_discr_for_variant(
+    /// Discriminant for a given variand index and args of a coroutine.
+    fn coroutine_discr_for_variant(
         &self,
         coroutine: CoroutineDef,
         args: &GenericArgs,
         variant: VariantIdx,
     ) -> Discr {
-        self.cx.coroutine_discr_for_variant(coroutine, args, variant)
+        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.
-    pub(crate) fn variant_name(&self, def: VariantDef) -> Symbol {
-        self.cx.variant_name(def)
+    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))
     }
 
-    pub(crate) fn variant_fields(&self, def: VariantDef) -> Vec<FieldDef> {
-        self.cx.variant_fields(def)
+    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.
-    pub(crate) fn eval_target_usize(&self, cnst: &MirConst) -> Result<u64, Error> {
-        self.cx.eval_target_usize(cnst)
+    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)
     }
 
-    pub(crate) fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result<u64, Error> {
-        self.cx.eval_target_usize_ty(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.
-    pub(crate) fn try_new_const_zst(&self, ty: Ty) -> Result<MirConst, Error> {
-        self.cx.try_new_const_zst(ty)
+    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.
-    pub(crate) fn new_const_str(&self, value: &str) -> MirConst {
-        self.cx.new_const_str(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.
-    pub(crate) fn new_const_bool(&self, value: bool) -> MirConst {
-        self.cx.new_const_bool(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.
-    pub(crate) fn try_new_const_uint(
-        &self,
-        value: u128,
-        uint_ty: UintTy,
-    ) -> Result<MirConst, Error> {
-        self.cx.try_new_const_uint(value, uint_ty)
+    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))
     }
 
-    pub(crate) fn try_new_ty_const_uint(
-        &self,
-        value: u128,
-        uint_ty: UintTy,
-    ) -> Result<TyConst, Error> {
-        self.cx.try_new_ty_const_uint(value, uint_ty)
+    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.
-    pub(crate) fn new_rigid_ty(&self, kind: RigidTy) -> Ty {
-        self.cx.new_rigid_ty(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`.
-    pub(crate) fn new_box_ty(&self, ty: Ty) -> Ty {
-        self.cx.new_box_ty(ty)
+    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.
-    pub(crate) fn def_ty(&self, item: DefId) -> Ty {
-        self.cx.def_ty(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.
-    pub(crate) fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty {
-        self.cx.def_ty_with_args(item, args)
+    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.
-    pub(crate) fn mir_const_pretty(&self, cnst: &MirConst) -> String {
-        self.cx.mir_const_pretty(cnst)
+    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.
-    pub(crate) fn span_of_an_item(&self, def_id: DefId) -> Span {
-        self.cx.span_of_an_item(def_id)
+    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)
     }
 
-    pub(crate) fn ty_const_pretty(&self, ct: TyConstId) -> String {
-        self.cx.ty_const_pretty(ct)
+    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.
-    pub(crate) fn ty_pretty(&self, ty: Ty) -> String {
-        self.cx.ty_pretty(ty)
+    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 representation of a type.
-    pub(crate) fn ty_kind(&self, ty: Ty) -> TyKind {
-        self.cx.ty_kind(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.
-    pub(crate) fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty {
-        self.cx.rigid_ty_discriminant_ty(ty)
+    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.
-    pub(crate) fn instance_body(&self, instance: InstanceDef) -> Option<Body> {
-        self.cx.instance_body(instance)
+    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.
-    pub(crate) fn instance_ty(&self, instance: InstanceDef) -> Ty {
-        self.cx.instance_ty(instance)
+    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.
-    pub(crate) fn instance_args(&self, def: InstanceDef) -> GenericArgs {
-        self.cx.instance_args(def)
+    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.
-    pub(crate) fn instance_def_id(&self, instance: InstanceDef) -> DefId {
-        self.cx.instance_def_id(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.
-    pub(crate) fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol {
-        self.cx.instance_mangled_name(instance)
+    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.
-    pub(crate) fn is_empty_drop_shim(&self, def: InstanceDef) -> bool {
-        self.cx.is_empty_drop_shim(def)
+    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.
-    pub(crate) fn mono_instance(&self, def_id: DefId) -> Instance {
-        self.cx.mono_instance(def_id)
+    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.
-    pub(crate) fn requires_monomorphization(&self, def_id: DefId) -> bool {
-        self.cx.requires_monomorphization(def_id)
+    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.
-    pub(crate) fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option<Instance> {
-        self.cx.resolve_instance(def, args)
+    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.
-    pub(crate) fn resolve_drop_in_place(&self, ty: Ty) -> Instance {
-        self.cx.resolve_drop_in_place(ty)
+    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.
-    pub(crate) fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option<Instance> {
-        self.cx.resolve_for_fn_ptr(def, args)
+    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.
-    pub(crate) fn resolve_closure(
+    fn resolve_closure(
         &self,
         def: ClosureDef,
         args: &GenericArgs,
         kind: ClosureKind,
     ) -> Option<Instance> {
-        self.cx.resolve_closure(def, args, kind)
+        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.
-    pub(crate) fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error> {
-        self.cx.eval_static_initializer(def)
+    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.
-    pub(crate) fn eval_instance(
-        &self,
-        def: InstanceDef,
-        const_ty: Ty,
-    ) -> Result<Allocation, Error> {
-        self.cx.eval_instance(def, const_ty)
+    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.
-    pub(crate) fn global_alloc(&self, id: AllocId) -> GlobalAlloc {
-        self.cx.global_alloc(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.
-    pub(crate) fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option<AllocId> {
-        self.cx.vtable_allocation(global_alloc)
+    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))
     }
 
-    pub(crate) fn krate(&self, def_id: DefId) -> Crate {
-        self.cx.krate(def_id)
+    fn krate(&self, def_id: DefId) -> Crate {
+        let tables = self.tables.borrow();
+        let cx = &*self.cx.borrow();
+        smir_crate(cx, tables[def_id].krate)
     }
 
-    pub(crate) fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol {
-        self.cx.instance_name(def, trimmed)
+    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.
-    pub(crate) fn target_info(&self) -> MachineInfo {
-        self.cx.target_info()
+    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.
-    pub(crate) fn instance_abi(&self, def: InstanceDef) -> Result<FnAbi, Error> {
-        self.cx.instance_abi(def)
+    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.
-    pub(crate) fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result<FnAbi, Error> {
-        self.cx.fn_ptr_abi(fn_ptr)
+    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.
-    pub(crate) fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
-        self.cx.ty_layout(ty)
+    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.
-    pub(crate) fn layout_shape(&self, id: Layout) -> LayoutShape {
-        self.cx.layout_shape(id)
+    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.
-    pub(crate) fn place_pretty(&self, place: &Place) -> String {
-        self.cx.place_pretty(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.
-    pub(crate) fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty {
-        self.cx.binop_ty(bin_op, rhs, lhs)
+    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.
-    pub(crate) fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty {
-        self.cx.unop_ty(un_op, arg)
+    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.
-    pub(crate) fn associated_items(&self, def_id: DefId) -> AssocItems {
-        self.cx.associated_items(def_id)
+    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<'tcx, T, F>(interface: &SmirInterface<'tcx>, f: F) -> Result<T, Error>
+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 () = (interface as *const SmirInterface<'tcx>) as *const ();
+        let ptr: *const () = (&raw const interface) as _;
         TLV.set(&Cell::new(ptr), || Ok(f()))
     }
 }
@@ -507,11 +1079,22 @@ where
 ///
 /// 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(&SmirInterface<'_>) -> R) -> R {
+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 SmirInterface<'_>) })
+        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_smir/src/stable_mir/error.rs b/compiler/rustc_smir/src/stable_mir/error.rs
index 050752e41eb92..3f9d67954b9d3 100644
--- a/compiler/rustc_smir/src/stable_mir/error.rs
+++ b/compiler/rustc_smir/src/stable_mir/error.rs
@@ -7,6 +7,10 @@
 use std::fmt::{Debug, Display, Formatter};
 use std::{fmt, io};
 
+use rustc_smir::bridge::SmirError;
+
+use crate::rustc_smir;
+
 macro_rules! error {
      ($fmt: literal $(,)?) => { Error(format!($fmt)) };
      ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) };
@@ -30,10 +34,14 @@ pub enum CompilerError<T> {
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Error(pub(crate) String);
 
-impl Error {
-    pub fn new(msg: String) -> Self {
+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 {
diff --git a/compiler/rustc_smir/src/stable_mir/mir/alloc.rs b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
index 782f52888b73d..26f30898a9cc0 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
@@ -5,8 +5,8 @@ use std::io::Read;
 use serde::Serialize;
 use stable_mir::mir::mono::{Instance, StaticDef};
 use stable_mir::target::{Endian, MachineInfo};
-use stable_mir::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty};
-use stable_mir::{Error, with};
+use stable_mir::ty::{Allocation, Binder, ExistentialTraitRef, Ty};
+use stable_mir::{Error, IndexedVal, with};
 
 use crate::stable_mir;
 
diff --git a/compiler/rustc_smir/src/stable_mir/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
index f5239cccae1eb..5f177416714eb 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/mono.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
@@ -1,14 +1,15 @@
 use std::fmt::{Debug, Formatter};
 use std::io;
 
+use rustc_smir::bridge::SmirError;
 use serde::Serialize;
 use stable_mir::abi::FnAbi;
 use stable_mir::crate_def::CrateDef;
 use stable_mir::mir::Body;
-use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
-use stable_mir::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with};
+use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, Ty};
+use stable_mir::{CrateItem, DefId, Error, IndexedVal, ItemKind, Opaque, Symbol, with};
 
-use crate::stable_mir;
+use crate::{rustc_smir, stable_mir};
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
 pub enum MonoItem {
diff --git a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
index b068a9a1081f1..a7347e9b02161 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
@@ -7,8 +7,8 @@ use fmt::{Display, Formatter};
 use stable_mir::mir::{
     Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents,
 };
-use stable_mir::ty::{AdtKind, AssocKind, IndexedVal, MirConst, Ty, TyConst};
-use stable_mir::{Body, CrateDef, Mutability, with};
+use stable_mir::ty::{AdtKind, AssocKind, MirConst, Ty, TyConst};
+use stable_mir::{Body, CrateDef, IndexedVal, Mutability, with};
 
 use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
 use crate::stable_mir;
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
index c59758d4ad3f2..70c09c1285416 100644
--- a/compiler/rustc_smir/src/stable_mir/mod.rs
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -27,17 +27,25 @@
 use std::fmt::Debug;
 use std::{fmt, io};
 
+pub(crate) use rustc_smir::IndexedVal;
+use rustc_smir::Tables;
+use rustc_smir::context::SmirCtxt;
 use serde::Serialize;
 use stable_mir::compiler_interface::with;
 pub use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId};
 pub use stable_mir::error::*;
 use stable_mir::mir::mono::StaticDef;
 use stable_mir::mir::{Body, Mutability};
-use stable_mir::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
+use stable_mir::ty::{
+    AssocItem, FnDef, ForeignModuleDef, ImplDef, ProvenanceMap, Span, TraitDef, Ty,
+};
+use stable_mir::unstable::Stable;
 
-use crate::stable_mir;
+use crate::{rustc_smir, stable_mir};
 
 pub mod abi;
+mod alloc;
+pub(crate) mod unstable;
 #[macro_use]
 pub mod crate_def;
 pub mod compiler_interface;
@@ -237,3 +245,60 @@ impl std::fmt::Debug for Opaque {
 pub fn opaque<T: Debug>(value: &T) -> Opaque {
     Opaque(format!("{value:?}"))
 }
+
+macro_rules! bridge_impl {
+    ($name: ident, $ty: ty) => {
+        impl rustc_smir::bridge::$name<compiler_interface::BridgeTys> for $ty {
+            fn new(def: stable_mir::DefId) -> Self {
+                Self(def)
+            }
+        }
+    };
+}
+
+bridge_impl!(CrateItem, stable_mir::CrateItem);
+bridge_impl!(AdtDef, stable_mir::ty::AdtDef);
+bridge_impl!(ForeignModuleDef, stable_mir::ty::ForeignModuleDef);
+bridge_impl!(ForeignDef, stable_mir::ty::ForeignDef);
+bridge_impl!(FnDef, stable_mir::ty::FnDef);
+bridge_impl!(ClosureDef, stable_mir::ty::ClosureDef);
+bridge_impl!(CoroutineDef, stable_mir::ty::CoroutineDef);
+bridge_impl!(CoroutineClosureDef, stable_mir::ty::CoroutineClosureDef);
+bridge_impl!(AliasDef, stable_mir::ty::AliasDef);
+bridge_impl!(ParamDef, stable_mir::ty::ParamDef);
+bridge_impl!(BrNamedDef, stable_mir::ty::BrNamedDef);
+bridge_impl!(TraitDef, stable_mir::ty::TraitDef);
+bridge_impl!(GenericDef, stable_mir::ty::GenericDef);
+bridge_impl!(ConstDef, stable_mir::ty::ConstDef);
+bridge_impl!(ImplDef, stable_mir::ty::ImplDef);
+bridge_impl!(RegionDef, stable_mir::ty::RegionDef);
+bridge_impl!(CoroutineWitnessDef, stable_mir::ty::CoroutineWitnessDef);
+bridge_impl!(AssocDef, stable_mir::ty::AssocDef);
+bridge_impl!(OpaqueDef, stable_mir::ty::OpaqueDef);
+bridge_impl!(StaticDef, stable_mir::mir::mono::StaticDef);
+
+impl rustc_smir::bridge::Prov<compiler_interface::BridgeTys> for stable_mir::ty::Prov {
+    fn new(aid: stable_mir::mir::alloc::AllocId) -> Self {
+        Self(aid)
+    }
+}
+
+impl rustc_smir::bridge::Allocation<compiler_interface::BridgeTys> for stable_mir::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_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 398738d1c3896..004a7c0223461 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -7,7 +7,7 @@ use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType};
 use stable_mir::mir::alloc::{AllocId, read_target_int, read_target_uint};
 use stable_mir::mir::mono::StaticDef;
 use stable_mir::target::MachineInfo;
-use stable_mir::{Filename, Opaque};
+use stable_mir::{Filename, IndexedVal, Opaque};
 
 use super::abi::ReprOptions;
 use super::mir::{Body, Mutability, Safety};
@@ -298,6 +298,12 @@ pub struct LineInfo {
     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),
@@ -1557,15 +1563,9 @@ pub enum PredicatePolarity {
     Negative,
 }
 
-pub trait IndexedVal {
-    fn to_val(index: usize) -> Self;
-
-    fn to_index(&self) -> usize;
-}
-
 macro_rules! index_impl {
     ($name:ident) => {
-        impl IndexedVal for $name {
+        impl stable_mir::IndexedVal for $name {
             fn to_val(index: usize) -> Self {
                 $name(index)
             }
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs
similarity index 72%
rename from compiler/rustc_smir/src/rustc_internal/internal.rs
rename to compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs
index f878b6e6b7170..4cbe02bfa0db0 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs
@@ -5,55 +5,76 @@
 
 // Prefer importing stable_mir over internal rustc constructs to make this file more readable.
 
-use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy, TyCtxt};
+use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy};
+use rustc_smir::Tables;
 use rustc_span::Symbol;
 use stable_mir::abi::Layout;
+use stable_mir::compiler_interface::BridgeTys;
 use stable_mir::mir::alloc::AllocId;
 use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
 use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp};
 use stable_mir::ty::{
     Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind,
     ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig,
-    GenericArgKind, GenericArgs, IndexedVal, IntTy, MirConst, Movability, Pattern, Region, RigidTy,
-    Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx,
+    GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span,
+    TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx,
 };
-use stable_mir::{CrateItem, CrateNum, DefId};
+use stable_mir::unstable::{InternalCx, RustcInternal};
+use stable_mir::{CrateItem, CrateNum, DefId, IndexedVal};
 
-use super::RustcInternal;
-use crate::rustc_smir::Tables;
-use crate::stable_mir;
+use crate::{rustc_smir, stable_mir};
 
 impl RustcInternal for CrateItem {
     type T<'tcx> = rustc_span::def_id::DefId;
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        tcx.mk_args_from_iter(self.0.iter().map(|arg| arg.internal(tables, 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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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(),
@@ -65,29 +86,45 @@ impl RustcInternal for GenericArgKind {
 
 impl RustcInternal for Region {
     type T<'tcx> = rustc_ty::Region<'tcx>;
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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
+        tcx.lifetimes_re_erased()
     }
 }
 
 impl RustcInternal for Ty {
     type T<'tcx> = InternalTy<'tcx>;
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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),
@@ -100,7 +137,11 @@ impl RustcInternal for Pattern {
 impl RustcInternal for RigidTy {
     type T<'tcx> = rustc_ty::TyKind<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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,
@@ -164,7 +205,11 @@ impl RustcInternal for RigidTy {
 impl RustcInternal for IntTy {
     type T<'tcx> = rustc_ty::IntTy;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -179,7 +224,11 @@ impl RustcInternal for IntTy {
 impl RustcInternal for UintTy {
     type T<'tcx> = rustc_ty::UintTy;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -194,7 +243,11 @@ impl RustcInternal for UintTy {
 impl RustcInternal for FloatTy {
     type T<'tcx> = rustc_ty::FloatTy;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -207,7 +260,11 @@ impl RustcInternal for FloatTy {
 impl RustcInternal for Mutability {
     type T<'tcx> = rustc_ty::Mutability;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -218,7 +275,11 @@ impl RustcInternal for Mutability {
 impl RustcInternal for Movability {
     type T<'tcx> = rustc_ty::Movability;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -229,7 +290,11 @@ impl RustcInternal for Movability {
 impl RustcInternal for RawPtrKind {
     type T<'tcx> = rustc_middle::mir::RawPtrKind;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -241,7 +306,11 @@ impl RustcInternal for RawPtrKind {
 impl RustcInternal for FnSig {
     type T<'tcx> = rustc_ty::FnSig<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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,
@@ -255,7 +324,11 @@ impl RustcInternal for FnSig {
 impl RustcInternal for VariantIdx {
     type T<'tcx> = rustc_abi::VariantIdx;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         rustc_abi::VariantIdx::from(self.to_index())
     }
 }
@@ -263,14 +336,22 @@ impl RustcInternal for VariantIdx {
 impl RustcInternal for VariantDef {
     type T<'tcx> = &'tcx rustc_ty::VariantDef;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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) => {
@@ -292,7 +373,11 @@ impl RustcInternal for MirConst {
 impl RustcInternal for MonoItem {
     type T<'tcx> = rustc_middle::mir::mono::MonoItem<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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)),
@@ -307,7 +392,11 @@ impl RustcInternal for MonoItem {
 impl RustcInternal for Instance {
     type T<'tcx> = rustc_ty::Instance<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         tcx.lift(tables.instances[self.def]).unwrap()
     }
 }
@@ -315,7 +404,11 @@ impl RustcInternal for Instance {
 impl RustcInternal for StaticDef {
     type T<'tcx> = rustc_span::def_id::DefId;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         self.0.internal(tables, tcx)
     }
 }
@@ -328,7 +421,11 @@ where
 {
     type T<'tcx> = rustc_ty::Binder<'tcx, T::T<'tcx>>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::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(
@@ -341,7 +438,11 @@ where
 impl RustcInternal for BoundVariableKind {
     type T<'tcx> = rustc_ty::BoundVariableKind;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -366,7 +467,11 @@ impl RustcInternal for BoundVariableKind {
 impl RustcInternal for DynKind {
     type T<'tcx> = rustc_ty::DynKind;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        _tables: &mut Tables<'_, BridgeTys>,
+        _tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         match self {
             DynKind::Dyn => rustc_ty::DynKind::Dyn,
         }
@@ -376,7 +481,11 @@ impl RustcInternal for DynKind {
 impl RustcInternal for ExistentialPredicate {
     type T<'tcx> = rustc_ty::ExistentialPredicate<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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))
@@ -394,9 +503,13 @@ impl RustcInternal for ExistentialPredicate {
 impl RustcInternal for ExistentialProjection {
     type T<'tcx> = rustc_ty::ExistentialProjection<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        rustc_ty::ExistentialProjection::new_from_args(
-            tcx,
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use rustc_smir::context::SmirExistentialProjection;
+        tcx.new_from_args(
             self.def_id.0.internal(tables, tcx),
             self.generic_args.internal(tables, tcx),
             self.term.internal(tables, tcx),
@@ -407,7 +520,11 @@ impl RustcInternal for ExistentialProjection {
 impl RustcInternal for TermKind {
     type T<'tcx> = rustc_ty::Term<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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(),
@@ -418,9 +535,13 @@ impl RustcInternal for TermKind {
 impl RustcInternal for ExistentialTraitRef {
     type T<'tcx> = rustc_ty::ExistentialTraitRef<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        rustc_ty::ExistentialTraitRef::new_from_args(
-            tcx,
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use rustc_smir::context::SmirExistentialTraitRef;
+        tcx.new_from_args(
             self.def_id.0.internal(tables, tcx),
             self.generic_args.internal(tables, tcx),
         )
@@ -430,18 +551,23 @@ impl RustcInternal for ExistentialTraitRef {
 impl RustcInternal for TraitRef {
     type T<'tcx> = rustc_ty::TraitRef<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        rustc_ty::TraitRef::new_from_args(
-            tcx,
-            self.def_id.0.internal(tables, tcx),
-            self.args().internal(tables, tcx),
-        )
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
+        use rustc_smir::context::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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         tcx.lift(tables.alloc_ids[*self]).unwrap()
     }
 }
@@ -449,7 +575,11 @@ impl RustcInternal for AllocId {
 impl RustcInternal for ClosureKind {
     type T<'tcx> = rustc_ty::ClosureKind;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -460,15 +590,23 @@ impl RustcInternal for ClosureKind {
 
 impl RustcInternal for AdtDef {
     type T<'tcx> = rustc_ty::AdtDef<'tcx>;
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        tcx.adt_def(self.0.internal(tables, 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<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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 },
@@ -504,7 +642,11 @@ impl RustcInternal for Abi {
 impl RustcInternal for Safety {
     type T<'tcx> = rustc_hir::Safety;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -514,15 +656,23 @@ impl RustcInternal for Safety {
 impl RustcInternal for Span {
     type T<'tcx> = rustc_span::Span;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        tables[*self]
+    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<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         tcx.lift(tables.layouts[*self]).unwrap()
     }
 }
@@ -530,7 +680,11 @@ impl RustcInternal for Layout {
 impl RustcInternal for Place {
     type T<'tcx> = rustc_middle::mir::Place<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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)),
@@ -541,7 +695,11 @@ impl RustcInternal for Place {
 impl RustcInternal for ProjectionElem {
     type T<'tcx> = rustc_middle::mir::PlaceElem<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'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) => {
@@ -574,7 +732,11 @@ impl RustcInternal for ProjectionElem {
 impl RustcInternal for BinOp {
     type T<'tcx> = rustc_middle::mir::BinOp;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -606,7 +768,11 @@ impl RustcInternal for BinOp {
 impl RustcInternal for UnOp {
     type T<'tcx> = rustc_middle::mir::UnOp;
 
-    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    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,
@@ -621,7 +787,11 @@ where
 {
     type T<'tcx> = T::T<'tcx>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+    fn internal<'tcx>(
+        &self,
+        tables: &mut Tables<'_, BridgeTys>,
+        tcx: impl InternalCx<'tcx>,
+    ) -> Self::T<'tcx> {
         (*self).internal(tables, tcx)
     }
 }
@@ -632,7 +802,11 @@ where
 {
     type T<'tcx> = Option<T::T<'tcx>>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::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))
     }
 }
@@ -643,7 +817,11 @@ where
 {
     type T<'tcx> = Vec<T::T<'tcx>>;
 
-    fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::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_smir/src/stable_mir/unstable/convert/mod.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/mod.rs
new file mode 100644
index 0000000000000..6e1b85671f843
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/mod.rs
@@ -0,0 +1,111 @@
+//! 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_smir functions.
+//!
+//! For contributors, please make sure to avoid calling rustc's internal functions and queries.
+//! These should be done via `rustc_smir` APIs, but it's possible to access ADT fields directly.
+
+use std::ops::RangeInclusive;
+
+use rustc_smir::Tables;
+use rustc_smir::context::SmirCtxt;
+use stable_mir::compiler_interface::BridgeTys;
+
+use super::Stable;
+use crate::{rustc_smir, stable_mir};
+
+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_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/abi.rs
similarity index 65%
rename from compiler/rustc_smir/src/rustc_smir/convert/abi.rs
rename to compiler/rustc_smir/src/stable_mir/unstable/convert/stable/abi.rs
index 35d5b7fb89afd..d8823a0d10c0e 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/abi.rs
@@ -4,22 +4,25 @@
 
 use rustc_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
 use rustc_middle::ty;
+use rustc_smir::Tables;
+use rustc_smir::context::SmirCtxt;
 use rustc_target::callconv;
 use stable_mir::abi::{
     AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength,
     IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar,
     TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange,
 };
-use stable_mir::opaque;
+use stable_mir::compiler_interface::BridgeTys;
 use stable_mir::target::MachineSize as Size;
-use stable_mir::ty::{Align, IndexedVal, VariantIdx};
+use stable_mir::ty::{Align, VariantIdx};
+use stable_mir::unstable::Stable;
+use stable_mir::{IndexedVal, opaque};
 
-use crate::rustc_smir::{Stable, Tables};
-use crate::stable_mir;
+use crate::{rustc_smir, stable_mir};
 
 impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx {
     type T = VariantIdx;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         VariantIdx::to_val(self.as_usize())
     }
 }
@@ -27,7 +30,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx {
 impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
     type T = stable_mir::target::Endian;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             rustc_abi::Endian::Little => stable_mir::target::Endian::Little,
             rustc_abi::Endian::Big => stable_mir::target::Endian::Big,
@@ -38,29 +41,41 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
 impl<'tcx> Stable<'tcx> for rustc_abi::TyAndLayout<'tcx, ty::Ty<'tcx>> {
     type T = TyAndLayout;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        TyAndLayout { ty: self.ty.stable(tables), layout: self.layout.stable(tables) }
+    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(&self, tables: &mut Tables<'_>) -> Self::T {
-        tables.layout_id(tables.tcx.lift(*self).unwrap())
+    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(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         LayoutShape {
-            fields: self.fields.stable(tables),
-            variants: self.variants.stable(tables),
-            abi: self.backend_repr.stable(tables),
-            abi_align: self.align.abi.stable(tables),
-            size: self.size.stable(tables),
+            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),
         }
     }
 }
@@ -68,14 +83,18 @@ impl<'tcx> Stable<'tcx> for rustc_abi::LayoutData<rustc_abi::FieldIdx, rustc_abi
 impl<'tcx> Stable<'tcx> for callconv::FnAbi<'tcx, ty::Ty<'tcx>> {
     type T = FnAbi;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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),
-            ret: self.ret.stable(tables),
+            args: self.args.as_ref().stable(tables, cx),
+            ret: self.ret.stable(tables, cx),
             fixed_count: self.fixed_count,
-            conv: self.conv.stable(tables),
+            conv: self.conv.stable(tables, cx),
             c_variadic: self.c_variadic,
         }
     }
@@ -84,11 +103,15 @@ impl<'tcx> Stable<'tcx> for callconv::FnAbi<'tcx, ty::Ty<'tcx>> {
 impl<'tcx> Stable<'tcx> for callconv::ArgAbi<'tcx, ty::Ty<'tcx>> {
     type T = ArgAbi;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         ArgAbi {
-            ty: self.layout.ty.stable(tables),
-            layout: self.layout.layout.stable(tables),
-            mode: self.mode.stable(tables),
+            ty: self.layout.ty.stable(tables, cx),
+            layout: self.layout.layout.stable(tables, cx),
+            mode: self.mode.stable(tables, cx),
         }
     }
 }
@@ -96,7 +119,7 @@ impl<'tcx> Stable<'tcx> for callconv::ArgAbi<'tcx, ty::Ty<'tcx>> {
 impl<'tcx> Stable<'tcx> for CanonAbi {
     type T = CallConvention;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             CanonAbi::C => CallConvention::C,
             CanonAbi::Rust => CallConvention::Rust,
@@ -132,7 +155,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi {
 impl<'tcx> Stable<'tcx> for callconv::PassMode {
     type T = PassMode;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    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)),
@@ -154,15 +177,19 @@ impl<'tcx> Stable<'tcx> for callconv::PassMode {
 impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape<rustc_abi::FieldIdx> {
     type T = FieldsShape;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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), count: *count }
+                FieldsShape::Array { stride: stride.stable(tables, cx), count: *count }
             }
             rustc_abi::FieldsShape::Arbitrary { offsets, .. } => {
-                FieldsShape::Arbitrary { offsets: offsets.iter().as_slice().stable(tables) }
+                FieldsShape::Arbitrary { offsets: offsets.iter().as_slice().stable(tables, cx) }
             }
         }
     }
@@ -171,18 +198,22 @@ impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape<rustc_abi::FieldIdx> {
 impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi::VariantIdx> {
     type T = VariantsShape;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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) }
+                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),
-                    tag_encoding: tag_encoding.stable(tables),
-                    tag_field: tag_field.stable(tables),
-                    variants: variants.iter().as_slice().stable(tables),
+                    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),
                 }
             }
         }
@@ -192,13 +223,17 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi::
 impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding<rustc_abi::VariantIdx> {
     type T = TagEncoding;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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),
-                    niche_variants: niche_variants.stable(tables),
+                    untagged_variant: untagged_variant.stable(tables, cx),
+                    niche_variants: niche_variants.stable(tables, cx),
                     niche_start: *niche_start,
                 }
             }
@@ -209,14 +244,18 @@ impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding<rustc_abi::VariantIdx> {
 impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr {
     type T = ValueAbi;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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)),
+            rustc_abi::BackendRepr::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables, cx)),
             rustc_abi::BackendRepr::ScalarPair(first, second) => {
-                ValueAbi::ScalarPair(first.stable(tables), second.stable(tables))
+                ValueAbi::ScalarPair(first.stable(tables, cx), second.stable(tables, cx))
             }
             rustc_abi::BackendRepr::SimdVector { element, count } => {
-                ValueAbi::Vector { element: element.stable(tables), count }
+                ValueAbi::Vector { element: element.stable(tables, cx), count }
             }
             rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized },
         }
@@ -226,7 +265,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr {
 impl<'tcx> Stable<'tcx> for rustc_abi::Size {
     type T = Size;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         Size::from_bits(self.bits_usize())
     }
 }
@@ -234,7 +273,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Size {
 impl<'tcx> Stable<'tcx> for rustc_abi::Align {
     type T = Align;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         self.bytes()
     }
 }
@@ -242,13 +281,17 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Align {
 impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
     type T = Scalar;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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),
-                valid_range: valid_range.stable(tables),
+                value: value.stable(tables, cx),
+                valid_range: valid_range.stable(tables, cx),
             },
-            rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables) },
+            rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables, cx) },
         }
     }
 }
@@ -256,15 +299,19 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
 impl<'tcx> Stable<'tcx> for rustc_abi::Primitive {
     type T = Primitive;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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), signed: *signed }
+                Primitive::Int { length: length.stable(tables, cx), signed: *signed }
             }
             rustc_abi::Primitive::Float(length) => {
-                Primitive::Float { length: length.stable(tables) }
+                Primitive::Float { length: length.stable(tables, cx) }
             }
-            rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables)),
+            rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables, cx)),
         }
     }
 }
@@ -272,7 +319,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Primitive {
 impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace {
     type T = AddressSpace;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         AddressSpace(self.0)
     }
 }
@@ -280,7 +327,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace {
 impl<'tcx> Stable<'tcx> for rustc_abi::Integer {
     type T = IntegerLength;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             rustc_abi::Integer::I8 => IntegerLength::I8,
             rustc_abi::Integer::I16 => IntegerLength::I16,
@@ -294,7 +341,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Integer {
 impl<'tcx> Stable<'tcx> for rustc_abi::Float {
     type T = FloatLength;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             rustc_abi::Float::F16 => FloatLength::F16,
             rustc_abi::Float::F32 => FloatLength::F32,
@@ -307,7 +354,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Float {
 impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange {
     type T = WrappingRange;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         WrappingRange { start: self.start, end: self.end }
     }
 }
@@ -315,7 +362,11 @@ impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange {
 impl<'tcx> Stable<'tcx> for rustc_abi::ReprFlags {
     type T = ReprFlags;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    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),
@@ -328,11 +379,15 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ReprFlags {
 impl<'tcx> Stable<'tcx> for rustc_abi::IntegerType {
     type T = IntegerType;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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), is_signed: *signed }
+                IntegerType::Fixed { length: integer.stable(tables, cx), is_signed: *signed }
             }
         }
     }
@@ -341,12 +396,16 @@ impl<'tcx> Stable<'tcx> for rustc_abi::IntegerType {
 impl<'tcx> Stable<'tcx> for rustc_abi::ReprOptions {
     type T = ReprOptions;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         ReprOptions {
-            int: self.int.map(|int| int.stable(tables)),
-            align: self.align.map(|align| align.stable(tables)),
-            pack: self.pack.map(|pack| pack.stable(tables)),
-            flags: self.flags.stable(tables),
+            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_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/mir.rs
similarity index 68%
rename from compiler/rustc_smir/src/rustc_smir/convert/mir.rs
rename to compiler/rustc_smir/src/stable_mir/unstable/convert/stable/mir.rs
index daea4acc36c0c..99f9f456567cb 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/mir.rs
@@ -1,56 +1,67 @@
 //! Conversion of internal Rust compiler `mir` items to stable ones.
 
-use rustc_middle::mir::interpret::alloc_range;
 use rustc_middle::mir::mono::MonoItem;
 use rustc_middle::{bug, mir};
+use rustc_smir::Tables;
+use rustc_smir::bridge::SmirError;
+use rustc_smir::context::SmirCtxt;
+use stable_mir::compiler_interface::BridgeTys;
 use stable_mir::mir::alloc::GlobalAlloc;
 use stable_mir::mir::{ConstOperand, Statement, UserTypeProjection, VarDebugInfoFragment};
 use stable_mir::ty::{Allocation, ConstantKind, MirConst};
-use stable_mir::{Error, opaque};
+use stable_mir::unstable::Stable;
+use stable_mir::{Error, alloc, opaque};
 
-use crate::rustc_smir::{Stable, Tables, alloc};
-use crate::stable_mir;
+use crate::{rustc_smir, stable_mir};
 
 impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
     type T = stable_mir::mir::Body;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::mir::Body::new(
             self.basic_blocks
                 .iter()
                 .map(|block| stable_mir::mir::BasicBlock {
-                    terminator: block.terminator().stable(tables),
+                    terminator: block.terminator().stable(tables, cx),
                     statements: block
                         .statements
                         .iter()
-                        .map(|statement| statement.stable(tables))
+                        .map(|statement| statement.stable(tables, cx))
                         .collect(),
                 })
                 .collect(),
             self.local_decls
                 .iter()
                 .map(|decl| stable_mir::mir::LocalDecl {
-                    ty: decl.ty.stable(tables),
-                    span: decl.source_info.span.stable(tables),
-                    mutability: decl.mutability.stable(tables),
+                    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)).collect(),
-            self.spread_arg.stable(tables),
-            self.span.stable(tables),
+            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 = stable_mir::mir::VarDebugInfo;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::mir::VarDebugInfo {
             name: self.name.to_string(),
-            source_info: self.source_info.stable(tables),
-            composite: self.composite.as_ref().map(|composite| composite.stable(tables)),
-            value: self.value.stable(tables),
+            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,
         }
     }
@@ -58,40 +69,59 @@ impl<'tcx> Stable<'tcx> for mir::VarDebugInfo<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> {
     type T = stable_mir::mir::Statement;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        Statement { kind: self.kind.stable(tables), span: self.source_info.span.stable(tables) }
+    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 = stable_mir::mir::SourceInfo;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        stable_mir::mir::SourceInfo { span: self.span.stable(tables), scope: self.scope.into() }
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        stable_mir::mir::SourceInfo { span: self.span.stable(tables, cx), scope: self.scope.into() }
     }
 }
 
 impl<'tcx> Stable<'tcx> for mir::VarDebugInfoFragment<'tcx> {
     type T = stable_mir::mir::VarDebugInfoFragment;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         VarDebugInfoFragment {
-            ty: self.ty.stable(tables),
-            projection: self.projection.iter().map(|e| e.stable(tables)).collect(),
+            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 = stable_mir::mir::VarDebugInfoContents;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         match self {
             mir::VarDebugInfoContents::Place(place) => {
-                stable_mir::mir::VarDebugInfoContents::Place(place.stable(tables))
+                stable_mir::mir::VarDebugInfoContents::Place(place.stable(tables, cx))
             }
             mir::VarDebugInfoContents::Const(const_operand) => {
                 let op = ConstOperand {
-                    span: const_operand.span.stable(tables),
+                    span: const_operand.span.stable(tables, cx),
                     user_ty: const_operand.user_ty.map(|index| index.as_usize()),
-                    const_: const_operand.const_.stable(tables),
+                    const_: const_operand.const_.stable(tables, cx),
                 };
                 stable_mir::mir::VarDebugInfoContents::Const(op)
             }
@@ -101,53 +131,58 @@ impl<'tcx> Stable<'tcx> for mir::VarDebugInfoContents<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::StatementKind<'tcx> {
     type T = stable_mir::mir::StatementKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         match self {
             mir::StatementKind::Assign(assign) => stable_mir::mir::StatementKind::Assign(
-                assign.0.stable(tables),
-                assign.1.stable(tables),
+                assign.0.stable(tables, cx),
+                assign.1.stable(tables, cx),
             ),
             mir::StatementKind::FakeRead(fake_read_place) => {
                 stable_mir::mir::StatementKind::FakeRead(
-                    fake_read_place.0.stable(tables),
-                    fake_read_place.1.stable(tables),
+                    fake_read_place.0.stable(tables, cx),
+                    fake_read_place.1.stable(tables, cx),
                 )
             }
             mir::StatementKind::SetDiscriminant { place, variant_index } => {
                 stable_mir::mir::StatementKind::SetDiscriminant {
-                    place: place.as_ref().stable(tables),
-                    variant_index: variant_index.stable(tables),
+                    place: place.as_ref().stable(tables, cx),
+                    variant_index: variant_index.stable(tables, cx),
                 }
             }
             mir::StatementKind::Deinit(place) => {
-                stable_mir::mir::StatementKind::Deinit(place.stable(tables))
+                stable_mir::mir::StatementKind::Deinit(place.stable(tables, cx))
             }
 
             mir::StatementKind::StorageLive(place) => {
-                stable_mir::mir::StatementKind::StorageLive(place.stable(tables))
+                stable_mir::mir::StatementKind::StorageLive(place.stable(tables, cx))
             }
 
             mir::StatementKind::StorageDead(place) => {
-                stable_mir::mir::StatementKind::StorageDead(place.stable(tables))
-            }
-            mir::StatementKind::Retag(retag, place) => {
-                stable_mir::mir::StatementKind::Retag(retag.stable(tables), place.stable(tables))
+                stable_mir::mir::StatementKind::StorageDead(place.stable(tables, cx))
             }
+            mir::StatementKind::Retag(retag, place) => stable_mir::mir::StatementKind::Retag(
+                retag.stable(tables, cx),
+                place.stable(tables, cx),
+            ),
             mir::StatementKind::PlaceMention(place) => {
-                stable_mir::mir::StatementKind::PlaceMention(place.stable(tables))
+                stable_mir::mir::StatementKind::PlaceMention(place.stable(tables, cx))
             }
             mir::StatementKind::AscribeUserType(place_projection, variance) => {
                 stable_mir::mir::StatementKind::AscribeUserType {
-                    place: place_projection.as_ref().0.stable(tables),
-                    projections: place_projection.as_ref().1.stable(tables),
-                    variance: variance.stable(tables),
+                    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) => {
                 stable_mir::mir::StatementKind::Coverage(opaque(coverage))
             }
             mir::StatementKind::Intrinsic(intrinstic) => {
-                stable_mir::mir::StatementKind::Intrinsic(intrinstic.stable(tables))
+                stable_mir::mir::StatementKind::Intrinsic(intrinstic.stable(tables, cx))
             }
             mir::StatementKind::ConstEvalCounter => {
                 stable_mir::mir::StatementKind::ConstEvalCounter
@@ -163,61 +198,68 @@ impl<'tcx> Stable<'tcx> for mir::StatementKind<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
     type T = stable_mir::mir::Rvalue;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::Rvalue::*;
         match self {
-            Use(op) => stable_mir::mir::Rvalue::Use(op.stable(tables)),
+            Use(op) => stable_mir::mir::Rvalue::Use(op.stable(tables, cx)),
             Repeat(op, len) => {
-                let len = len.stable(tables);
-                stable_mir::mir::Rvalue::Repeat(op.stable(tables), len)
+                let len = len.stable(tables, cx);
+                stable_mir::mir::Rvalue::Repeat(op.stable(tables, cx), len)
             }
             Ref(region, kind, place) => stable_mir::mir::Rvalue::Ref(
-                region.stable(tables),
-                kind.stable(tables),
-                place.stable(tables),
+                region.stable(tables, cx),
+                kind.stable(tables, cx),
+                place.stable(tables, cx),
             ),
             ThreadLocalRef(def_id) => {
                 stable_mir::mir::Rvalue::ThreadLocalRef(tables.crate_item(*def_id))
             }
-            RawPtr(mutability, place) => {
-                stable_mir::mir::Rvalue::AddressOf(mutability.stable(tables), place.stable(tables))
-            }
-            Len(place) => stable_mir::mir::Rvalue::Len(place.stable(tables)),
+            RawPtr(mutability, place) => stable_mir::mir::Rvalue::AddressOf(
+                mutability.stable(tables, cx),
+                place.stable(tables, cx),
+            ),
+            Len(place) => stable_mir::mir::Rvalue::Len(place.stable(tables, cx)),
             Cast(cast_kind, op, ty) => stable_mir::mir::Rvalue::Cast(
-                cast_kind.stable(tables),
-                op.stable(tables),
-                ty.stable(tables),
+                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() {
                     stable_mir::mir::Rvalue::CheckedBinaryOp(
-                        bin_op.stable(tables),
-                        ops.0.stable(tables),
-                        ops.1.stable(tables),
+                        bin_op.stable(tables, cx),
+                        ops.0.stable(tables, cx),
+                        ops.1.stable(tables, cx),
                     )
                 } else {
                     stable_mir::mir::Rvalue::BinaryOp(
-                        bin_op.stable(tables),
-                        ops.0.stable(tables),
-                        ops.1.stable(tables),
+                        bin_op.stable(tables, cx),
+                        ops.0.stable(tables, cx),
+                        ops.1.stable(tables, cx),
                     )
                 }
             }
-            NullaryOp(null_op, ty) => {
-                stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), ty.stable(tables))
-            }
+            NullaryOp(null_op, ty) => stable_mir::mir::Rvalue::NullaryOp(
+                null_op.stable(tables, cx),
+                ty.stable(tables, cx),
+            ),
             UnaryOp(un_op, op) => {
-                stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables))
+                stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables, cx), op.stable(tables, cx))
             }
-            Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable(tables)),
+            Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable(tables, cx)),
             Aggregate(agg_kind, operands) => {
-                let operands = operands.iter().map(|op| op.stable(tables)).collect();
-                stable_mir::mir::Rvalue::Aggregate(agg_kind.stable(tables), operands)
+                let operands = operands.iter().map(|op| op.stable(tables, cx)).collect();
+                stable_mir::mir::Rvalue::Aggregate(agg_kind.stable(tables, cx), operands)
             }
-            ShallowInitBox(op, ty) => {
-                stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), ty.stable(tables))
-            }
-            CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)),
+            ShallowInitBox(op, ty) => stable_mir::mir::Rvalue::ShallowInitBox(
+                op.stable(tables, cx),
+                ty.stable(tables, cx),
+            ),
+            CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables, cx)),
             WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
         }
     }
@@ -225,7 +267,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::Mutability {
     type T = stable_mir::mir::Mutability;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_hir::Mutability::*;
         match *self {
             Not => stable_mir::mir::Mutability::Not,
@@ -236,7 +278,7 @@ impl<'tcx> Stable<'tcx> for mir::Mutability {
 
 impl<'tcx> Stable<'tcx> for mir::RawPtrKind {
     type T = stable_mir::mir::RawPtrKind;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use mir::RawPtrKind::*;
         match *self {
             Const => stable_mir::mir::RawPtrKind::Const,
@@ -248,19 +290,23 @@ impl<'tcx> Stable<'tcx> for mir::RawPtrKind {
 
 impl<'tcx> Stable<'tcx> for mir::BorrowKind {
     type T = stable_mir::mir::BorrowKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::BorrowKind::*;
         match *self {
             Shared => stable_mir::mir::BorrowKind::Shared,
-            Fake(kind) => stable_mir::mir::BorrowKind::Fake(kind.stable(tables)),
-            Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) },
+            Fake(kind) => stable_mir::mir::BorrowKind::Fake(kind.stable(tables, cx)),
+            Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables, cx) },
         }
     }
 }
 
 impl<'tcx> Stable<'tcx> for mir::MutBorrowKind {
     type T = stable_mir::mir::MutBorrowKind;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::MutBorrowKind::*;
         match *self {
             Default => stable_mir::mir::MutBorrowKind::Default,
@@ -272,7 +318,7 @@ impl<'tcx> Stable<'tcx> for mir::MutBorrowKind {
 
 impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
     type T = stable_mir::mir::FakeBorrowKind;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::FakeBorrowKind::*;
         match *self {
             Deep => stable_mir::mir::FakeBorrowKind::Deep,
@@ -283,13 +329,17 @@ impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
 
 impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
     type T = stable_mir::mir::NullOp;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::NullOp::*;
         match self {
             SizeOf => stable_mir::mir::NullOp::SizeOf,
             AlignOf => stable_mir::mir::NullOp::AlignOf,
             OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf(
-                indices.iter().map(|idx| idx.stable(tables)).collect(),
+                indices.iter().map(|idx| idx.stable(tables, cx)).collect(),
             ),
             UbChecks => stable_mir::mir::NullOp::UbChecks,
             ContractChecks => stable_mir::mir::NullOp::ContractChecks,
@@ -299,12 +349,18 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::CastKind {
     type T = stable_mir::mir::CastKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::CastKind::*;
         match self {
             PointerExposeProvenance => stable_mir::mir::CastKind::PointerExposeAddress,
             PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance,
-            PointerCoercion(c, _) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)),
+            PointerCoercion(c, _) => {
+                stable_mir::mir::CastKind::PointerCoercion(c.stable(tables, cx))
+            }
             IntToInt => stable_mir::mir::CastKind::IntToInt,
             FloatToInt => stable_mir::mir::CastKind::FloatToInt,
             FloatToFloat => stable_mir::mir::CastKind::FloatToFloat,
@@ -318,7 +374,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
 
 impl<'tcx> Stable<'tcx> for mir::FakeReadCause {
     type T = stable_mir::mir::FakeReadCause;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::FakeReadCause::*;
         match self {
             ForMatchGuard => stable_mir::mir::FakeReadCause::ForMatchGuard,
@@ -334,12 +390,16 @@ impl<'tcx> Stable<'tcx> for mir::FakeReadCause {
 
 impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
     type T = stable_mir::mir::Operand;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::Operand::*;
         match self {
-            Copy(place) => stable_mir::mir::Operand::Copy(place.stable(tables)),
-            Move(place) => stable_mir::mir::Operand::Move(place.stable(tables)),
-            Constant(c) => stable_mir::mir::Operand::Constant(c.stable(tables)),
+            Copy(place) => stable_mir::mir::Operand::Copy(place.stable(tables, cx)),
+            Move(place) => stable_mir::mir::Operand::Move(place.stable(tables, cx)),
+            Constant(c) => stable_mir::mir::Operand::Constant(c.stable(tables, cx)),
         }
     }
 }
@@ -347,35 +407,48 @@ impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
 impl<'tcx> Stable<'tcx> for mir::ConstOperand<'tcx> {
     type T = stable_mir::mir::ConstOperand;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::mir::ConstOperand {
-            span: self.span.stable(tables),
+            span: self.span.stable(tables, cx),
             user_ty: self.user_ty.map(|u| u.as_usize()).or(None),
-            const_: self.const_.stable(tables),
+            const_: self.const_.stable(tables, cx),
         }
     }
 }
 
 impl<'tcx> Stable<'tcx> for mir::Place<'tcx> {
     type T = stable_mir::mir::Place;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::mir::Place {
             local: self.local.as_usize(),
-            projection: self.projection.iter().map(|e| e.stable(tables)).collect(),
+            projection: self.projection.iter().map(|e| e.stable(tables, cx)).collect(),
         }
     }
 }
 
 impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
     type T = stable_mir::mir::ProjectionElem;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::ProjectionElem::*;
         match self {
             Deref => stable_mir::mir::ProjectionElem::Deref,
-            Field(idx, ty) => {
-                stable_mir::mir::ProjectionElem::Field(idx.stable(tables), ty.stable(tables))
-            }
-            Index(local) => stable_mir::mir::ProjectionElem::Index(local.stable(tables)),
+            Field(idx, ty) => stable_mir::mir::ProjectionElem::Field(
+                idx.stable(tables, cx),
+                ty.stable(tables, cx),
+            ),
+            Index(local) => stable_mir::mir::ProjectionElem::Index(local.stable(tables, cx)),
             ConstantIndex { offset, min_length, from_end } => {
                 stable_mir::mir::ProjectionElem::ConstantIndex {
                     offset: *offset,
@@ -393,9 +466,9 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
             // 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) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables)),
-            OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables)),
-            Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables)),
+            Downcast(_, idx) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables, cx)),
+            OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables, cx)),
+            Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables, cx)),
             UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
         }
     }
@@ -404,21 +477,21 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
 impl<'tcx> Stable<'tcx> for mir::UserTypeProjection {
     type T = stable_mir::mir::UserTypeProjection;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    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 = stable_mir::mir::Local;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         self.as_usize()
     }
 }
 
 impl<'tcx> Stable<'tcx> for mir::RetagKind {
     type T = stable_mir::mir::RetagKind;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::RetagKind;
         match self {
             RetagKind::FnEntry => stable_mir::mir::RetagKind::FnEntry,
@@ -431,7 +504,7 @@ impl<'tcx> Stable<'tcx> for mir::RetagKind {
 
 impl<'tcx> Stable<'tcx> for mir::UnwindAction {
     type T = stable_mir::mir::UnwindAction;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::UnwindAction;
         match self {
             UnwindAction::Continue => stable_mir::mir::UnwindAction::Continue,
@@ -445,18 +518,22 @@ impl<'tcx> Stable<'tcx> for mir::UnwindAction {
 impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> {
     type T = stable_mir::mir::NonDivergingIntrinsic;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use rustc_middle::mir::NonDivergingIntrinsic;
         use stable_mir::mir::CopyNonOverlapping;
         match self {
             NonDivergingIntrinsic::Assume(op) => {
-                stable_mir::mir::NonDivergingIntrinsic::Assume(op.stable(tables))
+                stable_mir::mir::NonDivergingIntrinsic::Assume(op.stable(tables, cx))
             }
             NonDivergingIntrinsic::CopyNonOverlapping(copy_non_overlapping) => {
                 stable_mir::mir::NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
-                    src: copy_non_overlapping.src.stable(tables),
-                    dst: copy_non_overlapping.dst.stable(tables),
-                    count: copy_non_overlapping.count.stable(tables),
+                    src: copy_non_overlapping.src.stable(tables, cx),
+                    dst: copy_non_overlapping.dst.stable(tables, cx),
+                    count: copy_non_overlapping.count.stable(tables, cx),
                 })
             }
         }
@@ -465,47 +542,51 @@ impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
     type T = stable_mir::mir::AssertMessage;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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 } => stable_mir::mir::AssertMessage::BoundsCheck {
-                len: len.stable(tables),
-                index: index.stable(tables),
+                len: len.stable(tables, cx),
+                index: index.stable(tables, cx),
             },
             AssertKind::Overflow(bin_op, op1, op2) => stable_mir::mir::AssertMessage::Overflow(
-                bin_op.stable(tables),
-                op1.stable(tables),
-                op2.stable(tables),
+                bin_op.stable(tables, cx),
+                op1.stable(tables, cx),
+                op2.stable(tables, cx),
             ),
             AssertKind::OverflowNeg(op) => {
-                stable_mir::mir::AssertMessage::OverflowNeg(op.stable(tables))
+                stable_mir::mir::AssertMessage::OverflowNeg(op.stable(tables, cx))
             }
             AssertKind::DivisionByZero(op) => {
-                stable_mir::mir::AssertMessage::DivisionByZero(op.stable(tables))
+                stable_mir::mir::AssertMessage::DivisionByZero(op.stable(tables, cx))
             }
             AssertKind::RemainderByZero(op) => {
-                stable_mir::mir::AssertMessage::RemainderByZero(op.stable(tables))
+                stable_mir::mir::AssertMessage::RemainderByZero(op.stable(tables, cx))
             }
             AssertKind::ResumedAfterReturn(coroutine) => {
-                stable_mir::mir::AssertMessage::ResumedAfterReturn(coroutine.stable(tables))
+                stable_mir::mir::AssertMessage::ResumedAfterReturn(coroutine.stable(tables, cx))
             }
             AssertKind::ResumedAfterPanic(coroutine) => {
-                stable_mir::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables))
+                stable_mir::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables, cx))
             }
             AssertKind::ResumedAfterDrop(coroutine) => {
-                stable_mir::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables))
+                stable_mir::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables, cx))
             }
             AssertKind::MisalignedPointerDereference { required, found } => {
                 stable_mir::mir::AssertMessage::MisalignedPointerDereference {
-                    required: required.stable(tables),
-                    found: found.stable(tables),
+                    required: required.stable(tables, cx),
+                    found: found.stable(tables, cx),
                 }
             }
             AssertKind::NullPointerDereference => {
                 stable_mir::mir::AssertMessage::NullPointerDereference
             }
             AssertKind::InvalidEnumConstruction(source) => {
-                stable_mir::mir::AssertMessage::InvalidEnumConstruction(source.stable(tables))
+                stable_mir::mir::AssertMessage::InvalidEnumConstruction(source.stable(tables, cx))
             }
         }
     }
@@ -513,7 +594,7 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::BinOp {
     type T = stable_mir::mir::BinOp;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::BinOp;
         match self {
             BinOp::Add => stable_mir::mir::BinOp::Add,
@@ -548,7 +629,7 @@ impl<'tcx> Stable<'tcx> for mir::BinOp {
 
 impl<'tcx> Stable<'tcx> for mir::UnOp {
     type T = stable_mir::mir::UnOp;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::mir::UnOp;
         match self {
             UnOp::Not => stable_mir::mir::UnOp::Not,
@@ -560,17 +641,21 @@ impl<'tcx> Stable<'tcx> for mir::UnOp {
 
 impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
     type T = stable_mir::mir::AggregateKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         match self {
             mir::AggregateKind::Array(ty) => {
-                stable_mir::mir::AggregateKind::Array(ty.stable(tables))
+                stable_mir::mir::AggregateKind::Array(ty.stable(tables, cx))
             }
             mir::AggregateKind::Tuple => stable_mir::mir::AggregateKind::Tuple,
             mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => {
                 stable_mir::mir::AggregateKind::Adt(
                     tables.adt_def(*def_id),
-                    var_idx.stable(tables),
-                    generic_arg.stable(tables),
+                    var_idx.stable(tables, cx),
+                    generic_arg.stable(tables, cx),
                     user_ty_index.map(|idx| idx.index()),
                     field_idx.map(|idx| idx.index()),
                 )
@@ -578,39 +663,46 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
             mir::AggregateKind::Closure(def_id, generic_arg) => {
                 stable_mir::mir::AggregateKind::Closure(
                     tables.closure_def(*def_id),
-                    generic_arg.stable(tables),
+                    generic_arg.stable(tables, cx),
                 )
             }
             mir::AggregateKind::Coroutine(def_id, generic_arg) => {
                 stable_mir::mir::AggregateKind::Coroutine(
                     tables.coroutine_def(*def_id),
-                    generic_arg.stable(tables),
-                    tables.tcx.coroutine_movability(*def_id).stable(tables),
+                    generic_arg.stable(tables, cx),
+                    cx.coroutine_movability(*def_id).stable(tables, cx),
                 )
             }
             mir::AggregateKind::CoroutineClosure(def_id, generic_args) => {
                 stable_mir::mir::AggregateKind::CoroutineClosure(
                     tables.coroutine_closure_def(*def_id),
-                    generic_args.stable(tables),
+                    generic_args.stable(tables, cx),
                 )
             }
-            mir::AggregateKind::RawPtr(ty, mutability) => {
-                stable_mir::mir::AggregateKind::RawPtr(ty.stable(tables), mutability.stable(tables))
-            }
+            mir::AggregateKind::RawPtr(ty, mutability) => stable_mir::mir::AggregateKind::RawPtr(
+                ty.stable(tables, cx),
+                mutability.stable(tables, cx),
+            ),
         }
     }
 }
 
 impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> {
     type T = stable_mir::mir::InlineAsmOperand;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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)), None),
-            InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable(tables))),
+            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)), out_place.map(|place| place.stable(tables)))
+                (Some(in_value.stable(tables, cx)), out_place.map(|place| place.stable(tables, cx)))
             }
             InlineAsmOperand::Const { .. }
             | InlineAsmOperand::SymFn { .. }
@@ -624,22 +716,33 @@ impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> {
 
 impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> {
     type T = stable_mir::mir::Terminator;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::mir::Terminator;
-        Terminator { kind: self.kind.stable(tables), span: self.source_info.span.stable(tables) }
+        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 = stable_mir::mir::TerminatorKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::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),
+                discr: discr.stable(tables, cx),
                 targets: {
                     let branches = targets.iter().map(|(val, target)| (val, target.as_usize()));
                     stable_mir::mir::SwitchTargets::new(
@@ -660,9 +763,9 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
                 drop: _,
                 async_fut: _,
             } => TerminatorKind::Drop {
-                place: place.stable(tables),
+                place: place.stable(tables, cx),
                 target: target.as_usize(),
-                unwind: unwind.stable(tables),
+                unwind: unwind.stable(tables, cx),
             },
             mir::TerminatorKind::Call {
                 func,
@@ -673,20 +776,20 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
                 call_source: _,
                 fn_span: _,
             } => TerminatorKind::Call {
-                func: func.stable(tables),
-                args: args.iter().map(|arg| arg.node.stable(tables)).collect(),
-                destination: destination.stable(tables),
+                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),
+                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),
+                    cond: cond.stable(tables, cx),
                     expected: *expected,
-                    msg: msg.stable(tables),
+                    msg: msg.stable(tables, cx),
                     target: target.as_usize(),
-                    unwind: unwind.stable(tables),
+                    unwind: unwind.stable(tables, cx),
                 }
             }
             mir::TerminatorKind::InlineAsm {
@@ -699,12 +802,12 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
                 unwind,
             } => TerminatorKind::InlineAsm {
                 template: format!("{template:?}"),
-                operands: operands.iter().map(|operand| operand.stable(tables)).collect(),
+                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),
+                unwind: unwind.stable(tables, cx),
             },
             mir::TerminatorKind::Yield { .. }
             | mir::TerminatorKind::CoroutineDrop
@@ -717,22 +820,40 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
 impl<'tcx> Stable<'tcx> for mir::interpret::ConstAllocation<'tcx> {
     type T = Allocation;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        self.inner().stable(tables)
+    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 = stable_mir::ty::Allocation;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        alloc::allocation_filter(self, alloc_range(rustc_abi::Size::ZERO, self.size()), tables)
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        use rustc_smir::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 = stable_mir::mir::alloc::AllocId;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         tables.create_alloc_id(*self)
     }
 }
@@ -740,19 +861,25 @@ impl<'tcx> Stable<'tcx> for mir::interpret::AllocId {
 impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> {
     type T = GlobalAlloc;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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))
+                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), dyn_ty.principal().stable(tables))
+                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)),
+            mir::interpret::GlobalAlloc::Memory(alloc) => {
+                GlobalAlloc::Memory(alloc.stable(tables, cx))
+            }
         }
     }
 }
@@ -760,33 +887,37 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> {
 impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> {
     type T = stable_mir::ty::MirConst;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        let id = tables.intern_mir_const(tables.tcx.lift(*self).unwrap());
+    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(
-                stable_mir::ty::ConstantKind::Ty(c.stable(tables)),
-                ty.stable(tables),
+                stable_mir::ty::ConstantKind::Ty(c.stable(tables, cx)),
+                ty.stable(tables, cx),
                 id,
             ),
             mir::Const::Unevaluated(unev_const, ty) => {
                 let kind =
                     stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst {
                         def: tables.const_def(unev_const.def),
-                        args: unev_const.args.stable(tables),
+                        args: unev_const.args.stable(tables, cx),
                         promoted: unev_const.promoted.map(|u| u.as_u32()),
                     });
-                let ty = ty.stable(tables);
+                let ty = ty.stable(tables, cx);
                 MirConst::new(kind, ty, id)
             }
             mir::Const::Val(mir::ConstValue::ZeroSized, ty) => {
-                let ty = ty.stable(tables);
+                let ty = ty.stable(tables, cx);
                 MirConst::new(ConstantKind::ZeroSized, ty, id)
             }
             mir::Const::Val(val, ty) => {
-                let ty = tables.tcx.lift(ty).unwrap();
-                let val = tables.tcx.lift(val).unwrap();
-                let kind = ConstantKind::Allocated(alloc::new_allocation(ty, val, tables));
-                let ty = ty.stable(tables);
+                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)
             }
         }
@@ -796,7 +927,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> {
 impl<'tcx> Stable<'tcx> for mir::interpret::ErrorHandled {
     type T = Error;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         Error::new(format!("{self:?}"))
     }
 }
@@ -804,10 +935,14 @@ impl<'tcx> Stable<'tcx> for mir::interpret::ErrorHandled {
 impl<'tcx> Stable<'tcx> for MonoItem<'tcx> {
     type T = stable_mir::mir::mono::MonoItem;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::mir::mono::MonoItem as StableMonoItem;
         match self {
-            MonoItem::Fn(instance) => StableMonoItem::Fn(instance.stable(tables)),
+            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_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/mod.rs
similarity index 68%
rename from compiler/rustc_smir/src/rustc_smir/convert/mod.rs
rename to compiler/rustc_smir/src/stable_mir/unstable/convert/stable/mod.rs
index 3494de62d835b..799917c6e171b 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/mod.rs
@@ -1,18 +1,20 @@
 //! Conversion of internal Rust compiler items to stable ones.
 
 use rustc_abi::FieldIdx;
+use rustc_smir::Tables;
+use rustc_smir::context::SmirCtxt;
+use stable_mir::compiler_interface::BridgeTys;
 
-use crate::rustc_smir::{Stable, Tables};
-use crate::stable_mir;
+use super::Stable;
+use crate::{rustc_smir, stable_mir};
 
 mod abi;
-mod error;
 mod mir;
 mod ty;
 
 impl<'tcx> Stable<'tcx> for rustc_hir::Safety {
     type T = stable_mir::mir::Safety;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             rustc_hir::Safety::Unsafe => stable_mir::mir::Safety::Unsafe,
             rustc_hir::Safety::Safe => stable_mir::mir::Safety::Safe,
@@ -22,14 +24,14 @@ impl<'tcx> Stable<'tcx> for rustc_hir::Safety {
 
 impl<'tcx> Stable<'tcx> for FieldIdx {
     type T = usize;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         self.as_usize()
     }
 }
 
 impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
     type T = stable_mir::mir::CoroutineSource;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_hir::CoroutineSource;
         match self {
             CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block,
@@ -41,28 +43,32 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
 
 impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
     type T = stable_mir::mir::CoroutineKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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) => {
                 stable_mir::mir::CoroutineKind::Desugared(
                     stable_mir::mir::CoroutineDesugaring::Async,
-                    source.stable(tables),
+                    source.stable(tables, cx),
                 )
             }
             CoroutineKind::Desugared(CoroutineDesugaring::Gen, source) => {
                 stable_mir::mir::CoroutineKind::Desugared(
                     stable_mir::mir::CoroutineDesugaring::Gen,
-                    source.stable(tables),
+                    source.stable(tables, cx),
                 )
             }
             CoroutineKind::Coroutine(movability) => {
-                stable_mir::mir::CoroutineKind::Coroutine(movability.stable(tables))
+                stable_mir::mir::CoroutineKind::Coroutine(movability.stable(tables, cx))
             }
             CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, source) => {
                 stable_mir::mir::CoroutineKind::Desugared(
                     stable_mir::mir::CoroutineDesugaring::AsyncGen,
-                    source.stable(tables),
+                    source.stable(tables, cx),
                 )
             }
         }
@@ -72,7 +78,7 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
 impl<'tcx> Stable<'tcx> for rustc_span::Symbol {
     type T = stable_mir::Symbol;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         self.to_string()
     }
 }
@@ -80,7 +86,11 @@ impl<'tcx> Stable<'tcx> for rustc_span::Symbol {
 impl<'tcx> Stable<'tcx> for rustc_span::Span {
     type T = stable_mir::ty::Span;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         tables.create_span(*self)
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs
similarity index 69%
rename from compiler/rustc_smir/src/rustc_smir/convert/ty.rs
rename to compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs
index ff0b8e833dcce..c0a430079d8bb 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs
@@ -2,16 +2,20 @@
 
 use rustc_middle::ty::Ty;
 use rustc_middle::{mir, ty};
+use rustc_smir::Tables;
+use rustc_smir::context::SmirCtxt;
+use stable_mir::alloc;
+use stable_mir::compiler_interface::BridgeTys;
 use stable_mir::ty::{
     AdtKind, FloatTy, GenericArgs, GenericParamDef, IntTy, Region, RigidTy, TyKind, UintTy,
 };
+use stable_mir::unstable::Stable;
 
-use crate::rustc_smir::{Stable, Tables, alloc};
-use crate::stable_mir;
+use crate::{rustc_smir, stable_mir};
 
 impl<'tcx> Stable<'tcx> for ty::AliasTyKind {
     type T = stable_mir::ty::AliasKind;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::Projection => stable_mir::ty::AliasKind::Projection,
             ty::Inherent => stable_mir::ty::AliasKind::Inherent,
@@ -23,24 +27,35 @@ impl<'tcx> Stable<'tcx> for ty::AliasTyKind {
 
 impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> {
     type T = stable_mir::ty::AliasTy;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::AliasTy { args, def_id, .. } = self;
-        stable_mir::ty::AliasTy { def_id: tables.alias_def(*def_id), args: args.stable(tables) }
+        stable_mir::ty::AliasTy { def_id: tables.alias_def(*def_id), args: args.stable(tables, cx) }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> {
     type T = stable_mir::ty::AliasTerm;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::AliasTerm { args, def_id, .. } = self;
-        stable_mir::ty::AliasTerm { def_id: tables.alias_def(*def_id), args: args.stable(tables) }
+        stable_mir::ty::AliasTerm {
+            def_id: tables.alias_def(*def_id),
+            args: args.stable(tables, cx),
+        }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::DynKind {
     type T = stable_mir::ty::DynKind;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::Dyn => stable_mir::ty::DynKind::Dyn,
         }
@@ -50,14 +65,18 @@ impl<'tcx> Stable<'tcx> for ty::DynKind {
 impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> {
     type T = stable_mir::ty::ExistentialPredicate;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::ExistentialPredicate::*;
         match self {
             ty::ExistentialPredicate::Trait(existential_trait_ref) => {
-                Trait(existential_trait_ref.stable(tables))
+                Trait(existential_trait_ref.stable(tables, cx))
             }
             ty::ExistentialPredicate::Projection(existential_projection) => {
-                Projection(existential_projection.stable(tables))
+                Projection(existential_projection.stable(tables, cx))
             }
             ty::ExistentialPredicate::AutoTrait(def_id) => AutoTrait(tables.trait_def(*def_id)),
         }
@@ -67,11 +86,15 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> {
     type T = stable_mir::ty::ExistentialTraitRef;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::ExistentialTraitRef { def_id, args, .. } = self;
         stable_mir::ty::ExistentialTraitRef {
             def_id: tables.trait_def(*def_id),
-            generic_args: args.stable(tables),
+            generic_args: args.stable(tables, cx),
         }
     }
 }
@@ -79,12 +102,16 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> {
     type T = stable_mir::ty::TermKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::TermKind;
         match self {
-            ty::TermKind::Ty(ty) => TermKind::Type(ty.stable(tables)),
+            ty::TermKind::Ty(ty) => TermKind::Type(ty.stable(tables, cx)),
             ty::TermKind::Const(cnst) => {
-                let cnst = cnst.stable(tables);
+                let cnst = cnst.stable(tables, cx);
                 TermKind::Const(cnst)
             }
         }
@@ -94,25 +121,33 @@ impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> {
     type T = stable_mir::ty::ExistentialProjection;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::ExistentialProjection { def_id, args, term, .. } = self;
         stable_mir::ty::ExistentialProjection {
             def_id: tables.trait_def(*def_id),
-            generic_args: args.stable(tables),
-            term: term.kind().stable(tables),
+            generic_args: args.stable(tables, cx),
+            term: term.kind().stable(tables, cx),
         }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion {
     type T = stable_mir::mir::PointerCoercion;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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 => stable_mir::mir::PointerCoercion::ReifyFnPointer,
             PointerCoercion::UnsafeFnPointer => stable_mir::mir::PointerCoercion::UnsafeFnPointer,
             PointerCoercion::ClosureFnPointer(safety) => {
-                stable_mir::mir::PointerCoercion::ClosureFnPointer(safety.stable(tables))
+                stable_mir::mir::PointerCoercion::ClosureFnPointer(safety.stable(tables, cx))
             }
             PointerCoercion::MutToConstPointer => {
                 stable_mir::mir::PointerCoercion::MutToConstPointer
@@ -125,7 +160,7 @@ impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion {
 
 impl<'tcx> Stable<'tcx> for ty::UserTypeAnnotationIndex {
     type T = usize;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         self.as_usize()
     }
 }
@@ -133,7 +168,7 @@ impl<'tcx> Stable<'tcx> for ty::UserTypeAnnotationIndex {
 impl<'tcx> Stable<'tcx> for ty::AdtKind {
     type T = AdtKind;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::AdtKind::Struct => AdtKind::Struct,
             ty::AdtKind::Union => AdtKind::Union,
@@ -145,30 +180,44 @@ impl<'tcx> Stable<'tcx> for ty::AdtKind {
 impl<'tcx> Stable<'tcx> for ty::FieldDef {
     type T = stable_mir::ty::FieldDef;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::ty::FieldDef {
             def: tables.create_def_id(self.did),
-            name: self.name.stable(tables),
+            name: self.name.stable(tables, cx),
         }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> {
     type T = stable_mir::ty::GenericArgs;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        GenericArgs(self.iter().map(|arg| arg.kind().stable(tables)).collect())
+    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 = stable_mir::ty::GenericArgKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::GenericArgKind;
         match self {
-            ty::GenericArgKind::Lifetime(region) => GenericArgKind::Lifetime(region.stable(tables)),
-            ty::GenericArgKind::Type(ty) => GenericArgKind::Type(ty.stable(tables)),
-            ty::GenericArgKind::Const(cnst) => GenericArgKind::Const(cnst.stable(tables)),
+            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)),
         }
     }
 }
@@ -179,15 +228,19 @@ where
 {
     type T = stable_mir::ty::Binder<V>;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::Binder;
 
         Binder {
-            value: self.as_ref().skip_binder().stable(tables),
+            value: self.as_ref().skip_binder().stable(tables, cx),
             bound_vars: self
                 .bound_vars()
                 .iter()
-                .map(|bound_var| bound_var.stable(tables))
+                .map(|bound_var| bound_var.stable(tables, cx))
                 .collect(),
         }
     }
@@ -199,23 +252,35 @@ where
 {
     type T = stable_mir::ty::EarlyBinder<V>;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::EarlyBinder;
 
-        EarlyBinder { value: self.as_ref().skip_binder().stable(tables) }
+        EarlyBinder { value: self.as_ref().skip_binder().stable(tables, cx) }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
     type T = stable_mir::ty::FnSig;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::FnSig;
 
         FnSig {
-            inputs_and_output: self.inputs_and_output.iter().map(|ty| ty.stable(tables)).collect(),
+            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),
-            abi: self.abi.stable(tables),
+            safety: self.safety.stable(tables, cx),
+            abi: self.abi.stable(tables, cx),
         }
     }
 }
@@ -223,7 +288,11 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
     type T = stable_mir::ty::BoundTyKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::BoundTyKind;
 
         match self {
@@ -238,7 +307,11 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
 impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
     type T = stable_mir::ty::BoundRegionKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::BoundRegionKind;
 
         match self {
@@ -254,15 +327,19 @@ impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
 impl<'tcx> Stable<'tcx> for ty::BoundVariableKind {
     type T = stable_mir::ty::BoundVariableKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::BoundVariableKind;
 
         match self {
             ty::BoundVariableKind::Ty(bound_ty_kind) => {
-                BoundVariableKind::Ty(bound_ty_kind.stable(tables))
+                BoundVariableKind::Ty(bound_ty_kind.stable(tables, cx))
             }
             ty::BoundVariableKind::Region(bound_region_kind) => {
-                BoundVariableKind::Region(bound_region_kind.stable(tables))
+                BoundVariableKind::Region(bound_region_kind.stable(tables, cx))
             }
             ty::BoundVariableKind::Const => BoundVariableKind::Const,
         }
@@ -272,7 +349,7 @@ impl<'tcx> Stable<'tcx> for ty::BoundVariableKind {
 impl<'tcx> Stable<'tcx> for ty::IntTy {
     type T = IntTy;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::IntTy::Isize => IntTy::Isize,
             ty::IntTy::I8 => IntTy::I8,
@@ -287,7 +364,7 @@ impl<'tcx> Stable<'tcx> for ty::IntTy {
 impl<'tcx> Stable<'tcx> for ty::UintTy {
     type T = UintTy;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::UintTy::Usize => UintTy::Usize,
             ty::UintTy::U8 => UintTy::U8,
@@ -302,7 +379,7 @@ impl<'tcx> Stable<'tcx> for ty::UintTy {
 impl<'tcx> Stable<'tcx> for ty::FloatTy {
     type T = FloatTy;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::FloatTy::F16 => FloatTy::F16,
             ty::FloatTy::F32 => FloatTy::F32,
@@ -314,46 +391,55 @@ impl<'tcx> Stable<'tcx> for ty::FloatTy {
 
 impl<'tcx> Stable<'tcx> for Ty<'tcx> {
     type T = stable_mir::ty::Ty;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        tables.intern_ty(tables.tcx.lift(*self).unwrap())
+    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 = stable_mir::ty::TyKind;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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))),
-            ty::Uint(uint_ty) => TyKind::RigidTy(RigidTy::Uint(uint_ty.stable(tables))),
-            ty::Float(float_ty) => TyKind::RigidTy(RigidTy::Float(float_ty.stable(tables))),
+            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),
+                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), constant.stable(tables)))
+                TyKind::RigidTy(RigidTy::Array(ty.stable(tables, cx), constant.stable(tables, cx)))
             }
             ty::Pat(ty, pat) => {
-                TyKind::RigidTy(RigidTy::Pat(ty.stable(tables), pat.stable(tables)))
+                TyKind::RigidTy(RigidTy::Pat(ty.stable(tables, cx), pat.stable(tables, cx)))
             }
-            ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables))),
+            ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables, cx))),
             ty::RawPtr(ty, mutbl) => {
-                TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables), mutbl.stable(tables)))
+                TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables, cx), mutbl.stable(tables, cx)))
             }
             ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref(
-                region.stable(tables),
-                ty.stable(tables),
-                mutbl.stable(tables),
+                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::FnDef(def_id, generic_args) => {
-                TyKind::RigidTy(RigidTy::FnDef(tables.fn_def(*def_id), generic_args.stable(tables)))
-            }
             ty::FnPtr(sig_tys, hdr) => {
-                TyKind::RigidTy(RigidTy::FnPtr(sig_tys.with(*hdr).stable(tables)))
+                TyKind::RigidTy(RigidTy::FnPtr(sig_tys.with(*hdr).stable(tables, cx)))
             }
             // FIXME(unsafe_binders):
             ty::UnsafeBinder(_) => todo!(),
@@ -361,36 +447,36 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
                 TyKind::RigidTy(RigidTy::Dynamic(
                     existential_predicates
                         .iter()
-                        .map(|existential_predicate| existential_predicate.stable(tables))
+                        .map(|existential_predicate| existential_predicate.stable(tables, cx))
                         .collect(),
-                    region.stable(tables),
-                    dyn_kind.stable(tables),
+                    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),
+                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),
-                tables.tcx.coroutine_movability(*def_id).stable(tables),
+                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)).collect()))
-            }
+            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), alias_ty.stable(tables))
+                TyKind::Alias(alias_kind.stable(tables, cx), alias_ty.stable(tables, cx))
             }
-            ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables)),
+            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))
+                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),
+                args.stable(tables, cx),
             )),
             ty::Placeholder(..) | ty::Infer(_) | ty::Error(_) => {
                 unreachable!();
@@ -402,12 +488,16 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
     type T = stable_mir::ty::Pattern;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         match **self {
             ty::PatternKind::Range { start, end } => stable_mir::ty::Pattern::Range {
                 // FIXME(SMIR): update data structures to not have an Option here anymore
-                start: Some(start.stable(tables)),
-                end: Some(end.stable(tables)),
+                start: Some(start.stable(tables, cx)),
+                end: Some(end.stable(tables, cx)),
                 include_end: true,
             },
             ty::PatternKind::Or(_) => todo!(),
@@ -418,24 +508,30 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
     type T = stable_mir::ty::TyConst;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        let ct = tables.tcx.lift(*self).unwrap();
+    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 = tables.tcx.valtree_to_const_val(cv);
+                let const_val = cx.valtree_to_const_val(cv);
                 if matches!(const_val, mir::ConstValue::ZeroSized) {
-                    stable_mir::ty::TyConstKind::ZSTValue(cv.ty.stable(tables))
+                    stable_mir::ty::TyConstKind::ZSTValue(cv.ty.stable(tables, cx))
                 } else {
                     stable_mir::ty::TyConstKind::Value(
-                        cv.ty.stable(tables),
-                        alloc::new_allocation(cv.ty, const_val, tables),
+                        cv.ty.stable(tables, cx),
+                        alloc::new_allocation(cv.ty, const_val, tables, cx),
                     )
                 }
             }
-            ty::ConstKind::Param(param) => stable_mir::ty::TyConstKind::Param(param.stable(tables)),
+            ty::ConstKind::Param(param) => {
+                stable_mir::ty::TyConstKind::Param(param.stable(tables, cx))
+            }
             ty::ConstKind::Unevaluated(uv) => stable_mir::ty::TyConstKind::Unevaluated(
                 tables.const_def(uv.def),
-                uv.args.stable(tables),
+                uv.args.stable(tables, cx),
             ),
             ty::ConstKind::Error(_) => unreachable!(),
             ty::ConstKind::Infer(_) => unreachable!(),
@@ -450,7 +546,7 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
 
 impl<'tcx> Stable<'tcx> for ty::ParamConst {
     type T = stable_mir::ty::ParamConst;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use stable_mir::ty::ParamConst;
         ParamConst { index: self.index, name: self.name.to_string() }
     }
@@ -458,7 +554,7 @@ impl<'tcx> Stable<'tcx> for ty::ParamConst {
 
 impl<'tcx> Stable<'tcx> for ty::ParamTy {
     type T = stable_mir::ty::ParamTy;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use stable_mir::ty::ParamTy;
         ParamTy { index: self.index, name: self.name.to_string() }
     }
@@ -466,15 +562,19 @@ impl<'tcx> Stable<'tcx> for ty::ParamTy {
 
 impl<'tcx> Stable<'tcx> for ty::BoundTy {
     type T = stable_mir::ty::BoundTy;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::BoundTy;
-        BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables) }
+        BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables, cx) }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::trait_def::TraitSpecializationKind {
     type T = stable_mir::ty::TraitSpecializationKind;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use stable_mir::ty::TraitSpecializationKind;
 
         match self {
@@ -489,20 +589,24 @@ impl<'tcx> Stable<'tcx> for ty::trait_def::TraitSpecializationKind {
 
 impl<'tcx> Stable<'tcx> for ty::TraitDef {
     type T = stable_mir::ty::TraitDecl;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::opaque;
         use stable_mir::ty::TraitDecl;
 
         TraitDecl {
             def_id: tables.trait_def(self.def_id),
-            safety: self.safety.stable(tables),
+            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),
+            specialization_kind: self.specialization_kind.stable(tables, cx),
             must_implement_one_of: self
                 .must_implement_one_of
                 .as_ref()
@@ -515,20 +619,28 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef {
 
 impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> {
     type T = stable_mir::ty::TraitRef;
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::TraitRef;
 
-        TraitRef::try_new(tables.trait_def(self.def_id), self.args.stable(tables)).unwrap()
+        TraitRef::try_new(tables.trait_def(self.def_id), self.args.stable(tables, cx)).unwrap()
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::Generics {
     type T = stable_mir::ty::Generics;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::Generics;
 
-        let params: Vec<_> = self.own_params.iter().map(|param| param.stable(tables)).collect();
+        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();
 
@@ -541,7 +653,7 @@ impl<'tcx> Stable<'tcx> for ty::Generics {
             has_late_bound_regions: self
                 .has_late_bound_regions
                 .as_ref()
-                .map(|late_bound_regions| late_bound_regions.stable(tables)),
+                .map(|late_bound_regions| late_bound_regions.stable(tables, cx)),
         }
     }
 }
@@ -549,7 +661,7 @@ impl<'tcx> Stable<'tcx> for ty::Generics {
 impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
     type T = stable_mir::ty::GenericParamDefKind;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use stable_mir::ty::GenericParamDefKind;
         match self {
             ty::GenericParamDefKind::Lifetime => GenericParamDefKind::Lifetime,
@@ -566,13 +678,17 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
 impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDef {
     type T = stable_mir::ty::GenericParamDef;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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),
+            kind: self.kind.stable(tables, cx),
         }
     }
 }
@@ -580,31 +696,36 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDef {
 impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> {
     type T = stable_mir::ty::PredicateKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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) => {
-                stable_mir::ty::PredicateKind::Clause(clause_kind.stable(tables))
+                stable_mir::ty::PredicateKind::Clause(clause_kind.stable(tables, cx))
             }
             PredicateKind::DynCompatible(did) => {
                 stable_mir::ty::PredicateKind::DynCompatible(tables.trait_def(*did))
             }
             PredicateKind::Subtype(subtype_predicate) => {
-                stable_mir::ty::PredicateKind::SubType(subtype_predicate.stable(tables))
+                stable_mir::ty::PredicateKind::SubType(subtype_predicate.stable(tables, cx))
             }
             PredicateKind::Coerce(coerce_predicate) => {
-                stable_mir::ty::PredicateKind::Coerce(coerce_predicate.stable(tables))
-            }
-            PredicateKind::ConstEquate(a, b) => {
-                stable_mir::ty::PredicateKind::ConstEquate(a.stable(tables), b.stable(tables))
+                stable_mir::ty::PredicateKind::Coerce(coerce_predicate.stable(tables, cx))
             }
+            PredicateKind::ConstEquate(a, b) => stable_mir::ty::PredicateKind::ConstEquate(
+                a.stable(tables, cx),
+                b.stable(tables, cx),
+            ),
             PredicateKind::Ambiguous => stable_mir::ty::PredicateKind::Ambiguous,
             PredicateKind::NormalizesTo(_pred) => unimplemented!(),
             PredicateKind::AliasRelate(a, b, alias_relation_direction) => {
                 stable_mir::ty::PredicateKind::AliasRelate(
-                    a.kind().stable(tables),
-                    b.kind().stable(tables),
-                    alias_relation_direction.stable(tables),
+                    a.kind().stable(tables, cx),
+                    b.kind().stable(tables, cx),
+                    alias_relation_direction.stable(tables, cx),
                 )
             }
         }
@@ -614,34 +735,38 @@ impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
     type T = stable_mir::ty::ClauseKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    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) => {
-                stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables))
+                stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables, cx))
             }
             ClauseKind::RegionOutlives(region_outlives) => {
-                stable_mir::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables))
+                stable_mir::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables, cx))
             }
             ClauseKind::TypeOutlives(type_outlives) => {
                 let ty::OutlivesPredicate::<_, _>(a, b) = type_outlives;
                 stable_mir::ty::ClauseKind::TypeOutlives(stable_mir::ty::OutlivesPredicate(
-                    a.stable(tables),
-                    b.stable(tables),
+                    a.stable(tables, cx),
+                    b.stable(tables, cx),
                 ))
             }
             ClauseKind::Projection(projection_predicate) => {
-                stable_mir::ty::ClauseKind::Projection(projection_predicate.stable(tables))
+                stable_mir::ty::ClauseKind::Projection(projection_predicate.stable(tables, cx))
             }
             ClauseKind::ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType(
-                const_.stable(tables),
-                ty.stable(tables),
+                const_.stable(tables, cx),
+                ty.stable(tables, cx),
             ),
             ClauseKind::WellFormed(term) => {
-                stable_mir::ty::ClauseKind::WellFormed(term.kind().stable(tables))
+                stable_mir::ty::ClauseKind::WellFormed(term.kind().stable(tables, cx))
             }
             ClauseKind::ConstEvaluatable(const_) => {
-                stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables))
+                stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables, cx))
             }
             ClauseKind::HostEffect(..) => {
                 todo!()
@@ -653,7 +778,7 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::ClosureKind {
     type T = stable_mir::ty::ClosureKind;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::ty::ClosureKind::*;
         match self {
             Fn => stable_mir::ty::ClosureKind::Fn,
@@ -666,25 +791,33 @@ impl<'tcx> Stable<'tcx> for ty::ClosureKind {
 impl<'tcx> Stable<'tcx> for ty::SubtypePredicate<'tcx> {
     type T = stable_mir::ty::SubtypePredicate;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::SubtypePredicate { a, b, a_is_expected: _ } = self;
-        stable_mir::ty::SubtypePredicate { a: a.stable(tables), b: b.stable(tables) }
+        stable_mir::ty::SubtypePredicate { a: a.stable(tables, cx), b: b.stable(tables, cx) }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::CoercePredicate<'tcx> {
     type T = stable_mir::ty::CoercePredicate;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::CoercePredicate { a, b } = self;
-        stable_mir::ty::CoercePredicate { a: a.stable(tables), b: b.stable(tables) }
+        stable_mir::ty::CoercePredicate { a: a.stable(tables, cx), b: b.stable(tables, cx) }
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::AliasRelationDirection {
     type T = stable_mir::ty::AliasRelationDirection;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::ty::AliasRelationDirection::*;
         match self {
             Equate => stable_mir::ty::AliasRelationDirection::Equate,
@@ -696,11 +829,15 @@ impl<'tcx> Stable<'tcx> for ty::AliasRelationDirection {
 impl<'tcx> Stable<'tcx> for ty::TraitPredicate<'tcx> {
     type T = stable_mir::ty::TraitPredicate;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::TraitPredicate { trait_ref, polarity } = self;
         stable_mir::ty::TraitPredicate {
-            trait_ref: trait_ref.stable(tables),
-            polarity: polarity.stable(tables),
+            trait_ref: trait_ref.stable(tables, cx),
+            polarity: polarity.stable(tables, cx),
         }
     }
 }
@@ -711,20 +848,28 @@ where
 {
     type T = stable_mir::ty::OutlivesPredicate<T::T, Region>;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::OutlivesPredicate(a, b) = self;
-        stable_mir::ty::OutlivesPredicate(a.stable(tables), b.stable(tables))
+        stable_mir::ty::OutlivesPredicate(a.stable(tables, cx), b.stable(tables, cx))
     }
 }
 
 impl<'tcx> Stable<'tcx> for ty::ProjectionPredicate<'tcx> {
     type T = stable_mir::ty::ProjectionPredicate;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         let ty::ProjectionPredicate { projection_term, term } = self;
         stable_mir::ty::ProjectionPredicate {
-            projection_term: projection_term.stable(tables),
-            term: term.kind().stable(tables),
+            projection_term: projection_term.stable(tables, cx),
+            term: term.kind().stable(tables, cx),
         }
     }
 }
@@ -732,7 +877,7 @@ impl<'tcx> Stable<'tcx> for ty::ProjectionPredicate<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::ImplPolarity {
     type T = stable_mir::ty::ImplPolarity;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::ty::ImplPolarity::*;
         match self {
             Positive => stable_mir::ty::ImplPolarity::Positive,
@@ -745,7 +890,7 @@ impl<'tcx> Stable<'tcx> for ty::ImplPolarity {
 impl<'tcx> Stable<'tcx> for ty::PredicatePolarity {
     type T = stable_mir::ty::PredicatePolarity;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_middle::ty::PredicatePolarity::*;
         match self {
             Positive => stable_mir::ty::PredicatePolarity::Positive,
@@ -757,15 +902,23 @@ impl<'tcx> Stable<'tcx> for ty::PredicatePolarity {
 impl<'tcx> Stable<'tcx> for ty::Region<'tcx> {
     type T = stable_mir::ty::Region;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        Region { kind: self.kind().stable(tables) }
+    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 = stable_mir::ty::RegionKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::{BoundRegion, EarlyParamRegion, RegionKind};
         match self {
             ty::ReEarlyParam(early_reg) => RegionKind::ReEarlyParam(EarlyParamRegion {
@@ -774,7 +927,10 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> {
             }),
             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) },
+                BoundRegion {
+                    var: bound_reg.var.as_u32(),
+                    kind: bound_reg.kind.stable(tables, cx),
+                },
             ),
             ty::ReStatic => RegionKind::ReStatic,
             ty::RePlaceholder(place_holder) => {
@@ -782,7 +938,7 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> {
                     universe: place_holder.universe.as_u32(),
                     bound: BoundRegion {
                         var: place_holder.bound.var.as_u32(),
-                        kind: place_holder.bound.kind.stable(tables),
+                        kind: place_holder.bound.kind.stable(tables, cx),
                     },
                 })
             }
@@ -795,8 +951,12 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
     type T = stable_mir::mir::mono::Instance;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        let def = tables.instance_def(tables.tcx.lift(*self).unwrap());
+    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(..) => stable_mir::mir::mono::InstanceKind::Item,
             ty::InstanceKind::Intrinsic(..) => stable_mir::mir::mono::InstanceKind::Intrinsic,
@@ -824,7 +984,7 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
 
 impl<'tcx> Stable<'tcx> for ty::Variance {
     type T = stable_mir::mir::Variance;
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::Bivariant => stable_mir::mir::Variance::Bivariant,
             ty::Contravariant => stable_mir::mir::Variance::Contravariant,
@@ -837,7 +997,7 @@ impl<'tcx> Stable<'tcx> for ty::Variance {
 impl<'tcx> Stable<'tcx> for ty::Movability {
     type T = stable_mir::ty::Movability;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         match self {
             ty::Movability::Static => stable_mir::ty::Movability::Static,
             ty::Movability::Movable => stable_mir::ty::Movability::Movable,
@@ -848,7 +1008,7 @@ impl<'tcx> Stable<'tcx> for ty::Movability {
 impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
     type T = stable_mir::ty::Abi;
 
-    fn stable(&self, _: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use rustc_abi::ExternAbi;
         use stable_mir::ty::Abi;
         match *self {
@@ -886,10 +1046,14 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
 impl<'tcx> Stable<'tcx> for rustc_session::cstore::ForeignModule {
     type T = stable_mir::ty::ForeignModule;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::ty::ForeignModule {
             def_id: tables.foreign_module_def(self.def_id),
-            abi: self.abi.stable(tables),
+            abi: self.abi.stable(tables, cx),
         }
     }
 }
@@ -897,7 +1061,11 @@ impl<'tcx> Stable<'tcx> for rustc_session::cstore::ForeignModule {
 impl<'tcx> Stable<'tcx> for ty::AssocKind {
     type T = stable_mir::ty::AssocKind;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::{AssocKind, AssocTypeData};
         match *self {
             ty::AssocKind::Const { name } => AssocKind::Const { name: name.to_string() },
@@ -908,7 +1076,7 @@ impl<'tcx> Stable<'tcx> for ty::AssocKind {
                 data: match data {
                     ty::AssocTypeData::Normal(name) => AssocTypeData::Normal(name.to_string()),
                     ty::AssocTypeData::Rpitit(rpitit) => {
-                        AssocTypeData::Rpitit(rpitit.stable(tables))
+                        AssocTypeData::Rpitit(rpitit.stable(tables, cx))
                     }
                 },
             },
@@ -919,7 +1087,7 @@ impl<'tcx> Stable<'tcx> for ty::AssocKind {
 impl<'tcx> Stable<'tcx> for ty::AssocItemContainer {
     type T = stable_mir::ty::AssocItemContainer;
 
-    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+    fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &SmirCtxt<'_, BridgeTys>) -> Self::T {
         use stable_mir::ty::AssocItemContainer;
         match self {
             ty::AssocItemContainer::Trait => AssocItemContainer::Trait,
@@ -931,11 +1099,15 @@ impl<'tcx> Stable<'tcx> for ty::AssocItemContainer {
 impl<'tcx> Stable<'tcx> for ty::AssocItem {
     type T = stable_mir::ty::AssocItem;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         stable_mir::ty::AssocItem {
             def_id: tables.assoc_def(self.def_id),
-            kind: self.kind.stable(tables),
-            container: self.container.stable(tables),
+            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)),
         }
     }
@@ -944,7 +1116,11 @@ impl<'tcx> Stable<'tcx> for ty::AssocItem {
 impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData {
     type T = stable_mir::ty::ImplTraitInTraitData;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        _: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
         use stable_mir::ty::ImplTraitInTraitData;
         match self {
             ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => {
@@ -963,7 +1139,11 @@ impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData {
 impl<'tcx> Stable<'tcx> for rustc_middle::ty::util::Discr<'tcx> {
     type T = stable_mir::ty::Discr;
 
-    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        stable_mir::ty::Discr { val: self.val, ty: self.ty.stable(tables) }
+    fn stable<'cx>(
+        &self,
+        tables: &mut Tables<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
+    ) -> Self::T {
+        stable_mir::ty::Discr { val: self.val, ty: self.ty.stable(tables, cx) }
     }
 }
diff --git a/compiler/rustc_smir/src/stable_mir/unstable/mod.rs b/compiler/rustc_smir/src/stable_mir/unstable/mod.rs
new file mode 100644
index 0000000000000..77a772019ebe7
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/unstable/mod.rs
@@ -0,0 +1,210 @@
+//! 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_smir::Tables;
+use rustc_smir::context::{
+    SmirCtxt, SmirExistentialProjection, SmirExistentialTraitRef, SmirTraitRef,
+};
+use stable_mir::{CtorKind, ItemKind};
+
+use super::compiler_interface::BridgeTys;
+use crate::{rustc_smir, stable_mir};
+
+pub(crate) mod convert;
+
+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)
+    }
+}
+
+/// 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 [`crate::rustc_smir::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),
+    }
+}