[llvm] [InstCombine] Fold `p||q` to `q` and `p&&q` to `p` under `llvm.assume(p => q)` (PR #199868)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 10 01:16:04 PDT 2026
https://github.com/VachanVY updated https://github.com/llvm/llvm-project/pull/199868
>From 8ec0d71113865ad3a290dd1cd4bf2d0b32e608a3 Mon Sep 17 00:00:00 2001
From: Vachan V Y <vachanvy05 at gmail.com>
Date: Thu, 10 Sep 2026 13:37:05 +0530
Subject: [PATCH 1/2] [InstCombine] Add tests. NFC
---
llvm/test/Transforms/InstCombine/assume2.ll | 112 ++++++++++++++++++++
1 file changed, 112 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/assume2.ll b/llvm/test/Transforms/InstCombine/assume2.ll
index 6c5dd142af7c9..69ffa75009cca 100644
--- a/llvm/test/Transforms/InstCombine/assume2.ll
+++ b/llvm/test/Transforms/InstCombine/assume2.ll
@@ -154,6 +154,118 @@ define i32 @test11(i32 %a) #0 {
ret i32 %and1
}
+define i1 @fold_or_using_assume_implication(i1 %p, i1 %q) {
+; CHECK-LABEL: @fold_or_using_assume_implication(
+; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
+; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
+; CHECK-NEXT: [[OR:%.*]] = or i1 [[P]], [[Q]]
+; CHECK-NEXT: ret i1 [[OR]]
+;
+ %not_p = xor i1 %p, true
+ %impl = or i1 %not_p, %q
+ call void @llvm.assume(i1 %impl)
+ %or = or i1 %p, %q
+ ret i1 %or
+}
+
+define i1 @fold_or_using_assume_implication_commuted(i1 %p, i1 %q) {
+; CHECK-LABEL: @fold_or_using_assume_implication_commuted(
+; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
+; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
+; CHECK-NEXT: [[OR:%.*]] = or i1 [[Q]], [[P]]
+; CHECK-NEXT: ret i1 [[OR]]
+;
+ %not_p = xor i1 %p, true
+ %impl = or i1 %not_p, %q
+ call void @llvm.assume(i1 %impl)
+ %or = or i1 %q, %p
+ ret i1 %or
+}
+
+define i1 @fold_and_using_assume_implication(i1 %p, i1 %q) {
+; CHECK-LABEL: @fold_and_using_assume_implication(
+; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
+; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[P]], [[Q]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %not_p = xor i1 %p, true
+ %impl = or i1 %not_p, %q
+ call void @llvm.assume(i1 %impl)
+ %and = and i1 %p, %q
+ ret i1 %and
+}
+
+define i1 @fold_and_using_assume_implication_commuted(i1 %p, i1 %q) {
+; CHECK-LABEL: @fold_and_using_assume_implication_commuted(
+; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
+; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[Q]], [[P]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %not_p = xor i1 %p, true
+ %impl = or i1 %not_p, %q
+ call void @llvm.assume(i1 %impl)
+ %and = and i1 %q, %p
+ ret i1 %and
+}
+
+define i1 @dont_fold_or_using_implication_when_no_assume(i1 %p, i1 %q) {
+; CHECK-LABEL: @dont_fold_or_using_implication_when_no_assume(
+; CHECK-NEXT: [[OR:%.*]] = or i1 [[P:%.*]], [[Q:%.*]]
+; CHECK-NEXT: ret i1 [[OR]]
+;
+ %not_p = xor i1 %p, true
+ %impl = or i1 %not_p, %q
+ %or = or i1 %p, %q
+ ret i1 %or
+}
+
+define i1 @dont_fold_and_using_implication_when_no_assume(i1 %p, i1 %q) {
+; CHECK-LABEL: @dont_fold_and_using_implication_when_no_assume(
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[P:%.*]], [[Q:%.*]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %not_p = xor i1 %p, true
+ %impl = or i1 %not_p, %q
+ %and = and i1 %p, %q
+ ret i1 %and
+}
+
+define i1 @dont_fold_or_using_assume_when_wrong_implication(i1 %p, i1 %q) {
+; CHECK-LABEL: @dont_fold_or_using_assume_when_wrong_implication(
+; CHECK-NEXT: [[NOT_Q:%.*]] = xor i1 [[Q:%.*]], true
+; CHECK-NEXT: [[WRONG_IMPL:%.*]] = or i1 [[P:%.*]], [[NOT_Q]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[WRONG_IMPL]])
+; CHECK-NEXT: [[OR:%.*]] = or i1 [[P]], [[Q]]
+; CHECK-NEXT: ret i1 [[OR]]
+;
+ %not_q = xor i1 %q, true
+ %wrong_impl = or i1 %not_q, %p
+ call void @llvm.assume(i1 %wrong_impl)
+ %or = or i1 %p, %q
+ ret i1 %or
+}
+
+define i1 @dont_fold_and_using_assume_when_wrong_implication(i1 %p, i1 %q) {
+; CHECK-LABEL: @dont_fold_and_using_assume_when_wrong_implication(
+; CHECK-NEXT: [[NOT_Q:%.*]] = xor i1 [[Q:%.*]], true
+; CHECK-NEXT: [[WRONG_IMPL:%.*]] = or i1 [[P:%.*]], [[NOT_Q]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[WRONG_IMPL]])
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[P]], [[Q]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %not_q = xor i1 %q, true
+ %wrong_impl = or i1 %not_q, %p
+ call void @llvm.assume(i1 %wrong_impl)
+ %and = and i1 %p, %q
+ ret i1 %and
+}
+
attributes #0 = { nounwind uwtable }
attributes #1 = { nounwind }
>From 035e0dba1ac1e7bd52447f06451661cd4070b632 Mon Sep 17 00:00:00 2001
From: Vachan V Y <vachanvy05 at gmail.com>
Date: Thu, 10 Sep 2026 13:45:40 +0530
Subject: [PATCH 2/2] [InstSimplify][ValueTracking] Fold p||q to q and p&&q to
p under llvm.assume(p => q)
---
llvm/include/llvm/Analysis/ValueTracking.h | 7 ++++
llvm/lib/Analysis/InstructionSimplify.cpp | 28 +++++++++++++++
llvm/lib/Analysis/ValueTracking.cpp | 40 ++++++++++++++++++++-
llvm/test/Transforms/InstCombine/assume2.ll | 18 ++++------
4 files changed, 80 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index b2f664a9c9c0d..30d34f41d3a2d 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -1043,6 +1043,13 @@ isImpliedCondition(const Value *LHS, CmpPredicate RHSPred, const Value *RHSOp0,
const Value *RHSOp1, const DataLayout &DL,
bool LHSIsTrue = true, unsigned Depth = 0);
+/// If @llvm.assume encodes LHS => RHS, return whether that
+/// implication holds for the given LHS.
+LLVM_ABI std::optional<bool> isImpliedByAssume(const Value *LHS,
+ const Value *RHS,
+ const SimplifyQuery &Q,
+ bool LHSIsTrue = true);
+
/// Return the boolean condition value in the context of the given instruction
/// if it is known based on dominating conditions.
LLVM_ABI std::optional<bool>
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 4780c18bec421..8dc4a6301eeb1 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -2223,6 +2223,20 @@ static Value *simplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
return Constant::getNullValue(Op0->getType());
if (Op0->getType()->isIntOrIntVectorTy(1)) {
+ if (Q.AC && Q.CxtI) {
+ if (auto Implied = isImpliedByAssume(Op0, Op1, Q, /*LHSIsTrue=*/true)) {
+ if (*Implied == true)
+ return Op0;
+ if (*Implied == false)
+ return ConstantInt::getFalse(Op0->getType());
+ }
+ if (auto Implied = isImpliedByAssume(Op1, Op0, Q, /*LHSIsTrue=*/true)) {
+ if (*Implied)
+ return Op1;
+ if (!*Implied)
+ return ConstantInt::getFalse(Op1->getType());
+ }
+ }
if (std::optional<bool> Implied = isImpliedCondition(Op0, Op1, Q.DL)) {
// If Op0 is true implies Op1 is true, then Op0 is a subset of Op1.
if (*Implied == true)
@@ -2495,6 +2509,20 @@ static Value *simplifyOrInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
return Constant::getAllOnesValue(Op0->getType());
if (Op0->getType()->isIntOrIntVectorTy(1)) {
+ if (Q.AC && Q.CxtI) {
+ if (auto Imp = isImpliedByAssume(Op0, Op1, Q, /*LHSIsTrue=*/false)) {
+ if (*Imp == false)
+ return Op0;
+ if (*Imp == true)
+ return ConstantInt::getTrue(Op0->getType());
+ }
+ if (auto Imp = isImpliedByAssume(Op1, Op0, Q, /*LHSIsTrue=*/false)) {
+ if (*Imp == false)
+ return Op1;
+ if (*Imp == true)
+ return ConstantInt::getTrue(Op1->getType());
+ }
+ }
if (std::optional<bool> Implied =
isImpliedCondition(Op0, Op1, Q.DL, false)) {
// If Op0 is false implies Op1 is false, then Op1 is a subset of Op0.
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 3be0db5ad9c7e..5b22dffdfa4e7 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9978,6 +9978,40 @@ std::optional<bool> llvm::isImpliedByDomCondition(CmpPredicate Pred,
return std::nullopt;
}
+std::optional<bool> llvm::isImpliedByAssume(const Value *LHS, const Value *RHS,
+ const SimplifyQuery &Q,
+ bool LHSIsTrue) {
+ if (!Q.AC || !Q.CxtI)
+ return std::nullopt;
+ if (!LHS->getType()->isIntOrIntVectorTy(1))
+ return std::nullopt;
+
+ const Value *Antecedent = LHSIsTrue ? LHS : RHS;
+ const Value *Consequent = LHSIsTrue ? RHS : LHS;
+
+ auto CheckAssumes = [&](const Value *IndexedVal) -> std::optional<bool> {
+ for (auto &AssumeVH : Q.AC->assumptionsFor(IndexedVal)) {
+ if (!AssumeVH)
+ continue;
+ auto *Assume = cast<CallInst>(AssumeVH);
+ assert(Assume->getIntrinsicID() == Intrinsic::assume);
+ if (!isValidAssumeForContext(Assume, Q.CxtI, Q.DT))
+ continue;
+
+ if (match(Assume->getArgOperand(0),
+ m_c_Or(m_Not(m_Specific(Antecedent)), m_Specific(Consequent))))
+ return LHSIsTrue;
+ }
+ return std::nullopt;
+ };
+
+ if (auto R = CheckAssumes(LHS))
+ return R;
+ if (auto R = CheckAssumes(RHS))
+ return R;
+ return std::nullopt;
+}
+
static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
APInt &Upper, const InstrInfoQuery &IIQ,
bool PreferSignedRange) {
@@ -10536,7 +10570,11 @@ void llvm::findValuesAffectedByCondition(
AddAffected(X);
}
- if (match(V, m_LogicalOp(m_Value(A), m_Value(B)))) {
+ Value *P, *Q;
+ if (IsAssume && match(V, m_c_Or(m_Not(m_Value(P)), m_Value(Q)))) {
+ AddAffected(P);
+ AddAffected(Q);
+ } else if (match(V, m_LogicalOp(m_Value(A), m_Value(B)))) {
// assume(A && B) is split to -> assume(A); assume(B);
// assume(!(A || B)) is split to -> assume(!A); assume(!B);
// Finally, assume(A || B) / assume(!(A && B)) generally don't provide
diff --git a/llvm/test/Transforms/InstCombine/assume2.ll b/llvm/test/Transforms/InstCombine/assume2.ll
index 69ffa75009cca..96b962a1bc3da 100644
--- a/llvm/test/Transforms/InstCombine/assume2.ll
+++ b/llvm/test/Transforms/InstCombine/assume2.ll
@@ -159,8 +159,7 @@ define i1 @fold_or_using_assume_implication(i1 %p, i1 %q) {
; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
-; CHECK-NEXT: [[OR:%.*]] = or i1 [[P]], [[Q]]
-; CHECK-NEXT: ret i1 [[OR]]
+; CHECK-NEXT: ret i1 [[Q]]
;
%not_p = xor i1 %p, true
%impl = or i1 %not_p, %q
@@ -174,8 +173,7 @@ define i1 @fold_or_using_assume_implication_commuted(i1 %p, i1 %q) {
; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
-; CHECK-NEXT: [[OR:%.*]] = or i1 [[Q]], [[P]]
-; CHECK-NEXT: ret i1 [[OR]]
+; CHECK-NEXT: ret i1 [[Q]]
;
%not_p = xor i1 %p, true
%impl = or i1 %not_p, %q
@@ -189,8 +187,7 @@ define i1 @fold_and_using_assume_implication(i1 %p, i1 %q) {
; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
-; CHECK-NEXT: [[AND:%.*]] = and i1 [[P]], [[Q]]
-; CHECK-NEXT: ret i1 [[AND]]
+; CHECK-NEXT: ret i1 [[P]]
;
%not_p = xor i1 %p, true
%impl = or i1 %not_p, %q
@@ -204,8 +201,7 @@ define i1 @fold_and_using_assume_implication_commuted(i1 %p, i1 %q) {
; CHECK-NEXT: [[NOT_P:%.*]] = xor i1 [[P:%.*]], true
; CHECK-NEXT: [[IMPL:%.*]] = or i1 [[Q:%.*]], [[NOT_P]]
; CHECK-NEXT: call void @llvm.assume(i1 [[IMPL]])
-; CHECK-NEXT: [[AND:%.*]] = and i1 [[Q]], [[P]]
-; CHECK-NEXT: ret i1 [[AND]]
+; CHECK-NEXT: ret i1 [[P]]
;
%not_p = xor i1 %p, true
%impl = or i1 %not_p, %q
@@ -241,8 +237,7 @@ define i1 @dont_fold_or_using_assume_when_wrong_implication(i1 %p, i1 %q) {
; CHECK-NEXT: [[NOT_Q:%.*]] = xor i1 [[Q:%.*]], true
; CHECK-NEXT: [[WRONG_IMPL:%.*]] = or i1 [[P:%.*]], [[NOT_Q]]
; CHECK-NEXT: call void @llvm.assume(i1 [[WRONG_IMPL]])
-; CHECK-NEXT: [[OR:%.*]] = or i1 [[P]], [[Q]]
-; CHECK-NEXT: ret i1 [[OR]]
+; CHECK-NEXT: ret i1 [[P]]
;
%not_q = xor i1 %q, true
%wrong_impl = or i1 %not_q, %p
@@ -256,8 +251,7 @@ define i1 @dont_fold_and_using_assume_when_wrong_implication(i1 %p, i1 %q) {
; CHECK-NEXT: [[NOT_Q:%.*]] = xor i1 [[Q:%.*]], true
; CHECK-NEXT: [[WRONG_IMPL:%.*]] = or i1 [[P:%.*]], [[NOT_Q]]
; CHECK-NEXT: call void @llvm.assume(i1 [[WRONG_IMPL]])
-; CHECK-NEXT: [[AND:%.*]] = and i1 [[P]], [[Q]]
-; CHECK-NEXT: ret i1 [[AND]]
+; CHECK-NEXT: ret i1 [[Q]]
;
%not_q = xor i1 %q, true
%wrong_impl = or i1 %not_q, %p
More information about the llvm-commits
mailing list