]> granicus.if.org Git - llvm/commitdiff
[ValueTracking] Known bits support for unsigned saturating add/sub
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 1 Mar 2019 20:07:04 +0000 (20:07 +0000)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 1 Mar 2019 20:07:04 +0000 (20:07 +0000)
We have two sources of known bits:

1. For adds leading ones of either operand are preserved. For sub
leading zeros of LHS and leading ones of RHS become leading zeros in
the result.

2. The saturating math is a select between add/sub and an all-ones/
zero value. As such we can carry out the add/sub known bits
calculation, and only preseve the known one/zero bits respectively.

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

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

lib/Analysis/ValueTracking.cpp
unittests/Analysis/ValueTrackingTest.cpp

index 8b3d16a5125796d22f281b9acd3071afa29bbc67..b4473f857e2da7f828b7ff547a6b2d52d83ba034 100644 (file)
@@ -1523,6 +1523,37 @@ static void computeKnownBitsFromOperator(const Operator *I, KnownBits &Known,
             Known2.One.shl(ShiftAmt) | Known3.One.lshr(BitWidth - ShiftAmt);
         break;
       }
+      case Intrinsic::uadd_sat:
+      case Intrinsic::usub_sat: {
+        bool IsAdd = II->getIntrinsicID() == Intrinsic::uadd_sat;
+        computeKnownBits(I->getOperand(0), Known, Depth + 1, Q);
+        computeKnownBits(I->getOperand(1), Known2, Depth + 1, Q);
+
+        // Add: Leading ones of either operand are preserved.
+        // Sub: Leading zeros of LHS and leading ones of RHS are preserved
+        // as leading zeros in the result.
+        unsigned LeadingKnown;
+        if (IsAdd)
+          LeadingKnown = std::max(Known.countMinLeadingOnes(),
+                                  Known2.countMinLeadingOnes());
+        else
+          LeadingKnown = std::max(Known.countMinLeadingZeros(),
+                                  Known2.countMinLeadingOnes());
+
+        Known = KnownBits::computeForAddSub(
+            IsAdd, /* NSW */ false, Known, Known2);
+
+        // We select between the operation result and all-ones/zero
+        // respectively, so we can preserve known ones/zeros.
+        if (IsAdd) {
+          Known.One.setHighBits(LeadingKnown);
+          Known.Zero.clearAllBits();
+        } else {
+          Known.Zero.setHighBits(LeadingKnown);
+          Known.One.clearAllBits();
+        }
+        break;
+      }
       case Intrinsic::x86_sse42_crc32_64_64:
         Known.Zero.setBitsFrom(32);
         break;
index a0f5aed2b6d97b63d2141ccf0aab3d4775a676a5..df32287dbff20289a4e9db53a94ccc0b67aa9157 100644 (file)
@@ -615,3 +615,74 @@ TEST_F(ComputeKnownBitsTest, ComputeKnownFshlZero) {
       "declare i16 @llvm.fshl.i16(i16, i16, i16)\n");
   expectKnownBits(/*zero*/ 15u, /*one*/ 3840u);
 }
+
+TEST_F(ComputeKnownBitsTest, ComputeKnownUAddSatLeadingOnes) {
+  // uadd.sat(1111...1, ........)
+  // = 1111....
+  parseAssembly(
+      "define i8 @test(i8 %a, i8 %b) {\n"
+      "  %aa = or i8 %a, 241\n"
+      "  %A = call i8 @llvm.uadd.sat.i8(i8 %aa, i8 %b)\n"
+      "  ret i8 %A\n"
+      "}\n"
+      "declare i8 @llvm.uadd.sat.i8(i8, i8)\n");
+  expectKnownBits(/*zero*/ 0u, /*one*/ 240u);
+}
+
+TEST_F(ComputeKnownBitsTest, ComputeKnownUAddSatOnesPreserved) {
+  // uadd.sat(00...011, .1...110)
+  // = .......1
+  parseAssembly(
+      "define i8 @test(i8 %a, i8 %b) {\n"
+      "  %aa = or i8 %a, 3\n"
+      "  %aaa = and i8 %aa, 59\n"
+      "  %bb = or i8 %b, 70\n"
+      "  %bbb = and i8 %bb, 254\n"
+      "  %A = call i8 @llvm.uadd.sat.i8(i8 %aaa, i8 %bbb)\n"
+      "  ret i8 %A\n"
+      "}\n"
+      "declare i8 @llvm.uadd.sat.i8(i8, i8)\n");
+  expectKnownBits(/*zero*/ 0u, /*one*/ 1u);
+}
+
+TEST_F(ComputeKnownBitsTest, ComputeKnownUSubSatLHSLeadingZeros) {
+  // usub.sat(0000...0, ........)
+  // = 0000....
+  parseAssembly(
+      "define i8 @test(i8 %a, i8 %b) {\n"
+      "  %aa = and i8 %a, 14\n"
+      "  %A = call i8 @llvm.usub.sat.i8(i8 %aa, i8 %b)\n"
+      "  ret i8 %A\n"
+      "}\n"
+      "declare i8 @llvm.usub.sat.i8(i8, i8)\n");
+  expectKnownBits(/*zero*/ 240u, /*one*/ 0u);
+}
+
+TEST_F(ComputeKnownBitsTest, ComputeKnownUSubSatRHSLeadingOnes) {
+  // usub.sat(........, 1111...1)
+  // = 0000....
+  parseAssembly(
+      "define i8 @test(i8 %a, i8 %b) {\n"
+      "  %bb = or i8 %a, 241\n"
+      "  %A = call i8 @llvm.usub.sat.i8(i8 %a, i8 %bb)\n"
+      "  ret i8 %A\n"
+      "}\n"
+      "declare i8 @llvm.usub.sat.i8(i8, i8)\n");
+  expectKnownBits(/*zero*/ 240u, /*one*/ 0u);
+}
+
+TEST_F(ComputeKnownBitsTest, ComputeKnownUSubSatZerosPreserved) {
+  // usub.sat(11...011, .1...110)
+  // = ......0.
+  parseAssembly(
+      "define i8 @test(i8 %a, i8 %b) {\n"
+      "  %aa = or i8 %a, 195\n"
+      "  %aaa = and i8 %aa, 251\n"
+      "  %bb = or i8 %b, 70\n"
+      "  %bbb = and i8 %bb, 254\n"
+      "  %A = call i8 @llvm.usub.sat.i8(i8 %aaa, i8 %bbb)\n"
+      "  ret i8 %A\n"
+      "}\n"
+      "declare i8 @llvm.usub.sat.i8(i8, i8)\n");
+  expectKnownBits(/*zero*/ 2u, /*one*/ 0u);
+}