Skip to content

Commit

Permalink
[LVI][ConstantRange] Generalize mask not equal conditions handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
antoniofrighetto committed Jun 17, 2024
1 parent c9549e1 commit c22d391
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/ConstantRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ class [[nodiscard]] ConstantRange {
const APInt &Other,
unsigned NoWrapKind);

/// Initialize a range containing all values X that satisfy `(X & Mask)
/// != C`. Note that the range returned may contain values where `(X & Mask)
/// == C` holds, making it less precise, but still conservative.
static ConstantRange makeMaskNotEqualRange(const APInt &Mask, const APInt &C);

/// Returns true if ConstantRange calculations are supported for intrinsic
/// with \p IntrinsicID.
static bool isIntrinsicSupported(Intrinsic::ID IntrinsicID);
Expand Down
11 changes: 4 additions & 7 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1188,13 +1188,10 @@ std::optional<ValueLatticeElement> LazyValueInfoImpl::getValueFromICmpCondition(
return ValueLatticeElement::getRange(
ConstantRange::fromKnownBits(Known, /*IsSigned*/ false));
}
// If (Val & Mask) != 0 then the value must be larger than the lowest set
// bit of Mask.
if (EdgePred == ICmpInst::ICMP_NE && !Mask->isZero() && C->isZero()) {
return ValueLatticeElement::getRange(ConstantRange::getNonEmpty(
APInt::getOneBitSet(BitWidth, Mask->countr_zero()),
APInt::getZero(BitWidth)));
}

if (EdgePred == ICmpInst::ICMP_NE)
return ValueLatticeElement::getRange(
ConstantRange::makeMaskNotEqualRange(*Mask, *C));
}

// If (X urem Modulus) >= C, then X >= C.
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/IR/ConstantRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,23 @@ ConstantRange ConstantRange::makeExactNoWrapRegion(Instruction::BinaryOps BinOp,
return makeGuaranteedNoWrapRegion(BinOp, ConstantRange(Other), NoWrapKind);
}

ConstantRange ConstantRange::makeMaskNotEqualRange(const APInt &Mask,
const APInt &C) {
unsigned BitWidth = Mask.getBitWidth();

if ((Mask & C) != C)
return getFull(BitWidth);

if (Mask.isZero())
return getEmpty(BitWidth);

// If (Val & Mask) != C, constrained to the non-equality being
// satisfiable, then the value must be larger than the lowest set bit of
// Mask, offset by constant C.
return ConstantRange::getNonEmpty(
APInt::getOneBitSet(BitWidth, Mask.countr_zero()) + C, C);
}

bool ConstantRange::isFullSet() const {
return Lower == Upper && Lower.isMaxValue();
}
Expand Down
38 changes: 37 additions & 1 deletion llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ define i1 @test_assume_cmp_with_offset_or(i64 %idx, i1 %other) {
; CHECK: T:
; CHECK-NEXT: ret i1 true
; CHECK: F:
; CHECK-NEXT: ret i1 [[CMP2:%.*]]
; CHECK-NEXT: ret i1 [[OTHER:%.*]]
;
%idx.off1 = or disjoint i64 %idx, 5
%cmp1 = icmp ugt i64 %idx.off1, 10
Expand Down Expand Up @@ -1475,3 +1475,39 @@ entry:
%select = select i1 %cmp1, i1 %cmp2, i1 false
ret i1 %select
}

declare void @opaque()

define void @test_icmp_ne_from_implied_range(i32 noundef %arg) {
; CHECK-LABEL: @test_icmp_ne_from_implied_range(
; CHECK-NEXT: [[AND_MASK:%.*]] = and i32 [[ARG:%.*]], -8
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND_MASK]], -16
; CHECK-NEXT: br i1 [[CMP]], label [[END:%.*]], label [[ELSE:%.*]]
; CHECK: else:
; CHECK-NEXT: br label [[END]]
; CHECK: sw.case:
; CHECK-NEXT: call void @opaque()
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: ret void
;
%and.mask = and i32 %arg, -8
%cmp = icmp eq i32 %and.mask, -16
br i1 %cmp, label %end, label %else

else:
; %arg is within [-8, -16).
switch i32 %arg, label %end [
i32 -16, label %sw.case
i32 -12, label %sw.case
i32 -9, label %sw.case
]

sw.case:
call void @opaque()
br label %end

end:
; %arg is within [-16, -8).
ret void
}
48 changes: 48 additions & 0 deletions llvm/unittests/IR/ConstantRangeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2788,4 +2788,52 @@ TEST_F(ConstantRangeTest, isSizeLargerThan) {
EXPECT_FALSE(One.isSizeLargerThan(1));
}

TEST_F(ConstantRangeTest, MakeMaskNotEqualRange) {
// Mask: 0b0001, C: 0b0001. MMNE() = [2, 1)
ConstantRange CR(APInt(4, 2), APInt(4, 1));
EXPECT_EQ(CR, ConstantRange::makeMaskNotEqualRange(APInt(4, 1), APInt(4, 1)));
EXPECT_NE(CR, ConstantRange::makeMaskNotEqualRange(APInt(4, 0),
APInt(4, -1, true)));
EXPECT_TRUE(CR.contains(APInt(4, 7)));
EXPECT_TRUE(CR.contains(APInt(4, 15)));

// Mask: 0b0100, C: 0b0100. MMNE() = [-8, 4)
ConstantRange CR2(APInt(4, -8, true), APInt(4, 4));
auto MMNE = ConstantRange::makeMaskNotEqualRange(APInt(4, 4), APInt(4, 4));
EXPECT_EQ(CR2, MMNE);
EXPECT_NE(ConstantRange::getNonEmpty(APInt(4, 0), APInt(4, -4, true)), MMNE);

// CR: [-16, -8). MMNE() = [-8, -16)
ConstantRange CR3(APInt(8, 240), APInt(8, 248));
EXPECT_EQ(CR3.inverse(),
ConstantRange::makeMaskNotEqualRange(APInt(8, 248), APInt(8, 240)));

// Mask: 0, C: 0b1111: unsatisfiable.
EXPECT_EQ(ConstantRange::getFull(4),
ConstantRange::makeMaskNotEqualRange(APInt(4, 0), APInt(4, 15)));
}

TEST_F(ConstantRangeTest, MakeMaskNotEqualRangeExhaustive) {
unsigned Bits = 4;
unsigned Max = 1 << Bits;

EnumerateAPInts(Bits, [&](const APInt &Mask) {
EnumerateAPInts(Bits, [&](const APInt &C) {
SmallBitVector Elems(Max);
for (unsigned N = 0; N < Max; ++N) {
APInt Num(Bits, N);
if ((Num & Mask) == C)
continue;
Elems.set(Num.getZExtValue());
}

// Only test optimality with PreferSmallest. E.g., given Mask = 0b0001, C
// = 0b0001, a possible better range would be [0, 15) when preferring the
// smallest unsigned, however we conservatively return [2, 1).
TestRange(ConstantRange::makeMaskNotEqualRange(Mask, C), Elems,
PreferSmallest, {});
});
});
}

} // anonymous namespace

0 comments on commit c22d391

Please sign in to comment.