Skip to content

Commit

Permalink
Add methods to create constants
Browse files Browse the repository at this point in the history
I've been experimenting with transforming the StableMIR to instrument
the code with potential UB checks. The modified body will only
be used by our analysis tool, however, constants in StableMIR must be
backed by rustc constants. Thus, I'm adding a few functions to build
constants, such as building string and other primitives.
  • Loading branch information
celinval committed Mar 12, 2024
1 parent 7de1a1f commit c076509
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 12 deletions.
58 changes: 51 additions & 7 deletions compiler/rustc_smir/src/rustc_smir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ use stable_mir::mir::Body;
use stable_mir::target::{MachineInfo, MachineSize};
use stable_mir::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, VariantDef,
ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, UintTy,
VariantDef,
};
use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol};
use std::cell::RefCell;
Expand Down Expand Up @@ -341,15 +342,56 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
.ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
}

fn usize_to_const(&self, val: u64) -> Result<Const, Error> {
fn try_new_const_zst(&self, ty: Ty) -> Result<Const, Error> {
let mut tables = self.0.borrow_mut();
let ty = tables.tcx.types.usize;
let tcx = tables.tcx;
let ty_internal = ty.internal(&mut *tables, tcx);
let size = tables
.tcx
.layout_of(ParamEnv::empty().and(ty_internal))
.map_err(|err| {
Error::new(format!(
"Cannot create a zero-sized constant for type `{ty_internal}`: {err}"
))
})?
.size;
if size.bytes() != 0 {
return Err(Error::new(format!(
"Cannot create a zero-sized constant for type `{ty_internal}`: \
Type `{ty_internal}` has {} bytes",
size.bytes()
)));
}

Ok(ty::Const::zero_sized(tables.tcx, ty_internal).stable(&mut *tables))
}

fn new_const_str(&self, value: &str) -> Const {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let ty = ty::Ty::new_static_str(tcx);
let bytes = value.as_bytes();
let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes);

ty::Const::new_value(tcx, val_tree, ty).stable(&mut *tables)
}

fn new_const_bool(&self, value: bool) -> Const {
let mut tables = self.0.borrow_mut();
ty::Const::from_bool(tables.tcx, value).stable(&mut *tables)
}

fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<Const, Error> {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx));
let size = tables.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap().size;

let scalar = ScalarInt::try_from_uint(val, size).ok_or_else(|| {
Error::new(format!("Value overflow: cannot convert `{val}` to usize."))
// We don't use Const::from_bits since it doesn't have any error checking.
let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| {
Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`."))
})?;
Ok(rustc_middle::ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty)
Ok(ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty)
.stable(&mut *tables))
}

Expand Down Expand Up @@ -556,7 +598,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
global_alloc: &GlobalAlloc,
) -> Option<stable_mir::mir::alloc::AllocId> {
let mut tables = self.0.borrow_mut();
let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else { return None };
let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else {
return None;
};
let tcx = tables.tcx;
let alloc_id = tables.tcx.vtable_allocation((
ty.internal(&mut *tables, tcx),
Expand Down
17 changes: 13 additions & 4 deletions compiler/stable_mir/src/compiler_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics,
ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyKind,
VariantDef,
UintTy, VariantDef,
};
use crate::{
mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind,
Expand Down Expand Up @@ -101,8 +101,17 @@ pub trait Context {
/// Evaluate constant as a target usize.
fn eval_target_usize(&self, cnst: &Const) -> Result<u64, Error>;

/// Create a target usize constant for the given value.
fn usize_to_const(&self, val: u64) -> Result<Const, Error>;
/// Create a new zero-sized constant.
fn try_new_const_zst(&self, ty: Ty) -> Result<Const, Error>;

/// Create a new constant that represents the given string value.
fn new_const_str(&self, value: &str) -> Const;

/// Create a new constant that represents the given boolean value.
fn new_const_bool(&self, value: bool) -> Const;

/// Create a new constant that represents the given value.
fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<Const, Error>;

/// Create a new type from the given kind.
fn new_rigid_ty(&self, kind: RigidTy) -> Ty;
Expand Down Expand Up @@ -199,7 +208,7 @@ pub trait Context {

// A thread local variable that stores a pointer to the tables mapping between TyCtxt
// datastructures and stable MIR datastructures
scoped_thread_local! (static TLV: Cell<*const ()>);
scoped_thread_local!(static TLV: Cell<*const ()>);

pub fn run<F, T>(context: &dyn Context, f: F) -> Result<T, Error>
where
Expand Down
27 changes: 26 additions & 1 deletion compiler/stable_mir/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,38 @@ impl Const {

/// Creates an interned usize constant.
fn try_from_target_usize(val: u64) -> Result<Self, Error> {
with(|cx| cx.usize_to_const(val))
with(|cx| cx.try_new_const_uint(val.into(), UintTy::Usize))
}

/// Try to evaluate to a target `usize`.
pub fn eval_target_usize(&self) -> Result<u64, Error> {
with(|cx| cx.eval_target_usize(self))
}

/// Create a constant that represents a new zero-sized constant of type T.
/// Fails if the type is not a ZST or if it doesn't have a known size.
pub fn try_new_zero_sized(ty: Ty) -> Result<Const, Error> {
with(|cx| cx.try_new_const_zst(ty))
}

/// Build a new constant that represents the given string.
///
/// Note that there is no guarantee today about duplication of the same constant.
/// I.e.: Calling this function multiple times with the same argument may or may not return
/// the same allocation.
pub fn from_str(value: &str) -> Const {
with(|cx| cx.new_const_str(value))
}

/// Build a new constant that represents the given boolean value.
pub fn from_bool(value: bool) -> Const {
with(|cx| cx.new_const_bool(value))
}

/// Build a new constant that represents the given unsigned integer.
pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result<Const, Error> {
with(|cx| cx.try_new_const_uint(value, uint_ty))
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down

0 comments on commit c076509

Please sign in to comment.