Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add instance evaluation and methods to read an allocation in StableMIR #118694

Merged
merged 4 commits into from
Dec 9, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add instance evaluation and methods to read alloc
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.
celinval committed Dec 8, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 4c9e842a09bdde35bf76bc4690f665dfbac3bfd1
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -4492,6 +4492,7 @@ dependencies = [
name = "rustc_smir"
version = "0.0.0"
dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_hir",
"rustc_middle",
1 change: 1 addition & 0 deletions compiler/rustc_smir/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
37 changes: 26 additions & 11 deletions compiler/rustc_smir/src/rustc_smir/alloc.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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 } => {
@@ -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
@@ -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`.
30 changes: 28 additions & 2 deletions compiler/rustc_smir/src/rustc_smir/context.rs
Original file line number Diff line number Diff line change
@@ -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::{self, 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;
@@ -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);
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
@@ -5,6 +5,7 @@ use stable_mir::ty::{IndexedVal, VariantIdx};

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

mod error;
mod mir;
mod ty;

@@ -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
@@ -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,
@@ -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 the number of bytes for a pointer size.
fn target_info(&self) -> MachineInfo;
}

// A thread local variable that stores a pointer to the tables mapping between TyCtxt
10 changes: 8 additions & 2 deletions compiler/stable_mir/src/error.rs
Original file line number Diff line number Diff line change
@@ -6,11 +6,11 @@

use std::convert::From;
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.
@@ -79,3 +79,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
@@ -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;

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.
@@ -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 {
@@ -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))
}
}
}
2 changes: 1 addition & 1 deletion compiler/stable_mir/src/mir/body.rs
Original file line number Diff line number Diff line change
@@ -832,7 +832,7 @@ pub enum MutBorrowKind {
ClosureCapture,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Mutability {
Not,
Mut,
8 changes: 8 additions & 0 deletions compiler/stable_mir/src/mir/mono.rs
Original file line number Diff line number Diff line change
@@ -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 {
50 changes: 50 additions & 0 deletions compiler/stable_mir/src/target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! Provide information about the machine that this is being compiled into.

use crate::compiler_interface::with;

/// The properties of the target machine being compiled into.
#[derive(Clone, PartialEq, Eq)]
pub struct MachineInfo {
pub endian: Endian,
pub pointer_width: MachineSize,
}

impl MachineInfo {
pub fn target() -> MachineInfo {
with(|cx| cx.target_info().clone())
}

pub fn target_endianess() -> Endian {
with(|cx| cx.target_info().endian)
}

pub fn target_pointer_width() -> MachineSize {
with(|cx| cx.target_info().pointer_width)
}
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Endian {
Little,
Big,
}

/// Represent the size of a component.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MachineSize {
num_bits: usize,
}

impl MachineSize {
pub fn bytes(self) -> usize {
self.num_bits / 8
}

pub fn bits(self) -> usize {
self.num_bits
}

pub fn from_bits(num_bits: usize) -> MachineSize {
MachineSize { num_bits }
}
}
Loading