Auto merge of #88242 - bonega:allocation_range, r=oli-obk
Use custom wrap-around type instead of RangeInclusive Two reasons: 1. More memory is allocated than necessary for `valid_range` in `Scalar`. The range is not used as an iterator and `exhausted` is never used. 2. `contains`, `count` etc. methods in `RangeInclusive` are doing very unhelpful(and dangerous!) things when used as a wrap-around range. - In general this PR wants to limit potentially confusing methods, that have a low probability of working. Doing a local perf run, every metric shows improvement except for instructions. Max-rss seem to have a very consistent improvement. Sorry - newbie here, probably doing something wrong.
This commit is contained in:
commit
e5484cec0e
8 changed files with 115 additions and 81 deletions
|
@ -462,7 +462,6 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
load: &'ll Value,
|
load: &'ll Value,
|
||||||
scalar: &abi::Scalar,
|
scalar: &abi::Scalar,
|
||||||
) {
|
) {
|
||||||
let vr = scalar.valid_range.clone();
|
|
||||||
match scalar.value {
|
match scalar.value {
|
||||||
abi::Int(..) => {
|
abi::Int(..) => {
|
||||||
let range = scalar.valid_range_exclusive(bx);
|
let range = scalar.valid_range_exclusive(bx);
|
||||||
|
@ -470,7 +469,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
bx.range_metadata(load, range);
|
bx.range_metadata(load, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abi::Pointer if vr.start() < vr.end() && !vr.contains(&0) => {
|
abi::Pointer if !scalar.valid_range.contains_zero() => {
|
||||||
bx.nonnull_metadata(load);
|
bx.nonnull_metadata(load);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -16,7 +16,9 @@ use rustc_middle::mir::interpret::{
|
||||||
use rustc_middle::mir::mono::MonoItem;
|
use rustc_middle::mir::mono::MonoItem;
|
||||||
use rustc_middle::ty::{self, Instance, Ty};
|
use rustc_middle::ty::{self, Instance, Ty};
|
||||||
use rustc_middle::{bug, span_bug};
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_target::abi::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size};
|
use rustc_target::abi::{
|
||||||
|
AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size, WrappingRange,
|
||||||
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value {
|
pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value {
|
||||||
|
@ -59,7 +61,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
|
||||||
Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
|
Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
|
||||||
&cx.tcx,
|
&cx.tcx,
|
||||||
),
|
),
|
||||||
&Scalar { value: Primitive::Pointer, valid_range: 0..=!0 },
|
&Scalar { value: Primitive::Pointer, valid_range: WrappingRange { start: 0, end: !0 } },
|
||||||
cx.type_i8p_ext(address_space),
|
cx.type_i8p_ext(address_space),
|
||||||
));
|
));
|
||||||
next_offset = offset + pointer_size;
|
next_offset = offset + pointer_size;
|
||||||
|
|
|
@ -406,11 +406,11 @@ fn push_debuginfo_type_name<'tcx>(
|
||||||
let dataful_discriminant_range =
|
let dataful_discriminant_range =
|
||||||
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;
|
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;
|
||||||
|
|
||||||
let min = dataful_discriminant_range.start();
|
let min = dataful_discriminant_range.start;
|
||||||
let min = tag.value.size(&tcx).truncate(*min);
|
let min = tag.value.size(&tcx).truncate(min);
|
||||||
|
|
||||||
let max = dataful_discriminant_range.end();
|
let max = dataful_discriminant_range.end;
|
||||||
let max = tag.value.size(&tcx).truncate(*max);
|
let max = tag.value.size(&tcx).truncate(max);
|
||||||
|
|
||||||
let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();
|
let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();
|
||||||
|
|
||||||
|
|
|
@ -310,15 +310,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
|
||||||
let er = scalar.valid_range_exclusive(bx.cx());
|
let er = scalar.valid_range_exclusive(bx.cx());
|
||||||
if er.end != er.start
|
if er.end != er.start
|
||||||
&& scalar.valid_range.end() >= scalar.valid_range.start()
|
&& scalar.valid_range.end >= scalar.valid_range.start
|
||||||
{
|
{
|
||||||
// We want `table[e as usize ± k]` to not
|
// We want `table[e as usize ± k]` to not
|
||||||
// have bound checks, and this is the most
|
// have bound checks, and this is the most
|
||||||
// convenient place to put the `assume`s.
|
// convenient place to put the `assume`s.
|
||||||
if *scalar.valid_range.start() > 0 {
|
if scalar.valid_range.start > 0 {
|
||||||
let enum_value_lower_bound = bx
|
let enum_value_lower_bound = bx
|
||||||
.cx()
|
.cx()
|
||||||
.const_uint_big(ll_t_in, *scalar.valid_range.start());
|
.const_uint_big(ll_t_in, scalar.valid_range.start);
|
||||||
let cmp_start = bx.icmp(
|
let cmp_start = bx.icmp(
|
||||||
IntPredicate::IntUGE,
|
IntPredicate::IntUGE,
|
||||||
llval,
|
llval,
|
||||||
|
@ -328,7 +328,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let enum_value_upper_bound =
|
let enum_value_upper_bound =
|
||||||
bx.cx().const_uint_big(ll_t_in, *scalar.valid_range.end());
|
bx.cx().const_uint_big(ll_t_in, scalar.valid_range.end);
|
||||||
let cmp_end = bx.icmp(
|
let cmp_end = bx.icmp(
|
||||||
IntPredicate::IntULE,
|
IntPredicate::IntULE,
|
||||||
llval,
|
llval,
|
||||||
|
|
|
@ -795,7 +795,7 @@ crate fn repr_nullable_ptr<'tcx>(
|
||||||
// Return the nullable type this Option-like enum can be safely represented with.
|
// Return the nullable type this Option-like enum can be safely represented with.
|
||||||
let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi;
|
let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi;
|
||||||
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
|
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
|
||||||
match (field_ty_scalar.valid_range.start(), field_ty_scalar.valid_range.end()) {
|
match (field_ty_scalar.valid_range.start, field_ty_scalar.valid_range.end) {
|
||||||
(0, _) => unreachable!("Non-null optimisation extended to a non-zero value."),
|
(0, _) => unreachable!("Non-null optimisation extended to a non-zero value."),
|
||||||
(1, _) => {
|
(1, _) => {
|
||||||
return Some(get_nullable_type(cx, field_ty).unwrap());
|
return Some(get_nullable_type(cx, field_ty).unwrap());
|
||||||
|
|
|
@ -499,7 +499,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
let scalar_unit = |value: Primitive| {
|
let scalar_unit = |value: Primitive| {
|
||||||
let bits = value.size(dl).bits();
|
let bits = value.size(dl).bits();
|
||||||
assert!(bits <= 128);
|
assert!(bits <= 128);
|
||||||
Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) }
|
Scalar { value, valid_range: WrappingRange { start: 0, end: (!0 >> (128 - bits)) } }
|
||||||
};
|
};
|
||||||
let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value)));
|
let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value)));
|
||||||
|
|
||||||
|
@ -512,11 +512,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
// Basic scalars.
|
// Basic scalars.
|
||||||
ty::Bool => tcx.intern_layout(Layout::scalar(
|
ty::Bool => tcx.intern_layout(Layout::scalar(
|
||||||
self,
|
self,
|
||||||
Scalar { value: Int(I8, false), valid_range: 0..=1 },
|
Scalar { value: Int(I8, false), valid_range: WrappingRange { start: 0, end: 1 } },
|
||||||
)),
|
)),
|
||||||
ty::Char => tcx.intern_layout(Layout::scalar(
|
ty::Char => tcx.intern_layout(Layout::scalar(
|
||||||
self,
|
self,
|
||||||
Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF },
|
Scalar {
|
||||||
|
value: Int(I32, false),
|
||||||
|
valid_range: WrappingRange { start: 0, end: 0x10FFFF },
|
||||||
|
},
|
||||||
)),
|
)),
|
||||||
ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)),
|
ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)),
|
||||||
ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)),
|
ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)),
|
||||||
|
@ -526,7 +529,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
}),
|
}),
|
||||||
ty::FnPtr(_) => {
|
ty::FnPtr(_) => {
|
||||||
let mut ptr = scalar_unit(Pointer);
|
let mut ptr = scalar_unit(Pointer);
|
||||||
ptr.valid_range = 1..=*ptr.valid_range.end();
|
ptr.valid_range = ptr.valid_range.with_start(1);
|
||||||
tcx.intern_layout(Layout::scalar(self, ptr))
|
tcx.intern_layout(Layout::scalar(self, ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,7 +547,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
||||||
let mut data_ptr = scalar_unit(Pointer);
|
let mut data_ptr = scalar_unit(Pointer);
|
||||||
if !ty.is_unsafe_ptr() {
|
if !ty.is_unsafe_ptr() {
|
||||||
data_ptr.valid_range = 1..=*data_ptr.valid_range.end();
|
data_ptr.valid_range = data_ptr.valid_range.with_start(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
|
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
|
||||||
|
@ -560,7 +563,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
|
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
|
||||||
ty::Dynamic(..) => {
|
ty::Dynamic(..) => {
|
||||||
let mut vtable = scalar_unit(Pointer);
|
let mut vtable = scalar_unit(Pointer);
|
||||||
vtable.valid_range = 1..=*vtable.valid_range.end();
|
vtable.valid_range = vtable.valid_range.with_start(1);
|
||||||
vtable
|
vtable
|
||||||
}
|
}
|
||||||
_ => return Err(LayoutError::Unknown(unsized_part)),
|
_ => return Err(LayoutError::Unknown(unsized_part)),
|
||||||
|
@ -933,14 +936,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
if let Bound::Included(start) = start {
|
if let Bound::Included(start) = start {
|
||||||
// FIXME(eddyb) this might be incorrect - it doesn't
|
// FIXME(eddyb) this might be incorrect - it doesn't
|
||||||
// account for wrap-around (end < start) ranges.
|
// account for wrap-around (end < start) ranges.
|
||||||
assert!(*scalar.valid_range.start() <= start);
|
assert!(scalar.valid_range.start <= start);
|
||||||
scalar.valid_range = start..=*scalar.valid_range.end();
|
scalar.valid_range.start = start;
|
||||||
}
|
}
|
||||||
if let Bound::Included(end) = end {
|
if let Bound::Included(end) = end {
|
||||||
// FIXME(eddyb) this might be incorrect - it doesn't
|
// FIXME(eddyb) this might be incorrect - it doesn't
|
||||||
// account for wrap-around (end < start) ranges.
|
// account for wrap-around (end < start) ranges.
|
||||||
assert!(*scalar.valid_range.end() >= end);
|
assert!(scalar.valid_range.end >= end);
|
||||||
scalar.valid_range = *scalar.valid_range.start()..=end;
|
scalar.valid_range.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update `largest_niche` if we have introduced a larger niche.
|
// Update `largest_niche` if we have introduced a larger niche.
|
||||||
|
@ -1256,7 +1259,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
let tag_mask = !0u128 >> (128 - ity.size().bits());
|
let tag_mask = !0u128 >> (128 - ity.size().bits());
|
||||||
let tag = Scalar {
|
let tag = Scalar {
|
||||||
value: Int(ity, signed),
|
value: Int(ity, signed),
|
||||||
valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask),
|
valid_range: WrappingRange {
|
||||||
|
start: (min as u128 & tag_mask),
|
||||||
|
end: (max as u128 & tag_mask),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let mut abi = Abi::Aggregate { sized: true };
|
let mut abi = Abi::Aggregate { sized: true };
|
||||||
if tag.value.size(dl) == size {
|
if tag.value.size(dl) == size {
|
||||||
|
@ -1535,7 +1541,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
let max_discr = (info.variant_fields.len() - 1) as u128;
|
let max_discr = (info.variant_fields.len() - 1) as u128;
|
||||||
let discr_int = Integer::fit_unsigned(max_discr);
|
let discr_int = Integer::fit_unsigned(max_discr);
|
||||||
let discr_int_ty = discr_int.to_ty(tcx, false);
|
let discr_int_ty = discr_int.to_ty(tcx, false);
|
||||||
let tag = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr };
|
let tag = Scalar {
|
||||||
|
value: Primitive::Int(discr_int, false),
|
||||||
|
valid_range: WrappingRange { start: 0, end: max_discr },
|
||||||
|
};
|
||||||
let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone()));
|
let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone()));
|
||||||
let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout };
|
let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout };
|
||||||
|
|
||||||
|
@ -2846,11 +2855,9 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if scalar.valid_range.start() < scalar.valid_range.end() {
|
if !scalar.valid_range.contains_zero() {
|
||||||
if *scalar.valid_range.start() > 0 {
|
|
||||||
attrs.set(ArgAttribute::NonNull);
|
attrs.set(ArgAttribute::NonNull);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pointee) = layout.pointee_info_at(cx, offset) {
|
if let Some(pointee) = layout.pointee_info_at(cx, offset) {
|
||||||
if let Some(kind) = pointee.safe {
|
if let Some(kind) = pointee.safe {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -15,7 +14,9 @@ use rustc_middle::mir::interpret::InterpError;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_middle::ty::layout::TyAndLayout;
|
use rustc_middle::ty::layout::TyAndLayout;
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
use rustc_span::symbol::{sym, Symbol};
|
||||||
use rustc_target::abi::{Abi, LayoutOf, Scalar as ScalarAbi, Size, VariantIdx, Variants};
|
use rustc_target::abi::{
|
||||||
|
Abi, LayoutOf, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||||
|
};
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
@ -181,22 +182,10 @@ fn write_path(out: &mut String, path: &[PathElem]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test if a range that wraps at overflow contains `test`
|
|
||||||
fn wrapping_range_contains(r: &RangeInclusive<u128>, test: u128) -> bool {
|
|
||||||
let (lo, hi) = r.clone().into_inner();
|
|
||||||
if lo > hi {
|
|
||||||
// Wrapped
|
|
||||||
(..=hi).contains(&test) || (lo..).contains(&test)
|
|
||||||
} else {
|
|
||||||
// Normal
|
|
||||||
r.contains(&test)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formats such that a sentence like "expected something {}" to mean
|
// Formats such that a sentence like "expected something {}" to mean
|
||||||
// "expected something <in the given range>" makes sense.
|
// "expected something <in the given range>" makes sense.
|
||||||
fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String {
|
fn wrapping_range_format(r: WrappingRange, max_hi: u128) -> String {
|
||||||
let (lo, hi) = r.clone().into_inner();
|
let WrappingRange { start: lo, end: hi } = r;
|
||||||
assert!(hi <= max_hi);
|
assert!(hi <= max_hi);
|
||||||
if lo > hi {
|
if lo > hi {
|
||||||
format!("less or equal to {}, or greater or equal to {}", hi, lo)
|
format!("less or equal to {}, or greater or equal to {}", hi, lo)
|
||||||
|
@ -634,8 +623,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
scalar_layout: &ScalarAbi,
|
scalar_layout: &ScalarAbi,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let value = self.read_scalar(op)?;
|
let value = self.read_scalar(op)?;
|
||||||
let valid_range = &scalar_layout.valid_range;
|
let valid_range = scalar_layout.valid_range.clone();
|
||||||
let (lo, hi) = valid_range.clone().into_inner();
|
let WrappingRange { start: lo, end: hi } = valid_range;
|
||||||
// Determine the allowed range
|
// Determine the allowed range
|
||||||
// `max_hi` is as big as the size fits
|
// `max_hi` is as big as the size fits
|
||||||
let max_hi = u128::MAX >> (128 - op.layout.size.bits());
|
let max_hi = u128::MAX >> (128 - op.layout.size.bits());
|
||||||
|
@ -684,7 +673,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
Ok(int) => int.assert_bits(op.layout.size),
|
Ok(int) => int.assert_bits(op.layout.size),
|
||||||
};
|
};
|
||||||
// Now compare. This is slightly subtle because this is a special "wrap-around" range.
|
// Now compare. This is slightly subtle because this is a special "wrap-around" range.
|
||||||
if wrapping_range_contains(&valid_range, bits) {
|
if valid_range.contains(bits) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
throw_validation_failure!(self.path,
|
throw_validation_failure!(self.path,
|
||||||
|
|
|
@ -677,32 +677,80 @@ impl Primitive {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inclusive wrap-around range of valid values, 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
|
||||||
|
///
|
||||||
|
/// This is intended specifically to mirror LLVM’s `!range` metadata,
|
||||||
|
/// semantics.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[derive(HashStable_Generic)]
|
||||||
|
pub struct WrappingRange {
|
||||||
|
pub start: u128,
|
||||||
|
pub end: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WrappingRange {
|
||||||
|
/// Returns `true` if `v` is contained in the range.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn contains(&self, v: u128) -> bool {
|
||||||
|
if self.start <= self.end {
|
||||||
|
self.start <= v && v <= self.end
|
||||||
|
} else {
|
||||||
|
self.start <= v || v <= self.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if zero is contained in the range.
|
||||||
|
/// Equal to `range.contains(0)` but should be faster.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn contains_zero(&self) -> bool {
|
||||||
|
self.start > self.end || self.start == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `self` with replaced `start`
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn with_start(mut self, start: u128) -> Self {
|
||||||
|
self.start = start;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `self` with replaced `end`
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn with_end(mut self, end: u128) -> Self {
|
||||||
|
self.end = end;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for WrappingRange {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(fmt, "{}..={}", self.start, self.end)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about one scalar component of a Rust type.
|
/// Information about one scalar component of a Rust type.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
#[derive(HashStable_Generic)]
|
#[derive(HashStable_Generic)]
|
||||||
pub struct Scalar {
|
pub struct Scalar {
|
||||||
pub value: Primitive,
|
pub value: Primitive,
|
||||||
|
|
||||||
/// Inclusive wrap-around range of valid values, 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
|
|
||||||
///
|
|
||||||
/// This is intended specifically to mirror LLVM’s `!range` metadata,
|
|
||||||
/// semantics.
|
|
||||||
// FIXME(eddyb) always use the shortest range, e.g., by finding
|
// FIXME(eddyb) always use the shortest range, e.g., by finding
|
||||||
// the largest space between two consecutive valid values and
|
// the largest space between two consecutive valid values and
|
||||||
// taking everything else as the (shortest) valid range.
|
// taking everything else as the (shortest) valid range.
|
||||||
pub valid_range: RangeInclusive<u128>,
|
pub valid_range: WrappingRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scalar {
|
impl Scalar {
|
||||||
pub fn is_bool(&self) -> bool {
|
pub fn is_bool(&self) -> bool {
|
||||||
matches!(self.value, Int(I8, false)) && self.valid_range == (0..=1)
|
matches!(self.value, Int(I8, false))
|
||||||
|
&& matches!(self.valid_range, WrappingRange { start: 0, end: 1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the valid range as a `x..y` range.
|
/// Returns the valid range as a `x..y` range.
|
||||||
|
@ -715,8 +763,8 @@ impl Scalar {
|
||||||
let bits = self.value.size(cx).bits();
|
let bits = self.value.size(cx).bits();
|
||||||
assert!(bits <= 128);
|
assert!(bits <= 128);
|
||||||
let mask = !0u128 >> (128 - bits);
|
let mask = !0u128 >> (128 - bits);
|
||||||
let start = *self.valid_range.start();
|
let start = self.valid_range.start;
|
||||||
let end = *self.valid_range.end();
|
let end = self.valid_range.end;
|
||||||
assert_eq!(start, start & mask);
|
assert_eq!(start, start & mask);
|
||||||
assert_eq!(end, end & mask);
|
assert_eq!(end, end & mask);
|
||||||
start..(end.wrapping_add(1) & mask)
|
start..(end.wrapping_add(1) & mask)
|
||||||
|
@ -971,14 +1019,14 @@ impl Niche {
|
||||||
let max_value = !0u128 >> (128 - bits);
|
let max_value = !0u128 >> (128 - bits);
|
||||||
|
|
||||||
// Find out how many values are outside the valid range.
|
// Find out how many values are outside the valid range.
|
||||||
let niche = v.end().wrapping_add(1)..*v.start();
|
let niche = v.end.wrapping_add(1)..v.start;
|
||||||
niche.end.wrapping_sub(niche.start) & max_value
|
niche.end.wrapping_sub(niche.start) & max_value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
|
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
|
||||||
assert!(count > 0);
|
assert!(count > 0);
|
||||||
|
|
||||||
let Scalar { value, valid_range: ref v } = self.scalar;
|
let Scalar { value, valid_range: v } = self.scalar.clone();
|
||||||
let bits = value.size(cx).bits();
|
let bits = value.size(cx).bits();
|
||||||
assert!(bits <= 128);
|
assert!(bits <= 128);
|
||||||
let max_value = !0u128 >> (128 - bits);
|
let max_value = !0u128 >> (128 - bits);
|
||||||
|
@ -988,24 +1036,14 @@ impl Niche {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the range of invalid values being reserved.
|
// Compute the range of invalid values being reserved.
|
||||||
let start = v.end().wrapping_add(1) & max_value;
|
let start = v.end.wrapping_add(1) & max_value;
|
||||||
let end = v.end().wrapping_add(count) & max_value;
|
let end = v.end.wrapping_add(count) & max_value;
|
||||||
|
|
||||||
// If the `end` of our range is inside the valid range,
|
if v.contains(end) {
|
||||||
// then we ran out of invalid values.
|
|
||||||
// FIXME(eddyb) abstract this with a wraparound range type.
|
|
||||||
let valid_range_contains = |x| {
|
|
||||||
if v.start() <= v.end() {
|
|
||||||
*v.start() <= x && x <= *v.end()
|
|
||||||
} else {
|
|
||||||
*v.start() <= x || x <= *v.end()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if valid_range_contains(end) {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((start, Scalar { value, valid_range: *v.start()..=end }))
|
Some((start, Scalar { value, valid_range: v.with_end(end) }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,9 +1250,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||||
{
|
{
|
||||||
let scalar_allows_raw_init = move |s: &Scalar| -> bool {
|
let scalar_allows_raw_init = move |s: &Scalar| -> bool {
|
||||||
if zero {
|
if zero {
|
||||||
let range = &s.valid_range;
|
|
||||||
// The range must contain 0.
|
// The range must contain 0.
|
||||||
range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0
|
s.valid_range.contains_zero()
|
||||||
} else {
|
} else {
|
||||||
// The range must include all values. `valid_range_exclusive` handles
|
// The range must include all values. `valid_range_exclusive` handles
|
||||||
// the wrap-around using target arithmetic; with wrap-around then the full
|
// the wrap-around using target arithmetic; with wrap-around then the full
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue