Skip to content

Commit 03a4f1f

Browse files
committed
[ConstantRange] Sign-flipping of signedness-invariant comparisons
For certain combination of LHS and RHS constant ranges, the signedness of the relational comparison predicate is irrelevant. This implements complete and precise model for all predicates, as confirmed by the brute-force tests. I'm not sure if there are some more cases that we can handle here. In a follow-up, CVP will make use of this. Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D90924
1 parent 164194a commit 03a4f1f

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,28 @@ class LLVM_NODISCARD ConstantRange {
128128
/// NOTE: false does not mean that inverse predicate holds!
129129
bool icmp(CmpInst::Predicate Pred, const ConstantRange &Other) const;
130130

131+
/// Return true iff CR1 ult CR2 is equivalent to CR1 slt CR2.
132+
/// Does not depend on strictness/direction of the predicate.
133+
static bool
134+
areInsensitiveToSignednessOfICmpPredicate(const ConstantRange &CR1,
135+
const ConstantRange &CR2);
136+
137+
/// Return true iff CR1 ult CR2 is equivalent to CR1 sge CR2.
138+
/// Does not depend on strictness/direction of the predicate.
139+
static bool
140+
areInsensitiveToSignednessOfInvertedICmpPredicate(const ConstantRange &CR1,
141+
const ConstantRange &CR2);
142+
143+
/// If the comparison between constant ranges this and Other
144+
/// is insensitive to the signedness of the comparison predicate,
145+
/// return a predicate equivalent to \p Pred, with flipped signedness
146+
/// (i.e. unsigned instead of signed or vice versa), and maybe inverted,
147+
/// otherwise returns CmpInst::Predicate::BAD_ICMP_PREDICATE.
148+
static CmpInst::Predicate
149+
getEquivalentPredWithFlippedSignedness(CmpInst::Predicate Pred,
150+
const ConstantRange &CR1,
151+
const ConstantRange &CR2);
152+
131153
/// Produce the largest range containing all X such that "X BinOp Y" is
132154
/// guaranteed not to wrap (overflow) for *all* Y in Other. However, there may
133155
/// be *some* Y in Other for which additional X not contained in the result

llvm/lib/IR/ConstantRange.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,42 @@ ConstantRange ConstantRange::makeExactICmpRegion(CmpInst::Predicate Pred,
147147
return makeAllowedICmpRegion(Pred, C);
148148
}
149149

150+
bool ConstantRange::areInsensitiveToSignednessOfICmpPredicate(
151+
const ConstantRange &CR1, const ConstantRange &CR2) {
152+
if (CR1.isEmptySet() || CR2.isEmptySet())
153+
return true;
154+
155+
return (CR1.isAllNonNegative() && CR2.isAllNonNegative()) ||
156+
(CR1.isAllNegative() && CR2.isAllNegative());
157+
}
158+
159+
bool ConstantRange::areInsensitiveToSignednessOfInvertedICmpPredicate(
160+
const ConstantRange &CR1, const ConstantRange &CR2) {
161+
if (CR1.isEmptySet() || CR2.isEmptySet())
162+
return true;
163+
164+
return (CR1.isAllNonNegative() && CR2.isAllNegative()) ||
165+
(CR1.isAllNegative() && CR2.isAllNonNegative());
166+
}
167+
168+
CmpInst::Predicate ConstantRange::getEquivalentPredWithFlippedSignedness(
169+
CmpInst::Predicate Pred, const ConstantRange &CR1,
170+
const ConstantRange &CR2) {
171+
assert(CmpInst::isIntPredicate(Pred) && CmpInst::isRelational(Pred) &&
172+
"Only for relational integer predicates!");
173+
174+
CmpInst::Predicate FlippedSignednessPred =
175+
CmpInst::getFlippedSignednessPredicate(Pred);
176+
177+
if (areInsensitiveToSignednessOfICmpPredicate(CR1, CR2))
178+
return FlippedSignednessPred;
179+
180+
if (areInsensitiveToSignednessOfInvertedICmpPredicate(CR1, CR2))
181+
return CmpInst::getInversePredicate(FlippedSignednessPred);
182+
183+
return CmpInst::Predicate::BAD_ICMP_PREDICATE;
184+
}
185+
150186
bool ConstantRange::getEquivalentICmp(CmpInst::Predicate &Pred,
151187
APInt &RHS) const {
152188
bool Success = false;

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2505,4 +2505,80 @@ TEST_F(ConstantRangeTest, binaryNot) {
25052505
[](const APInt &N) { return ~N; }, PreferSmallest);
25062506
}
25072507

2508-
} // anonymous namespace
2508+
template <typename T>
2509+
void testConstantRangeICmpPredEquivalence(ICmpInst::Predicate SrcPred, T Func) {
2510+
unsigned Bits = 4;
2511+
EnumerateTwoConstantRanges(
2512+
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
2513+
ICmpInst::Predicate TgtPred;
2514+
bool ExpectedEquivalent;
2515+
std::tie(TgtPred, ExpectedEquivalent) = Func(CR1, CR2);
2516+
if (TgtPred == CmpInst::Predicate::BAD_ICMP_PREDICATE)
2517+
return;
2518+
bool TrulyEquivalent = true;
2519+
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
2520+
if (!TrulyEquivalent)
2521+
return;
2522+
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
2523+
if (!TrulyEquivalent)
2524+
return;
2525+
TrulyEquivalent &= ICmpInst::compare(N1, N2, SrcPred) ==
2526+
ICmpInst::compare(N1, N2, TgtPred);
2527+
});
2528+
});
2529+
ASSERT_EQ(TrulyEquivalent, ExpectedEquivalent);
2530+
});
2531+
}
2532+
2533+
TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfICmpPredicate) {
2534+
for (auto Pred : seq_inclusive(ICmpInst::Predicate::FIRST_ICMP_PREDICATE,
2535+
ICmpInst::Predicate::LAST_ICMP_PREDICATE)) {
2536+
if (ICmpInst::isEquality(Pred))
2537+
continue;
2538+
ICmpInst::Predicate FlippedSignednessPred =
2539+
ICmpInst::getFlippedSignednessPredicate(Pred);
2540+
testConstantRangeICmpPredEquivalence(Pred, [FlippedSignednessPred](
2541+
const ConstantRange &CR1,
2542+
const ConstantRange &CR2) {
2543+
return std::make_pair(
2544+
FlippedSignednessPred,
2545+
ConstantRange::areInsensitiveToSignednessOfICmpPredicate(CR1, CR2));
2546+
});
2547+
}
2548+
}
2549+
2550+
TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfInvertedICmpPredicate) {
2551+
for (auto Pred : seq_inclusive(ICmpInst::Predicate::FIRST_ICMP_PREDICATE,
2552+
ICmpInst::Predicate::LAST_ICMP_PREDICATE)) {
2553+
if (ICmpInst::isEquality(Pred))
2554+
continue;
2555+
ICmpInst::Predicate InvertedFlippedSignednessPred =
2556+
ICmpInst::getInversePredicate(
2557+
ICmpInst::getFlippedSignednessPredicate(Pred));
2558+
testConstantRangeICmpPredEquivalence(
2559+
Pred, [InvertedFlippedSignednessPred](const ConstantRange &CR1,
2560+
const ConstantRange &CR2) {
2561+
return std::make_pair(
2562+
InvertedFlippedSignednessPred,
2563+
ConstantRange::areInsensitiveToSignednessOfInvertedICmpPredicate(
2564+
CR1, CR2));
2565+
});
2566+
}
2567+
}
2568+
2569+
TEST_F(ConstantRangeTest, getEquivalentPredWithFlippedSignedness) {
2570+
for (auto Pred : seq_inclusive(ICmpInst::Predicate::FIRST_ICMP_PREDICATE,
2571+
ICmpInst::Predicate::LAST_ICMP_PREDICATE)) {
2572+
if (ICmpInst::isEquality(Pred))
2573+
continue;
2574+
testConstantRangeICmpPredEquivalence(
2575+
Pred, [Pred](const ConstantRange &CR1, const ConstantRange &CR2) {
2576+
return std::make_pair(
2577+
ConstantRange::getEquivalentPredWithFlippedSignedness(Pred, CR1,
2578+
CR2),
2579+
/*ExpectedEquivalent=*/true);
2580+
});
2581+
}
2582+
}
2583+
2584+
} // anonymous namespace

0 commit comments

Comments
 (0)