Mark scalar layout unions so that backends that do not support partially initialized scalars can special case them.
This commit is contained in:
parent
2ed6786404
commit
d32ce37a17
37 changed files with 356 additions and 288 deletions
|
@ -6,7 +6,7 @@ use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
|
|||
fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) {
|
||||
// Always sign extend u32 values on 64-bit mips
|
||||
if let abi::Abi::Scalar(scalar) = arg.layout.abi {
|
||||
if let abi::Int(i, signed) = scalar.value {
|
||||
if let abi::Int(i, signed) = scalar.primitive() {
|
||||
if !signed && i.size().bits() == 32 {
|
||||
if let PassMode::Direct(ref mut attrs) = arg.mode {
|
||||
attrs.ext(ArgExtension::Sext);
|
||||
|
@ -25,7 +25,7 @@ where
|
|||
C: HasDataLayout,
|
||||
{
|
||||
match ret.layout.field(cx, i).abi {
|
||||
abi::Abi::Scalar(scalar) => match scalar.value {
|
||||
abi::Abi::Scalar(scalar) => match scalar.primitive() {
|
||||
abi::F32 => Some(Reg::f32()),
|
||||
abi::F64 => Some(Reg::f64()),
|
||||
_ => None,
|
||||
|
@ -110,7 +110,7 @@ where
|
|||
|
||||
// We only care about aligned doubles
|
||||
if let abi::Abi::Scalar(scalar) = field.abi {
|
||||
if let abi::F64 = scalar.value {
|
||||
if let abi::F64 = scalar.primitive() {
|
||||
if offset.is_aligned(dl.f64_align.abi) {
|
||||
// Insert enough integers to cover [last_offset, offset)
|
||||
assert!(last_offset.is_aligned(dl.f64_align.abi));
|
||||
|
|
|
@ -348,7 +348,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
|
||||
// The primitive for this algorithm.
|
||||
Abi::Scalar(scalar) => {
|
||||
let kind = match scalar.value {
|
||||
let kind = match scalar.primitive() {
|
||||
abi::Int(..) | abi::Pointer => RegKind::Integer,
|
||||
abi::F32 | abi::F64 => RegKind::Float,
|
||||
};
|
||||
|
@ -482,7 +482,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
|
|||
Abi::Scalar(scalar) => PassMode::Direct(scalar_attrs(&layout, scalar, Size::ZERO)),
|
||||
Abi::ScalarPair(a, b) => PassMode::Pair(
|
||||
scalar_attrs(&layout, a, Size::ZERO),
|
||||
scalar_attrs(&layout, b, a.value.size(cx).align_to(b.value.align(cx).abi)),
|
||||
scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)),
|
||||
),
|
||||
Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()),
|
||||
Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()),
|
||||
|
@ -534,7 +534,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
|
|||
pub fn extend_integer_width_to(&mut self, bits: u64) {
|
||||
// Only integers have signedness
|
||||
if let Abi::Scalar(scalar) = self.layout.abi {
|
||||
if let abi::Int(i, signed) = scalar.value {
|
||||
if let abi::Int(i, signed) = scalar.primitive() {
|
||||
if i.size().bits() < bits {
|
||||
if let PassMode::Direct(ref mut attrs) = self.mode {
|
||||
if signed {
|
||||
|
|
|
@ -44,7 +44,7 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
match arg_layout.abi {
|
||||
Abi::Scalar(scalar) => match scalar.value {
|
||||
Abi::Scalar(scalar) => match scalar.primitive() {
|
||||
abi::Int(..) | abi::Pointer => {
|
||||
if arg_layout.size.bits() > xlen {
|
||||
return Err(CannotUseFpConv);
|
||||
|
@ -298,7 +298,7 @@ fn classify_arg<'a, Ty, C>(
|
|||
|
||||
fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) {
|
||||
if let Abi::Scalar(scalar) = arg.layout.abi {
|
||||
if let abi::Int(i, _) = scalar.value {
|
||||
if let abi::Int(i, _) = scalar.primitive() {
|
||||
// 32-bit integers are always sign-extended
|
||||
if i.size().bits() == 32 && xlen > 32 {
|
||||
if let PassMode::Direct(ref mut attrs) = arg.mode {
|
||||
|
|
|
@ -20,7 +20,7 @@ where
|
|||
{
|
||||
let dl = cx.data_layout();
|
||||
|
||||
if scalar.value != abi::F32 && scalar.value != abi::F64 {
|
||||
if !scalar.primitive().is_float() {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ where
|
|||
return data;
|
||||
}
|
||||
|
||||
if scalar.value == abi::F32 {
|
||||
if scalar.primitive() == abi::F32 {
|
||||
data.arg_attribute = ArgAttribute::InReg;
|
||||
data.prefix[data.prefix_index] = Some(Reg::f32());
|
||||
data.last_offset = offset + Reg::f32().size;
|
||||
|
@ -79,17 +79,15 @@ where
|
|||
C: HasDataLayout,
|
||||
{
|
||||
data = arg_scalar(cx, &scalar1, offset, data);
|
||||
if scalar1.value == abi::F32 {
|
||||
offset += Reg::f32().size;
|
||||
} else if scalar2.value == abi::F64 {
|
||||
offset += Reg::f64().size;
|
||||
} else if let abi::Int(i, _signed) = scalar1.value {
|
||||
offset += i.size();
|
||||
} else if scalar1.value == abi::Pointer {
|
||||
offset = offset + Reg::i64().size;
|
||||
match (scalar1.primitive(), scalar2.primitive()) {
|
||||
(abi::F32, _) => offset += Reg::f32().size,
|
||||
(_, abi::F64) => offset += Reg::f64().size,
|
||||
(abi::Int(i, _signed), _) => offset += i.size(),
|
||||
(abi::Pointer, _) => offset += Reg::i64().size,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if (offset.raw % 4) != 0 && (scalar2.value == abi::F32 || scalar2.value == abi::F64) {
|
||||
if (offset.raw % 4) != 0 && scalar2.primitive().is_float() {
|
||||
offset.raw += 4 - (offset.raw % 4);
|
||||
}
|
||||
data = arg_scalar(cx, &scalar2, offset, data);
|
||||
|
|
|
@ -49,7 +49,7 @@ where
|
|||
let mut c = match layout.abi {
|
||||
Abi::Uninhabited => return Ok(()),
|
||||
|
||||
Abi::Scalar(scalar) => match scalar.value {
|
||||
Abi::Scalar(scalar) => match scalar.primitive() {
|
||||
abi::Int(..) | abi::Pointer => Class::Int,
|
||||
abi::F32 | abi::F64 => Class::Sse,
|
||||
},
|
||||
|
|
|
@ -752,6 +752,10 @@ pub struct WrappingRange {
|
|||
}
|
||||
|
||||
impl WrappingRange {
|
||||
pub fn full(size: Size) -> Self {
|
||||
Self { start: 0, end: size.unsigned_int_max() }
|
||||
}
|
||||
|
||||
/// Returns `true` if `v` is contained in the range.
|
||||
#[inline(always)]
|
||||
pub fn contains(&self, v: u128) -> bool {
|
||||
|
@ -799,13 +803,23 @@ impl fmt::Debug for WrappingRange {
|
|||
/// Information about one scalar component of a Rust type.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct Scalar {
|
||||
pub value: Primitive,
|
||||
pub enum Scalar {
|
||||
Initialized {
|
||||
value: Primitive,
|
||||
|
||||
// FIXME(eddyb) always use the shortest range, e.g., by finding
|
||||
// the largest space between two consecutive valid values and
|
||||
// taking everything else as the (shortest) valid range.
|
||||
pub valid_range: WrappingRange,
|
||||
// FIXME(eddyb) always use the shortest range, e.g., by finding
|
||||
// the largest space between two consecutive valid values and
|
||||
// taking everything else as the (shortest) valid range.
|
||||
valid_range: WrappingRange,
|
||||
},
|
||||
Union {
|
||||
/// 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. We
|
||||
/// also use it to compute the size of the scalar.
|
||||
/// However, unions never have niches and even allow undef,
|
||||
/// so there is no `valid_range`.
|
||||
value: Primitive,
|
||||
},
|
||||
}
|
||||
|
||||
impl Scalar {
|
||||
|
@ -813,14 +827,58 @@ impl Scalar {
|
|||
pub fn is_bool(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Scalar { value: Int(I8, false), valid_range: WrappingRange { start: 0, end: 1 } }
|
||||
Scalar::Initialized {
|
||||
value: Int(I8, false),
|
||||
valid_range: WrappingRange { start: 0, end: 1 }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the primitive representation of this type, ignoring the valid range and whether the
|
||||
/// value is allowed to be undefined (due to being a union).
|
||||
pub fn primitive(&self) -> Primitive {
|
||||
match *self {
|
||||
Scalar::Initialized { value, .. } | Scalar::Union { value } => value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn align(self, cx: &impl HasDataLayout) -> AbiAndPrefAlign {
|
||||
self.primitive().align(cx)
|
||||
}
|
||||
|
||||
pub fn size(self, cx: &impl HasDataLayout) -> Size {
|
||||
self.primitive().size(cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_union(&self) -> Self {
|
||||
Self::Union { value: self.primitive() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid_range(&self, cx: &impl HasDataLayout) -> WrappingRange {
|
||||
match *self {
|
||||
Scalar::Initialized { valid_range, .. } => valid_range,
|
||||
Scalar::Union { value } => WrappingRange::full(value.size(cx)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Allows the caller to mutate the valid range. This operation will panic if attempted on a union.
|
||||
pub fn valid_range_mut(&mut self) -> &mut WrappingRange {
|
||||
match self {
|
||||
Scalar::Initialized { valid_range, .. } => valid_range,
|
||||
Scalar::Union { .. } => panic!("cannot change the valid range of a union"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if all possible numbers are valid, i.e `valid_range` covers the whole layout
|
||||
#[inline]
|
||||
pub fn is_always_valid<C: HasDataLayout>(&self, cx: &C) -> bool {
|
||||
self.valid_range.is_full_for(self.value.size(cx))
|
||||
match *self {
|
||||
Scalar::Initialized { valid_range, .. } => valid_range.is_full_for(self.size(cx)),
|
||||
Scalar::Union { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -988,7 +1046,7 @@ impl Abi {
|
|||
#[inline]
|
||||
pub fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Abi::Scalar(scal) => match scal.value {
|
||||
Abi::Scalar(scal) => match scal.primitive() {
|
||||
Primitive::Int(_, signed) => signed,
|
||||
_ => false,
|
||||
},
|
||||
|
@ -1060,17 +1118,19 @@ pub enum TagEncoding {
|
|||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
pub struct Niche {
|
||||
pub offset: Size,
|
||||
pub scalar: Scalar,
|
||||
pub value: Primitive,
|
||||
pub valid_range: WrappingRange,
|
||||
}
|
||||
|
||||
impl Niche {
|
||||
pub fn from_scalar<C: HasDataLayout>(cx: &C, offset: Size, scalar: Scalar) -> Option<Self> {
|
||||
let niche = Niche { offset, scalar };
|
||||
let Scalar::Initialized { value, valid_range } = scalar else { return None };
|
||||
let niche = Niche { offset, value, valid_range };
|
||||
if niche.available(cx) > 0 { Some(niche) } else { None }
|
||||
}
|
||||
|
||||
pub fn available<C: HasDataLayout>(&self, cx: &C) -> u128 {
|
||||
let Scalar { value, valid_range: v } = self.scalar;
|
||||
let Self { value, valid_range: v, .. } = *self;
|
||||
let size = value.size(cx);
|
||||
assert!(size.bits() <= 128);
|
||||
let max_value = size.unsigned_int_max();
|
||||
|
@ -1083,7 +1143,7 @@ impl Niche {
|
|||
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
|
||||
assert!(count > 0);
|
||||
|
||||
let Scalar { value, valid_range: v } = self.scalar;
|
||||
let Self { value, valid_range: v, .. } = *self;
|
||||
let size = value.size(cx);
|
||||
assert!(size.bits() <= 128);
|
||||
let max_value = size.unsigned_int_max();
|
||||
|
@ -1107,12 +1167,12 @@ impl Niche {
|
|||
// If niche zero is already reserved, the selection of bounds are of little interest.
|
||||
let move_start = |v: WrappingRange| {
|
||||
let start = v.start.wrapping_sub(count) & max_value;
|
||||
Some((start, Scalar { value, valid_range: v.with_start(start) }))
|
||||
Some((start, Scalar::Initialized { value, valid_range: v.with_start(start) }))
|
||||
};
|
||||
let move_end = |v: WrappingRange| {
|
||||
let start = v.end.wrapping_add(1) & max_value;
|
||||
let end = v.end.wrapping_add(count) & max_value;
|
||||
Some((start, Scalar { value, valid_range: v.with_end(end) }))
|
||||
Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) }))
|
||||
};
|
||||
let distance_end_zero = max_value - v.end;
|
||||
if v.start > v.end {
|
||||
|
@ -1172,8 +1232,8 @@ pub struct LayoutS<'a> {
|
|||
impl<'a> LayoutS<'a> {
|
||||
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
|
||||
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
|
||||
let size = scalar.value.size(cx);
|
||||
let align = scalar.value.align(cx);
|
||||
let size = scalar.size(cx);
|
||||
let align = scalar.align(cx);
|
||||
LayoutS {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Primitive,
|
||||
|
@ -1325,7 +1385,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
C: HasDataLayout,
|
||||
{
|
||||
match self.abi {
|
||||
Abi::Scalar(scalar) => scalar.value.is_float(),
|
||||
Abi::Scalar(scalar) => scalar.primitive().is_float(),
|
||||
Abi::Aggregate { .. } => {
|
||||
if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
|
||||
self.field(cx, 0).is_single_fp_element(cx)
|
||||
|
@ -1371,7 +1431,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
let scalar_allows_raw_init = move |s: Scalar| -> bool {
|
||||
if zero {
|
||||
// The range must contain 0.
|
||||
s.valid_range.contains(0)
|
||||
s.valid_range(cx).contains(0)
|
||||
} else {
|
||||
// The range must include all values.
|
||||
s.is_always_valid(cx)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue