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.
This commit is contained in:
parent
370c91100c
commit
4c9e842a09
14 changed files with 295 additions and 22 deletions
|
@ -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,6 +151,9 @@ 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;
|
||||
|
||||
|
@ -157,6 +161,9 @@ pub trait Context {
|
|||
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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
compiler/stable_mir/src/target.rs
Normal file
50
compiler/stable_mir/src/target.rs
Normal file
|
@ -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 }
|
||||
}
|
||||
}
|
|
@ -4,9 +4,11 @@ use super::{
|
|||
with, DefId, Error, Symbol,
|
||||
};
|
||||
use crate::crate_def::CrateDef;
|
||||
use crate::mir::alloc::AllocId;
|
||||
use crate::mir::alloc::{read_target_int, read_target_uint, AllocId};
|
||||
use crate::target::MachineInfo;
|
||||
use crate::{Filename, Opaque};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Ty(pub usize);
|
||||
|
@ -366,6 +368,19 @@ pub enum IntTy {
|
|||
I128,
|
||||
}
|
||||
|
||||
impl IntTy {
|
||||
pub fn num_bytes(self) -> usize {
|
||||
match self {
|
||||
IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes().into(),
|
||||
IntTy::I8 => 1,
|
||||
IntTy::I16 => 2,
|
||||
IntTy::I32 => 4,
|
||||
IntTy::I64 => 8,
|
||||
IntTy::I128 => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum UintTy {
|
||||
Usize,
|
||||
|
@ -376,6 +391,19 @@ pub enum UintTy {
|
|||
U128,
|
||||
}
|
||||
|
||||
impl UintTy {
|
||||
pub fn num_bytes(self) -> usize {
|
||||
match self {
|
||||
UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes().into(),
|
||||
UintTy::U8 => 1,
|
||||
UintTy::U16 => 2,
|
||||
UintTy::U32 => 4,
|
||||
UintTy::U64 => 8,
|
||||
UintTy::U128 => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum FloatTy {
|
||||
F32,
|
||||
|
@ -821,21 +849,21 @@ pub struct BoundTy {
|
|||
pub type Bytes = Vec<Option<u8>>;
|
||||
pub type Size = usize;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct Prov(pub AllocId);
|
||||
pub type Align = u64;
|
||||
pub type Promoted = u32;
|
||||
pub type InitMaskMaterialized = Vec<u64>;
|
||||
|
||||
/// Stores the provenance information of pointers stored in memory.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct ProvenanceMap {
|
||||
/// Provenance in this map applies from the given offset for an entire pointer-size worth of
|
||||
/// bytes. Two entries in this map are always at least a pointer size apart.
|
||||
pub ptrs: Vec<(Size, Prov)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct Allocation {
|
||||
pub bytes: Bytes,
|
||||
pub provenance: ProvenanceMap,
|
||||
|
@ -843,6 +871,70 @@ pub struct Allocation {
|
|||
pub mutability: Mutability,
|
||||
}
|
||||
|
||||
impl Allocation {
|
||||
/// Get a vector of bytes for an Allocation that has been fully initialized
|
||||
pub fn raw_bytes(&self) -> Result<Vec<u8>, Error> {
|
||||
self.bytes
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes))
|
||||
}
|
||||
|
||||
/// Read a uint value from the specified range.
|
||||
pub fn read_partial_uint(&self, range: Range<usize>) -> Result<u128, Error> {
|
||||
if range.end - range.start > 16 {
|
||||
return Err(error!("Allocation is bigger than largest integer"));
|
||||
}
|
||||
if range.end > self.bytes.len() {
|
||||
return Err(error!(
|
||||
"Range is out of bounds. Allocation length is `{}`, but requested range `{:?}`",
|
||||
self.bytes.len(),
|
||||
range
|
||||
));
|
||||
}
|
||||
let raw = self.bytes[range]
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes))?;
|
||||
read_target_uint(&raw)
|
||||
}
|
||||
|
||||
pub fn read_uint(&self) -> Result<u128, Error> {
|
||||
if self.bytes.len() > 16 {
|
||||
return Err(error!("Allocation is bigger than largest integer"));
|
||||
}
|
||||
let raw = self.raw_bytes()?;
|
||||
read_target_uint(&raw)
|
||||
}
|
||||
|
||||
pub fn read_int(&self) -> Result<i128, Error> {
|
||||
if self.bytes.len() > 16 {
|
||||
return Err(error!("Allocation is bigger than largest integer"));
|
||||
}
|
||||
let raw = self.raw_bytes()?;
|
||||
read_target_int(&raw)
|
||||
}
|
||||
|
||||
pub fn read_bool(&self) -> Result<bool, Error> {
|
||||
match self.read_int()? {
|
||||
0 => Ok(false),
|
||||
1 => Ok(true),
|
||||
val @ _ => Err(error!("Unexpected value for bool: `{val}`")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> Result<bool, Error> {
|
||||
let len = self.bytes.len();
|
||||
let ptr_len = MachineInfo::target_pointer_width().bytes();
|
||||
if len != ptr_len {
|
||||
return Err(error!("Expected width of pointer (`{ptr_len}`), but found: `{len}`"));
|
||||
}
|
||||
Ok(self.read_uint()? == 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ConstantKind {
|
||||
Allocated(Allocation),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue