Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions compiler/rustc_abi/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ fn wrapping_range_smallest_range_containing() {
check([200, 50, 60], 1, 200, 60);
check([200, 50, 125], 1, 50, 200);

// A wraparound range is only potentially interesting when
// the non-wraparound range covers over half the range.
check([0, 127], 1, 0, 127); // `0..=127` is smaller
check([0, 128], 1, 0, 128); // both are the same size
check([0, 129], 1, 129, 0); // `(..=0) | (129..)` is smaller

// The mem::Alignment case
check((0..64).map(|n| 1 << n), 8, 1, i64::MIN.cast_unsigned().into());

Expand All @@ -66,6 +72,9 @@ fn wrapping_range_smallest_range_containing() {
// `(..=32) | (96..)`, `(..=96) | (160..)`, `(..=160) | (224..)`, and `32..=224`.
// We pick the last one as the only one that doesn't contain zero.
check([0xA0, 0xE0, 0x20, 0x60], 1, 0x20, 0xE0);

// Wraparound can still be needed even in `max - min < values.len()`.
check(std::iter::chain([50; 100], [200; 100]), 1, 200, 50);
}

#[test]
Expand Down
34 changes: 25 additions & 9 deletions compiler/rustc_abi/src/wrapping_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,36 @@ impl WrappingRange {
size: Size,
) -> Option<Self> {
let mut values: Vec<_> = values.into_iter().collect();
values.sort_unstable();

// The simple answer is the non-wraparound range `min..=max`.
let obvious_range = WrappingRange { start: *values.first()?, end: *values.last()? };
let (min, max) = values
.iter()
.copied()
.map(|x| (x, x))
.reduce(|a, b| (u128::min(a.0, b.0), u128::max(a.1, b.1)))?;

// Just checking the max is enough to ensure that everything is in-range.
assert!(max <= size.unsigned_int_max(), "Value {max:?} is too big for {size:?}");

// Having sorted the inputs, one test is enough to double-check they all fit in `size`.
let max_input = obvious_range.end;
assert!(
max_input <= size.unsigned_int_max(),
"Value {max_input:?} is too big for {size:?}",
);
// The simple answer is the non-wraparound range `min..=max`.
let obvious_range = WrappingRange { start: min, end: max };

// It's very common that the input was only small non-negative integers and the
// obvious range turns out to be the best we can do.
// Every possible wraparound range must include at least `(...=min) | (max..)`,
// so if what we found already is at least that good, just use it.
// WR(min, max).width() ≤ WR(max, min).width()
// (max - min) % 2ⁿ ≤ (min - max) % 2ⁿ
// max - min ≤ min - max + 2ⁿ
// 2×(max - min) ≤ 2ⁿ
// max - min ≤ 2ⁿ⁻¹
let half_range = 1 << (size.bits() - 1);
if max - min <= half_range {
return Some(obvious_range);
}

// But every `[.., end, start, ..]` is also a potential candidate for a wraparound
// range `(..=end) | (start..)`, so long as `start` and `end` aren't duplicates.
values.sort_unstable();
let wraparound_ranges = values
.array_windows::<2>()
.filter_map(|&[end, start]| (start != end).then_some(WrappingRange { start, end }));
Expand Down
Loading