Skip to content

Commit 63e916e

Browse files
authored
Rollup merge of rust-lang#121824 - celinval:smir-scalar, r=oli-obk
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
2 parents d2d1823 + 0567162 commit 63e916e

File tree

6 files changed

+296
-25
lines changed

6 files changed

+296
-25
lines changed

compiler/rustc_smir/src/rustc_smir/convert/abi.rs

+62-7
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ use crate::rustc_smir::{Stable, Tables};
66
use rustc_middle::ty;
77
use rustc_target::abi::call::Conv;
88
use stable_mir::abi::{
9-
ArgAbi, CallConvention, FieldsShape, FnAbi, Layout, LayoutShape, PassMode, TagEncoding,
10-
TyAndLayout, ValueAbi, VariantsShape,
9+
AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, Layout,
10+
LayoutShape, PassMode, Primitive, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantsShape,
11+
WrappingRange,
1112
};
12-
use stable_mir::ty::{Align, IndexedVal, Size, VariantIdx};
13-
use stable_mir::{opaque, Opaque};
13+
use stable_mir::opaque;
14+
use stable_mir::target::MachineSize as Size;
15+
use stable_mir::ty::{Align, IndexedVal, VariantIdx};
1416

1517
impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
1618
type T = VariantIdx;
@@ -220,7 +222,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Size {
220222
type T = Size;
221223

222224
fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
223-
self.bytes_usize()
225+
Size::from_bits(self.bits_usize())
224226
}
225227
}
226228

@@ -233,9 +235,62 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Align {
233235
}
234236

235237
impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
236-
type T = Opaque;
238+
type T = Scalar;
239+
240+
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
241+
match self {
242+
rustc_abi::Scalar::Initialized { value, valid_range } => Scalar::Initialized {
243+
value: value.stable(tables),
244+
valid_range: valid_range.stable(tables),
245+
},
246+
rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables) },
247+
}
248+
}
249+
}
250+
251+
impl<'tcx> Stable<'tcx> for rustc_abi::Primitive {
252+
type T = Primitive;
253+
254+
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
255+
match self {
256+
rustc_abi::Primitive::Int(length, signed) => {
257+
Primitive::Int { length: length.stable(tables), signed: *signed }
258+
}
259+
rustc_abi::Primitive::F16 => Primitive::Float { length: FloatLength::F16 },
260+
rustc_abi::Primitive::F32 => Primitive::Float { length: FloatLength::F32 },
261+
rustc_abi::Primitive::F64 => Primitive::Float { length: FloatLength::F64 },
262+
rustc_abi::Primitive::F128 => Primitive::Float { length: FloatLength::F128 },
263+
rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables)),
264+
}
265+
}
266+
}
267+
268+
impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace {
269+
type T = AddressSpace;
270+
271+
fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
272+
AddressSpace(self.0)
273+
}
274+
}
275+
276+
impl<'tcx> Stable<'tcx> for rustc_abi::Integer {
277+
type T = IntegerLength;
278+
279+
fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
280+
match self {
281+
rustc_abi::Integer::I8 => IntegerLength::I8,
282+
rustc_abi::Integer::I16 => IntegerLength::I16,
283+
rustc_abi::Integer::I32 => IntegerLength::I32,
284+
rustc_abi::Integer::I64 => IntegerLength::I64,
285+
rustc_abi::Integer::I128 => IntegerLength::I128,
286+
}
287+
}
288+
}
289+
290+
impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange {
291+
type T = WrappingRange;
237292

238293
fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
239-
opaque(self)
294+
WrappingRange { start: self.start, end: self.end }
240295
}
241296
}

compiler/stable_mir/src/abi.rs

+175-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use crate::compiler_interface::with;
2+
use crate::error;
23
use crate::mir::FieldIdx;
3-
use crate::ty::{Align, IndexedVal, Size, Ty, VariantIdx};
4+
use crate::target::{MachineInfo, MachineSize as Size};
5+
use crate::ty::{Align, IndexedVal, Ty, VariantIdx};
6+
use crate::Error;
47
use crate::Opaque;
8+
use std::fmt::{self, Debug};
59
use std::num::NonZeroUsize;
610
use std::ops::RangeInclusive;
711

