[llvm] [ValueTracking] Make `computeKnownBitsFromContext` deterministic (PR #75452)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 14 01:44:47 PST 2023
https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/75452
Fixes #75369.
The latter `getFreelyInverted` failed because the former call increased the size of the basic block, and we reached the scan limit in `isValidAssumeForContext`.
https://github.com/llvm/llvm-project/blob/8cb842879d44ba7b5c1d3e158b8824cae1db6092/llvm/lib/Analysis/ValueTracking.cpp#L509-L517
This patch adds an option `Deterministic` to `SimplifyQuery`. If `Deterministic` is true, we guarantee that the result of the query is deterministic during instruction construction.
>From b681ecd900d2abdc963565abf63ddcb740abd8e2 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 14 Dec 2023 17:34:21 +0800
Subject: [PATCH] [ValueTracking] Make `computeKnownBitsFromContext`
deterministic
---
llvm/include/llvm/Analysis/SimplifyQuery.h | 18 +++++++---
llvm/include/llvm/Analysis/ValueTracking.h | 5 ++-
llvm/lib/Analysis/ValueTracking.cpp | 20 ++++++-----
.../InstCombine/InstructionCombining.cpp | 5 +--
llvm/test/Transforms/InstCombine/pr75369.ll | 36 +++++++++++++++++++
5 files changed, 68 insertions(+), 16 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/pr75369.ll
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index e5e6ae0d3d8e3e..51ddbe1f0bd25f 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -75,6 +75,10 @@ struct SimplifyQuery {
/// allowed to assume a particular value for a use of undef for example.
bool CanUseUndef = true;
+ /// Controls whether the result of query is guaranteed to be deterministic.
+ /// If it is false, the result may change during instruction construction.
+ bool Deterministic = false;
+
SimplifyQuery(const DataLayout &DL, const Instruction *CXTI = nullptr)
: DL(DL), CxtI(CXTI) {}
@@ -82,22 +86,28 @@ struct SimplifyQuery {
const DominatorTree *DT = nullptr,
AssumptionCache *AC = nullptr,
const Instruction *CXTI = nullptr, bool UseInstrInfo = true,
- bool CanUseUndef = true, const DomConditionCache *DC = nullptr)
+ bool CanUseUndef = true, const DomConditionCache *DC = nullptr,
+ bool Deterministic = false)
: DL(DL), TLI(TLI), DT(DT), AC(AC), CxtI(CXTI), DC(DC), IIQ(UseInstrInfo),
- CanUseUndef(CanUseUndef) {}
+ CanUseUndef(CanUseUndef), Deterministic(Deterministic) {}
SimplifyQuery(const DataLayout &DL, const DominatorTree *DT,
AssumptionCache *AC = nullptr,
const Instruction *CXTI = nullptr, bool UseInstrInfo = true,
- bool CanUseUndef = true)
+ bool CanUseUndef = true, bool Deterministic = false)
: DL(DL), DT(DT), AC(AC), CxtI(CXTI), IIQ(UseInstrInfo),
- CanUseUndef(CanUseUndef) {}
+ CanUseUndef(CanUseUndef), Deterministic(Deterministic) {}
SimplifyQuery getWithInstruction(const Instruction *I) const {
SimplifyQuery Copy(*this);
Copy.CxtI = I;
return Copy;
}
+ SimplifyQuery getWithDeterministic() const {
+ SimplifyQuery Copy(*this);
+ Copy.Deterministic = true;
+ return Copy;
+ }
SimplifyQuery getWithoutUndef() const {
SimplifyQuery Copy(*this);
Copy.CanUseUndef = false;
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index a3186e61b94adf..bf155406d554a6 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -811,8 +811,11 @@ bool isAssumeLikeIntrinsic(const Instruction *I);
/// Return true if it is valid to use the assumptions provided by an
/// assume intrinsic, I, at the point in the control-flow identified by the
/// context instruction, CxtI.
+/// If \p Deterministic is true, the result is guaranteed to be deterministic
+/// regardless of the size of a basic block.
bool isValidAssumeForContext(const Instruction *I, const Instruction *CxtI,
- const DominatorTree *DT = nullptr);
+ const DominatorTree *DT = nullptr,
+ bool Deterministic = false);
enum class OverflowResult {
/// Always overflows in the direction of signed/unsigned min value.
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5445746ab2a1bc..f0ae6a71020382 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -485,7 +485,8 @@ bool llvm::isAssumeLikeIntrinsic(const Instruction *I) {
bool llvm::isValidAssumeForContext(const Instruction *Inv,
const Instruction *CxtI,
- const DominatorTree *DT) {
+ const DominatorTree *DT,
+ bool Deterministic) {
// There are two restrictions on the use of an assume:
// 1. The assume must dominate the context (or the control flow must
// reach the assume whenever it reaches the context).
@@ -513,7 +514,7 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
// to avoid a compile-time explosion. This limit is chosen arbitrarily, so
// it can be adjusted if needed (could be turned into a cl::opt).
auto Range = make_range(CxtI->getIterator(), Inv->getIterator());
- if (!isGuaranteedToTransferExecutionToSuccessor(Range, 15))
+ if (Deterministic || !isGuaranteedToTransferExecutionToSuccessor(Range, 15))
return false;
return !isEphemeralValueOf(Inv, CxtI);
@@ -591,7 +592,7 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
(RK.AttrKind == Attribute::Dereferenceable &&
!NullPointerIsDefined(Q.CxtI->getFunction(),
V->getType()->getPointerAddressSpace()))) &&
- isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic))
return true;
}
continue;
@@ -607,7 +608,8 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
if (!match(I->getArgOperand(0), m_c_ICmp(Pred, m_V, m_Value(RHS))))
return false;
- if (cmpExcludesZero(Pred, RHS) && isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ if (cmpExcludesZero(Pred, RHS) &&
+ isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic))
return true;
}
@@ -756,7 +758,7 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
*I, I->bundle_op_info_begin()[Elem.Index])) {
if (RK.WasOn == V && RK.AttrKind == Attribute::Alignment &&
isPowerOf2_64(RK.ArgValue) &&
- isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic))
Known.Zero.setLowBits(Log2_64(RK.ArgValue));
}
continue;
@@ -768,14 +770,14 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
Value *Arg = I->getArgOperand(0);
- if (Arg == V && isValidAssumeForContext(I, Q.CxtI, Q.DT)) {
+ if (Arg == V && isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic)) {
assert(BitWidth == 1 && "assume operand is not i1?");
(void)BitWidth;
Known.setAllOnes();
return;
}
if (match(Arg, m_Not(m_Specific(V))) &&
- isValidAssumeForContext(I, Q.CxtI, Q.DT)) {
+ isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic)) {
assert(BitWidth == 1 && "assume operand is not i1?");
(void)BitWidth;
Known.setAllZero();
@@ -790,7 +792,7 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
if (!Cmp)
continue;
- if (!isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ if (!isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic))
continue;
computeKnownBitsFromCmp(V, Cmp->getPredicate(), Cmp->getOperand(0),
@@ -4213,7 +4215,7 @@ static FPClassTest computeKnownFPClassFromAssumes(const Value *V,
assert(I->getCalledFunction()->getIntrinsicID() == Intrinsic::assume &&
"must be an assume intrinsic");
- if (!isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ if (!isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.Deterministic))
continue;
CmpInst::Predicate Pred;
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index ba40ee7693636e..0eb5d364802777 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2192,8 +2192,9 @@ Value *InstCombiner::getFreelyInvertedImpl(Value *V, bool WillInvertAllUses,
// Treat lshr with non-negative operand as ashr.
if (match(V, m_LShr(m_Value(A), m_Value(B))) &&
- isKnownNonNegative(A, SQ.getWithInstruction(cast<Instruction>(V)),
- Depth)) {
+ isKnownNonNegative(
+ A, SQ.getWithInstruction(cast<Instruction>(V)).getWithDeterministic(),
+ Depth)) {
if (auto *AV = getFreelyInvertedImpl(A, A->hasOneUse(), Builder,
DoesConsume, Depth))
return Builder ? Builder->CreateAShr(AV, B) : NonNull;
diff --git a/llvm/test/Transforms/InstCombine/pr75369.ll b/llvm/test/Transforms/InstCombine/pr75369.ll
new file mode 100644
index 00000000000000..2f90753504b36d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/pr75369.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+define i32 @main(ptr %a, i8 %a0, i32 %conv, i8 %a1) {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: ptr [[A:%.*]], i8 [[A0:%.*]], i32 [[CONV:%.*]], i8 [[A1:%.*]]) {
+; CHECK-NEXT: [[A3:%.*]] = trunc i32 [[CONV]] to i8
+; CHECK-NEXT: [[OR11:%.*]] = or i8 [[A3]], [[A0]]
+; CHECK-NEXT: store i8 [[OR11]], ptr [[A]], align 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A1]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i32 [[CONV]]
+;
+ %conv1 = sext i8 %a1 to i32
+ %a2 = xor i32 %conv, 1
+ %or = or i32 %conv1, %conv
+ %not = xor i32 %or, -1
+ %shr = lshr i32 %not, 1
+ %add.neg3 = sub i32 %a2, %shr
+ %conv24 = trunc i32 %add.neg3 to i8
+ store i8 %conv24, ptr %a, align 1
+ %sext = shl i32 %conv, 0
+ %conv3 = ashr i32 %sext, 0
+ %a3 = trunc i32 %conv to i8
+ %conv5 = or i8 %a3, 0
+ %xor6 = xor i8 %conv5, 0
+ %xor816 = xor i8 %a0, 0
+ %a4 = xor i8 %xor816, 0
+ %or11 = or i8 %xor6, %a4
+ store i8 %or11, ptr %a, align 1
+ %cmp = icmp slt i8 %a1, 0
+ call void @llvm.assume(i1 %cmp)
+ ret i32 %conv3
+}
+
+declare void @llvm.assume(i1)
More information about the llvm-commits
mailing list