diff --git a/src/error.rs b/src/error.rs index ee805695c510..496cefad33d5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), + Intrinsic(String), OverflowingMath, InvalidChar(u128), OutOfMemory { @@ -104,6 +105,8 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", + EvalError::Intrinsic(..) => + "intrinsic failed", EvalError::OverflowingMath => "attempted to do overflowing math", EvalError::NoMirFor(..) => @@ -168,6 +171,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => write!(f, "{:?} at {:?}", err, span), + EvalError::Intrinsic(ref err) => + write!(f, "{}", err), EvalError::InvalidChar(c) => write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } => diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 0b9a37512ede..5fd0cc580220 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -154,12 +154,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "ctpop" | "cttz" | + "cttz_nonzero" | "ctlz" | + "ctlz_nonzero" | "bswap" => { let ty = substs.type_at(0); - let num = self.value_to_primval(arg_vals[0], ty)?; + let num = self.value_to_primval(arg_vals[0], ty)?.to_bytes()?; let kind = self.ty_to_primval_kind(ty)?; - let num = numeric_intrinsic(intrinsic_name, num, kind)?; + let num = if intrinsic_name.ends_with("_nonzero") { + if num == 0 { + return Err(EvalError::Intrinsic(format!("{} called on 0", intrinsic_name))) + } + numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), num, kind)? + } else { + numeric_intrinsic(intrinsic_name, num, kind)? + }; self.write_primval(dest, num, ty)?; } @@ -538,13 +547,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn numeric_intrinsic<'tcx>( name: &str, - val: PrimVal, + bytes: u128, kind: PrimValKind ) -> EvalResult<'tcx, PrimVal> { macro_rules! integer_intrinsic { ($method:ident) => ({ - let bytes = val.to_bytes()?; - use value::PrimValKind::*; let result_bytes = match kind { I8 => (bytes as i8).$method() as u128, @@ -557,7 +564,7 @@ fn numeric_intrinsic<'tcx>( U64 => (bytes as u64).$method() as u128, I128 => (bytes as i128).$method() as u128, U128 => bytes.$method() as u128, - _ => bug!("invalid `{}` argument: {:?}", name, val), + _ => bug!("invalid `{}` argument: {:?}", name, bytes), }; PrimVal::Bytes(result_bytes) diff --git a/tests/run-pass/intrinsics-integer.rs b/tests/run-pass/intrinsics-integer.rs index 759dc515456d..4896f02da20b 100644 --- a/tests/run-pass/intrinsics-integer.rs +++ b/tests/run-pass/intrinsics-integer.rs @@ -14,7 +14,9 @@ mod rusti { extern "rust-intrinsic" { pub fn ctpop(x: T) -> T; pub fn ctlz(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> T; pub fn cttz(x: T) -> T; + pub fn cttz_nonzero(x: T) -> T; pub fn bswap(x: T) -> T; } } @@ -68,6 +70,21 @@ pub fn main() { assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25); assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57); + assert_eq!(ctlz_nonzero(1u8), 7); assert_eq!(ctlz_nonzero(1i8), 7); + assert_eq!(ctlz_nonzero(1u16), 15); assert_eq!(ctlz_nonzero(1i16), 15); + assert_eq!(ctlz_nonzero(1u32), 31); assert_eq!(ctlz_nonzero(1i32), 31); + assert_eq!(ctlz_nonzero(1u64), 63); assert_eq!(ctlz_nonzero(1i64), 63); + + assert_eq!(ctlz_nonzero(10u8), 4); assert_eq!(ctlz_nonzero(10i8), 4); + assert_eq!(ctlz_nonzero(10u16), 12); assert_eq!(ctlz_nonzero(10i16), 12); + assert_eq!(ctlz_nonzero(10u32), 28); assert_eq!(ctlz_nonzero(10i32), 28); + assert_eq!(ctlz_nonzero(10u64), 60); assert_eq!(ctlz_nonzero(10i64), 60); + + assert_eq!(ctlz_nonzero(100u8), 1); assert_eq!(ctlz_nonzero(100i8), 1); + assert_eq!(ctlz_nonzero(100u16), 9); assert_eq!(ctlz_nonzero(100i16), 9); + assert_eq!(ctlz_nonzero(100u32), 25); assert_eq!(ctlz_nonzero(100i32), 25); + assert_eq!(ctlz_nonzero(100u64), 57); assert_eq!(ctlz_nonzero(100i64), 57); + assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0); assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0); assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0); @@ -93,6 +110,26 @@ pub fn main() { assert_eq!(cttz(100u32), 2); assert_eq!(cttz(100i32), 2); assert_eq!(cttz(100u64), 2); assert_eq!(cttz(100i64), 2); + assert_eq!(cttz_nonzero(-1i8 as u8), 0); assert_eq!(cttz_nonzero(-1i8), 0); + assert_eq!(cttz_nonzero(-1i16 as u16), 0); assert_eq!(cttz_nonzero(-1i16), 0); + assert_eq!(cttz_nonzero(-1i32 as u32), 0); assert_eq!(cttz_nonzero(-1i32), 0); + assert_eq!(cttz_nonzero(-1i64 as u64), 0); assert_eq!(cttz_nonzero(-1i64), 0); + + assert_eq!(cttz_nonzero(1u8), 0); assert_eq!(cttz_nonzero(1i8), 0); + assert_eq!(cttz_nonzero(1u16), 0); assert_eq!(cttz_nonzero(1i16), 0); + assert_eq!(cttz_nonzero(1u32), 0); assert_eq!(cttz_nonzero(1i32), 0); + assert_eq!(cttz_nonzero(1u64), 0); assert_eq!(cttz_nonzero(1i64), 0); + + assert_eq!(cttz_nonzero(10u8), 1); assert_eq!(cttz_nonzero(10i8), 1); + assert_eq!(cttz_nonzero(10u16), 1); assert_eq!(cttz_nonzero(10i16), 1); + assert_eq!(cttz_nonzero(10u32), 1); assert_eq!(cttz_nonzero(10i32), 1); + assert_eq!(cttz_nonzero(10u64), 1); assert_eq!(cttz_nonzero(10i64), 1); + + assert_eq!(cttz_nonzero(100u8), 2); assert_eq!(cttz_nonzero(100i8), 2); + assert_eq!(cttz_nonzero(100u16), 2); assert_eq!(cttz_nonzero(100i16), 2); + assert_eq!(cttz_nonzero(100u32), 2); assert_eq!(cttz_nonzero(100i32), 2); + assert_eq!(cttz_nonzero(100u64), 2); assert_eq!(cttz_nonzero(100i64), 2); + assert_eq!(bswap(0x0Au8), 0x0A); // no-op assert_eq!(bswap(0x0Ai8), 0x0A); // no-op assert_eq!(bswap(0x0A0Bu16), 0x0B0A);