Skip to content

Commit 7671fc7

Browse files
committed
[KnownBits] Add computeForAddCarry()
This is for D60460. computeForAddSub() essentially already supports carries because it has to deal with subtractions. This revision extracts a lower-level computeForAddCarry() function, which allows computing the known bits for add (carry known zero), sub (carry known one) and addcarry (carry unknown). As we don't seem to have any yet, I've added a unit test file for KnownBits and exhaustive tests for the new computeForAddCarry() functionality, as well the existing computeForAddSub() function. Differential Revision: https://reviews.llvm.org/D60522 llvm-svn: 358297
1 parent 7a60cd3 commit 7671fc7

File tree

4 files changed

+166
-12
lines changed

4 files changed

+166
-12
lines changed

llvm/include/llvm/Support/KnownBits.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ struct KnownBits {
202202
return getBitWidth() - Zero.countPopulation();
203203
}
204204

205+
/// Compute known bits resulting from adding LHS, RHS and a 1-bit Carry.
206+
static KnownBits computeForAddCarry(
207+
const KnownBits &LHS, const KnownBits &RHS, const KnownBits &Carry);
208+
205209
/// Compute known bits resulting from adding LHS and RHS.
206210
static KnownBits computeForAddSub(bool Add, bool NSW, const KnownBits &LHS,
207211
KnownBits RHS);

llvm/lib/Support/KnownBits.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@
1515

1616
using namespace llvm;
1717

18-
KnownBits KnownBits::computeForAddSub(bool Add, bool NSW,
19-
const KnownBits &LHS, KnownBits RHS) {
20-
// Carry in a 1 for a subtract, rather than 0.
21-
bool CarryIn = false;
22-
if (!Add) {
23-
// Sum = LHS + ~RHS + 1
24-
std::swap(RHS.Zero, RHS.One);
25-
CarryIn = true;
26-
}
18+
static KnownBits computeForAddCarry(
19+
const KnownBits &LHS, const KnownBits &RHS,
20+
bool CarryZero, bool CarryOne) {
21+
assert(!(CarryZero && CarryOne) &&
22+
"Carry can't be zero and one at the same time");
2723

28-
APInt PossibleSumZero = ~LHS.Zero + ~RHS.Zero + CarryIn;
29-
APInt PossibleSumOne = LHS.One + RHS.One + CarryIn;
24+
APInt PossibleSumZero = ~LHS.Zero + ~RHS.Zero + !CarryZero;
25+
APInt PossibleSumOne = LHS.One + RHS.One + CarryOne;
3026

3127
// Compute known bits of the carry.
3228
APInt CarryKnownZero = ~(PossibleSumZero ^ LHS.Zero ^ RHS.Zero);
@@ -45,9 +41,32 @@ KnownBits KnownBits::computeForAddSub(bool Add, bool NSW,
4541
KnownBits KnownOut;
4642
KnownOut.Zero = ~std::move(PossibleSumZero) & Known;
4743
KnownOut.One = std::move(PossibleSumOne) & Known;
44+
return KnownOut;
45+
}
46+
47+
KnownBits KnownBits::computeForAddCarry(
48+
const KnownBits &LHS, const KnownBits &RHS, const KnownBits &Carry) {
49+
assert(Carry.getBitWidth() == 1 && "Carry must be 1-bit");
50+
return ::computeForAddCarry(
51+
LHS, RHS, Carry.Zero.getBoolValue(), Carry.One.getBoolValue());
52+
}
53+
54+
KnownBits KnownBits::computeForAddSub(bool Add, bool NSW,
55+
const KnownBits &LHS, KnownBits RHS) {
56+
KnownBits KnownOut;
57+
if (Add) {
58+
// Sum = LHS + RHS + 0
59+
KnownOut = ::computeForAddCarry(
60+
LHS, RHS, /*CarryZero*/true, /*CarryOne*/false);
61+
} else {
62+
// Sum = LHS + ~RHS + 1
63+
std::swap(RHS.Zero, RHS.One);
64+
KnownOut = ::computeForAddCarry(
65+
LHS, RHS, /*CarryZero*/false, /*CarryOne*/true);
66+
}
4867

4968
// Are we still trying to solve for the sign bit?
50-
if (!Known.isSignBitSet()) {
69+
if (!KnownOut.isNegative() && !KnownOut.isNonNegative()) {
5170
if (NSW) {
5271
// Adding two non-negative numbers, or subtracting a negative number from
5372
// a non-negative one, can't wrap into negative.

llvm/unittests/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ add_llvm_unittest(SupportTests
3434
Host.cpp
3535
ItaniumManglingCanonicalizerTest.cpp
3636
JSONTest.cpp
37+
KnownBitsTest.cpp
3738
LEB128Test.cpp
3839
LineIteratorTest.cpp
3940
LockFileManagerTest.cpp
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//===- llvm/unittest/Support/KnownBitsTest.cpp - KnownBits tests ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements unit tests for KnownBits functions.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/Support/KnownBits.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
18+
namespace {
19+
20+
template<typename FnTy>
21+
void ForeachKnownBits(unsigned Bits, FnTy Fn) {
22+
unsigned Max = 1 << Bits;
23+
KnownBits Known(Bits);
24+
for (unsigned Zero = 0; Zero < Max; ++Zero) {
25+
for (unsigned One = 0; One < Max; ++One) {
26+
Known.Zero = Zero;
27+
Known.One = One;
28+
if (Known.hasConflict())
29+
continue;
30+
31+
Fn(Known);
32+
}
33+
}
34+
}
35+
36+
template<typename FnTy>
37+
void ForeachNumInKnownBits(const KnownBits &Known, FnTy Fn) {
38+
unsigned Bits = Known.getBitWidth();
39+
unsigned Max = 1 << Bits;
40+
for (unsigned N = 0; N < Max; ++N) {
41+
APInt Num(Bits, N);
42+
if ((Num & Known.Zero) != 0 || (~Num & Known.One) != 0)
43+
continue;
44+
45+
Fn(Num);
46+
}
47+
}
48+
49+
TEST(KnownBitsTest, AddCarryExhaustive) {
50+
unsigned Bits = 4;
51+
ForeachKnownBits(Bits, [&](const KnownBits &Known1) {
52+
ForeachKnownBits(Bits, [&](const KnownBits &Known2) {
53+
ForeachKnownBits(1, [&](const KnownBits &KnownCarry) {
54+
// Explicitly compute known bits of the addition by trying all
55+
// possibilities.
56+
KnownBits Known(Bits);
57+
Known.Zero.setAllBits();
58+
Known.One.setAllBits();
59+
ForeachNumInKnownBits(Known1, [&](const APInt &N1) {
60+
ForeachNumInKnownBits(Known2, [&](const APInt &N2) {
61+
ForeachNumInKnownBits(KnownCarry, [&](const APInt &Carry) {
62+
APInt Add = N1 + N2;
63+
if (Carry.getBoolValue())
64+
++Add;
65+
66+
Known.One &= Add;
67+
Known.Zero &= ~Add;
68+
});
69+
});
70+
});
71+
72+
KnownBits KnownComputed = KnownBits::computeForAddCarry(
73+
Known1, Known2, KnownCarry);
74+
EXPECT_EQ(Known.Zero, KnownComputed.Zero);
75+
EXPECT_EQ(Known.One, KnownComputed.One);
76+
});
77+
});
78+
});
79+
}
80+
81+
static void TestAddSubExhaustive(bool IsAdd) {
82+
unsigned Bits = 4;
83+
ForeachKnownBits(Bits, [&](const KnownBits &Known1) {
84+
ForeachKnownBits(Bits, [&](const KnownBits &Known2) {
85+
KnownBits Known(Bits), KnownNSW(Bits);
86+
Known.Zero.setAllBits();
87+
Known.One.setAllBits();
88+
KnownNSW.Zero.setAllBits();
89+
KnownNSW.One.setAllBits();
90+
91+
ForeachNumInKnownBits(Known1, [&](const APInt &N1) {
92+
ForeachNumInKnownBits(Known2, [&](const APInt &N2) {
93+
bool Overflow;
94+
APInt Res;
95+
if (IsAdd)
96+
Res = N1.sadd_ov(N2, Overflow);
97+
else
98+
Res = N1.ssub_ov(N2, Overflow);
99+
100+
Known.One &= Res;
101+
Known.Zero &= ~Res;
102+
103+
if (!Overflow) {
104+
KnownNSW.One &= Res;
105+
KnownNSW.Zero &= ~Res;
106+
}
107+
});
108+
});
109+
110+
KnownBits KnownComputed = KnownBits::computeForAddSub(
111+
IsAdd, /*NSW*/false, Known1, Known2);
112+
EXPECT_EQ(Known.Zero, KnownComputed.Zero);
113+
EXPECT_EQ(Known.One, KnownComputed.One);
114+
115+
// The NSW calculation is not precise, only check that it's
116+
// conservatively correct.
117+
KnownBits KnownNSWComputed = KnownBits::computeForAddSub(
118+
IsAdd, /*NSW*/true, Known1, Known2);
119+
EXPECT_TRUE(KnownNSWComputed.Zero.isSubsetOf(KnownNSW.Zero));
120+
EXPECT_TRUE(KnownNSWComputed.One.isSubsetOf(KnownNSW.One));
121+
});
122+
});
123+
}
124+
125+
TEST(KnownBitsTest, AddSubExhaustive) {
126+
TestAddSubExhaustive(true);
127+
TestAddSubExhaustive(false);
128+
}
129+
130+
} // end anonymous namespace

0 commit comments

Comments
 (0)