Skip to content

Commit

Permalink
Rollup merge of rust-lang#121824 - celinval:smir-scalar, r=oli-obk
Browse files Browse the repository at this point in the history
Implement missing ABI structures in StableMIR

Add implementations for Scalar, Primitive and WrappingRange for StableMIR.

FYI, I thought about reusing the `rustc_abi` module, since it is designed to not necessarily depend on the `rustc` internals, but the maintenance burden to maintain this crate in crates.io doesn't seem worth it at this point.

Fixes rust-lang/project-stable-mir#58
  • Loading branch information
matthiaskrgr authored Mar 1, 2024
2 parents 7a4a471 + 3898029 commit 57ac137
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 25 deletions.
66 changes: 59 additions & 7 deletions compiler/rustc_smir/src/rustc_smir/convert/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use crate::rustc_smir::{Stable, Tables};
use rustc_middle::ty;
use rustc_target::abi::call::Conv;
use stable_mir::abi::{
ArgAbi, CallConvention, FieldsShape, FnAbi, Layout, LayoutShape, PassMode, TagEncoding,
TyAndLayout, ValueAbi, VariantsShape,
AddressSpace, ArgAbi, CallConvention, FieldsShape, FnAbi, IntegerLength, Layout, LayoutShape,
PassMode, Primitive, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange,
};
use stable_mir::ty::{Align, IndexedVal, Size, VariantIdx};
use stable_mir::{opaque, Opaque};
use stable_mir::opaque;
use stable_mir::target::MachineSize as Size;
use stable_mir::ty::{Align, IndexedVal, VariantIdx};

impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
type T = VariantIdx;
Expand Down Expand Up @@ -220,7 +221,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Size {
type T = Size;

fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
self.bytes_usize()
Size::from_bits(self.bits_usize())
}
}

Expand All @@ -233,9 +234,60 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Align {
}

impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
type T = Opaque;
type T = Scalar;

fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
match self {
rustc_abi::Scalar::Initialized { value, valid_range } => Scalar::Initialized {
value: value.stable(tables),
valid_range: valid_range.stable(tables),
},
rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables) },
}
}
}

impl<'tcx> Stable<'tcx> for rustc_abi::Primitive {
type T = Primitive;

fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
match self {
rustc_abi::Primitive::Int(length, signed) => {
Primitive::Int { length: length.stable(tables), signed: *signed }
}
rustc_abi::Primitive::F32 => Primitive::F32,
rustc_abi::Primitive::F64 => Primitive::F64,
rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables)),
}
}
}

impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace {
type T = AddressSpace;

fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
AddressSpace(self.0)
}
}

impl<'tcx> Stable<'tcx> for rustc_abi::Integer {
type T = IntegerLength;

fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
match self {
rustc_abi::Integer::I8 => IntegerLength::I8,
rustc_abi::Integer::I16 => IntegerLength::I16,
rustc_abi::Integer::I32 => IntegerLength::I32,
rustc_abi::Integer::I64 => IntegerLength::I64,
rustc_abi::Integer::I128 => IntegerLength::I128,
}
}
}

impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange {
type T = WrappingRange;

fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
opaque(self)
WrappingRange { start: self.start, end: self.end }
}
}
159 changes: 155 additions & 4 deletions compiler/stable_mir/src/abi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use crate::compiler_interface::with;
use crate::error;
use crate::mir::FieldIdx;
use crate::ty::{Align, IndexedVal, Size, Ty, VariantIdx};
use crate::target::{MachineInfo, MachineSize as Size};
use crate::ty::{Align, IndexedVal, Ty, VariantIdx};
use crate::Error;
use crate::Opaque;
use std::fmt::{self, Debug};
use std::num::NonZeroUsize;
use std::ops::RangeInclusive;

Expand Down Expand Up @@ -100,7 +104,7 @@ impl LayoutShape {

/// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1).
pub fn is_1zst(&self) -> bool {
self.is_sized() && self.size == 0 && self.abi_align == 1
self.is_sized() && self.size.bits() == 0 && self.abi_align == 1
}
}

Expand Down Expand Up @@ -245,8 +249,155 @@ impl ValueAbi {
}
}

/// We currently do not support `Scalar`, and use opaque instead.
type Scalar = Opaque;
/// Information about one scalar component of a Rust type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Scalar {
Initialized {
/// The primitive type used to represent this value.
value: Primitive,
/// The range that represents valid values.
/// The range must be valid for the `primitive` size.
valid_range: WrappingRange,
},
Union {
/// Unions never have niches, so there is no `valid_range`.
/// Even for unions, we need to use the correct registers for the kind of
/// values inside the union, so we keep the `Primitive` type around.
/// It is also used to compute the size of the scalar.
value: Primitive,
},
}

