Implement a few more rvalue translation to smir

- Introduce an Opaque type for adding information that is still
   internal to the compiler.
This commit is contained in:
Celina G. Val 2023-06-13 15:08:14 -07:00
parent 697563289d
commit b9f378b19b
5 changed files with 318 additions and 10 deletions

View file

@ -4234,8 +4234,8 @@ dependencies = [
"rustc_hir",
"rustc_middle",
"rustc_span",
"scoped-tls",
"rustc_target",
"scoped-tls",
"tracing",
]

View file

@ -13,6 +13,7 @@
#![cfg_attr(not(feature = "default"), feature(rustc_private))]
#![feature(local_key_cell_methods)]
#![feature(ptr_metadata)]
#![feature(type_alias_impl_trait)] // Used to define opaque types.
// Declare extern rustc_* crates to enable building this crate separately from the compiler.
#[cfg(not(feature = "default"))]

View file

@ -3,6 +3,9 @@
//! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
//! until stable MIR is complete.
use std::fmt::Debug;
use std::string::ToString;
use crate::{
rustc_smir::Tables,
stable_mir::{self, with},
@ -49,3 +52,10 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
crate::stable_mir::run(Tables { tcx, def_ids: vec![], types: vec![] }, f);
}
/// A type that provides internal information but that can still be used for debug purpose.
pub type Opaque = impl Debug + ToString + Clone;
pub(crate) fn opaque<T: Debug>(value: &T) -> Opaque {
format!("{value:?}")
}

View file

