1
Fork 0

Rollup merge of #119094 - celinval:smir-layout, r=compiler-errors

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.

r? ```@compiler-errors```
This commit is contained in:
Matthias Krüger 2023-12-20 09:46:12 +01:00 committed by GitHub
commit 8b3914ce48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 796 additions and 25 deletions

View file

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

View file

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

View file

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

View file

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

View file

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