diff options
author | Antonio Frighetto <me@antoniofrighetto.com> | 2024-06-06 08:26:40 +0200 |
---|---|---|
committer | Antonio Frighetto <me@antoniofrighetto.com> | 2024-06-17 21:13:52 +0200 |
commit | c22d3917b93a6d54613d2e5b2ea4c97546144c46 (patch) | |
tree | 7ffae85640b3d3449a92ffa7a878aa2fac39381e | |
parent | c9549e10e9ea70428ada80a34d15afeaf5710b2d (diff) | |
download | llvm-c22d3917b93a6d54613d2e5b2ea4c97546144c46.zip llvm-c22d3917b93a6d54613d2e5b2ea4c97546144c46.tar.gz llvm-c22d3917b93a6d54613d2e5b2ea4c97546144c46.tar.bz2 |
[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.
-rw-r--r-- | llvm/include/llvm/IR/ConstantRange.h | 5 | ||||
-rw-r--r-- | llvm/lib/Analysis/LazyValueInfo.cpp | 11 | ||||
-rw-r--r-- | llvm/lib/IR/ConstantRange.cpp | 17 | ||||
-rw-r--r-- | llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll | 38 | ||||
-rw-r--r-- | llvm/unittests/IR/ConstantRangeTest.cpp | 48 |
5 files changed, 111 insertions, 8 deletions
diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h index a5e2f80..7b94b9c 100644 --- a/llvm/include/llvm/IR/ConstantRange.h +++ b/llvm/include/llvm/IR/ConstantRange.h @@ -176,6 +176,11 @@ public: 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); diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp index 8b6e56c..aaa7baa 100644 --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -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. diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index 08041c9..1904170 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -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(); } diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll b/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll index b5337b9..ca70713 100644 --- a/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll @@ -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 @@ -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 +} diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index ac2075c..392c41f 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -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 |