Skip to content

Commit c22d391

Browse files
[LVI][ConstantRange] Generalize mask not equal conditions handling
Extend `V & Mask != 0` for non-zero constants if satisfiable, when retrieving constraint value information from a non-equality comparison. Proof: https://alive2.llvm.org/ce/z/dc5BeT. Motivating example: https://github.com/gcc-mirror/gcc/blob/master/gcc/testsuite/gcc.dg/tree-ssa/vrp76.c.
1 parent c9549e1 commit c22d391

File tree

5 files changed

+111
-8
lines changed

5 files changed

+111
-8
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ class [[nodiscard]] ConstantRange {
176176
const APInt &Other,
177177
unsigned NoWrapKind);
178178

179+
/// Initialize a range containing all values X that satisfy `(X & Mask)
180+
/// != C`. Note that the range returned may contain values where `(X & Mask)
181+
/// == C` holds, making it less precise, but still conservative.
182+
static ConstantRange makeMaskNotEqualRange(const APInt &Mask, const APInt &C);
183+
179184
/// Returns true if ConstantRange calculations are supported for intrinsic
180185
/// with \p IntrinsicID.
181186
static bool isIntrinsicSupported(Intrinsic::ID IntrinsicID);

llvm/lib/Analysis/LazyValueInfo.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,13 +1188,10 @@ std::optional<ValueLatticeElement> LazyValueInfoImpl::getValueFromICmpCondition(
11881188
return ValueLatticeElement::getRange(
11891189
ConstantRange::fromKnownBits(Known, /*IsSigned*/ false));
11901190
}
1191-
// If (Val & Mask) != 0 then the value must be larger than the lowest set
1192-
// bit of Mask.
1193-
if (EdgePred == ICmpInst::ICMP_NE && !Mask->isZero() && C->isZero()) {
1194-
return ValueLatticeElement::getRange(ConstantRange::getNonEmpty(
1195-
APInt::getOneBitSet(BitWidth, Mask->countr_zero()),
1196-
APInt::getZero(BitWidth)));
1197-
}
1191+
1192+
if (EdgePred == ICmpInst::ICMP_NE)
1193+
return ValueLatticeElement::getRange(
1194+
ConstantRange::makeMaskNotEqualRange(*Mask, *C));
11981195
}
11991196

12001197
// If (X urem Modulus) >= C, then X >= C.

llvm/lib/IR/ConstantRange.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,23 @@ ConstantRange ConstantRange::makeExactNoWrapRegion(Instruction::BinaryOps BinOp,
364364
return makeGuaranteedNoWrapRegion(BinOp, ConstantRange(Other), NoWrapKind);
365365
}
366366

367+
ConstantRange ConstantRange::makeMaskNotEqualRange(const APInt &Mask,
368+
const APInt &C) {
369+
unsigned BitWidth = Mask.getBitWidth();
370+
371+
if ((Mask & C) != C)
372+
return getFull(BitWidth);
373+
374+
if (Mask.isZero())
375+
return getEmpty(BitWidth);
376+
377+
// If (Val & Mask) != C, constrained to the non-equality being
378+
// satisfiable, then the value must be larger than the lowest set bit of
379+
// Mask, offset by constant C.
380+
return ConstantRange::getNonEmpty(
381+
APInt::getOneBitSet(BitWidth, Mask.countr_zero()) + C, C);
382+
}
383+
367384
bool ConstantRange::isFullSet() const {
368385
return Lower == Upper && Lower.isMaxValue();
369386
}

llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ define i1 @test_assume_cmp_with_offset_or(i64 %idx, i1 %other) {
595595
; CHECK: T:
596596
; CHECK-NEXT: ret i1 true
597597
; CHECK: F:
598-
; CHECK-NEXT: ret i1 [[CMP2:%.*]]
598+
; CHECK-NEXT: ret i1 [[OTHER:%.*]]
599599
;
600600
%idx.off1 = or disjoint i64 %idx, 5
601601
%cmp1 = icmp ugt i64 %idx.off1, 10
@@ -1475,3 +1475,39 @@ entry:
14751475
%select = select i1 %cmp1, i1 %cmp2, i1 false
14761476
ret i1 %select
14771477
}
1478+
1479+
declare void @opaque()
1480+
1481+
define void @test_icmp_ne_from_implied_range(i32 noundef %arg) {
1482+
; CHECK-LABEL: @test_icmp_ne_from_implied_range(
1483+
; CHECK-NEXT: [[AND_MASK:%.*]] = and i32 [[ARG:%.*]], -8
1484+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND_MASK]], -16
1485+
; CHECK-NEXT: br i1 [[CMP]], label [[END:%.*]], label [[ELSE:%.*]]
1486+
; CHECK: else:
1487+
; CHECK-NEXT: br label [[END]]
1488+
; CHECK: sw.case:
1489+
; CHECK-NEXT: call void @opaque()
1490+
; CHECK-NEXT: br label [[END]]
1491+
; CHECK: end:
1492+
; CHECK-NEXT: ret void
1493+
;
1494+
%and.mask = and i32 %arg, -8
1495+
%cmp = icmp eq i32 %and.mask, -16
1496+
br i1 %cmp, label %end, label %else
1497+
1498+
else:
1499+
; %arg is within [-8, -16).
1500+
switch i32 %arg, label %end [
1501+
i32 -16, label %sw.case
1502+
i32 -12, label %sw.case
1503+
i32 -9, label %sw.case
1504+
]
1505+
1506+
sw.case:
1507+
call void @opaque()
1508+
br label %end
1509+
1510+
end:
1511+
; %arg is within [-16, -8).
1512+
ret void
1513+
}

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,4 +2788,52 @@ TEST_F(ConstantRangeTest, isSizeLargerThan) {
27882788
EXPECT_FALSE(One.isSizeLargerThan(1));
27892789
}
27902790

2791+
TEST_F(ConstantRangeTest, MakeMaskNotEqualRange) {
2792+
// Mask: 0b0001, C: 0b0001. MMNE() = [2, 1)
2793+
ConstantRange CR(APInt(4, 2), APInt(4, 1));
2794+
EXPECT_EQ(CR, ConstantRange::makeMaskNotEqualRange(APInt(4, 1), APInt(4, 1)));
2795+
EXPECT_NE(CR, ConstantRange::makeMaskNotEqualRange(APInt(4, 0),
2796+
APInt(4, -1, true)));
2797+
EXPECT_TRUE(CR.contains(APInt(4, 7)));
2798+
EXPECT_TRUE(CR.contains(APInt(4, 15)));
2799+
2800+
// Mask: 0b0100, C: 0b0100. MMNE() = [-8, 4)
2801+
ConstantRange CR2(APInt(4, -8, true), APInt(4, 4));
2802+
auto MMNE = ConstantRange::makeMaskNotEqualRange(APInt(4, 4), APInt(4, 4));
2803+
EXPECT_EQ(CR2, MMNE);
2804+
EXPECT_NE(ConstantRange::getNonEmpty(APInt(4, 0), APInt(4, -4, true)), MMNE);
2805+
2806+
// CR: [-16, -8). MMNE() = [-8, -16)
2807+
ConstantRange CR3(APInt(8, 240), APInt(8, 248));
2808+
EXPECT_EQ(CR3.inverse(),
2809+
ConstantRange::makeMaskNotEqualRange(APInt(8, 248), APInt(8, 240)));
2810+
2811+
// Mask: 0, C: 0b1111: unsatisfiable.
2812+
EXPECT_EQ(ConstantRange::getFull(4),
2813+
ConstantRange::makeMaskNotEqualRange(APInt(4, 0), APInt(4, 15)));
2814+
}
2815+
2816+
TEST_F(ConstantRangeTest, MakeMaskNotEqualRangeExhaustive) {
2817+
unsigned Bits = 4;
2818+
unsigned Max = 1 << Bits;
2819+
2820+
EnumerateAPInts(Bits, [&](const APInt &Mask) {
2821+
EnumerateAPInts(Bits, [&](const APInt &C) {
2822+
SmallBitVector Elems(Max);
2823+
for (unsigned N = 0; N < Max; ++N) {
2824+
APInt Num(Bits, N);
2825+
if ((Num & Mask) == C)
2826+
continue;
2827+
Elems.set(Num.getZExtValue());
2828+
}
2829+
2830+
// Only test optimality with PreferSmallest. E.g., given Mask = 0b0001, C
2831+
// = 0b0001, a possible better range would be [0, 15) when preferring the
2832+
// smallest unsigned, however we conservatively return [2, 1).
2833+
TestRange(ConstantRange::makeMaskNotEqualRange(Mask, C), Elems,
2834+
PreferSmallest, {});
2835+
});
2836+
});
2837+
}
2838+
27912839
} // anonymous namespace

0 commit comments

Comments
 (0)