librustc_middle: Add function for computing unsigned abs

This is tricky to get right if we want to avoid panicking or wrapping.

Signed-off-by: Joe Richey <joerichey@google.com>
This commit is contained in:
Joe Richey 2020-05-26 01:57:49 -07:00
parent 55577b411c
commit 6367b544b7
2 changed files with 14 additions and 8 deletions

View file

@ -598,3 +598,12 @@ pub fn truncate(value: u128, size: Size) -> u128 {
// Truncate (shift left to drop out leftover values, shift right to fill with zeroes).
(value << shift) >> shift
}
/// Computes the unsigned absolute value without wrapping or panicking.
#[inline]
pub fn uabs(value: i64) -> u64 {
// The only tricky part here is if value == i64::MIN. In that case,
// wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64
// gives 2^63, the correct value.
value.wrapping_abs() as u64
}

View file

@ -1,4 +1,4 @@
use super::{AllocId, InterpResult};
use super::{uabs, AllocId, InterpResult};
use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size};
@ -48,15 +48,12 @@ pub trait PointerArithmetic: HasDataLayout {
#[inline]
fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) {
if i < 0 {
// Trickery to ensure that `i64::MIN` works fine: compute `n = -i`.
// This formula only works for true negative values; it overflows for zero!
let n = u64::MAX - (i as u64) + 1;
let n = uabs(i);
if i >= 0 {
self.overflowing_offset(val, n)
} else {
let res = val.overflowing_sub(n);
self.truncate_to_ptr(res)
} else {
// `i >= 0`, so the cast is safe.
self.overflowing_offset(val, i as u64)
}
}