]> granicus.if.org Git - llvm/commitdiff
[Support] Added overflow checking add, sub and mul.
authorJF Bastien <jfbastien@apple.com>
Wed, 31 Jul 2019 19:40:07 +0000 (19:40 +0000)
committerJF Bastien <jfbastien@apple.com>
Wed, 31 Jul 2019 19:40:07 +0000 (19:40 +0000)
Added AddOverflow, SubOverflow and MulOverflow to compute truncated results and return a flag indicating whether overflow occured.

 Differential Revision: https://reviews.llvm.org/D65494

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@367470 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Support/MathExtras.h
unittests/Support/MathExtrasTest.cpp

index 249139e824b550db8d8fd1ac15ec06adbdf41624..40256d102cd9cad24fa2d5007a2a5e1f74516704 100644 (file)
@@ -853,6 +853,83 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
 
 /// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
 extern const float huge_valf;
+
+
+/// Add two signed integers, computing the two's complement truncated result,
+/// returning true if overflow occured.
+template <typename T>
+typename std::enable_if<std::is_signed<T>::value, T>::type
+AddOverflow(T X, T Y, T &Result) {
+  // Perform the unsigned addition.
+  using U = typename std::make_unsigned<T>::type;
+  const U UX = static_cast<U>(X);
+  const U UY = static_cast<U>(Y);
+  const U UResult = UX + UY;
+
+  // Convert to signed.
+  Result = static_cast<T>(UResult);
+
+  // Adding two positive numbers should result in a positive number.
+  if (X > 0 && Y > 0)
+    return Result <= 0;
+  // Adding two negatives should result in a negative number.
+  if (X < 0 && Y < 0)
+    return Result >= 0;
+  return false;
+}
+
+/// Subtract two signed integers, computing the two's complement truncated
+/// result, returning true if an overflow ocurred.
+template <typename T>
+typename std::enable_if<std::is_signed<T>::value, T>::type
+SubOverflow(T X, T Y, T &Result) {
+  // Perform the unsigned addition.
+  using U = typename std::make_unsigned<T>::type;
+  const U UX = static_cast<U>(X);
+  const U UY = static_cast<U>(Y);
+  const U UResult = UX - UY;
+
+  // Convert to signed.
+  Result = static_cast<T>(UResult);
+
+  // Subtracting a positive number from a negative results in a negative number.
+  if (X <= 0 && Y > 0)
+    return Result >= 0;
+  // Subtracting a negative number from a positive results in a positive number.
+  if (X >= 0 && Y < 0)
+    return Result <= 0;
+  return false;
+}
+
+
+/// Multiply two signed integers, computing the two's complement truncated
+/// result, returning true if an overflow ocurred.
+template <typename T>
+typename std::enable_if<std::is_signed<T>::value, T>::type
+MulOverflow(T X, T Y, T &Result) {
+  // Perform the unsigned multiplication on absolute values.
+  using U = typename std::make_unsigned<T>::type;
+  const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X);
+  const U UY = Y < 0 ? (0 - static_cast<U>(Y)) : static_cast<U>(Y);
+  const U UResult = UX * UY;
+
+  // Convert to signed.
+  const bool IsNegative = (X < 0) ^ (Y < 0);
+  Result = IsNegative ? (0 - UResult) : UResult;
+
+  // If any of the args was 0, result is 0 and no overflow occurs.
+  if (UX == 0 || UY == 0)
+    return false;
+
+  // UX and UY are in [1, 2^n], where n is the number of digits.
+  // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
+  // positive) divided by an argument compares to the other.
+  if (IsNegative)
+    return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
+  else
+    return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
+}
+
 } // End llvm namespace
 
 #endif
index de8771402fbbd1cde134d047be7615e46652197a..01c83c9e14d3f7250af93ff88d562446d7f5c0fc 100644 (file)
@@ -468,4 +468,131 @@ TEST(MathExtras, IsShiftedInt) {
   EXPECT_FALSE((isShiftedInt<6, 10>(int64_t(1) << 15)));
 }
 