@ -7,11 +7,13 @@
//!
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
use crate::rustc_internal::{self, opaque};
use crate::stable_mir::ty::{FloatTy, IntTy, RigidTy, TyKind, UintTy};
use crate::stable_mir::{self, Context};
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_target::abi::FieldIdx;
use tracing::debug;
impl<'tcx> Context for Tables<'tcx> {
@ -145,6 +147,13 @@ pub(crate) trait Stable {
fn stable(&self) -> Self::T;
}
impl Stable for DefId {
type T = stable_mir::CrateItem;
fn stable(&self) -> Self::T {
rustc_internal::crate_item(*self)
}
}
impl<'tcx> Stable for mir::Statement<'tcx> {
type T = stable_mir::mir::Statement;
fn stable(&self) -> Self::T {
@ -176,12 +185,18 @@ fn stable(&self) -> Self::T {
match self {
Use(op) => stable_mir::mir::Rvalue::Use(op.stable()),
Repeat(_, _) => todo!(),
Ref(_, _, _) => todo!(),
ThreadLocalRef(_) => todo!(),
AddressOf(_, _) => todo!(),
Len(_) => todo!(),
Ref(region, kind, place) => {
stable_mir::mir::Rvalue::Ref(opaque(region), kind.stable(), place.stable())
}
ThreadLocalRef(def_id) => stable_mir::mir::Rvalue::ThreadLocalRef(def_id.stable()),
AddressOf(mutability, place) => {
stable_mir::mir::Rvalue::AddressOf(mutability.stable(), place.stable())
}
Len(place) => stable_mir::mir::Rvalue::Len(place.stable()),
Cast(_, _, _) => todo!(),
BinaryOp(_, _) => todo!(),
BinaryOp(bin_op, ops) => {
stable_mir::mir::Rvalue::BinaryOp(bin_op.stable(), ops.0.stable(), ops.1.stable())
}
CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp(
bin_op.stable(),
ops.0.stable(),
@ -189,14 +204,119 @@ fn stable(&self) -> Self::T {
),
NullaryOp(_, _) => todo!(),
UnaryOp(un_op, op) => stable_mir::mir::Rvalue::UnaryOp(un_op.stable(), op.stable()),
Discriminant(_) => todo!(),
Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable()),
Aggregate(_, _) => todo!(),
ShallowInitBox(_, _) => todo!(),
CopyForDeref(_) => todo!(),
CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable()),
}
}
}
impl Stable for mir::Mutability {
type T = stable_mir::mir::Mutability;
fn stable(&self) -> Self::T {
use mir::Mutability::*;
match *self {
Not => stable_mir::mir::Mutability::Not,
Mut => stable_mir::mir::Mutability::Mut,
}
}
}
impl Stable for mir::BorrowKind {
type T = stable_mir::mir::BorrowKind;
fn stable(&self) -> Self::T {
use mir::BorrowKind::*;
match *self {
Shared => stable_mir::mir::BorrowKind::Shared,
Shallow => stable_mir::mir::BorrowKind::Shallow,
Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable() },
}
}
}
impl Stable for mir::MutBorrowKind {
type T = stable_mir::mir::MutBorrowKind;
fn stable(&self) -> Self::T {
use mir::MutBorrowKind::*;
match *self {
Default => stable_mir::mir::MutBorrowKind::Default,
TwoPhaseBorrow => stable_mir::mir::MutBorrowKind::TwoPhaseBorrow,
ClosureCapture => stable_mir::mir::MutBorrowKind::ClosureCapture,
}
}
}
impl<'tcx> Stable for mir::NullOp<'tcx> {
type T = stable_mir::mir::NullOp;
fn stable(&self) -> Self::T {
use mir::NullOp::*;
match self {
SizeOf => stable_mir::mir::NullOp::SizeOf,
AlignOf => stable_mir::mir::NullOp::AlignOf,
OffsetOf(indices) => {
stable_mir::mir::NullOp::OffsetOf(indices.iter().map(|idx| idx.stable()).collect())
}
}
}
}
impl Stable for mir::CastKind {
type T = stable_mir::mir::CastKind;
fn stable(&self) -> Self::T {
use mir::CastKind::*;
match self {
PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress,
PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress,
PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable()),
DynStar => stable_mir::mir::CastKind::DynStar,
IntToInt => stable_mir::mir::CastKind::IntToInt,
FloatToInt => stable_mir::mir::CastKind::FloatToInt,
FloatToFloat => stable_mir::mir::CastKind::FloatToFloat,
IntToFloat => stable_mir::mir::CastKind::IntToFloat,
PtrToPtr => stable_mir::mir::CastKind::PtrToPtr,
FnPtrToPtr => stable_mir::mir::CastKind::FnPtrToPtr,
Transmute => stable_mir::mir::CastKind::Transmute,
}
}
}
impl Stable for ty::adjustment::PointerCoercion {
type T = stable_mir::mir::PointerCoercion;
fn stable(&self) -> Self::T {
use ty::adjustment::PointerCoercion;
match self {
PointerCoercion::ReifyFnPointer => stable_mir::mir::PointerCoercion::ReifyFnPointer,
PointerCoercion::UnsafeFnPointer => stable_mir::mir::PointerCoercion::UnsafeFnPointer,
PointerCoercion::ClosureFnPointer(unsafety) => {
stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable())
}
PointerCoercion::MutToConstPointer => {
stable_mir::mir::PointerCoercion::MutToConstPointer
}
PointerCoercion::ArrayToPointer => stable_mir::mir::PointerCoercion::ArrayToPointer,
PointerCoercion::Unsize => stable_mir::mir::PointerCoercion::Unsize,
}
}
}
impl Stable for rustc_hir::Unsafety {
type T = stable_mir::mir::Safety;
fn stable(&self) -> Self::T {
match self {
rustc_hir::Unsafety::Unsafe => stable_mir::mir::Safety::Unsafe,
rustc_hir::Unsafety::Normal => stable_mir::mir::Safety::Normal,
}
}
}
impl Stable for FieldIdx {
type T = usize;
fn stable(&self) -> Self::T {
self.as_usize()
}
}
impl<'tcx> Stable for mir::Operand<'tcx> {
type T = stable_mir::mir::Operand;
fn stable(&self) -> Self::T {

View file

@ -1,4 +1,5 @@
use crate::stable_mir::ty::Ty;
use crate::rustc_internal::Opaque;
use crate::stable_mir::{self, ty::Ty};
#[derive(Clone, Debug)]
pub struct Body {
@ -136,12 +137,98 @@ pub enum Statement {
Nop,
}
type Region = Opaque;
// FIXME this is incomplete
#[derive(Clone, Debug)]
pub enum Rvalue {
Use(Operand),
/// Creates a pointer with the indicated mutability to the place.
///
/// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
/// `&raw v` or `addr_of!(v)`.
AddressOf(Mutability, Place),
/// * `Offset` has the same semantics as [`offset`](pointer::offset), except that the second
/// parameter may be a `usize` as well.
/// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
/// raw pointers, or function pointers and return a `bool`. The types of the operands must be
/// matching, up to the usual caveat of the lifetimes in function pointers.
/// * Left and right shift operations accept signed or unsigned integers not necessarily of the
/// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
/// truncated as needed.
/// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
/// types and return a value of that type.
/// * The remaining operations accept signed integers, unsigned integers, or floats with
/// matching types and return a value of that type.
BinaryOp(BinOp, Operand, Operand),
/// Performs essentially all of the casts that can be performed via `as`.
///
/// This allows for casts from/to a variety of types.
Cast(CastKind, Operand, Ty),
/// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
///
/// For addition, subtraction, and multiplication on integers the error condition is set when
/// the infinite precision result would not be equal to the actual result.
CheckedBinaryOp(BinOp, Operand, Operand),
/// A CopyForDeref is equivalent to a read from a place.
/// When such a read happens, it is guaranteed that the only use of the returned value is a
/// deref operation, immediately followed by one or more projections.
CopyForDeref(Place),
/// Computes the discriminant of the place, returning it as an integer of type
/// [`discriminant_ty`]. Returns zero for types without discriminant.
///
/// The validity requirements for the underlying value are undecided for this rvalue, see
/// [#91095]. Note too that the value of the discriminant is not the same thing as the
/// variant index; use [`discriminant_for_variant`] to convert.
///
/// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
/// [#91095]: https://github.com/rust-lang/rust/issues/91095
/// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
Discriminant(Place),
/// Yields the length of the place, as a `usize`.
///
/// If the type of the place is an array, this is the array length. For slices (`[T]`, not
/// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
/// ill-formed for places of other types.
Len(Place),
/// Creates a reference to the place.
Ref(Region, BorrowKind, Place),
/// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
///
/// This is different from a normal transmute because dataflow analysis will treat the box as
/// initialized but its content as uninitialized. Like other pointer casts, this in general
/// affects alias analysis.
ShallowInitBox(Operand, Ty),
/// Creates a pointer/reference to the given thread local.
///
/// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
/// `*const T`, and if neither of those apply a `&T`.
///
/// **Note:** This is a runtime operation that actually executes code and is in this sense more
/// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
/// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
///
/// **Needs clarification**: Are there weird additional semantics here related to the runtime
/// nature of this operation?
ThreadLocalRef(stable_mir::CrateItem),
/// Exactly like `BinaryOp`, but less operands.
///
/// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
/// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
/// return a value with the same type as their operand.
UnaryOp(UnOp, Operand),
/// Yields the operand unchanged
Use(Operand),
}
#[derive(Clone, Debug)]
@ -157,8 +244,98 @@ pub struct Place {
pub projection: String,
}
type FieldIdx = usize;
#[derive(Clone, Debug)]
pub struct SwitchTarget {
pub value: u128,
pub target: usize,
}
#[derive(Clone, Debug)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
/// The immediately borrowed place must be immutable, but projections from
/// it don't need to be. For example, a shallow borrow of `a.b` doesn't
/// conflict with a mutable borrow of `a.b.c`.
Shallow,
/// Data is mutable and not aliasable.
Mut {
/// `true` if this borrow arose from method-call auto-ref
kind: MutBorrowKind,
},
}
#[derive(Clone, Debug)]
pub enum MutBorrowKind {
Default,
TwoPhaseBorrow,
ClosureCapture,
}
#[derive(Clone, Debug)]
pub enum Mutability {
Not,
Mut,
}
#[derive(Clone, Debug)]
pub enum Safety {
Unsafe,
Normal,
}
#[derive(Clone, Debug)]
pub enum PointerCoercion {
/// Go from a fn-item type to a fn-pointer type.
ReifyFnPointer,
/// Go from a safe fn pointer to an unsafe fn pointer.
UnsafeFnPointer,
/// Go from a non-capturing closure to an fn pointer or an unsafe fn pointer.
/// It cannot convert a closure that requires unsafe.
ClosureFnPointer(Safety),
/// Go from a mut raw pointer to a const raw pointer.
MutToConstPointer,
/// Go from `*const [T; N]` to `*const T`
ArrayToPointer,
/// Unsize a pointer/reference value, e.g., `&[T; n]` to
/// `&[T]`. Note that the source could be a thin or fat pointer.
/// This will do things like convert thin pointers to fat
/// pointers, or convert structs containing thin pointers to
/// structs containing fat pointers, or convert between fat
/// pointers.
Unsize,
}
#[derive(Clone, Debug)]
pub enum CastKind {
PointerExposeAddress,
PointerFromExposedAddress,
PointerCoercion(PointerCoercion),
DynStar,
IntToInt,
FloatToInt,
FloatToFloat,
IntToFloat,
PtrToPtr,
FnPtrToPtr,
Transmute,
}
#[derive(Clone, Debug)]
pub enum NullOp {
/// Returns the size of a value of that type.
SizeOf,
/// Returns the minimum alignment of a type.
AlignOf,
/// Returns the offset of a field.
OffsetOf(Vec<FieldIdx>),
}