[llvm] [JumpThreading] Thread a common icmp across PHI nodes (PR #221125)
Abhay Kanhere via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 07:09:10 PDT 2026
https://github.com/AbhayKanhere updated https://github.com/llvm/llvm-project/pull/221125
>From 5aa655b070a1f4ced005ea502151e325c84f4f6e Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Thu, 3 Sep 2026 10:32:40 -0700
Subject: [PATCH 1/5] [JumpThreading] Thread a common icmp across PHI nodes
When both icmp operands are block PHIs, use isImpliedCondition of the
predecessor's branch to recover its per-edge value and thread known edges.
---
llvm/lib/Transforms/Scalar/JumpThreading.cpp | 28 ++++
.../JumpThreading/thread-cmp-of-phis.ll | 134 ++++++++++++++++++
2 files changed, 162 insertions(+)
create mode 100644 llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 7a0542aac83fc..203165bb27672 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -767,6 +767,34 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
RHS = PN->getIncomingValue(i);
}
Value *Res = simplifyCmpInst(Pred, LHS, RHS, {DL});
+
+ // Even without folding, the compare may be known on this edge if
+ // PredBB's branch tests a condition that implies it -- most usefully
+ // the same compare, when both operands are PHIs. PredBB's false arm
+ // reaches BB, so C is false there; BB re-tests C via the PHIs:
+ //
+ // PredBB: br (C = icmp <pred> A, B), T, BB ; BB is the false arm
+ // \
+ // v
+ // BB: L = phi [A, PredBB], ... ; L -> A on the PredBB edge
+ // R = phi [B, PredBB], ... ; R -> B on the PredBB edge
+ // br (icmp <pred> L, R) ; == C, false => thread past it
+ //
+ // Require both operands available on the edge (not defined in BB).
+ if (!Res && isa<ICmpInst>(Cmp) && !CmpType->isVectorTy()) {
+ auto DefinedInBB = [&](Value *V) {
+ auto *VI = dyn_cast<Instruction>(V);
+ return VI && VI->getParent() == BB;
+ };
+ auto *PredBI = dyn_cast<CondBrInst>(PredBB->getTerminator());
+ if (PredBI && !DefinedInBB(LHS) && !DefinedInBB(RHS) &&
+ PredBI->getSuccessor(0) != PredBI->getSuccessor(1))
+ if (std::optional<bool> Implied = isImpliedCondition(
+ PredBI->getCondition(), Pred, LHS, RHS, DL,
+ /*LHSIsTrue=*/PredBI->getSuccessor(0) == BB))
+ Res = ConstantInt::getBool(Cmp->getContext(), *Implied);
+ }
+
if (!Res) {
if (!isa<Constant>(RHS))
continue;
diff --git a/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll b/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll
new file mode 100644
index 0000000000000..d56ae2ae6a4e9
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll
@@ -0,0 +1,134 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=jump-threading -S < %s | FileCheck %s
+;
+; JumpThreading can thread a block whose condition is `icmp <pred> (phiA, phiB)`
+; (both operands PHIs defined in the block) on the predecessor edges where the
+; predecessor's controlling branch implies the comparison. Threading is partial:
+; only edges with a proven result are threaded; others keep the branch.
+
+declare i64 @opaque()
+
+; Positive: on the %entry edge the PHIs reconstruct to `icmp eq %a, %b` = %c
+; (false there), so thread %entry -> %if.ne; the opaque %grow edge keeps its branch.
+define i32 @thread_cmp_of_phis(i64 %a, i64 %b) {
+; CHECK-LABEL: @thread_cmp_of_phis(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[C:%.*]] = icmp eq i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: br i1 [[C]], label [[JOIN:%.*]], label [[IF_NE:%.*]]
+; CHECK: join:
+; CHECK-NEXT: [[A2:%.*]] = call i64 @opaque()
+; CHECK-NEXT: [[B2:%.*]] = call i64 @opaque()
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[A2]], [[B2]]
+; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_NE]]
+; CHECK: if.eq:
+; CHECK-NEXT: ret i32 1
+; CHECK: if.ne:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c = icmp eq i64 %a, %b
+ br i1 %c, label %grow, label %join
+
+grow:
+ %a2 = call i64 @opaque()
+ %b2 = call i64 @opaque()
+ br label %join
+
+join:
+ %p1 = phi i64 [ %a, %entry ], [ %a2, %grow ]
+ %p2 = phi i64 [ %b, %entry ], [ %b2, %grow ]
+ %cmp = icmp eq i64 %p1, %p2
+ br i1 %cmp, label %if.eq, label %if.ne
+
+if.eq:
+ ret i32 1
+
+if.ne:
+ ret i32 0
+}
+
+; Negative: %c compares %a/%b, but the join re-tests %a2/%b2 -- different
+; operands, so %c does not imply it; %entry is not threaded.
+define i32 @no_thread_operands_differ(i64 %a, i64 %b, i64 %a2, i64 %b2) {
+; CHECK-LABEL: @no_thread_operands_differ(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[C:%.*]] = icmp eq i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: br i1 [[C]], label [[MID:%.*]], label [[JOIN:%.*]]
+; CHECK: mid:
+; CHECK-NEXT: [[PA:%.*]] = call i64 @opaque()
+; CHECK-NEXT: [[PB:%.*]] = call i64 @opaque()
+; CHECK-NEXT: br label [[JOIN]]
+; CHECK: join:
+; CHECK-NEXT: [[P1:%.*]] = phi i64 [ [[A2:%.*]], [[ENTRY:%.*]] ], [ [[PA]], [[MID]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi i64 [ [[B2:%.*]], [[ENTRY]] ], [ [[PB]], [[MID]] ]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[P1]], [[P2]]
+; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_NE:%.*]]
+; CHECK: if.eq:
+; CHECK-NEXT: ret i32 1
+; CHECK: if.ne:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c = icmp eq i64 %a, %b
+ br i1 %c, label %mid, label %join
+
+mid:
+ %pa = call i64 @opaque()
+ %pb = call i64 @opaque()
+ br label %join
+
+join:
+ %p1 = phi i64 [ %a2, %entry ], [ %pa, %mid ]
+ %p2 = phi i64 [ %b2, %entry ], [ %pb, %mid ]
+ %cmp = icmp eq i64 %p1, %p2
+ br i1 %cmp, label %if.eq, label %if.ne
+
+if.eq:
+ ret i32 1
+
+if.ne:
+ ret i32 0
+}
+
+; Negative: same operands, but %c is unsigned (ult) while the join re-tests
+; signed (slt) -- ult does not imply slt; %entry is not threaded.
+define i32 @no_thread_predicate_differs(i64 %a, i64 %b) {
+; CHECK-LABEL: @no_thread_predicate_differs(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: br i1 [[C]], label [[GROW:%.*]], label [[JOIN:%.*]]
+; CHECK: grow:
+; CHECK-NEXT: [[A2:%.*]] = call i64 @opaque()
+; CHECK-NEXT: [[B2:%.*]] = call i64 @opaque()
+; CHECK-NEXT: br label [[JOIN]]
+; CHECK: join:
+; CHECK-NEXT: [[P1:%.*]] = phi i64 [ [[A]], [[ENTRY:%.*]] ], [ [[A2]], [[GROW]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi i64 [ [[B]], [[ENTRY]] ], [ [[B2]], [[GROW]] ]
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[P1]], [[P2]]
+; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_NE:%.*]]
+; CHECK: if.eq:
+; CHECK-NEXT: ret i32 1
+; CHECK: if.ne:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c = icmp ult i64 %a, %b
+ br i1 %c, label %grow, label %join
+
+grow:
+ %a2 = call i64 @opaque()
+ %b2 = call i64 @opaque()
+ br label %join
+
+join:
+ %p1 = phi i64 [ %a, %entry ], [ %a2, %grow ]
+ %p2 = phi i64 [ %b, %entry ], [ %b2, %grow ]
+ %cmp = icmp slt i64 %p1, %p2
+ br i1 %cmp, label %if.eq, label %if.ne
+
+if.eq:
+ ret i32 1
+
+if.ne:
+ ret i32 0
+}
>From 0454212f84f7995936977a3e5a2a96c319dd3ccb Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Tue, 8 Sep 2026 10:40:07 -0700
Subject: [PATCH 2/5] [JumpThreading] Address review feedback; refactor for
readability (NFC)
Extract the edge-implied-compare check into a static isImpliedByEdgeBranch
helper (keeping the CFG diagram). No functional change.
---
llvm/lib/Transforms/Scalar/JumpThreading.cpp | 62 ++++++++++++--------
1 file changed, 36 insertions(+), 26 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 203165bb27672..3e20249478306 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -549,6 +549,36 @@ static Constant *getKnownConstant(Value *Val, ConstantPreference Preference) {
return dyn_cast<ConstantInt>(Val);
}
+/// If the edge PredBB->BB is controlled by a conditional branch whose condition
+/// implies `icmp Pred LHS, RHS`, return that compare's value on the edge (else
+/// std::nullopt). LHS/RHS are the operands as seen on the edge and must be
+/// available there -- a compare operand defined in BB does not exist on the
+/// edge. Most useful when both compare operands are PHIs in BB and PredBB
+/// already branched on the same compare:
+///
+/// PredBB: br (C = icmp <pred> A, B), T, BB ; here BB is the false arm
+/// \
+/// v
+/// BB: L = phi [A, PredBB], ... ; L -> A on the PredBB edge
+/// R = phi [B, PredBB], ... ; R -> B on the PredBB edge
+/// br (icmp <pred> L, R) ; == C, false => thread past it
+static std::optional<bool> isImpliedByEdgeBranch(BasicBlock *PredBB,
+ BasicBlock *BB,
+ CmpInst::Predicate Pred,
+ Value *LHS, Value *RHS,
+ const DataLayout &DL) {
+ auto DefinedInBB = [&](Value *V) {
+ auto *I = dyn_cast<Instruction>(V);
+ return I && I->getParent() == BB;
+ };
+ auto *PredBI = dyn_cast<CondBrInst>(PredBB->getTerminator());
+ if (!PredBI || PredBI->getSuccessor(0) == PredBI->getSuccessor(1) ||
+ DefinedInBB(LHS) || DefinedInBB(RHS))
+ return std::nullopt;
+ return isImpliedCondition(PredBI->getCondition(), Pred, LHS, RHS, DL,
+ /*LHSIsTrue=*/PredBI->getSuccessor(0) == BB);
+}
+
/// computeValueKnownInPredecessors - Given a basic block BB and a value V, see
/// if we can infer that the value is a known ConstantInt/BlockAddress or undef
/// in any of our predecessors. If so, return the known list of value and pred
@@ -768,32 +798,12 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
}
Value *Res = simplifyCmpInst(Pred, LHS, RHS, {DL});
- // Even without folding, the compare may be known on this edge if
- // PredBB's branch tests a condition that implies it -- most usefully
- // the same compare, when both operands are PHIs. PredBB's false arm
- // reaches BB, so C is false there; BB re-tests C via the PHIs:
- //
- // PredBB: br (C = icmp <pred> A, B), T, BB ; BB is the false arm
- // \
- // v
- // BB: L = phi [A, PredBB], ... ; L -> A on the PredBB edge
- // R = phi [B, PredBB], ... ; R -> B on the PredBB edge
- // br (icmp <pred> L, R) ; == C, false => thread past it
- //
- // Require both operands available on the edge (not defined in BB).
- if (!Res && isa<ICmpInst>(Cmp) && !CmpType->isVectorTy()) {
- auto DefinedInBB = [&](Value *V) {
- auto *VI = dyn_cast<Instruction>(V);
- return VI && VI->getParent() == BB;
- };
- auto *PredBI = dyn_cast<CondBrInst>(PredBB->getTerminator());
- if (PredBI && !DefinedInBB(LHS) && !DefinedInBB(RHS) &&
- PredBI->getSuccessor(0) != PredBI->getSuccessor(1))
- if (std::optional<bool> Implied = isImpliedCondition(
- PredBI->getCondition(), Pred, LHS, RHS, DL,
- /*LHSIsTrue=*/PredBI->getSuccessor(0) == BB))
- Res = ConstantInt::getBool(Cmp->getContext(), *Implied);
- }
+ // If it doesn't fold, the compare may still be known on this edge when
+ // PredBB's branch condition implies it (see isImpliedByEdgeBranch).
+ if (!Res && isa<ICmpInst>(Cmp) && !CmpType->isVectorTy())
+ if (std::optional<bool> Implied =
+ isImpliedByEdgeBranch(PredBB, BB, Pred, LHS, RHS, DL))
+ Res = ConstantInt::getBool(Cmp->getContext(), *Implied);
if (!Res) {
if (!isa<Constant>(RHS))
>From 5e37f71300356be1a53b3a770c29f8c46c6ca1c9 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Mon, 21 Sep 2026 17:18:02 -0700
Subject: [PATCH 3/5] [Transforms/Utils] Add isImpliedByEdgeCondition to Local
Extract the shared check that returns an icmp's value on the edge
PredBB->SuccBB when the predecessor's conditional branch implies it, so
JumpThreading and InstCombine can reuse it instead of duplicating it.
---
llvm/include/llvm/Transforms/Utils/Local.h | 9 +++++++++
llvm/lib/Transforms/Utils/Local.cpp | 10 ++++++++++
2 files changed, 19 insertions(+)
diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h
index a7b687304d63c..abae9cb60ac7b 100644
--- a/llvm/include/llvm/Transforms/Utils/Local.h
+++ b/llvm/include/llvm/Transforms/Utils/Local.h
@@ -36,6 +36,7 @@ class AssumptionCache;
class BasicBlock;
class CallBase;
class CallInst;
+class CmpPredicate;
class CondBrInst;
class DIBuilder;
class DomTreeUpdater;
@@ -566,6 +567,14 @@ LLVM_ABI bool canReplaceOperandWithVariable(const Instruction *I,
/// Invert the given true/false value, possibly reusing an existing copy.
LLVM_ABI Value *invertCondition(Value *Condition);
+/// If PredBB's conditional branch implies `icmp Pred, LHS, RHS` on the
+/// PredBB->SuccBB edge, return that compare's value on the edge, else
+/// std::nullopt.
+LLVM_ABI std::optional<bool>
+isImpliedByEdgeCondition(const BasicBlock *PredBB, const BasicBlock *SuccBB,
+ CmpPredicate Pred, const Value *LHS, const Value *RHS,
+ const DataLayout &DL);
+
//===----------------------------------------------------------------------===//
// Assorted
//
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index b88e506ae681a..43a98176045ab 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -4021,6 +4021,16 @@ Value *llvm::invertCondition(Value *Condition) {
return Inverted;
}
+std::optional<bool> llvm::isImpliedByEdgeCondition(
+ const BasicBlock *PredBB, const BasicBlock *SuccBB, CmpPredicate Pred,
+ const Value *LHS, const Value *RHS, const DataLayout &DL) {
+ const auto *PredBI = dyn_cast<CondBrInst>(PredBB->getTerminator());
+ if (!PredBI || PredBI->getSuccessor(0) == PredBI->getSuccessor(1))
+ return std::nullopt;
+ return isImpliedCondition(PredBI->getCondition(), Pred, LHS, RHS, DL,
+ /*LHSIsTrue=*/PredBI->getSuccessor(0) == SuccBB);
+}
+
bool llvm::inferAttributesFromOthers(Function &F) {
// Note: We explicitly check for attributes rather than using cover functions
// because some of the cover functions include the logic being implemented.
>From cea47ab4dd53a4eb8932d936592e8a5e44a2907f Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Mon, 21 Sep 2026 17:18:03 -0700
Subject: [PATCH 4/5] [InstCombine] Reuse isImpliedByEdgeCondition helper (NFC)
Replace the inline predecessor-branch implied-condition check in
simplifyInstructionWithPHI with the shared helper; no functional change.
---
.../InstCombine/InstructionCombining.cpp | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index b877f79a000cf..9a23d0834be84 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1869,19 +1869,11 @@ static Value *simplifyInstructionWithPHI(Instruction &I, PHINode *PN,
if (NewVal && NewVal != PN && !match(NewVal, m_ConstantExpr()))
return NewVal;
- // Check if incoming PHI value can be replaced with constant
- // based on implied condition.
- CondBrInst *TerminatorBI = dyn_cast<CondBrInst>(InBB->getTerminator());
- const ICmpInst *ICmp = dyn_cast<ICmpInst>(&I);
- if (TerminatorBI &&
- TerminatorBI->getSuccessor(0) != TerminatorBI->getSuccessor(1) && ICmp) {
- bool LHSIsTrue = TerminatorBI->getSuccessor(0) == PN->getParent();
- std::optional<bool> ImpliedCond = isImpliedCondition(
- TerminatorBI->getCondition(), ICmp->getCmpPredicate(), Ops[0], Ops[1],
- DL, LHSIsTrue);
- if (ImpliedCond)
- return ConstantInt::getBool(I.getType(), ImpliedCond.value());
- }
+ // Fold to a constant when the predecessor edge's branch implies the compare.
+ if (auto *ICmp = dyn_cast<ICmpInst>(&I))
+ if (std::optional<bool> ImpliedCond = isImpliedByEdgeCondition(
+ InBB, PN->getParent(), ICmp->getCmpPredicate(), Ops[0], Ops[1], DL))
+ return ConstantInt::getBool(I.getType(), *ImpliedCond);
return nullptr;
}
>From e5d2fbdfbb3ddfdb0488950134c16e6a1b5dcbb4 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Mon, 21 Sep 2026 17:18:03 -0700
Subject: [PATCH 5/5] [JumpThreading] Reuse isImpliedByEdgeCondition for
edge-implied compares
Delegate the predecessor-branch implied-condition check to the shared
Local helper, keeping the JumpThreading-specific guard that rejects
operands defined in BB. Regenerate thread-cmp-of-phis.ll with UTC v6.
---
llvm/lib/Transforms/Scalar/JumpThreading.cpp | 7 +-
.../JumpThreading/thread-cmp-of-phis.ll | 69 ++++++++++---------
2 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 3e20249478306..15c855c9c5a80 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -571,12 +571,9 @@ static std::optional<bool> isImpliedByEdgeBranch(BasicBlock *PredBB,
auto *I = dyn_cast<Instruction>(V);
return I && I->getParent() == BB;
};
- auto *PredBI = dyn_cast<CondBrInst>(PredBB->getTerminator());
- if (!PredBI || PredBI->getSuccessor(0) == PredBI->getSuccessor(1) ||
- DefinedInBB(LHS) || DefinedInBB(RHS))
+ if (DefinedInBB(LHS) || DefinedInBB(RHS))
return std::nullopt;
- return isImpliedCondition(PredBI->getCondition(), Pred, LHS, RHS, DL,
- /*LHSIsTrue=*/PredBI->getSuccessor(0) == BB);
+ return isImpliedByEdgeCondition(PredBB, BB, Pred, LHS, RHS, DL);
}
/// computeValueKnownInPredecessors - Given a basic block BB and a value V, see
diff --git a/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll b/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll
index d56ae2ae6a4e9..169a26fc774f4 100644
--- a/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll
+++ b/llvm/test/Transforms/JumpThreading/thread-cmp-of-phis.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=jump-threading -S < %s | FileCheck %s
;
; JumpThreading can thread a block whose condition is `icmp <pred> (phiA, phiB)`
@@ -11,18 +11,19 @@ declare i64 @opaque()
; Positive: on the %entry edge the PHIs reconstruct to `icmp eq %a, %b` = %c
; (false there), so thread %entry -> %if.ne; the opaque %grow edge keeps its branch.
define i32 @thread_cmp_of_phis(i64 %a, i64 %b) {
-; CHECK-LABEL: @thread_cmp_of_phis(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[C:%.*]] = icmp eq i64 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: br i1 [[C]], label [[JOIN:%.*]], label [[IF_NE:%.*]]
-; CHECK: join:
+; CHECK-LABEL: define i32 @thread_cmp_of_phis(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp eq i64 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[C]], label %[[JOIN:.*]], label %[[IF_NE:.*]]
+; CHECK: [[JOIN]]:
; CHECK-NEXT: [[A2:%.*]] = call i64 @opaque()
; CHECK-NEXT: [[B2:%.*]] = call i64 @opaque()
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[A2]], [[B2]]
-; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_NE]]
-; CHECK: if.eq:
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_EQ:.*]], label %[[IF_NE]]
+; CHECK: [[IF_EQ]]:
; CHECK-NEXT: ret i32 1
-; CHECK: if.ne:
+; CHECK: [[IF_NE]]:
; CHECK-NEXT: ret i32 0
;
entry:
@@ -50,22 +51,23 @@ if.ne:
; Negative: %c compares %a/%b, but the join re-tests %a2/%b2 -- different
; operands, so %c does not imply it; %entry is not threaded.
define i32 @no_thread_operands_differ(i64 %a, i64 %b, i64 %a2, i64 %b2) {
-; CHECK-LABEL: @no_thread_operands_differ(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[C:%.*]] = icmp eq i64 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: br i1 [[C]], label [[MID:%.*]], label [[JOIN:%.*]]
-; CHECK: mid:
+; CHECK-LABEL: define i32 @no_thread_operands_differ(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]], i64 [[A2:%.*]], i64 [[B2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[C:%.*]] = icmp eq i64 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[C]], label %[[MID:.*]], label %[[JOIN:.*]]
+; CHECK: [[MID]]:
; CHECK-NEXT: [[PA:%.*]] = call i64 @opaque()
; CHECK-NEXT: [[PB:%.*]] = call i64 @opaque()
-; CHECK-NEXT: br label [[JOIN]]
-; CHECK: join:
-; CHECK-NEXT: [[P1:%.*]] = phi i64 [ [[A2:%.*]], [[ENTRY:%.*]] ], [ [[PA]], [[MID]] ]
-; CHECK-NEXT: [[P2:%.*]] = phi i64 [ [[B2:%.*]], [[ENTRY]] ], [ [[PB]], [[MID]] ]
+; CHECK-NEXT: br label %[[JOIN]]
+; CHECK: [[JOIN]]:
+; CHECK-NEXT: [[P1:%.*]] = phi i64 [ [[A2]], %[[ENTRY]] ], [ [[PA]], %[[MID]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi i64 [ [[B2]], %[[ENTRY]] ], [ [[PB]], %[[MID]] ]
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[P1]], [[P2]]
-; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_NE:%.*]]
-; CHECK: if.eq:
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_EQ:.*]], label %[[IF_NE:.*]]
+; CHECK: [[IF_EQ]]:
; CHECK-NEXT: ret i32 1
-; CHECK: if.ne:
+; CHECK: [[IF_NE]]:
; CHECK-NEXT: ret i32 0
;
entry:
@@ -93,22 +95,23 @@ if.ne:
; Negative: same operands, but %c is unsigned (ult) while the join re-tests
; signed (slt) -- ult does not imply slt; %entry is not threaded.
define i32 @no_thread_predicate_differs(i64 %a, i64 %b) {
-; CHECK-LABEL: @no_thread_predicate_differs(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: br i1 [[C]], label [[GROW:%.*]], label [[JOIN:%.*]]
-; CHECK: grow:
+; CHECK-LABEL: define i32 @no_thread_predicate_differs(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[C]], label %[[GROW:.*]], label %[[JOIN:.*]]
+; CHECK: [[GROW]]:
; CHECK-NEXT: [[A2:%.*]] = call i64 @opaque()
; CHECK-NEXT: [[B2:%.*]] = call i64 @opaque()
-; CHECK-NEXT: br label [[JOIN]]
-; CHECK: join:
-; CHECK-NEXT: [[P1:%.*]] = phi i64 [ [[A]], [[ENTRY:%.*]] ], [ [[A2]], [[GROW]] ]
-; CHECK-NEXT: [[P2:%.*]] = phi i64 [ [[B]], [[ENTRY]] ], [ [[B2]], [[GROW]] ]
+; CHECK-NEXT: br label %[[JOIN]]
+; CHECK: [[JOIN]]:
+; CHECK-NEXT: [[P1:%.*]] = phi i64 [ [[A]], %[[ENTRY]] ], [ [[A2]], %[[GROW]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi i64 [ [[B]], %[[ENTRY]] ], [ [[B2]], %[[GROW]] ]
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[P1]], [[P2]]
-; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_NE:%.*]]
-; CHECK: if.eq:
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_EQ:.*]], label %[[IF_NE:.*]]
+; CHECK: [[IF_EQ]]:
; CHECK-NEXT: ret i32 1
-; CHECK: if.ne:
+; CHECK: [[IF_NE]]:
; CHECK-NEXT: ret i32 0
;
entry:
More information about the llvm-commits
mailing list