Skip to content

Commit

Permalink
Rollup merge of rust-lang#118694 - celinval:smir-alloc-methods, r=ouz-a
Browse files Browse the repository at this point in the history
Add instance evaluation and methods to read an allocation in StableMIR

The instance evaluation is needed to handle intrinsics such as `type_id` and `type_name`.

Since we now use Allocation to represent all evaluated constants, provide a few methods to help process the data inside an allocation.

I've also started to add a structured way to get information about the compilation target machine. For now, I've only added information needed to process an allocation.

r? ````@ouz-a````
  • Loading branch information
GuillaumeGomez committed Dec 8, 2023
2 parents 7db9e23 + 9e15c49 commit 6f4ce6c
Show file tree
Hide file tree
Showing 15 changed files with 458 additions and 29 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4487,6 +4487,7 @@ dependencies = [
name = "rustc_smir"
version = "0.0.0"
dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_hir",
"rustc_middle",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_smir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
# tidy-alphabetical-start
rustc_abi = { path = "../rustc_abi" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_hir = { path = "../rustc_hir" }
rustc_middle = { path = "../rustc_middle" }
Expand Down
37 changes: 26 additions & 11 deletions compiler/rustc_smir/src/rustc_smir/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use rustc_middle::mir::{
interpret::{alloc_range, AllocRange, Pointer},
ConstValue,
};
use stable_mir::Error;

use crate::rustc_smir::{Stable, Tables};
use stable_mir::mir::Mutability;
Expand All @@ -26,23 +27,35 @@ pub fn new_allocation<'tcx>(
const_value: ConstValue<'tcx>,
tables: &mut Tables<'tcx>,
) -> Allocation {
match const_value {
try_new_allocation(ty, const_value, tables).unwrap()
}

#[allow(rustc::usage_of_qualified_ty)]
pub fn try_new_allocation<'tcx>(
ty: rustc_middle::ty::Ty<'tcx>,
const_value: ConstValue<'tcx>,
tables: &mut Tables<'tcx>,
) -> Result<Allocation, Error> {
Ok(match const_value {
ConstValue::Scalar(scalar) => {
let size = scalar.size();
let align = tables
.tcx
.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty))
.unwrap()
.map_err(|e| e.stable(tables))?
.align;
let mut allocation = rustc_middle::mir::interpret::Allocation::uninit(size, align.abi);
allocation
.write_scalar(&tables.tcx, alloc_range(rustc_target::abi::Size::ZERO, size), scalar)
.unwrap();
.map_err(|e| e.stable(tables))?;
allocation.stable(tables)
}
ConstValue::ZeroSized => {
let align =
tables.tcx.layout_of(rustc_middle::ty::ParamEnv::empty().and(ty)).unwrap().align;
let align = tables
.tcx
.layout_of(rustc_middle::ty::ParamEnv::empty().and(ty))
.map_err(|e| e.stable(tables))?
.align;
new_empty_allocation(align.abi)
}
ConstValue::Slice { data, meta } => {
Expand All @@ -51,8 +64,10 @@ pub fn new_allocation<'tcx>(
let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
let scalar_meta =
rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx);
let layout =
tables.tcx.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty)).unwrap();
let layout = tables
.tcx
.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty))
.map_err(|e| e.stable(tables))?;
let mut allocation =
rustc_middle::mir::interpret::Allocation::uninit(layout.size, layout.align.abi);
allocation
Expand All @@ -61,26 +76,26 @@ pub fn new_allocation<'tcx>(
alloc_range(rustc_target::abi::Size::ZERO, tables.tcx.data_layout.pointer_size),
scalar_ptr,
)
.unwrap();
.map_err(|e| e.stable(tables))?;
allocation
.write_scalar(
&tables.tcx,
alloc_range(tables.tcx.data_layout.pointer_size, scalar_meta.size()),
scalar_meta,
)
.unwrap();
.map_err(|e| e.stable(tables))?;
allocation.stable(tables)
}
ConstValue::Indirect { alloc_id, offset } => {
let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
let ty_size = tables
.tcx
.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty))
.unwrap()
.map_err(|e| e.stable(tables))?
.size;
allocation_filter(&alloc.0, alloc_range(offset, ty_size), tables)
}
}
})
}