@@ -100,7 +104,7 @@ impl LayoutShape {
100104

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

@@ -245,8 +249,175 @@ impl ValueAbi {
245249
}
246250
}
247251

248-
/// We currently do not support `Scalar`, and use opaque instead.
249-
type Scalar = Opaque;
252+
/// Information about one scalar component of a Rust type.
253+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
254+
pub enum Scalar {
255+
Initialized {
256+
/// The primitive type used to represent this value.
257+
value: Primitive,
258+
/// The range that represents valid values.
259+
/// The range must be valid for the `primitive` size.
260+
valid_range: WrappingRange,
261+
},
262+
Union {
263+
/// Unions never have niches, so there is no `valid_range`.
264+
/// Even for unions, we need to use the correct registers for the kind of
265+
/// values inside the union, so we keep the `Primitive` type around.
266+
/// It is also used to compute the size of the scalar.
267+
value: Primitive,
268+
},
269+
}
270+
271+
impl Scalar {
272+
pub fn has_niche(&self, target: &MachineInfo) -> bool {
273+
match self {
274+
Scalar::Initialized { value, valid_range } => {
275+
!valid_range.is_full(value.size(target)).unwrap()
276+
}
277+
Scalar::Union { .. } => false,
278+
}
279+
}
280+
}
281+
282+
/// Fundamental unit of memory access and layout.
283+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
284+
pub enum Primitive {
285+
/// The `bool` is the signedness of the `Integer` type.
286+
///
287+
/// One would think we would not care about such details this low down,
288+
/// but some ABIs are described in terms of C types and ISAs where the
289+
/// integer arithmetic is done on {sign,zero}-extended registers, e.g.
290+
/// a negative integer passed by zero-extension will appear positive in
291+
/// the callee, and most operations on it will produce the wrong values.
292+
Int {
293+
length: IntegerLength,
294+
signed: bool,
295+
},
296+
Float {
297+
length: FloatLength,
298+
},
299+
Pointer(AddressSpace),
300+
}
301+
302+
impl Primitive {
303+
pub fn size(self, target: &MachineInfo) -> Size {
304+
match self {
305+
Primitive::Int { length, .. } => Size::from_bits(length.bits()),
306+
Primitive::Float { length } => Size::from_bits(length.bits()),
307+
Primitive::Pointer(_) => target.pointer_width,
308+
}
309+
}
310+
}
311+
312+
/// Enum representing the existing integer lengths.
313+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
314+
pub enum IntegerLength {
315+
I8,
316+
I16,
317+
I32,
318+
I64,
319+
I128,
320+
}
321+
322+
/// Enum representing the existing float lengths.
323+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
324+
pub enum FloatLength {
325+
F16,
326+
F32,
327+
F64,
328+
F128,
329+
}
330+
331+
impl IntegerLength {
332+
pub fn bits(self) -> usize {
333+
match self {
334+
IntegerLength::I8 => 8,
335+
IntegerLength::I16 => 16,
336+
IntegerLength::I32 => 32,
337+
IntegerLength::I64 => 64,
338+
IntegerLength::I128 => 128,
339+
}
340+
}
341+
}
342+
343+
impl FloatLength {
344+
pub fn bits(self) -> usize {
345+
match self {
346+
FloatLength::F16 => 16,
347+
FloatLength::F32 => 32,
348+
FloatLength::F64 => 64,
349+
FloatLength::F128 => 128,
350+
}
351+
}
352+
}
353+
354+
/// An identifier that specifies the address space that some operation
355+
/// should operate on. Special address spaces have an effect on code generation,
356+
/// depending on the target and the address spaces it implements.
357+
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
358+
pub struct AddressSpace(pub u32);
359+
360+
impl AddressSpace {
361+
/// The default address space, corresponding to data space.
362+
pub const DATA: Self = AddressSpace(0);
363+
}
364+
365+
/// Inclusive wrap-around range of valid values (bitwise representation), that is, if
366+
/// start > end, it represents `start..=MAX`, followed by `0..=end`.
367+
///
368+
/// That is, for an i8 primitive, a range of `254..=2` means following
369+
/// sequence:
370+
///
371+
/// 254 (-2), 255 (-1), 0, 1, 2
372+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
373+
pub struct WrappingRange {
374+
pub start: u128,
375+
pub end: u128,
376+
}
377+
378+
impl WrappingRange {
379+
/// Returns `true` if `size` completely fills the range.
380+
#[inline]
381+
pub fn is_full(&self, size: Size) -> Result<bool, Error> {
382+
let Some(max_value) = size.unsigned_int_max() else {
383+
return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits()));
384+
};
385+
if self.start <= max_value && self.end <= max_value {
386+
Ok(self.start == 0 && max_value == self.end)
387+
} else {
388+
Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits()))
389+
}
390+
}
391+
392+
/// Returns `true` if `v` is contained in the range.
393+
#[inline(always)]
394+
pub fn contains(&self, v: u128) -> bool {
395+
if self.wraps_around() {
396+
self.start <= v || v <= self.end
397+
} else {
398+
self.start <= v && v <= self.end
399+
}
400+
}
401+
402+
/// Returns `true` if the range wraps around.
403+
/// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`.
404+
/// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`.
405+
#[inline]
406+
pub fn wraps_around(&self) -> bool {
407+
self.start > self.end
408+
}
409+
}
410+
411+
impl Debug for WrappingRange {
412+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
413+
if self.start > self.end {
414+
write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
415+
} else {
416+
write!(fmt, "{}..={}", self.start, self.end)?;
417+
}
418+
Ok(())
419+
}
420+
}
250421

251422
/// General language calling conventions.
252423
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]

compiler/stable_mir/src/error.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled.
66
77
use std::fmt::{Debug, Display, Formatter};
8-
use std::{error, fmt, io};
8+
use std::{fmt, io};
99

1010
macro_rules! error {
1111
($fmt: literal $(,)?) => { Error(format!($fmt)) };
1212
($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) };
13-
}
13+
}
14+
15+
pub(crate) use error;
1416

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

75-
impl error::Error for Error {}
76-
impl<T> error::Error for CompilerError<T> where T: Display + Debug {}
77+
impl std::error::Error for Error {}
78+
79+
impl<T> std::error::Error for CompilerError<T> where T: Display + Debug {}
7780

7881
impl From<io::Error> for Error {
7982
fn from(value: io::Error) -> Self {

compiler/stable_mir/src/target.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,29 @@ pub enum Endian {
3030
}
3131

3232
/// Represent the size of a component.
33-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
33+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
3434
pub struct MachineSize {
3535
num_bits: usize,
3636
}
3737

3838
impl MachineSize {
39+
#[inline(always)]
3940
pub fn bytes(self) -> usize {
4041
self.num_bits / 8
4142
}
4243

44+
#[inline(always)]
4345
pub fn bits(self) -> usize {
4446
self.num_bits
4547
}
4648

49+
#[inline(always)]
4750
pub fn from_bits(num_bits: usize) -> MachineSize {
4851
MachineSize { num_bits }
4952
}
53+
54+
#[inline]
55+
pub fn unsigned_int_max(self) -> Option<u128> {
56+
(self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits()))
57+
}
5058
}

compiler/stable_mir/src/ty.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,9 @@ impl TyKind {
324324

325325
#[inline]
326326
pub fn is_cstr(&self) -> bool {
327-
let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { return false };
327+
let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else {
328+
return false;
329+
};
328330
with(|cx| cx.adt_is_cstr(*def))
329331
}
330332

@@ -1032,10 +1034,13 @@ pub struct BoundTy {
10321034
}
10331035

10341036
pub type Bytes = Vec<Option<u8>>;
1037+
1038+
/// Size in bytes.
10351039
pub type Size = usize;
10361040

10371041
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
10381042
pub struct Prov(pub AllocId);
1043+
10391044
pub type Align = u64;
10401045
pub type Promoted = u32;
10411046
pub type InitMaskMaterialized = Vec<u64>;

0 commit comments

Comments
 (0)