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);