From 9204497c2999ce4a3df9802d48cb990be7ee1164 Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Tue, 5 Feb 2019 15:36:31 -0500 Subject: [PATCH 1/9] Allow const assignment for int saturating_add() calls for #58030 --- src/libcore/num/mod.rs | 57 +++++++++++++++---- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc_mir/interpret/intrinsics.rs | 29 +++++++++- src/librustc_mir/transform/qualify_consts.rs | 1 + .../transform/qualify_min_const_fn.rs | 1 + .../run-pass/const-int-saturating-arith.rs | 13 +++++ 6 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 src/test/run-pass/const-int-saturating-arith.rs diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index f80f839282781..55de04db028e1 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -882,17 +882,37 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg(stage0)] pub fn saturating_add(self, rhs: Self) -> Self { - #[cfg(stage0)] match self.checked_add(rhs) { Some(x) => x, None if rhs >= 0 => Self::max_value(), None => Self::min_value(), } - #[cfg(not(stage0))] - { - intrinsics::saturating_add(self, rhs) - } + } + + } + + doc_comment! { + concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric +bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); +assert_eq!(", stringify!($SelfT), "::max_value().saturating_add(100), ", stringify!($SelfT), +"::max_value());", +$EndFeature, " +```"), + + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + #[cfg(not(stage0))] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) } } @@ -2753,16 +2773,33 @@ assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg(stage0)] pub fn saturating_add(self, rhs: Self) -> Self { - #[cfg(stage0)] match self.checked_add(rhs) { Some(x) => x, None => Self::max_value(), } - #[cfg(not(stage0))] - { - intrinsics::saturating_add(self, rhs) - } + } + } + + doc_comment! { + concat!("Saturating integer addition. Computes `self + rhs`, saturating at +the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); +assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, " +```"), + + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + #[cfg(not(stage0))] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) } } diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index e6a560b2ad7b6..4013cfb9558e1 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -418,4 +418,4 @@ pub fn truncate(value: u128, size: Size) -> u128 { let shift = 128 - size; // truncate (shift left to drop out leftover values, shift right to fill with zeroes) (value << shift) >> shift -} +} \ No newline at end of file diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index d8778dfeef7a1..64be3969640e5 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -4,7 +4,7 @@ use syntax::symbol::Symbol; use rustc::ty; -use rustc::ty::layout::{LayoutOf, Primitive}; +use rustc::ty::layout::{LayoutOf, Primitive, Size}; use rustc::mir::BinOp; use rustc::mir::interpret::{ EvalResult, EvalErrorKind, Scalar, @@ -122,6 +122,33 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.binop_with_overflow(bin_op, lhs, rhs, dest)?; } } + "saturating_add" => { + let l = self.read_immediate(args[0])?; + let r = self.read_immediate(args[1])?; + let (val, overflowed) = self.binary_op_imm(BinOp::Add, l, r)?; + if overflowed { + let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; + let num_bits = l.layout.size.bits(); + let val = if l.layout.abi.is_signed() { + // For signed addition the saturated value depends on the sign of either term + if first_term & (1 << (num_bits-1)) == 0 { // signed term is positive + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) // max signed val + } else { // signed term is negative + Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) // min signed val + } + } else { + if num_bits == 128 { // General bit shift method causes overflow for u128 terms + Scalar::from_uint(u128::max_value(), Size::from_bits(128)) + } else { + Scalar::from_uint(u128::max_value() & ((1 << num_bits) - 1), + Size::from_bits(num_bits)) + } + }; + self.write_scalar(val, dest)?; + } else { + self.write_scalar(val, dest)?; + } + } "unchecked_shl" | "unchecked_shr" => { let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2d941902debc3..ac75a95dbe0fa 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -820,6 +820,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" + | "saturating_add" // no need to check feature gates, intrinsics are only callable // from the libstd or with forever unstable feature gates => is_const_fn = true, diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 85bf1e70ebf42..bb7fe0dab548a 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -374,6 +374,7 @@ fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool | "overflowing_add" // ~> .wrapping_add | "overflowing_sub" // ~> .wrapping_sub | "overflowing_mul" // ~> .wrapping_mul + | "saturating_add" // ~> .saturating_add | "unchecked_shl" // ~> .wrapping_shl | "unchecked_shr" // ~> .wrapping_shr | "rotate_left" // ~> .rotate_left diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs new file mode 100644 index 0000000000000..3ff6a1fc08aa2 --- /dev/null +++ b/src/test/run-pass/const-int-saturating-arith.rs @@ -0,0 +1,13 @@ +const INT_U32_NO: u32 = (42 as u32).saturating_add(2); +const INT_U32: u32 = u32::max_value().saturating_add(1); +const INT_U128: u128 = u128::max_value().saturating_add(1); +const INT_I128: i128 = i128::max_value().saturating_add(1); +const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1); + +fn main() { + assert_eq!(INT_U32_NO, 44); + assert_eq!(INT_U32, u32::max_value()); + assert_eq!(INT_U128, u128::max_value()); + assert_eq!(INT_I128, i128::max_value()); + assert_eq!(INT_I128_NEG, i128::min_value()); +} \ No newline at end of file From 0efb8e4d7392cd4b9c410b96ea209a18c161d92b Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Wed, 6 Feb 2019 16:15:17 -0500 Subject: [PATCH 2/9] Allow const assignment for int saturating_sub() for #58030 --- src/libcore/num/mod.rs | 54 +++++++++++++++---- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc_mir/interpret/intrinsics.rs | 28 ++++++++-- src/librustc_mir/transform/qualify_consts.rs | 1 + .../transform/qualify_min_const_fn.rs | 1 + .../run-pass/const-int-saturating-arith.rs | 16 +++++- 6 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 55de04db028e1..104b124bad491 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -932,17 +932,35 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg(stage0)] pub fn saturating_sub(self, rhs: Self) -> Self { - #[cfg(stage0)] match self.checked_sub(rhs) { Some(x) => x, None if rhs >= 0 => Self::min_value(), None => Self::max_value(), } - #[cfg(not(stage0))] - { - intrinsics::saturating_sub(self, rhs) - } + } + } + + doc_comment! { + concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the +numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27); +assert_eq!(", stringify!($SelfT), "::min_value().saturating_sub(100), ", stringify!($SelfT), +"::min_value());", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + #[cfg(not(stage0))] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) } } @@ -2817,16 +2835,32 @@ assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg(stage0)] pub fn saturating_sub(self, rhs: Self) -> Self { - #[cfg(stage0)] match self.checked_sub(rhs) { Some(x) => x, None => Self::min_value(), } - #[cfg(not(stage0))] - { - intrinsics::saturating_sub(self, rhs) - } + } + } + + doc_comment! { + concat!("Saturating integer subtraction. Computes `self - rhs`, saturating +at the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73); +assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + #[cfg(not(stage0))] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) } } diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 4013cfb9558e1..e6a560b2ad7b6 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -418,4 +418,4 @@ pub fn truncate(value: u128, size: Size) -> u128 { let shift = 128 - size; // truncate (shift left to drop out leftover values, shift right to fill with zeroes) (value << shift) >> shift -} \ No newline at end of file +} diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 64be3969640e5..dd165d9cb2fe7 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -130,14 +130,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; let num_bits = l.layout.size.bits(); let val = if l.layout.abi.is_signed() { - // For signed addition the saturated value depends on the sign of either term + // For signed addition the saturated value depends on the + // sign of either term if first_term & (1 << (num_bits-1)) == 0 { // signed term is positive - Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) // max signed val + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) } else { // signed term is negative - Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) // min signed val + Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) } } else { - if num_bits == 128 { // General bit shift method causes overflow for u128 terms + if num_bits == 128 { Scalar::from_uint(u128::max_value(), Size::from_bits(128)) } else { Scalar::from_uint(u128::max_value() & ((1 << num_bits) - 1), @@ -149,6 +150,25 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.write_scalar(val, dest)?; } } + "saturating_sub" => { + let l = self.read_immediate(args[0])?; + let r = self.read_immediate(args[1])?; + let (val, overflowed) = self.binary_op_imm(BinOp::Sub, l, r)?; + if overflowed { + let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; + let num_bits = l.layout.size.bits(); + let val = if first_term & (1 << (num_bits-1)) == 0 { // first term is positive + // so overflow is positive + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) + } else { + // if first term negative, overflow must be negative + Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) + }; + self.write_scalar(val, dest)?; + } else { + self.write_scalar(val, dest)?; + } + } "unchecked_shl" | "unchecked_shr" => { let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index ac75a95dbe0fa..d6ec207434243 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -821,6 +821,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | "sub_with_overflow" | "mul_with_overflow" | "saturating_add" + | "saturating_sub" // no need to check feature gates, intrinsics are only callable // from the libstd or with forever unstable feature gates => is_const_fn = true, diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index bb7fe0dab548a..d8dc6c2845b28 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -375,6 +375,7 @@ fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool | "overflowing_sub" // ~> .wrapping_sub | "overflowing_mul" // ~> .wrapping_mul | "saturating_add" // ~> .saturating_add + | "saturating_sub" // ~> .saturating_sub | "unchecked_shl" // ~> .wrapping_shl | "unchecked_shr" // ~> .wrapping_shr | "rotate_left" // ~> .rotate_left diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs index 3ff6a1fc08aa2..283a2a2484fe2 100644 --- a/src/test/run-pass/const-int-saturating-arith.rs +++ b/src/test/run-pass/const-int-saturating-arith.rs @@ -4,10 +4,24 @@ const INT_U128: u128 = u128::max_value().saturating_add(1); const INT_I128: i128 = i128::max_value().saturating_add(1); const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1); +const INT_U32_NO_SUB: u32 = (42 as u32).saturating_sub(2); +const INT_I32_NO_SUB: i32 = (-42 as i32).saturating_sub(2); +const INT_I32_NEG_SUB: i32 = i32::min_value().saturating_sub(1); +const INT_I32_POS_SUB: i32 = i32::max_value().saturating_sub(-1); +const INT_I128_NEG_SUB: i128 = i128::min_value().saturating_sub(1); +const INT_I128_POS_SUB: i128 = i128::max_value().saturating_sub(-1); + fn main() { assert_eq!(INT_U32_NO, 44); assert_eq!(INT_U32, u32::max_value()); assert_eq!(INT_U128, u128::max_value()); assert_eq!(INT_I128, i128::max_value()); assert_eq!(INT_I128_NEG, i128::min_value()); -} \ No newline at end of file + + assert_eq!(INT_U32_NO_SUB, 40); + assert_eq!(INT_I32_NO_SUB, -44); + assert_eq!(INT_I32_NEG_SUB, i32::min_value()); + assert_eq!(INT_I32_POS_SUB, i32::max_value()); + assert_eq!(INT_I128_NEG_SUB, i128::min_value()); + assert_eq!(INT_I128_POS_SUB, i128::max_value()); +} From c5586ebe26eae41db94549903aa597cb0247e217 Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Wed, 6 Feb 2019 16:21:50 -0500 Subject: [PATCH 3/9] tidy line length split --- src/librustc_mir/interpret/intrinsics.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index dd165d9cb2fe7..62a0b3d9524a4 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -133,7 +133,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // For signed addition the saturated value depends on the // sign of either term if first_term & (1 << (num_bits-1)) == 0 { // signed term is positive - Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, + Size::from_bits(num_bits)) } else { // signed term is negative Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) } From f857199073753956db92e6710f9178e34de4122e Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Wed, 6 Feb 2019 18:10:08 -0500 Subject: [PATCH 4/9] fix saturating_sub() underflow for unsigned ints #58030 --- src/librustc_mir/interpret/intrinsics.rs | 22 +++++++++---------- .../run-pass/const-int-saturating-arith.rs | 4 ++++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 62a0b3d9524a4..827c4e62ecdac 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -139,12 +139,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) } } else { - if num_bits == 128 { - Scalar::from_uint(u128::max_value(), Size::from_bits(128)) - } else { - Scalar::from_uint(u128::max_value() & ((1 << num_bits) - 1), - Size::from_bits(num_bits)) - } + Scalar::from_uint(u128::max_value() >> (128 - num_bits), Size::from_bits(num_bits)) }; self.write_scalar(val, dest)?; } else { @@ -158,12 +153,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> if overflowed { let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; let num_bits = l.layout.size.bits(); - let val = if first_term & (1 << (num_bits-1)) == 0 { // first term is positive - // so overflow is positive - Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) + let val = if l.layout.abi.is_signed() { + if first_term & (1 << (num_bits-1)) == 0 { // first term is positive + // so overflow is positive + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) + } else { + // if first term negative, overflow must be negative + Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) + } } else { - // if first term negative, overflow must be negative - Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) + // unsigned underflow saturates to 0 + Scalar::from_uint(0u128, Size::from_bits(num_bits)) }; self.write_scalar(val, dest)?; } else { diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs index 283a2a2484fe2..92372e073cf2e 100644 --- a/src/test/run-pass/const-int-saturating-arith.rs +++ b/src/test/run-pass/const-int-saturating-arith.rs @@ -5,9 +5,11 @@ const INT_I128: i128 = i128::max_value().saturating_add(1); const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1); const INT_U32_NO_SUB: u32 = (42 as u32).saturating_sub(2); +const INT_U32_SUB: u32 = (1 as u32).saturating_sub(2); const INT_I32_NO_SUB: i32 = (-42 as i32).saturating_sub(2); const INT_I32_NEG_SUB: i32 = i32::min_value().saturating_sub(1); const INT_I32_POS_SUB: i32 = i32::max_value().saturating_sub(-1); +const INT_U128_SUB: u128 = (0 as u128).saturating_sub(1); const INT_I128_NEG_SUB: i128 = i128::min_value().saturating_sub(1); const INT_I128_POS_SUB: i128 = i128::max_value().saturating_sub(-1); @@ -19,9 +21,11 @@ fn main() { assert_eq!(INT_I128_NEG, i128::min_value()); assert_eq!(INT_U32_NO_SUB, 40); + assert_eq!(INT_U32_SUB, 0); assert_eq!(INT_I32_NO_SUB, -44); assert_eq!(INT_I32_NEG_SUB, i32::min_value()); assert_eq!(INT_I32_POS_SUB, i32::max_value()); + assert_eq!(INT_U128_SUB, 0); assert_eq!(INT_I128_NEG_SUB, i128::min_value()); assert_eq!(INT_I128_POS_SUB, i128::max_value()); } From d7efc76b7ef8c60aefd603dd4773b2fa039bab80 Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Wed, 6 Feb 2019 18:12:21 -0500 Subject: [PATCH 5/9] tidy line length --- src/librustc_mir/interpret/intrinsics.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 827c4e62ecdac..5ec18d133c665 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -139,7 +139,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) } } else { - Scalar::from_uint(u128::max_value() >> (128 - num_bits), Size::from_bits(num_bits)) + Scalar::from_uint(u128::max_value() >> (128 - num_bits), + Size::from_bits(num_bits)) }; self.write_scalar(val, dest)?; } else { @@ -156,7 +157,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let val = if l.layout.abi.is_signed() { if first_term & (1 << (num_bits-1)) == 0 { // first term is positive // so overflow is positive - Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, + Size::from_bits(num_bits)) } else { // if first term negative, overflow must be negative Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) From 17998961d4d08d08be3cee5e09ca62b75b832f1a Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Thu, 7 Feb 2019 13:12:17 -0500 Subject: [PATCH 6/9] Refactor const saturating intrinsics emulation and add unstable feature attribute #58030 --- src/libcore/num/mod.rs | 2 + src/librustc_mir/interpret/intrinsics.rs | 64 ++++++++----------- .../run-pass/const-int-saturating-arith.rs | 2 + 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 104b124bad491..d4fade7613848 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -909,6 +909,7 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_saturating_int_methods")] #[inline] #[cfg(not(stage0))] pub const fn saturating_add(self, rhs: Self) -> Self { @@ -957,6 +958,7 @@ assert_eq!(", stringify!($SelfT), "::min_value().saturating_sub(100), ", stringi $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_saturating_int_methods")] #[inline] #[cfg(not(stage0))] pub const fn saturating_sub(self, rhs: Self) -> Self { diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 5ec18d133c665..e8dc22b8a596f 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -122,55 +122,41 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.binop_with_overflow(bin_op, lhs, rhs, dest)?; } } - "saturating_add" => { + "saturating_add" | "saturating_sub" => { let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; - let (val, overflowed) = self.binary_op_imm(BinOp::Add, l, r)?; - if overflowed { - let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; - let num_bits = l.layout.size.bits(); - let val = if l.layout.abi.is_signed() { - // For signed addition the saturated value depends on the - // sign of either term - if first_term & (1 << (num_bits-1)) == 0 { // signed term is positive - Scalar::from_uint((1u128 << (num_bits - 1)) - 1, - Size::from_bits(num_bits)) - } else { // signed term is negative - Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) - } - } else { - Scalar::from_uint(u128::max_value() >> (128 - num_bits), - Size::from_bits(num_bits)) - }; - self.write_scalar(val, dest)?; + let is_add = intrinsic_name == "saturating_add"; + let (val, overflowed) = self.binary_op_imm(if is_add { + BinOp::Add } else { - self.write_scalar(val, dest)?; - } - } - "saturating_sub" => { - let l = self.read_immediate(args[0])?; - let r = self.read_immediate(args[1])?; - let (val, overflowed) = self.binary_op_imm(BinOp::Sub, l, r)?; - if overflowed { + BinOp::Sub + }, l, r)?; + let val = if overflowed { + // For signed ints the saturated value depends on the + // sign of the first term let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; let num_bits = l.layout.size.bits(); - let val = if l.layout.abi.is_signed() { + if l.layout.abi.is_signed() { if first_term & (1 << (num_bits-1)) == 0 { // first term is positive - // so overflow is positive - Scalar::from_uint((1u128 << (num_bits - 1)) - 1, + Scalar::from_uint((1u128 << (num_bits - 1)) - 1, // max positive Size::from_bits(num_bits)) - } else { - // if first term negative, overflow must be negative + } else { // first term is negative + // max negative Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) } - } else { - // unsigned underflow saturates to 0 - Scalar::from_uint(0u128, Size::from_bits(num_bits)) - }; - self.write_scalar(val, dest)?; + } else { // unsigned + if is_add { + // max unsigned + Scalar::from_uint(u128::max_value() >> (128 - num_bits), + Size::from_bits(num_bits)) + } else { // underflow to 0 + Scalar::from_uint(0u128, Size::from_bits(num_bits)) + } + } } else { - self.write_scalar(val, dest)?; - } + val + }; + self.write_scalar(val, dest)?; } "unchecked_shl" | "unchecked_shr" => { let l = self.read_immediate(args[0])?; diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs index 92372e073cf2e..4f586a276f0d2 100644 --- a/src/test/run-pass/const-int-saturating-arith.rs +++ b/src/test/run-pass/const-int-saturating-arith.rs @@ -1,3 +1,5 @@ +#![feature(const_saturating_int_methods)] + const INT_U32_NO: u32 = (42 as u32).saturating_add(2); const INT_U32: u32 = u32::max_value().saturating_add(1); const INT_U128: u128 = u128::max_value().saturating_add(1); From da13fbda5e28482d01205d791f38a645a396ea65 Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Thu, 7 Feb 2019 13:46:20 -0500 Subject: [PATCH 7/9] Add unstable feature attribute for unsigned const saturating add/sub intrinsics #58030 --- src/libcore/num/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index d4fade7613848..d34173dafb351 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2816,6 +2816,7 @@ assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_saturating_int_methods")] #[inline] #[cfg(not(stage0))] pub const fn saturating_add(self, rhs: Self) -> Self { @@ -2859,6 +2860,7 @@ Basic usage: assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_saturating_int_methods")] #[inline] #[cfg(not(stage0))] pub const fn saturating_sub(self, rhs: Self) -> Self { From 7854067044466de2dd123ca4a3fb6251c0683ead Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Fri, 8 Feb 2019 13:04:11 -0500 Subject: [PATCH 8/9] Saturating add/sub intrinsic emulation refactor/comments #58030 --- src/librustc_mir/interpret/intrinsics.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index e8dc22b8a596f..48fa856839602 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -132,15 +132,22 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> BinOp::Sub }, l, r)?; let val = if overflowed { - // For signed ints the saturated value depends on the - // sign of the first term - let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; let num_bits = l.layout.size.bits(); if l.layout.abi.is_signed() { - if first_term & (1 << (num_bits-1)) == 0 { // first term is positive + // For signed ints the saturated value depends on the sign of the first + // term since the sign of the second term can be inferred from this and + // the fact that the operation has overflowed (if either is 0 no + // overflow can occur) + let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; + let first_term_pos = first_term & (1 << (num_bits-1)) == 0; + if first_term_pos { + // Negative overflow not possible since the positive first term + // can only increase an (in range) negative term for addition + // or corresponding negated positive term for subtraction Scalar::from_uint((1u128 << (num_bits - 1)) - 1, // max positive Size::from_bits(num_bits)) - } else { // first term is negative + } else { + // Positive overflow not possible for similar reason // max negative Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) } From b04d8aa7744ac797736a543a3a425a279a58ad56 Mon Sep 17 00:00:00 2001 From: Patrick McCarter Date: Mon, 11 Feb 2019 11:20:50 -0500 Subject: [PATCH 9/9] rename variable and add test directive for #58030 --- src/librustc_mir/interpret/intrinsics.rs | 4 ++-- src/test/run-pass/const-int-saturating-arith.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 48fa856839602..dcbf6a52ba9f5 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -139,8 +139,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // the fact that the operation has overflowed (if either is 0 no // overflow can occur) let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; - let first_term_pos = first_term & (1 << (num_bits-1)) == 0; - if first_term_pos { + let first_term_positive = first_term & (1 << (num_bits-1)) == 0; + if first_term_positive { // Negative overflow not possible since the positive first term // can only increase an (in range) negative term for addition // or corresponding negated positive term for subtraction diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs index 4f586a276f0d2..dae4c7216b2c0 100644 --- a/src/test/run-pass/const-int-saturating-arith.rs +++ b/src/test/run-pass/const-int-saturating-arith.rs @@ -1,3 +1,4 @@ +// ignore-emscripten no i128 support #![feature(const_saturating_int_methods)] const INT_U32_NO: u32 = (42 as u32).saturating_add(2);