/// Creates an `Allocation` only from information within the `AllocRange`.
Expand Down
30 changes: 28 additions & 2 deletions compiler/rustc_smir/src/rustc_smir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,29 @@ use stable_mir::compiler_interface::Context;
use stable_mir::mir::alloc::GlobalAlloc;
use stable_mir::mir::mono::{InstanceDef, StaticDef};
use stable_mir::mir::Body;
use stable_mir::target::{MachineInfo, MachineSize};
use stable_mir::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs,
LineInfo, PolyFnSig, RigidTy, Span, TyKind, VariantDef,
LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, VariantDef,
};
use stable_mir::{Crate, CrateItem, DefId, Error, Filename, ItemKind, Symbol};
use std::cell::RefCell;

use crate::rustc_internal::{internal, RustcInternal};
use crate::rustc_smir::builder::BodyBuilder;
use crate::rustc_smir::{new_item_kind, smir_crate, Stable, Tables};
use crate::rustc_smir::{alloc, new_item_kind, smir_crate, Stable, Tables};

impl<'tcx> Context for TablesWrapper<'tcx> {
fn target_info(&self) -> MachineInfo {
let mut tables = self.0.borrow_mut();
MachineInfo {
endian: tables.tcx.data_layout.endian.stable(&mut *tables),
pointer_width: MachineSize::from_bits(
tables.tcx.data_layout.pointer_size.bits().try_into().unwrap(),
),
}
}

fn entry_fn(&self) -> Option<stable_mir::CrateItem> {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
Expand Down Expand Up @@ -382,6 +393,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind).stable(&mut *tables)
}

fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error> {
let mut tables = self.0.borrow_mut();
let instance = tables.instances[def];
let result = tables.tcx.const_eval_instance(
ParamEnv::reveal_all(),
instance,
Some(tables.tcx.def_span(instance.def_id())),
);
result
.map(|const_val| {
alloc::try_new_allocation(const_ty.internal(&mut *tables), const_val, &mut *tables)
})
.map_err(|e| e.stable(&mut *tables))?
}

fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error> {
let mut tables = self.0.borrow_mut();
let def_id = def.0.internal(&mut *tables);
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_smir/src/rustc_smir/convert/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Handle the conversion of different internal errors into a stable version.
//!
//! Currently we encode everything as [stable_mir::Error], which is represented as a string.
use crate::rustc_smir::{Stable, Tables};
use rustc_middle::mir::interpret::AllocError;
use rustc_middle::ty::layout::LayoutError;

impl<'tcx> Stable<'tcx> for LayoutError<'tcx> {
type T = stable_mir::Error;

fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
stable_mir::Error::new(format!("{self:?}"))
}
}

impl<'tcx> Stable<'tcx> for AllocError {
type T = stable_mir::Error;

fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
stable_mir::Error::new(format!("{self:?}"))
}
}
12 changes: 12 additions & 0 deletions compiler/rustc_smir/src/rustc_smir/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use stable_mir::ty::{IndexedVal, VariantIdx};

use crate::rustc_smir::{Stable, Tables};

mod error;
mod mir;
mod ty;

Expand Down Expand Up @@ -75,3 +76,14 @@ 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,
}
}
}
7 changes: 7 additions & 0 deletions compiler/stable_mir/src/compiler_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::cell::Cell;
use crate::mir::alloc::{AllocId, GlobalAlloc};
use crate::mir::mono::{Instance, InstanceDef, StaticDef};
use crate::mir::Body;
use crate::target::MachineInfo;
use crate::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs,
GenericPredicates, Generics, ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl,
Expand Down Expand Up @@ -150,13 +151,19 @@ pub trait Context {
/// Evaluate a static's initializer.
fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error>;

/// Try to evaluate an instance into a constant.
fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error>;

/// Retrieve global allocation for the given allocation ID.
fn global_alloc(&self, id: AllocId) -> GlobalAlloc;

/// Retrieve the id for the virtual table.
fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option<AllocId>;
fn krate(&self, def_id: DefId) -> Crate;
fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol;

/// Return information about the target machine.
fn target_info(&self) -> MachineInfo;
}

// A thread local variable that stores a pointer to the tables mapping between TyCtxt
Expand Down
10 changes: 8 additions & 2 deletions compiler/stable_mir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled.

use std::fmt::{Debug, Display, Formatter};
use std::{error, fmt};
use std::{error, fmt, io};

macro_rules! error {
($fmt: literal $(,)?) => { Error(format!($fmt)) };
($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg:tt)*)) };
($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) };
}

