[llvm] [LVI][CVP] `getRangeFor` propagates `MayIncludeUndef` (PR #68190)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 6 00:42:42 PDT 2023
https://github.com/DianQK updated https://github.com/llvm/llvm-project/pull/68190
>From 97047321556f214083eb7d00e4a26c8e435c9f2c Mon Sep 17 00:00:00 2001
From: DianQK <dianqk at dianqk.net>
Date: Fri, 6 Oct 2023 12:57:04 +0800
Subject: [PATCH 1/2] [LVI][CVP] Pre-commit for `getRangeFor` (NFC)
---
.../merge-range-and-undef-2.ll | 107 ++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll b/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll
new file mode 100644
index 000000000000000..2975ca0ec54f87e
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll
@@ -0,0 +1,107 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt -S -passes=correlated-propagation %s | FileCheck %s
+
+; Test case for PR68381.
+
+; Don't delete the `and` instruction.
+
+define i32 @constant_range_and_undef_and(i1 %c0, i1 %c1, i8 %v1, i8 %v2) {
+; CHECK-LABEL: define i32 @constant_range_and_undef_and(
+; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]], i8 [[V1:%.*]], i8 [[V2:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: br i1 [[C0]], label [[BB0:%.*]], label [[BB1:%.*]]
+; CHECK: bb0:
+; CHECK-NEXT: [[V1_I32:%.*]] = zext i8 [[V1]] to i32
+; CHECK-NEXT: br label [[BB1]]
+; CHECK: bb1:
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ]
+; CHECK-NEXT: br i1 [[C1]], label [[BB0]], label [[BB2:%.*]]
+; CHECK: bb2:
+; CHECK-NEXT: [[V2_I32:%.*]] = zext i8 [[V2]] to i32
+; CHECK-NEXT: [[Y:%.*]] = or i32 [[X]], [[V2_I32]]
+; CHECK-NEXT: ret i32 [[Y]]
+;
+start:
+ br i1 %c0, label %bb0, label %bb1
+
+bb0:
+ %v1_i32 = zext i8 %v1 to i32
+ br label %bb1
+
+bb1:
+ %x = phi i32 [ %v1_i32, %bb0 ], [ undef, %start ]
+ br i1 %c1, label %bb0, label %bb2
+
+bb2:
+ %v2_i32 = zext i8 %v2 to i32
+ %y = or i32 %x, %v2_i32
+ %z = and i32 %y, 255
+ ret i32 %z
+}
+
+; Don't delete the `abs` intrinsic function.
+
+define i32 @constant_range_and_undef_abs_false(i1 %c0, i1 %c1, i8 %v1) {
+; CHECK-LABEL: define i32 @constant_range_and_undef_abs_false(
+; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]], i8 [[V1:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: br i1 [[C0]], label [[BB0:%.*]], label [[BB1:%.*]]
+; CHECK: bb0:
+; CHECK-NEXT: [[V1_I32:%.*]] = zext i8 [[V1]] to i32
+; CHECK-NEXT: br label [[BB1]]
+; CHECK: bb1:
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ]
+; CHECK-NEXT: br i1 [[C1]], label [[BB0]], label [[BB2:%.*]]
+; CHECK: bb2:
+; CHECK-NEXT: ret i32 [[X]]
+;
+start:
+ br i1 %c0, label %bb0, label %bb1
+
+bb0:
+ %v1_i32 = zext i8 %v1 to i32
+ br label %bb1
+
+bb1:
+ %x = phi i32 [ %v1_i32, %bb0 ], [ undef, %start ]
+ br i1 %c1, label %bb0, label %bb2
+
+bb2:
+ %z = call i32 @llvm.abs.i32(i32 %x, i1 false)
+ ret i32 %z
+}
+
+; We can delete the `abs` intrinsic function.
+
+define i32 @constant_range_and_undef_true(i1 %c0, i1 %c1, i8 %v1) {
+; CHECK-LABEL: define i32 @constant_range_and_undef_true(
+; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]], i8 [[V1:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: br i1 [[C0]], label [[BB0:%.*]], label [[BB1:%.*]]
+; CHECK: bb0:
+; CHECK-NEXT: [[V1_I32:%.*]] = zext i8 [[V1]] to i32
+; CHECK-NEXT: br label [[BB1]]
+; CHECK: bb1:
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ]
+; CHECK-NEXT: br i1 [[C1]], label [[BB0]], label [[BB2:%.*]]
+; CHECK: bb2:
+; CHECK-NEXT: ret i32 [[X]]
+;
+start:
+ br i1 %c0, label %bb0, label %bb1
+
+bb0:
+ %v1_i32 = zext i8 %v1 to i32
+ br label %bb1
+
+bb1:
+ %x = phi i32 [ %v1_i32, %bb0 ], [ undef, %start ]
+ br i1 %c1, label %bb0, label %bb2
+
+bb2:
+ %z = call i32 @llvm.abs.i32(i32 %x, i1 true)
+ ret i32 %z
+}
+
+declare i32 @llvm.abs.i32(i32, i1)
+
>From 488367e54bff737ef16b60b8ce24f35131511378 Mon Sep 17 00:00:00 2001
From: DianQK <dianqk at dianqk.net>
Date: Wed, 4 Oct 2023 15:50:25 +0800
Subject: [PATCH 2/2] [LVI][CVP] propagates `undef` to range and `abs`
---
llvm/include/llvm/Analysis/LazyValueInfo.h | 6 +-
llvm/lib/Analysis/LazyValueInfo.cpp | 70 +++++++++++++------
.../Scalar/CorrelatedValuePropagation.cpp | 6 +-
.../merge-range-and-undef-2.ll | 6 +-
4 files changed, 58 insertions(+), 30 deletions(-)
diff --git a/llvm/include/llvm/Analysis/LazyValueInfo.h b/llvm/include/llvm/Analysis/LazyValueInfo.h
index f013a4a75d3d6ad..55f9b3c14ca673d 100644
--- a/llvm/include/llvm/Analysis/LazyValueInfo.h
+++ b/llvm/include/llvm/Analysis/LazyValueInfo.h
@@ -70,14 +70,16 @@ namespace llvm {
/// predicate.
Tristate getPredicateOnEdge(unsigned Pred, Value *V, Constant *C,
BasicBlock *FromBB, BasicBlock *ToBB,
- Instruction *CxtI = nullptr);
+ Instruction *CxtI = nullptr,
+ bool UndefinedAllowed = true);
/// Determine whether the specified value comparison with a constant is
/// known to be true or false at the specified instruction. \p Pred is a
/// CmpInst predicate. If \p UseBlockValue is true, the block value is also
/// taken into account.
Tristate getPredicateAt(unsigned Pred, Value *V, Constant *C,
- Instruction *CxtI, bool UseBlockValue);
+ Instruction *CxtI, bool UseBlockValue,
+ bool UndefinedAllowed = true);
/// Determine whether the specified value comparison is known to be true
/// or false at the specified instruction. While this takes two Value's,
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 0892aa9d75fb417..d4da9c05ccfed90 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -411,7 +411,8 @@ class LazyValueInfoImpl {
std::optional<ValueLatticeElement> solveBlockValueSelect(SelectInst *S,
BasicBlock *BB);
std::optional<ConstantRange> getRangeFor(Value *V, Instruction *CxtI,
- BasicBlock *BB);
+ BasicBlock *BB,
+ bool &MayIncludeUndef);
std::optional<ValueLatticeElement> solveBlockValueBinaryOpImpl(
Instruction *I, BasicBlock *BB,
std::function<ConstantRange(const ConstantRange &, const ConstantRange &)>
@@ -892,11 +893,14 @@ LazyValueInfoImpl::solveBlockValueSelect(SelectInst *SI, BasicBlock *BB) {
}
std::optional<ConstantRange>
-LazyValueInfoImpl::getRangeFor(Value *V, Instruction *CxtI, BasicBlock *BB) {
+LazyValueInfoImpl::getRangeFor(Value *V, Instruction *CxtI, BasicBlock *BB,
+ bool &MayIncludeUndef) {
std::optional<ValueLatticeElement> OptVal = getBlockValue(V, BB, CxtI);
if (!OptVal)
return std::nullopt;
- return getConstantRangeOrFull(*OptVal, V->getType(), DL);
+ const auto &Val = *OptVal;
+ MayIncludeUndef = Val.isConstantRangeIncludingUndef();
+ return getConstantRangeOrFull(Val, V->getType(), DL);
}
std::optional<ValueLatticeElement>
@@ -922,10 +926,12 @@ LazyValueInfoImpl::solveBlockValueCast(CastInst *CI, BasicBlock *BB) {
return ValueLatticeElement::getOverdefined();
}
+ bool MayIncludeUndef = false;
// Figure out the range of the LHS. If that fails, we still apply the
// transfer rule on the full set since we may be able to locally infer
// interesting facts.
- std::optional<ConstantRange> LHSRes = getRangeFor(CI->getOperand(0), CI, BB);
+ std::optional<ConstantRange> LHSRes =
+ getRangeFor(CI->getOperand(0), CI, BB, MayIncludeUndef);
if (!LHSRes)
// More work to do before applying this transfer rule.
return std::nullopt;
@@ -936,8 +942,8 @@ LazyValueInfoImpl::solveBlockValueCast(CastInst *CI, BasicBlock *BB) {
// NOTE: We're currently limited by the set of operations that ConstantRange
// can evaluate symbolically. Enhancing that set will allows us to analyze
// more definitions.
- return ValueLatticeElement::getRange(LHSRange.castOp(CI->getOpcode(),
- ResultBitWidth));
+ return ValueLatticeElement::getRange(
+ LHSRange.castOp(CI->getOpcode(), ResultBitWidth), MayIncludeUndef);
}
std::optional<ValueLatticeElement>
@@ -949,15 +955,20 @@ LazyValueInfoImpl::solveBlockValueBinaryOpImpl(
// conservative range, but apply the transfer rule anyways. This
// lets us pick up facts from expressions like "and i32 (call i32
// @foo()), 32"
- std::optional<ConstantRange> LHSRes = getRangeFor(I->getOperand(0), I, BB);
- std::optional<ConstantRange> RHSRes = getRangeFor(I->getOperand(1), I, BB);
+ bool LMayIncludeUndef = false;
+ bool RMayIncludeUndef = false;
+ std::optional<ConstantRange> LHSRes =
+ getRangeFor(I->getOperand(0), I, BB, LMayIncludeUndef);
+ std::optional<ConstantRange> RHSRes =
+ getRangeFor(I->getOperand(1), I, BB, RMayIncludeUndef);
if (!LHSRes || !RHSRes)
// More work to do before applying this transfer rule.
return std::nullopt;
const ConstantRange &LHSRange = *LHSRes;
const ConstantRange &RHSRange = *RHSRes;
- return ValueLatticeElement::getRange(OpFn(LHSRange, RHSRange));
+ return ValueLatticeElement::getRange(OpFn(LHSRange, RHSRange),
+ LMayIncludeUndef || RMayIncludeUndef);
}
std::optional<ValueLatticeElement>
@@ -1002,16 +1013,21 @@ LazyValueInfoImpl::solveBlockValueIntrinsic(IntrinsicInst *II, BasicBlock *BB) {
return MetadataVal;
}
+ bool ArgsMayIncludeUndef = false;
SmallVector<ConstantRange, 2> OpRanges;
for (Value *Op : II->args()) {
- std::optional<ConstantRange> Range = getRangeFor(Op, II, BB);
+ bool MayIncludeUndef = false;
+ std::optional<ConstantRange> Range =
+ getRangeFor(Op, II, BB, MayIncludeUndef);
if (!Range)
return std::nullopt;
+ ArgsMayIncludeUndef |= MayIncludeUndef;
OpRanges.push_back(*Range);
}
- return intersect(ValueLatticeElement::getRange(ConstantRange::intrinsic(
- II->getIntrinsicID(), OpRanges)),
+ return intersect(ValueLatticeElement::getRange(
+ ConstantRange::intrinsic(II->getIntrinsicID(), OpRanges),
+ ArgsMayIncludeUndef),
MetadataVal);
}
@@ -1750,7 +1766,8 @@ ConstantRange LazyValueInfo::getConstantRangeOnEdge(Value *V,
static LazyValueInfo::Tristate
getPredicateResult(unsigned Pred, Constant *C, const ValueLatticeElement &Val,
- const DataLayout &DL, TargetLibraryInfo *TLI) {
+ const DataLayout &DL, TargetLibraryInfo *TLI,
+ bool UndefinedAllowed = true) {
// If we know the value is a constant, evaluate the conditional.
Constant *Res = nullptr;
if (Val.isConstant()) {
@@ -1761,6 +1778,8 @@ getPredicateResult(unsigned Pred, Constant *C, const ValueLatticeElement &Val,
}
if (Val.isConstantRange()) {
+ if (!UndefinedAllowed && Val.isConstantRangeIncludingUndef())
+ return LazyValueInfo::Unknown;
ConstantInt *CI = dyn_cast<ConstantInt>(C);
if (!CI) return LazyValueInfo::Unknown;
@@ -1818,17 +1837,20 @@ getPredicateResult(unsigned Pred, Constant *C, const ValueLatticeElement &Val,
LazyValueInfo::Tristate
LazyValueInfo::getPredicateOnEdge(unsigned Pred, Value *V, Constant *C,
BasicBlock *FromBB, BasicBlock *ToBB,
- Instruction *CxtI) {
+ Instruction *CxtI, bool UndefinedAllowed) {
Module *M = FromBB->getModule();
ValueLatticeElement Result =
getOrCreateImpl(M).getValueOnEdge(V, FromBB, ToBB, CxtI);
- return getPredicateResult(Pred, C, Result, M->getDataLayout(), TLI);
+ return getPredicateResult(Pred, C, Result, M->getDataLayout(), TLI,
+ UndefinedAllowed);
}
-LazyValueInfo::Tristate
-LazyValueInfo::getPredicateAt(unsigned Pred, Value *V, Constant *C,
- Instruction *CxtI, bool UseBlockValue) {
+LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(unsigned Pred, Value *V,
+ Constant *C,
+ Instruction *CxtI,
+ bool UseBlockValue,
+ bool UndefinedAllowed) {
// Is or is not NonNull are common predicates being queried. If
// isKnownNonZero can tell us the result of the predicate, we can
// return it quickly. But this is only a fastpath, and falling
@@ -1847,7 +1869,7 @@ LazyValueInfo::getPredicateAt(unsigned Pred, Value *V, Constant *C,
ValueLatticeElement Result =
UseBlockValue ? Impl.getValueInBlock(V, CxtI->getParent(), CxtI)
: Impl.getValueAt(V, CxtI);
- Tristate Ret = getPredicateResult(Pred, C, Result, DL, TLI);
+ Tristate Ret = getPredicateResult(Pred, C, Result, DL, TLI, UndefinedAllowed);
if (Ret != Unknown)
return Ret;
@@ -1892,8 +1914,8 @@ LazyValueInfo::getPredicateAt(unsigned Pred, Value *V, Constant *C,
Value *Incoming = PHI->getIncomingValue(i);
BasicBlock *PredBB = PHI->getIncomingBlock(i);
// Note that PredBB may be BB itself.
- Tristate Result =
- getPredicateOnEdge(Pred, Incoming, C, PredBB, BB, CxtI);
+ Tristate Result = getPredicateOnEdge(Pred, Incoming, C, PredBB, BB,
+ CxtI, UndefinedAllowed);
// Keep going as long as we've seen a consistent known result for
// all inputs.
@@ -1914,11 +1936,13 @@ LazyValueInfo::getPredicateAt(unsigned Pred, Value *V, Constant *C,
// For predecessor edge, determine if the comparison is true or false
// on that edge. If they're all true or all false, we can conclude
// the value of the comparison in this block.
- Tristate Baseline = getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI);
+ Tristate Baseline =
+ getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI, UndefinedAllowed);
if (Baseline != Unknown) {
// Check that all remaining incoming values match the first one.
while (++PI != PE) {
- Tristate Ret = getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI);
+ Tristate Ret =
+ getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI, UndefinedAllowed);
if (Ret != Baseline)
break;
}
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 48b27a1ea0a2984..b5d34638456af41 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -479,7 +479,7 @@ static bool processAbsIntrinsic(IntrinsicInst *II, LazyValueInfo *LVI) {
// Is X in [0, IntMin]? NOTE: INT_MIN is fine!
Result = LVI->getPredicateAt(CmpInst::Predicate::ICMP_ULE, X, IntMin, II,
- /*UseBlockValue=*/true);
+ /*UseBlockValue=*/true, IsIntMinPoison);
if (Result == LazyValueInfo::True) {
++NumAbs;
II->replaceAllUsesWith(X);
@@ -490,7 +490,7 @@ static bool processAbsIntrinsic(IntrinsicInst *II, LazyValueInfo *LVI) {
// Is X in [IntMin, 0]? NOTE: INT_MIN is fine!
Constant *Zero = ConstantInt::getNullValue(Ty);
Result = LVI->getPredicateAt(CmpInst::Predicate::ICMP_SLE, X, Zero, II,
- /*UseBlockValue=*/true);
+ /*UseBlockValue=*/true, IsIntMinPoison);
assert(Result != LazyValueInfo::False && "Should have been handled already.");
if (Result == LazyValueInfo::Unknown) {
@@ -499,7 +499,7 @@ static bool processAbsIntrinsic(IntrinsicInst *II, LazyValueInfo *LVI) {
if (!IsIntMinPoison) {
// Can we at least tell that the argument is never INT_MIN?
Result = LVI->getPredicateAt(CmpInst::Predicate::ICMP_NE, X, IntMin, II,
- /*UseBlockValue=*/true);
+ /*UseBlockValue=*/true, IsIntMinPoison);
if (Result == LazyValueInfo::True) {
++NumNSW;
++NumSubNSW;
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll b/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll
index 2975ca0ec54f87e..4188f5e732d640d 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/merge-range-and-undef-2.ll
@@ -19,7 +19,8 @@ define i32 @constant_range_and_undef_and(i1 %c0, i1 %c1, i8 %v1, i8 %v2) {
; CHECK: bb2:
; CHECK-NEXT: [[V2_I32:%.*]] = zext i8 [[V2]] to i32
; CHECK-NEXT: [[Y:%.*]] = or i32 [[X]], [[V2_I32]]
-; CHECK-NEXT: ret i32 [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = and i32 [[Y]], 255
+; CHECK-NEXT: ret i32 [[Z]]
;
start:
br i1 %c0, label %bb0, label %bb1
@@ -53,7 +54,8 @@ define i32 @constant_range_and_undef_abs_false(i1 %c0, i1 %c1, i8 %v1) {
; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ]
; CHECK-NEXT: br i1 [[C1]], label [[BB0]], label [[BB2:%.*]]
; CHECK: bb2:
-; CHECK-NEXT: ret i32 [[X]]
+; CHECK-NEXT: [[Z:%.*]] = call i32 @llvm.abs.i32(i32 [[X]], i1 false)
+; CHECK-NEXT: ret i32 [[Z]]
;
start:
br i1 %c0, label %bb0, label %bb1
More information about the llvm-commits
mailing list