+template <typename T>
+class OverflowTest : public ::testing::Test { };
+
+using OverflowTestTypes = ::testing::Types<signed char, short, int, long,
+                                           long long>;
+
+TYPED_TEST_CASE(OverflowTest, OverflowTestTypes);
+
+TYPED_TEST(OverflowTest, AddNoOverflow) {
+  TypeParam Result;
+  EXPECT_FALSE(AddOverflow<TypeParam>(1, 2, Result));
+  EXPECT_EQ(Result, TypeParam(3));
+}
+
+TYPED_TEST(OverflowTest, AddOverflowToNegative) {
+  TypeParam Result;
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  EXPECT_TRUE(AddOverflow<TypeParam>(MaxValue, MaxValue, Result));
+  EXPECT_EQ(Result, TypeParam(-2));
+}
+
+TYPED_TEST(OverflowTest, AddOverflowToMin) {
+  TypeParam Result;
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  EXPECT_TRUE(AddOverflow<TypeParam>(MaxValue, TypeParam(1), Result));
+  EXPECT_EQ(Result, std::numeric_limits<TypeParam>::min());
+}
+
+TYPED_TEST(OverflowTest, AddOverflowToZero) {
+  TypeParam Result;
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(AddOverflow<TypeParam>(MinValue, MinValue, Result));
+  EXPECT_EQ(Result, TypeParam(0));
+}
+
+TYPED_TEST(OverflowTest, AddOverflowToMax) {
+  TypeParam Result;
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(AddOverflow<TypeParam>(MinValue, TypeParam(-1), Result));
+  EXPECT_EQ(Result, std::numeric_limits<TypeParam>::max());
+}
+
+TYPED_TEST(OverflowTest, SubNoOverflow) {
+  TypeParam Result;
+  EXPECT_FALSE(SubOverflow<TypeParam>(1, 2, Result));
+  EXPECT_EQ(Result, TypeParam(-1));
+}
+
+TYPED_TEST(OverflowTest, SubOverflowToMax) {
+  TypeParam Result;
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(SubOverflow<TypeParam>(0, MinValue, Result));
+  EXPECT_EQ(Result, MinValue);
+}
+
+TYPED_TEST(OverflowTest, SubOverflowToMin) {
+  TypeParam Result;
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(SubOverflow<TypeParam>(0, MinValue, Result));
+  EXPECT_EQ(Result, MinValue);
+}
+
+TYPED_TEST(OverflowTest, SubOverflowToNegative) {
+  TypeParam Result;
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(SubOverflow<TypeParam>(MaxValue, MinValue, Result));
+  EXPECT_EQ(Result, TypeParam(-1));
+}
+
+TYPED_TEST(OverflowTest, SubOverflowToPositive) {
+  TypeParam Result;
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(SubOverflow<TypeParam>(MinValue, MaxValue, Result));
+  EXPECT_EQ(Result, TypeParam(1));
+}
+
+TYPED_TEST(OverflowTest, MulNoOverflow) {
+  TypeParam Result;
+  EXPECT_FALSE(MulOverflow<TypeParam>(1, 2, Result));
+  EXPECT_EQ(Result, 2);
+  EXPECT_FALSE(MulOverflow<TypeParam>(-1, 3, Result));
+  EXPECT_EQ(Result, -3);
+  EXPECT_FALSE(MulOverflow<TypeParam>(4, -2, Result));
+  EXPECT_EQ(Result, -8);
+  EXPECT_FALSE(MulOverflow<TypeParam>(-6, -5, Result));
+  EXPECT_EQ(Result, 30);
+}
+
+TYPED_TEST(OverflowTest, MulNoOverflowToMax) {
+  TypeParam Result;
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_FALSE(MulOverflow<TypeParam>(MinValue + 1, -1, Result));
+  EXPECT_EQ(Result, MaxValue);
+}
+
+TYPED_TEST(OverflowTest, MulOverflowToMin) {
+  TypeParam Result;
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  EXPECT_TRUE(MulOverflow<TypeParam>(MinValue, -1, Result));
+  EXPECT_EQ(Result, MinValue);
+}
+
+TYPED_TEST(OverflowTest, MulOverflowMax) {
+  TypeParam Result;
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  EXPECT_TRUE(MulOverflow<TypeParam>(MinValue, MinValue, Result));
+  EXPECT_EQ(Result, 0);
+  EXPECT_TRUE(MulOverflow<TypeParam>(MaxValue, MaxValue, Result));
+  EXPECT_EQ(Result, 1);
+}
+
+TYPED_TEST(OverflowTest, MulResultZero) {
+  TypeParam Result;
+  EXPECT_FALSE(MulOverflow<TypeParam>(4, 0, Result));
+  EXPECT_EQ(Result, TypeParam(0));
+  EXPECT_FALSE(MulOverflow<TypeParam>(-5, 0, Result));
+  EXPECT_EQ(Result, TypeParam(0));
+  EXPECT_FALSE(MulOverflow<TypeParam>(0, 5, Result));
+  EXPECT_EQ(Result, TypeParam(0));
+  EXPECT_FALSE(MulOverflow<TypeParam>(0, -5, Result));
+  EXPECT_EQ(Result, TypeParam(0));
+}
+
 } // namespace