diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index 632072003539f..bbc98af45c0d7 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -7,6 +7,7 @@
 use crate::rustc_smir::Tables;
 use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy};
 use rustc_span::Symbol;
+use stable_mir::abi::Layout;
 use stable_mir::mir::alloc::AllocId;
 use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
 use stable_mir::mir::{Mutability, Safety};
@@ -460,6 +461,14 @@ impl<'tcx> RustcInternal<'tcx> for Span {
     }
 }
 
+impl<'tcx> RustcInternal<'tcx> for Layout {
+    type T = rustc_target::abi::Layout<'tcx>;
+
+    fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        tables.layouts[*self]
+    }
+}
+
 impl<'tcx, T> RustcInternal<'tcx> for &T
 where
     T: RustcInternal<'tcx>,
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index c3db9b358e8b5..4bac98909add0 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -12,6 +12,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{CrateNum, DefId};
 use rustc_span::Span;
 use scoped_tls::scoped_thread_local;
+use stable_mir::abi::Layout;
 use stable_mir::ty::IndexedVal;
 use stable_mir::Error;
 use std::cell::Cell;
@@ -136,6 +137,10 @@ impl<'tcx> Tables<'tcx> {
     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_target::abi::Layout<'tcx>) -> Layout {
+        self.layouts.create_or_fetch(layout)
+    }
 }
 
 pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
@@ -180,6 +185,7 @@ where
         types: IndexMap::default(),
         instances: IndexMap::default(),
         constants: IndexMap::default(),
+        layouts: IndexMap::default(),
     }));
     stable_mir::compiler_interface::run(&tables, || init(&tables, f))
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 2361a04a6d7c2..621766c695e43 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -3,12 +3,19 @@
 //! This trait is currently the main interface between the Rust compiler,
 //! and the `stable_mir` crate.
 
+#![allow(rustc::usage_of_qualified_ty)]
+
+use rustc_abi::HasDataLayout;
 use rustc_middle::ty;
+use rustc_middle::ty::layout::{
+    FnAbiOf, FnAbiOfHelpers, HasParamEnv, HasTyCtxt, LayoutOf, LayoutOfHelpers,
+};
 use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
 use rustc_middle::ty::{
-    GenericPredicates, Instance, ParamEnv, ScalarInt, TypeVisitableExt, ValTree,
+    GenericPredicates, Instance, List, ParamEnv, ScalarInt, TyCtxt, TypeVisitableExt, ValTree,
 };
 use rustc_span::def_id::LOCAL_CRATE;
+use stable_mir::abi::{FnAbi, Layout, LayoutShape};
 use stable_mir::compiler_interface::Context;
 use stable_mir::mir::alloc::GlobalAlloc;
 use stable_mir::mir::mono::{InstanceDef, StaticDef};
@@ -280,7 +287,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         tables.tcx.mk_ty_from_kind(internal_kind).stable(&mut *tables)
     }
 
-    #[allow(rustc::usage_of_qualified_ty)]
     fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty {
         let mut tables = self.0.borrow_mut();
         let inner = ty.internal(&mut *tables);
@@ -335,6 +341,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         instance.ty(tables.tcx, ParamEnv::reveal_all()).stable(&mut *tables)
     }
 
+    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))
+    }
+
     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();
@@ -473,6 +485,65 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
             )
         }
     }
+
+    fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
+        let mut tables = self.0.borrow_mut();
+        let ty = ty.internal(&mut *tables);
+        let layout = tables.layout_of(ty)?.layout;
+        Ok(layout.stable(&mut *tables))
+    }
+
+    fn layout_shape(&self, id: Layout) -> LayoutShape {
+        let mut tables = self.0.borrow_mut();
+        id.internal(&mut *tables).0.stable(&mut *tables)
+    }
 }
 
 pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);
+
+/// Implement error handling for extracting function ABI information.
+impl<'tcx> FnAbiOfHelpers<'tcx> for Tables<'tcx> {
+    type FnAbiOfResult = Result<&'tcx rustc_target::abi::call::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> HasParamEnv<'tcx> for Tables<'tcx> {
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        ty::ParamEnv::reveal_all()
+    }
+}
+
+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/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
new file mode 100644
index 0000000000000..632e97b32f520
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
@@ -0,0 +1,242 @@
+//! Conversion of internal Rust compiler `rustc_target::abi` and `rustc_abi` items to stable ones.
+
+#![allow(rustc::usage_of_qualified_ty)]
+
+use crate::rustc_smir::{Stable, Tables};
+use rustc_middle::ty;
+use rustc_target::abi::call::Conv;
+use stable_mir::abi::{
+    ArgAbi, CallConvention, FieldsShape, FnAbi, Layout, LayoutShape, PassMode, TagEncoding,
+    TyAndLayout, ValueAbi, VariantsShape,
+};
+use stable_mir::ty::{Align, IndexedVal, Size, VariantIdx};
+use stable_mir::{opaque, Opaque};
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
+    type T = VariantIdx;
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        VariantIdx::to_val(self.as_usize())
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
+    type T = stable_mir::target::Endian;
+
+    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            rustc_abi::Endian::Little => stable_mir::target::Endian::Little,
+            rustc_abi::Endian::Big => stable_mir::target::Endian::Big,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::TyAndLayout<'tcx, ty::Ty<'tcx>> {
+    type T = TyAndLayout;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        TyAndLayout { ty: self.ty.stable(tables), layout: self.layout.stable(tables) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::Layout<'tcx> {
+    type T = Layout;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        tables.layout_id(*self)
+    }
+}
+
+impl<'tcx> Stable<'tcx>
+    for rustc_abi::LayoutS<rustc_target::abi::FieldIdx, rustc_target::abi::VariantIdx>
+{
+    type T = LayoutShape;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        LayoutShape {
+            fields: self.fields.stable(tables),
+            variants: self.variants.stable(tables),
+            abi: self.abi.stable(tables),
+            abi_align: self.align.abi.stable(tables),
+            size: self.size.stable(tables),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::call::FnAbi<'tcx, ty::Ty<'tcx>> {
+    type T = FnAbi;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        assert!(self.args.len() >= self.fixed_count as usize);
+        assert!(!self.c_variadic || matches!(self.conv, Conv::C));
+        FnAbi {
+            args: self.args.as_ref().stable(tables),
+            ret: self.ret.stable(tables),
+            fixed_count: self.fixed_count,
+            conv: self.conv.stable(tables),
+            c_variadic: self.c_variadic,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::call::ArgAbi<'tcx, ty::Ty<'tcx>> {
+    type T = ArgAbi;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        ArgAbi {
+            ty: self.layout.ty.stable(tables),
+            layout: self.layout.layout.stable(tables),
+            mode: self.mode.stable(tables),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::call::Conv {
+    type T = CallConvention;
+
+    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            Conv::C => CallConvention::C,
+            Conv::Rust => CallConvention::Rust,
+            Conv::Cold => CallConvention::Cold,
+            Conv::PreserveMost => CallConvention::PreserveMost,
+            Conv::PreserveAll => CallConvention::PreserveAll,
+            Conv::ArmAapcs => CallConvention::ArmAapcs,
+            Conv::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,
+            Conv::Msp430Intr => CallConvention::Msp430Intr,
+            Conv::PtxKernel => CallConvention::PtxKernel,
+            Conv::X86Fastcall => CallConvention::X86Fastcall,
+            Conv::X86Intr => CallConvention::X86Intr,
+            Conv::X86Stdcall => CallConvention::X86Stdcall,
+            Conv::X86ThisCall => CallConvention::X86ThisCall,
+            Conv::X86VectorCall => CallConvention::X86VectorCall,
+            Conv::X86_64SysV => CallConvention::X86_64SysV,
+            Conv::X86_64Win64 => CallConvention::X86_64Win64,
+            Conv::AmdGpuKernel => CallConvention::AmdGpuKernel,
+            Conv::AvrInterrupt => CallConvention::AvrInterrupt,
+            Conv::AvrNonBlockingInterrupt => CallConvention::AvrNonBlockingInterrupt,
+            Conv::RiscvInterrupt { .. } => CallConvention::RiscvInterrupt,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_target::abi::call::PassMode {
+    type T = PassMode;
+
+    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            rustc_target::abi::call::PassMode::Ignore => PassMode::Ignore,
+            rustc_target::abi::call::PassMode::Direct(attr) => PassMode::Direct(opaque(attr)),
+            rustc_target::abi::call::PassMode::Pair(first, second) => {
+                PassMode::Pair(opaque(first), opaque(second))
+            }
+            rustc_target::abi::call::PassMode::Cast { pad_i32, cast } => {
+                PassMode::Cast { pad_i32: *pad_i32, cast: opaque(cast) }
+            }
+            rustc_target::abi::call::PassMode::Indirect { attrs, meta_attrs, on_stack } => {
+                PassMode::Indirect {
+                    attrs: opaque(attrs),
+                    meta_attrs: opaque(meta_attrs),
+                    on_stack: *on_stack,
+                }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape<rustc_target::abi::FieldIdx> {
+    type T = FieldsShape;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> 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 }
+            }
+            rustc_abi::FieldsShape::Arbitrary { offsets, .. } => {
+                FieldsShape::Arbitrary { offsets: offsets.iter().as_slice().stable(tables) }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx>
+    for rustc_abi::Variants<rustc_target::abi::FieldIdx, rustc_target::abi::VariantIdx>
+{
+    type T = VariantsShape;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            rustc_abi::Variants::Single { index } => {
+                VariantsShape::Single { index: index.stable(tables) }
+            }
+            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,
+                    variants: variants.iter().as_slice().stable(tables),
+                }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding<rustc_target::abi::VariantIdx> {
+    type T = TagEncoding;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> 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),
+                    niche_start: *niche_start,
+                }
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Abi {
+    type T = ValueAbi;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        match *self {
+            rustc_abi::Abi::Uninhabited => ValueAbi::Uninhabited,
+            rustc_abi::Abi::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables)),
+            rustc_abi::Abi::ScalarPair(first, second) => {
+                ValueAbi::ScalarPair(first.stable(tables), second.stable(tables))
+            }
+            rustc_abi::Abi::Vector { element, count } => {
+                ValueAbi::Vector { element: element.stable(tables), count }
+            }
+            rustc_abi::Abi::Aggregate { sized } => ValueAbi::Aggregate { sized },
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Size {
+    type T = Size;
+
+    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        self.bytes_usize()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Align {
+    type T = Align;
+
+    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        self.bytes()
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
+    type T = Opaque;
+
+    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        opaque(self)
+    }
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
index 7021bdda73501..8b7b26f969c96 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
@@ -1,10 +1,10 @@
 //! Conversion of internal Rust compiler items to stable ones.
 
 use rustc_target::abi::FieldIdx;
-use stable_mir::ty::{IndexedVal, VariantIdx};
 
 use crate::rustc_smir::{Stable, Tables};
 
+mod abi;
 mod error;
 mod mir;
 mod ty;
@@ -26,13 +26,6 @@ impl<'tcx> Stable<'tcx> for FieldIdx {
     }
 }
 
-impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
-    type T = VariantIdx;
-    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
-        VariantIdx::to_val(self.as_usize())
-    }
-}
-
 impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
     type T = stable_mir::mir::CoroutineSource;
     fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
@@ -79,14 +72,3 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span {
         tables.create_span(*self)
     }
 }
-
-impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
-    type T = stable_mir::target::Endian;
-
-    fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
-        match self {
-            rustc_abi::Endian::Little => stable_mir::target::Endian::Little,
-            rustc_abi::Endian::Big => stable_mir::target::Endian::Big,
-        }
-    }
-}
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index ae6cf3fe3e86d..cf6ac5285c245 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -12,9 +12,11 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::AllocId;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use stable_mir::abi::Layout;
 use stable_mir::mir::mono::InstanceDef;
 use stable_mir::ty::{ConstId, Span};
 use stable_mir::ItemKind;
+use std::ops::RangeInclusive;
 use tracing::debug;
 
 use crate::rustc_internal::IndexMap;
@@ -32,6 +34,7 @@ pub struct Tables<'tcx> {
     pub(crate) types: IndexMap<Ty<'tcx>, stable_mir::ty::Ty>,
     pub(crate) instances: IndexMap<ty::Instance<'tcx>, InstanceDef>,
     pub(crate) constants: IndexMap<mir::Const<'tcx>, ConstId>,
+    pub(crate) layouts: IndexMap<rustc_target::abi::Layout<'tcx>, Layout>,
 }
 
 impl<'tcx> Tables<'tcx> {
@@ -162,3 +165,13 @@ where
         (self.0.stable(tables), self.1.stable(tables))
     }
 }
+
+impl<'tcx, T> Stable<'tcx> for RangeInclusive<T>
+where
+    T: Stable<'tcx>,
+{
+    type T = RangeInclusive<T::T>;
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        RangeInclusive::new(self.start().stable(tables), self.end().stable(tables))
+    }
+}
diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs
new file mode 100644
index 0000000000000..53dac6abefb30
--- /dev/null
+++ b/compiler/stable_mir/src/abi.rs
@@ -0,0 +1,283 @@
+use crate::compiler_interface::with;
+use crate::mir::FieldIdx;
+use crate::ty::{Align, IndexedVal, Size, Ty, VariantIdx};
+use crate::Opaque;
+use std::num::NonZeroUsize;
+use std::ops::RangeInclusive;
+
+/// A function ABI definition.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct FnAbi {
+    /// The types of each argument.
+    pub args: Vec<ArgAbi>,
+
+    /// The expected return type.
+    pub ret: ArgAbi,
+
+    /// The count of non-variadic arguments.
+    ///
+    /// Should only be different from `args.len()` when a function is a C variadic function.
+    pub fixed_count: u32,
+
+    /// The ABI convention.
+    pub conv: CallConvention,
+
+    /// Whether this is a variadic C function,
+    pub c_variadic: bool,
+}
+
+/// Information about the ABI of a function's argument, or return value.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ArgAbi {
+    pub ty: Ty,
+    pub layout: Layout,
+    pub mode: PassMode,
+}
+
+/// How a function argument should be passed in to the target function.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum PassMode {
+    /// Ignore the argument.
+    ///
+    /// The argument is either uninhabited or a ZST.
+    Ignore,
+    /// Pass the argument directly.
+    ///
+    /// The argument has a layout abi of `Scalar` or `Vector`.
+    Direct(Opaque),
+    /// Pass a pair's elements directly in two arguments.
+    ///
+    /// The argument has a layout abi of `ScalarPair`.
+    Pair(Opaque, Opaque),
+    /// Pass the argument after casting it.
+    Cast { pad_i32: bool, cast: Opaque },
+    /// Pass the argument indirectly via a hidden pointer.
+    Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool },
+}
+
+/// The layout of a type, alongside the type itself.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct TyAndLayout {
+    pub ty: Ty,
+    pub layout: Layout,
+}
+
+/// The layout of a type in memory.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct LayoutShape {
+    /// The fields location withing the layout
+    pub fields: FieldsShape,
+
+    /// Encodes information about multi-variant layouts.
+    /// Even with `Multiple` variants, a layout still has its own fields! Those are then
+    /// shared between all variants.
+    ///
+    /// To access all fields of this layout, both `fields` and the fields of the active variant
+    /// must be taken into account.
+    pub variants: VariantsShape,
+
+    /// The `abi` defines how this data is passed between functions.
+    pub abi: ValueAbi,
+
+    /// The ABI mandated alignment in bytes.
+    pub abi_align: Align,
+
+    /// The size of this layout in bytes.
+    pub size: Size,
+}
+
+impl LayoutShape {
+    /// Returns `true` if the layout corresponds to an unsized type.
+    #[inline]
+    pub fn is_unsized(&self) -> bool {
+        self.abi.is_unsized()
+    }
+
+    #[inline]
+    pub fn is_sized(&self) -> bool {
+        !self.abi.is_unsized()
+    }
+
+    /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1).
+    pub fn is_1zst(&self) -> bool {
+        self.is_sized() && self.size == 0 && self.abi_align == 1
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Layout(usize);
+
+impl Layout {
+    pub fn shape(self) -> LayoutShape {
+        with(|cx| cx.layout_shape(self))
+    }
+}
+
+impl IndexedVal for Layout {
+    fn to_val(index: usize) -> Self {
+        Layout(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
+/// Describes how the fields of a type are shaped in memory.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum FieldsShape {
+    /// Scalar primitives and `!`, which never have fields.
+    Primitive,
+
+    /// All fields start at no offset. The `usize` is the field count.
+    Union(NonZeroUsize),
+
+    /// Array/vector-like placement, with all fields of identical types.
+    Array { stride: Size, count: u64 },
+
+    /// Struct-like placement, with precomputed offsets.
+    ///
+    /// Fields are guaranteed to not overlap, but note that gaps
+    /// before, between and after all the fields are NOT always
+    /// padding, and as such their contents may not be discarded.
+    /// For example, enum variants leave a gap at the start,
+    /// where the discriminant field in the enum layout goes.
+    Arbitrary {
+        /// Offsets for the first byte of each field,
+        /// ordered to match the source definition order.
+        /// I.e.: It follows the same order as [crate::ty::VariantDef::fields()].
+        /// This vector does not go in increasing order.
+        offsets: Vec<Size>,
+    },
+}
+
+impl FieldsShape {
+    pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> {
+        match self {
+            FieldsShape::Primitive => vec![],
+            FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(),
+            FieldsShape::Arbitrary { offsets, .. } => {
+                let mut indices = (0..offsets.len()).collect::<Vec<_>>();
+                indices.sort_by_key(|idx| offsets[*idx]);
+                indices
+            }
+        }
+    }
+
+    pub fn count(&self) -> usize {
+        match self {
+            FieldsShape::Primitive => 0,
+            FieldsShape::Union(count) => count.get(),
+            FieldsShape::Array { count, .. } => *count as usize,
+            FieldsShape::Arbitrary { offsets, .. } => offsets.len(),
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum VariantsShape {
+    /// Single enum variants, structs/tuples, unions, and all non-ADTs.
+    Single { index: VariantIdx },
+
+    /// Enum-likes with more than one inhabited variant: each variant comes with
+    /// a *discriminant* (usually the same as the variant index but the user can
+    /// assign explicit discriminant values). That discriminant is encoded
+    /// as a *tag* on the machine. The layout of each variant is
+    /// a struct, and they all have space reserved for the tag.
+    /// For enums, the tag is the sole field of the layout.
+    Multiple {
+        tag: Scalar,
+        tag_encoding: TagEncoding,
+        tag_field: usize,
+        variants: Vec<LayoutShape>,
+    },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum TagEncoding {
+    /// The tag directly stores the discriminant, but possibly with a smaller layout
+    /// (so converting the tag to the discriminant can require sign extension).
+    Direct,
+
+    /// Niche (values invalid for a type) encoding the discriminant:
+    /// Discriminant and variant index coincide.
+    /// The variant `untagged_variant` contains a niche at an arbitrary
+    /// offset (field `tag_field` of the enum), which for a variant with
+    /// discriminant `d` is set to
+    /// `(d - niche_variants.start).wrapping_add(niche_start)`.
+    ///
+    /// For example, `Option<(usize, &T)>`  is represented such that
+    /// `None` has a null pointer for the second tuple field, and
+    /// `Some` is the identity function (with a non-null reference).
+    Niche {
+        untagged_variant: VariantIdx,
+        niche_variants: RangeInclusive<VariantIdx>,
+        niche_start: u128,
+    },
+}
+
+/// Describes how values of the type are passed by target ABIs,
+/// in terms of categories of C types there are ABI rules for.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum ValueAbi {
+    Uninhabited,
+    Scalar(Scalar),
+    ScalarPair(Scalar, Scalar),
+    Vector {
+        element: Scalar,
+        count: u64,
+    },
+    Aggregate {
+        /// If true, the size is exact, otherwise it's only a lower bound.
+        sized: bool,
+    },
+}
+
+impl ValueAbi {
+    /// Returns `true` if the layout corresponds to an unsized type.
+    pub fn is_unsized(&self) -> bool {
+        match *self {
+            ValueAbi::Uninhabited
+            | ValueAbi::Scalar(_)
+            | ValueAbi::ScalarPair(..)
+            | ValueAbi::Vector { .. } => false,
+            ValueAbi::Aggregate { sized } => !sized,
+        }
+    }
+}
+
+/// We currently do not support `Scalar`, and use opaque instead.
+type Scalar = Opaque;
+
+/// General language calling conventions.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CallConvention {
+    C,
+    Rust,
+
+    Cold,
+    PreserveMost,
+    PreserveAll,
+
+    // Target-specific calling conventions.
+    ArmAapcs,
+    CCmseNonSecureCall,
+
+    Msp430Intr,
+
+    PtxKernel,
+
+    X86Fastcall,
+    X86Intr,
+    X86Stdcall,
+    X86ThisCall,
+    X86VectorCall,
+
+    X86_64SysV,
+    X86_64Win64,
+
+    AmdGpuKernel,
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+
+    RiscvInterrupt,
+}
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index 57c60b70d8aa3..98b1d484c034e 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -5,6 +5,7 @@
 
 use std::cell::Cell;
 
+use crate::abi::{FnAbi, Layout, LayoutShape};
 use crate::mir::alloc::{AllocId, GlobalAlloc};
 use crate::mir::mono::{Instance, InstanceDef, StaticDef};
 use crate::mir::Body;
@@ -173,6 +174,15 @@ pub trait Context {
 
     /// 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 layout of a type.
+    fn ty_layout(&self, ty: Ty) -> Result<Layout, Error>;
+
+    /// Get the layout shape.
+    fn layout_shape(&self, id: Layout) -> LayoutShape;
 }
 
 // A thread local variable that stores a pointer to the tables mapping between TyCtxt
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index 8c66bfb2e9890..4941e54fe4b7e 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -33,6 +33,7 @@ use crate::mir::Body;
 use crate::mir::Mutability;
 use crate::ty::{ImplDef, ImplTrait, IndexedVal, Span, TraitDecl, TraitDef, Ty};
 
+pub mod abi;
 #[macro_use]
 pub mod crate_def;
 pub mod compiler_interface;
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
index c126de23c4b6f..70d44ef8c2256 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -1,3 +1,4 @@
+use crate::abi::FnAbi;
 use crate::crate_def::CrateDef;
 use crate::mir::Body;
 use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
@@ -56,6 +57,11 @@ impl Instance {
         with(|context| context.instance_ty(self.def))
     }
 
+    /// Retrieve information about this instance binary interface.
+    pub fn fn_abi(&self) -> Result<FnAbi, Error> {
+        with(|cx| cx.instance_abi(self.def))
+    }
+
     /// Retrieve the instance's mangled name used for calling the given instance.
     ///
     /// This will also look up the correct name of instances from upstream crates.
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index 4807a9028eb83..1d4d7b6d3520f 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -3,6 +3,7 @@ use super::{
     mir::{Body, Mutability},
     with, DefId, Error, Symbol,
 };
+use crate::abi::Layout;
 use crate::crate_def::CrateDef;
 use crate::mir::alloc::{read_target_int, read_target_uint, AllocId};
 use crate::target::MachineInfo;
@@ -85,6 +86,11 @@ impl Ty {
     pub fn unsigned_ty(inner: UintTy) -> Ty {
         Ty::from_rigid_kind(RigidTy::Uint(inner))
     }
+
+    /// Get a type layout.
+    pub fn layout(self) -> Result<Layout, Error> {
+        with(|cx| cx.ty_layout(self))
+    }
 }
 
 impl Ty {
diff --git a/tests/ui-fulldeps/stable-mir/check_abi.rs b/tests/ui-fulldeps/stable-mir/check_abi.rs
new file mode 100644
index 0000000000000..30b42bc3bfafc
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/check_abi.rs
@@ -0,0 +1,143 @@
+// run-pass
+//! Test information regarding type layout.
+
+// ignore-stage1
+// ignore-cross-compile
+// ignore-remote
+// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
+
+#![feature(rustc_private)]
+#![feature(assert_matches)]
+#![feature(control_flow_enum)]
+#![feature(ascii_char, ascii_char_variants)]
+
+extern crate rustc_hir;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::abi::{ArgAbi, CallConvention, FieldsShape, PassMode, VariantsShape};
+use stable_mir::mir::mono::Instance;
+use stable_mir::{CrateDef, CrateItem, CrateItems, ItemKind};
+use std::assert_matches::assert_matches;
+use std::convert::TryFrom;
+use std::io::Write;
+use std::ops::ControlFlow;
+
+const CRATE_NAME: &str = "input";
+
+/// This function uses the Stable MIR APIs to get information about the test crate.
+fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+    // Find items in the local crate.
+    let items = stable_mir::all_local_items();
+
+    // Test fn_abi
+    let target_fn = *get_item(&items, (ItemKind::Fn, "fn_abi")).unwrap();
+    let instance = Instance::try_from(target_fn).unwrap();
+    let fn_abi = instance.fn_abi().unwrap();
+    assert_eq!(fn_abi.conv, CallConvention::Rust);
+    assert_eq!(fn_abi.args.len(), 2);
+
+    check_ignore(&fn_abi.args[0]);
+    check_primitive(&fn_abi.args[1]);
+    check_result(fn_abi.ret);
+
+    // Test variadic function.
+    let variadic_fn = *get_item(&items, (ItemKind::Fn, "variadic_fn")).unwrap();
+    check_variadic(variadic_fn);
+
+    ControlFlow::Continue(())
+}
+
+/// Check the variadic function ABI:
+/// ```no_run
+/// pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) -> usize {
+///     0
+/// }
+/// ```
+fn check_variadic(variadic_fn: CrateItem) {
+    let instance = Instance::try_from(variadic_fn).unwrap();
+    let abi = instance.fn_abi().unwrap();
+    assert!(abi.c_variadic);
+    assert_eq!(abi.args.len(), 1);
+}
+
+/// Check the argument to be ignored: `ignore: [u8; 0]`.
+fn check_ignore(abi: &ArgAbi) {
+    assert!(abi.ty.kind().is_array());
+    assert_eq!(abi.mode, PassMode::Ignore);
+    let layout = abi.layout.shape();
+    assert!(layout.is_sized());
+    assert!(layout.is_1zst());
+}
+
+/// Check the primitive argument: `primitive: char`.
+fn check_primitive(abi: &ArgAbi) {
+    assert!(abi.ty.kind().is_char());
+    assert_matches!(abi.mode, PassMode::Direct(_));
+    let layout = abi.layout.shape();
+    assert!(layout.is_sized());
+    assert!(!layout.is_1zst());
+    assert_matches!(layout.fields, FieldsShape::Primitive);
+}
+
+/// Check the return value: `Result<usize, &str>`.
+fn check_result(abi: ArgAbi) {
+    assert!(abi.ty.kind().is_enum());
+    assert_matches!(abi.mode, PassMode::Indirect { .. });
+    let layout = abi.layout.shape();
+    assert!(layout.is_sized());
+    assert_matches!(layout.fields, FieldsShape::Arbitrary { .. });
+    assert_matches!(layout.variants, VariantsShape::Multiple { .. })
+}
+
+fn get_item<'a>(
+    items: &'a CrateItems,
+    item: (ItemKind, &str),
+) -> Option<&'a stable_mir::CrateItem> {
+    items.iter().find(|crate_item| (item.0 == crate_item.kind()) && crate_item.name() == item.1)
+}
+
+/// This test will generate and analyze a dummy crate using the stable mir.
+/// For that, it will first write the dummy crate into a file.
+/// Then it will create a `StableMir` using custom arguments and then
+/// it will run the compiler.
+fn main() {
+    let path = "alloc_input.rs";
+    generate_input(&path).unwrap();
+    let args = vec![
+        "rustc".to_string(),
+        "--crate-type=lib".to_string(),
+        "--crate-name".to_string(),
+        CRATE_NAME.to_string(),
+        path.to_string(),
+    ];
+    run!(args, tcx, test_stable_mir(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+    let mut file = std::fs::File::create(path)?;
+    write!(
+        file,
+        r#"
+        #![feature(c_variadic)]
+        #![allow(unused_variables)]
+
+        pub fn fn_abi(ignore: [u8; 0], primitive: char) -> Result<usize, &'static str> {{
+            // We only care about the signature.
+            todo!()
+        }}
+
+
+        pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) -> usize {{
+            0
+        }}
+        "#
+    )?;
+    Ok(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs
index 8554630e9c999..7ce3597206b62 100644
--- a/tests/ui-fulldeps/stable-mir/check_allocation.rs
+++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs
@@ -209,7 +209,6 @@ fn check_len(item: CrateItem) {
     assert_eq!(alloc.read_uint(), Ok(2));
 }
 
-// Use internal API to find a function in a crate.
 fn get_item<'a>(
     items: &'a CrateItems,
     item: (ItemKind, &str),
diff --git a/tests/ui-fulldeps/stable-mir/check_defs.rs b/tests/ui-fulldeps/stable-mir/check_defs.rs
index ad66751133214..e9a2599d8732c 100644
--- a/tests/ui-fulldeps/stable-mir/check_defs.rs
+++ b/tests/ui-fulldeps/stable-mir/check_defs.rs
@@ -69,9 +69,9 @@ fn extract_elem_ty(ty: Ty) -> Ty {
 
 /// Check signature and type of `Vec::<u8>::new` and its generic version.
 fn test_vec_new(instance: mir::mono::Instance) {
-    let sig = instance.ty().kind().fn_sig().unwrap().skip_binder();
-    assert_matches!(sig.inputs(), &[]);
-    let elem_ty = extract_elem_ty(sig.output());
+    let sig = instance.fn_abi().unwrap();
+    assert_eq!(&sig.args, &[]);
+    let elem_ty = extract_elem_ty(sig.ret.ty);
     assert_matches!(elem_ty.kind(), TyKind::RigidTy(RigidTy::Uint(UintTy::U8)));
 
     // Get the signature for Vec::<T>::new.