/// An error type used to represent an error that has already been reported by the compiler.
Expand Down Expand Up @@ -78,3 +78,9 @@ where

impl error::Error for Error {}
impl<T> error::Error for CompilerError<T> where T: Display + Debug {}

impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Error(value.to_string())
}
}
1 change: 1 addition & 0 deletions compiler/stable_mir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub mod compiler_interface;
#[macro_use]
pub mod error;
pub mod mir;
pub mod target;
pub mod ty;
pub mod visitor;

Expand Down
36 changes: 34 additions & 2 deletions compiler/stable_mir/src/mir/alloc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! This module provides methods to retrieve allocation information, such as static variables.
use crate::mir::mono::{Instance, StaticDef};
use crate::target::{Endian, MachineInfo};
use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty};
use crate::with;
use crate::{with, Error};
use std::io::Read;

/// An allocation in the SMIR global memory can be either a function pointer,
/// a static, or a "real" allocation with some data in it.
Expand Down Expand Up @@ -38,7 +40,7 @@ impl GlobalAlloc {
}

/// A unique identification number for each provenance
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct AllocId(usize);

impl IndexedVal for AllocId {
Expand All @@ -49,3 +51,33 @@ impl IndexedVal for AllocId {
self.0
}
}

/// Utility function used to read an allocation data into a unassigned integer.
pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result<u128, Error> {
let mut buf = [0u8; std::mem::size_of::<u128>()];
match MachineInfo::target_endianess() {
Endian::Little => {
bytes.read(&mut buf)?;
Ok(u128::from_le_bytes(buf))
}
Endian::Big => {
bytes.read(&mut buf[16 - bytes.len()..])?;
Ok(u128::from_be_bytes(buf))
}
}
}

/// Utility function used to read an allocation data into an assigned integer.
pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result<i128, Error> {
let mut buf = [0u8; std::mem::size_of::<i128>()];
match MachineInfo::target_endianess() {
Endian::Little => {
bytes.read(&mut buf)?;
Ok(i128::from_le_bytes(buf))
}
Endian::Big => {
bytes.read(&mut buf[16 - bytes.len()..])?;
Ok(i128::from_be_bytes(buf))
}
}
}
22 changes: 20 additions & 2 deletions compiler/stable_mir/src/mir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub struct Body {
pub(super) arg_count: usize,

/// Debug information pertaining to user variables, including captures.
pub(super) var_debug_info: Vec<VarDebugInfo>,
pub var_debug_info: Vec<VarDebugInfo>,
}

pub type BasicBlockIdx = usize;
Expand Down Expand Up @@ -616,6 +616,24 @@ pub struct VarDebugInfo {
pub argument_index: Option<u16>,
}

impl VarDebugInfo {
/// Return a local variable if this info is related to one.
pub fn local(&self) -> Option<Local> {
match &self.value {
VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local),
VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None,
}
}

/// Return a constant if this info is related to one.
pub fn constant(&self) -> Option<&ConstOperand> {
match &self.value {
VarDebugInfoContents::Place(_) => None,
VarDebugInfoContents::Const(const_op) => Some(const_op),
}
}
}

pub type SourceScope = u32;

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -832,7 +850,7 @@ pub enum MutBorrowKind {
ClosureCapture,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Mutability {
Not,
Mut,
Expand Down
10 changes: 9 additions & 1 deletion compiler/stable_mir/src/mir/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ impl Instance {
pub fn is_empty_shim(&self) -> bool {
self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def))
}

/// Try to constant evaluate the instance into a constant with the given type.
///
/// This can be used to retrieve a constant that represents an intrinsic return such as
/// `type_id`.
pub fn try_const_eval(&self, const_ty: Ty) -> Result<Allocation, Error> {
with(|cx| cx.eval_instance(self.def, const_ty))
}
}

impl Debug for Instance {
Expand Down Expand Up @@ -212,7 +220,7 @@ impl TryFrom<CrateItem> for StaticDef {
type Error = crate::Error;

fn try_from(value: CrateItem) -> Result<Self, Self::Error> {
if matches!(value.kind(), ItemKind::Static | ItemKind::Const) {
if matches!(value.kind(), ItemKind::Static) {
Ok(StaticDef(value.0))
} else {
Err(Error::new(format!("Expected a static item, but found: {value:?}")))
Expand Down

0 comments on commit 6f4ce6c

Please sign in to comment.