mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-16 17:03:35 +00:00
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:
parent
2a7634047a
commit
1a83c5b55b
@ -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>,
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
229
compiler/rustc_smir/src/rustc_smir/convert/abi.rs
Normal file
229
compiler/rustc_smir/src/rustc_smir/convert/abi.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
286
compiler/stable_mir/src/abi.rs
Normal file
286
compiler/stable_mir/src/abi.rs
Normal file
@ -0,0 +1,286 @@
|
||||
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,
|
||||
}
|
||||
|
||||
impl FnAbi {
|
||||
pub fn is_c_variadic(&self) -> bool {
|
||||
self.args.len() > self.fixed_count as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// Pass a pair's elements directly in two arguments.
|
||||
///
|
||||
/// The argument has a layout abi of `ScalarPair`.
|
||||
Pair,
|
||||
/// Pass the argument after casting it.
|
||||
Cast,
|
||||
/// Pass the argument indirectly via a hidden pointer.
|
||||
Indirect,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
117
tests/ui-fulldeps/stable-mir/check_layout.rs
Normal file
117
tests/ui-fulldeps/stable-mir/check_layout.rs
Normal file
@ -0,0 +1,117 @@
|
||||
// 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, 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();
|
||||
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);
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// 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_eq!(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_eq!(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#"
|
||||
#[allow(unused_variables)]
|
||||
pub fn fn_abi(ignore: [u8; 0], primitive: char) -> Result<usize, &'static str> {{
|
||||
// We only care about the signature.
|
||||
todo!()
|
||||
}}
|
||||
"#
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user