impl Scalar {
pub fn has_niche(&self, target: &MachineInfo) -> bool {
match self {
Scalar::Initialized { value, valid_range } => {
!valid_range.is_full(value.size(target)).unwrap()
}
Scalar::Union { .. } => false,
}
}
}

/// Fundamental unit of memory access and layout.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Primitive {
/// The `bool` is the signedness of the `Integer` type.
///
/// One would think we would not care about such details this low down,
/// but some ABIs are described in terms of C types and ISAs where the
/// integer arithmetic is done on {sign,zero}-extended registers, e.g.
/// a negative integer passed by zero-extension will appear positive in
/// the callee, and most operations on it will produce the wrong values.
Int {
length: IntegerLength,
signed: bool,
},
F32,
F64,
Pointer(AddressSpace),
}

impl Primitive {
pub fn size(self, target: &MachineInfo) -> Size {
match self {
Primitive::Int { length, .. } => Size::from_bits(length.bits()),
Primitive::F32 => Size::from_bits(32),
Primitive::F64 => Size::from_bits(64),
Primitive::Pointer(_) => target.pointer_width,
}
}
}

/// Enum representing the existing integer lengths.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum IntegerLength {
I8,
I16,
I32,
I64,
I128,
}

impl IntegerLength {
pub fn bits(self) -> usize {
match self {
IntegerLength::I8 => 8,
IntegerLength::I16 => 16,
IntegerLength::I32 => 32,
IntegerLength::I64 => 64,
IntegerLength::I128 => 128,
}
}
}

/// An identifier that specifies the address space that some operation
/// should operate on. Special address spaces have an effect on code generation,
/// depending on the target and the address spaces it implements.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AddressSpace(pub u32);

impl AddressSpace {
/// The default address space, corresponding to data space.
pub const DATA: Self = AddressSpace(0);
}

/// Inclusive wrap-around range of valid values (bitwise representation), that is, if
/// start > end, it represents `start..=MAX`, followed by `0..=end`.
///
/// That is, for an i8 primitive, a range of `254..=2` means following
/// sequence:
///
/// 254 (-2), 255 (-1), 0, 1, 2
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct WrappingRange {
pub start: u128,
pub end: u128,
}

impl WrappingRange {
/// Returns `true` if `size` completely fills the range.
#[inline]
pub fn is_full(&self, size: Size) -> Result<bool, Error> {
let Some(max_value) = size.unsigned_int_max() else {
return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits()));
};
if self.start <= max_value && self.end <= max_value {
Ok(self.start == 0 && max_value == self.end)
} else {
Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits()))
}
}

/// Returns `true` if `v` is contained in the range.
#[inline(always)]
pub fn contains(&self, v: u128) -> bool {
if self.wraps_around() {
self.start <= v || v <= self.end
} else {
self.start <= v && v <= self.end
}
}

/// Returns `true` if the range wraps around.
/// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`.
/// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`.
#[inline]
pub fn wraps_around(&self) -> bool {
self.start > self.end
}
}

impl Debug for WrappingRange {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.start > self.end {
write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
} else {
write!(fmt, "{}..={}", self.start, self.end)?;
}
Ok(())
}
}

/// General language calling conventions.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
Expand Down
11 changes: 7 additions & 4 deletions compiler/stable_mir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
//! - [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, io};
use std::{fmt, io};

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

pub(crate) use error;

/// An error type used to represent an error that has already been reported by the compiler.
#[derive(Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -72,8 +74,9 @@ where
}
}

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

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

impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Expand Down
10 changes: 9 additions & 1 deletion compiler/stable_mir/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,29 @@ pub enum Endian {
}

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

impl MachineSize {
#[inline(always)]
pub fn bytes(self) -> usize {
self.num_bits / 8
}

#[inline(always)]
pub fn bits(self) -> usize {
self.num_bits
}

#[inline(always)]
pub fn from_bits(num_bits: usize) -> MachineSize {
MachineSize { num_bits }
}

#[inline]
pub fn unsigned_int_max(self) -> Option<u128> {
(self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits()))
}
}
7 changes: 6 additions & 1 deletion compiler/stable_mir/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,9 @@ impl TyKind {

#[inline]
pub fn is_cstr(&self) -> bool {
let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { return false };
let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else {
return false;
};
with(|cx| cx.adt_is_cstr(*def))
}

Expand Down Expand Up @@ -1032,10 +1034,13 @@ pub struct BoundTy {
}

pub type Bytes = Vec<Option<u8>>;

/// Size in bytes.
pub type Size = usize;

#[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>;
Expand Down
Loading

0 comments on commit 57ac137

Please sign in to comment.