Use custom wrap-around type instead of Range
This commit is contained in:
parent
7481e6d1a4
commit
5a501f73ff
12 changed files with 255 additions and 116 deletions
|
@ -677,32 +677,52 @@ 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, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct AllocationRange {
|
||||
pub start: u128,
|
||||
pub end: u128,
|
||||
}
|
||||
|
||||
impl AllocationRange {
|
||||
/// Returns `true` if `v` is contained in the range.
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about one scalar component of a Rust type.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct Scalar {
|
||||
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
|
||||
// the largest space between two consecutive valid values and
|
||||
// taking everything else as the (shortest) valid range.
|
||||
pub valid_range: RangeInclusive<u128>,
|
||||
pub valid_range: AllocationRange,
|
||||
}
|
||||
|
||||
impl Scalar {
|
||||
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, AllocationRange { start: 0, end: 1 })
|
||||
}
|
||||
|
||||
/// Returns the valid range as a `x..y` range.
|
||||
|
@ -715,8 +735,8 @@ impl Scalar {
|
|||
let bits = self.value.size(cx).bits();
|
||||
assert!(bits <= 128);
|
||||
let mask = !0u128 >> (128 - bits);
|
||||
let start = *self.valid_range.start();
|
||||
let end = *self.valid_range.end();
|
||||
let start = self.valid_range.start;
|
||||
let end = self.valid_range.end;
|
||||
assert_eq!(start, start & mask);
|
||||
assert_eq!(end, end & mask);
|
||||
start..(end.wrapping_add(1) & mask)
|
||||
|
@ -965,20 +985,20 @@ impl Niche {
|
|||
}
|
||||
|
||||
pub fn available<C: HasDataLayout>(&self, cx: &C) -> u128 {
|
||||
let Scalar { value, valid_range: ref v } = self.scalar;
|
||||
let Scalar { value, valid_range: v } = self.scalar;
|
||||
let bits = value.size(cx).bits();
|
||||
assert!(bits <= 128);
|
||||
let max_value = !0u128 >> (128 - bits);
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
|
||||
assert!(count > 0);
|
||||
|
||||
let Scalar { value, valid_range: ref v } = self.scalar;
|
||||
let Scalar { value, valid_range: v } = self.scalar;
|
||||
let bits = value.size(cx).bits();
|
||||
assert!(bits <= 128);
|
||||
let max_value = !0u128 >> (128 - bits);
|
||||
|
@ -988,24 +1008,17 @@ impl Niche {
|
|||
}
|
||||
|
||||
// Compute the range of invalid values being reserved.
|
||||
let start = v.end().wrapping_add(1) & max_value;
|
||||
let end = v.end().wrapping_add(count) & max_value;
|
||||
let start = v.end.wrapping_add(1) & max_value;
|
||||
let end = v.end.wrapping_add(count) & max_value;
|
||||
|
||||
// If the `end` of our range is inside the valid range,
|
||||
// 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) {
|
||||
if v.contains(end) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((start, Scalar { value, valid_range: *v.start()..=end }))
|
||||
Some((start, Scalar { value, valid_range: AllocationRange { start: v.start, end } }))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1214,7 +1227,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
if zero {
|
||||
let range = &s.valid_range;
|
||||
// The range must contain 0.
|
||||
range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0
|
||||
range.contains(0) || (range.start > range.end) // wrap-around allows 0
|
||||
} else {
|
||||
// The range must include all values. `valid_range_exclusive` handles
|
||||
// the wrap-around using target arithmetic; with wrap-around then the full
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue