1
Fork 0

Add function ABI and type layout to StableMIR

This change introduces a new module to StableMIR named `abi` with
information from `rustc_target::abi` and `rustc_abi`, that allow users
to retrieve more low level information required to perform
bit-precise analysis.

The layout of a type can be retrieved via `Ty::layout`, and the instance
ABI can be retrieved via `Instance::fn_abi()`.

To properly handle errors while retrieve layout information, we had
to implement a few layout related traits.
This commit is contained in:
Celina G. Val 2023-12-18 19:52:25 +00:00
parent 2a7634047a
commit 1a83c5b55b
14 changed files with 760 additions and 25 deletions

View file

@ -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>,

View file

@ -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))
}

View file

@ -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()
}
}

View file

@ -0,0 +1,229 @@
//! 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 {
FnAbi {
args: self.args.as_ref().stable(tables),
ret: self.ret.stable(tables),
fixed_count: self.fixed_count,
conv: self.conv.stable(tables),
}
}
}
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(_) => PassMode::Direct,
rustc_target::abi::call::PassMode::Pair(_, _) => PassMode::Pair,
rustc_target::abi::call::PassMode::Cast { .. } => PassMode::Cast,
rustc_target::abi::call::PassMode::Indirect { .. } => PassMode::Indirect,
}
}
}
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)
}
}

View file

@ -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,
}
}
}

View file

@ -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))
}
}