[llvm] [SimplifyCFG] Add foldCondStoreToSelect optimization (PR #207654)
Karthika Devi C via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 6 05:10:21 PDT 2026
https://github.com/kartcq updated https://github.com/llvm/llvm-project/pull/207654
>From eafe0f09205a0e5f526f48ddb4842a60f0e72ae7 Mon Sep 17 00:00:00 2001
From: Karthika Devi C <kartc at qti.qualcomm.com>
Date: Tue, 30 Jun 2026 10:39:17 -0700
Subject: [PATCH 1/3] [SimplifyCFG] Add foldCondStoreToSelect optimization
Add a new SimplifyCFG transform that folds a triangle+diamond CFG pattern
where three branches all store to the same address into a single
select+unconditional store in the head block.
Pattern matched:
HeadBB: br i1 %cond1, ThenBB, ElseBB
ThenBB: store VThen, addr; br MergeBB
ElseBB: [cheap instrs]; br i1 %cond2, ElseThenBB, ElseElseBB
ElseThenBB: store VElseThen, addr; br MergeBB
ElseElseBB: store VElseElse, addr; br MergeBB
Transformed to:
HeadBB: %sel = select cond1, VThen, (select cond2, VElseThen, VElseElse)
store %sel, addr
br MergeBB
When VThen == VElseThen, the select simplifies to:
select (cond1 || cond2), VThen, VElseElse
This enables loop vectorization of patterns like:
if (a) dst[i]=X; else if (b) dst[i]=X; else dst[i]=Y;
The transform is guarded by a new SimplifyCFGOptions flag
(FoldCondStoreToSelect) and uses TTI::isExpensiveToSpeculativelyExecute
for profitability checks. It is enabled in the function simplification
and vector pass pipelines at -O2 and above.
Fixes #207651
---
.../Transforms/Utils/SimplifyCFGOptions.h | 5 +
llvm/lib/Passes/PassBuilder.cpp | 2 +
llvm/lib/Passes/PassBuilderPipelines.cpp | 6 +-
llvm/lib/Passes/PassRegistry.def | 1 +
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 282 ++++++++++++++++++
5 files changed, 294 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h b/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h
index 2d0f95741077f..368adb4378a96 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h
@@ -33,6 +33,7 @@ struct SimplifyCFGOptions {
bool SimplifyCondBranch = true;
bool SpeculateBlocks = true;
bool SpeculateUnpredictables = false;
+ bool FoldCondStoreToSelect = false;
AssumptionCache *AC = nullptr;
@@ -90,6 +91,10 @@ struct SimplifyCFGOptions {
SpeculateUnpredictables = B;
return *this;
}
+ SimplifyCFGOptions &foldCondStoreToSelect(bool B) {
+ FoldCondStoreToSelect = B;
+ return *this;
+ }
};
} // namespace llvm
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 43c76c74c8930..831f89e543897 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1241,6 +1241,8 @@ Expected<SimplifyCFGOptions> parseSimplifyCFGOptions(StringRef Params) {
Result.sinkCommonInsts(Enable);
} else if (ParamName == "speculate-unpredictables") {
Result.speculateUnpredictables(Enable);
+ } else if (ParamName == "fold-cond-store-to-select") {
+ Result.foldCondStoreToSelect(Enable);
} else if (Enable && ParamName.consume_front("bonus-inst-threshold=")) {
APInt BonusInstThreshold;
if (ParamName.getAsInteger(0, BonusInstThreshold))
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 6b8f3c8806c86..0f243f01de704 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -828,7 +828,8 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
.convertSwitchRangeToICmp(true)
.convertSwitchToArithmetic(true)
.hoistCommonInsts(true)
- .sinkCommonInsts(true)));
+ .sinkCommonInsts(true)
+ .foldCondStoreToSelect(true)));
FPM.addPass(InstCombinePass());
invokePeepholeEPCallbacks(FPM, Level);
@@ -1436,7 +1437,8 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
.convertSwitchToLookupTable(true)
.needCanonicalLoops(false)
.hoistCommonInsts(true)
- .sinkCommonInsts(true)));
+ .sinkCommonInsts(true)
+ .foldCondStoreToSelect(true)));
if (isFullLTOPostLink(LTOPhase)) {
FPM.addPass(SCCPPass());
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 04fa421cebb4b..16e4d51e69ed5 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -717,6 +717,7 @@ FUNCTION_PASS_WITH_PARAMS(
"hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;"
"hoist-loads-stores-with-cond-faulting;no-sink-common-insts;"
"sink-common-insts;no-speculate-unpredictables;speculate-unpredictables;"
+ "no-fold-cond-store-to-select;fold-cond-store-to-select;"
"bonus-inst-threshold=N")
FUNCTION_PASS_WITH_PARAMS(
"speculative-execution", "SpeculativeExecutionPass",
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 98593129cbb7f..374abb4a37a7b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -4292,6 +4292,283 @@ static StoreInst *findUniqueStoreInBlocks(BasicBlock *BB1, BasicBlock *BB2) {
return S;
}
+/// Return true if two pointer-producing values refer to the same address.
+/// Strips pointer casts and matches identical GEPs with the same base and
+/// indices.
+static bool isSameGEPAddress(Value *A, Value *B) {
+ if (A == B)
+ return true;
+ A = A->stripPointerCasts();
+ B = B->stripPointerCasts();
+ if (A == B)
+ return true;
+ auto *GA = dyn_cast<GetElementPtrInst>(A);
+ auto *GB = dyn_cast<GetElementPtrInst>(B);
+ if (!GA || !GB)
+ return false;
+ if (GA->getPointerOperand()->stripPointerCasts() !=
+ GB->getPointerOperand()->stripPointerCasts())
+ return false;
+ if (GA->getSourceElementType() != GB->getSourceElementType())
+ return false;
+ if (GA->getNumIndices() != GB->getNumIndices())
+ return false;
+ return std::equal(GA->idx_begin(), GA->idx_end(), GB->idx_begin());
+}
+
+/// Find the unique simple store in \p BB. Returns nullptr if there is no
+/// store, if there are multiple stores, or if the store is not simple.
+static StoreInst *findUniqueSimpleStoreInBlock(BasicBlock *BB) {
+ StoreInst *Found = nullptr;
+ for (Instruction &I : *BB) {
+ auto *SI = dyn_cast<StoreInst>(&I);
+ if (!SI)
+ continue;
+ if (!SI->isSimple() || Found)
+ return nullptr;
+ Found = SI;
+ }
+ return Found;
+}
+
+/// Return true if none of the non-terminator, non-store instructions in \p BB
+/// are too expensive to speculate. Uses TTI::isExpensiveToSpeculativelyExecute
+/// which is the established LLVM norm for this check (e.g. CodeGenPrepare).
+/// \p FoldedStore is the store being folded and is considered free.
+static bool isBlockCheapToSpeculate(BasicBlock *BB, StoreInst *FoldedStore,
+ const TargetTransformInfo &TTI) {
+ for (Instruction &I : *BB) {
+ if (I.isTerminator())
+ continue;
+ if (&I == FoldedStore)
+ continue;
+ if (TTI.isExpensiveToSpeculativelyExecute(&I))
+ return false;
+ }
+ return true;
+}
+
+/// Fold a triangle+diamond CFG pattern where all three branches store to the
+/// same address into a single select+store in the head block.
+///
+/// HeadBB: br i1 %cond1, ThenBB, ElseBB
+/// ThenBB: store VThen, addr; br MergeBB
+/// ElseBB: [cheap instrs]; br i1 %cond2, ElseThenBB, ElseElseBB
+/// ElseThenBB: store VElseThen, addr; br MergeBB
+/// ElseElseBB: store VElseElse, addr; br MergeBB
+///
+/// Transforms to:
+/// HeadBB: [hoisted ElseBB instrs]
+/// %sel = select cond1, VThen, (select cond2, VElseThen, VElseElse)
+/// store %sel, addr
+/// br MergeBB
+///
+/// The profitability check uses TTI::isExpensiveToSpeculativelyExecute on the
+/// four speculated blocks (ThenBB, ElseBB, ElseThenBB, ElseElseBB) — HeadBB
+/// instructions already execute unconditionally so they are not checked.
+static bool foldCondStoreToSelectImpl(BranchInst *BI, DomTreeUpdater *DTU,
+ const TargetTransformInfo &TTI) {
+ if (!BI->isConditional())
+ return false;
+
+ BasicBlock *HeadBB = BI->getParent();
+ BasicBlock *ThenBB = BI->getSuccessor(0);
+ BasicBlock *ElseBB = BI->getSuccessor(1);
+
+ // ThenBB: single predecessor (HeadBB), one simple store, unconditional
+ // branch to MergeBB.
+ if (ThenBB->getSinglePredecessor() != HeadBB)
+ return false;
+ BranchInst *ThenTerm = dyn_cast<BranchInst>(ThenBB->getTerminator());
+ if (!ThenTerm || ThenTerm->isConditional())
+ return false;
+ BasicBlock *MergeBB = ThenTerm->getSuccessor(0);
+
+ StoreInst *ThenStore = findUniqueSimpleStoreInBlock(ThenBB);
+ if (!ThenStore)
+ return false;
+
+ // ElseBB: single predecessor (HeadBB), conditional branch to two leaf
+ // blocks. May contain cheap instructions before the branch (e.g. icmp).
+ if (ElseBB->getSinglePredecessor() != HeadBB)
+ return false;
+ BranchInst *ElseTerm = dyn_cast<BranchInst>(ElseBB->getTerminator());
+ if (!ElseTerm || !ElseTerm->isConditional())
+ return false;
+
+ // Collect non-terminator instructions in ElseBB to hoist into HeadBB.
+ // They must be side-effect-free and not read/write memory.
+ SmallVector<Instruction *, 4> ElseBBInstsToHoist;
+ for (Instruction &I : *ElseBB) {
+ if (&I == ElseTerm)
+ break;
+ if (I.mayHaveSideEffects() || I.mayReadOrWriteMemory())
+ return false;
+ ElseBBInstsToHoist.push_back(&I);
+ }
+
+ BasicBlock *ElseThenBB = ElseTerm->getSuccessor(0);
+ BasicBlock *ElseElseBB = ElseTerm->getSuccessor(1);
+
+ // Each leaf block: single predecessor (ElseBB), one simple store,
+ // unconditional branch to MergeBB.
+ auto CheckLeafBlock = [&](BasicBlock *BB, StoreInst *&Store) -> bool {
+ if (BB->getSinglePredecessor() != ElseBB)
+ return false;
+ BranchInst *Term = dyn_cast<BranchInst>(BB->getTerminator());
+ if (!Term || Term->isConditional() || Term->getSuccessor(0) != MergeBB)
+ return false;
+ Store = findUniqueSimpleStoreInBlock(BB);
+ return Store != nullptr;
+ };
+
+ StoreInst *ElseThenStore, *ElseElseStore;
+ if (!CheckLeafBlock(ElseThenBB, ElseThenStore) ||
+ !CheckLeafBlock(ElseElseBB, ElseElseStore))
+ return false;
+
+ // All three stores must write to the same logical address and have the same
+ // value type.
+ Value *Addr = ThenStore->getPointerOperand();
+ if (!isSameGEPAddress(Addr, ElseThenStore->getPointerOperand()) ||
+ !isSameGEPAddress(Addr, ElseElseStore->getPointerOperand()))
+ return false;
+
+ Type *StoreTy = ThenStore->getValueOperand()->getType();
+ if (ElseThenStore->getValueOperand()->getType() != StoreTy ||
+ ElseElseStore->getValueOperand()->getType() != StoreTy)
+ return false;
+
+ // Profitability: reject if any of the four speculated blocks contains an
+ // instruction that is expensive to execute speculatively (e.g. fdiv).
+ // HeadBB is excluded — its instructions already run unconditionally.
+ if (!isBlockCheapToSpeculate(ThenBB, ThenStore, TTI) ||
+ !isBlockCheapToSpeculate(ElseBB, nullptr, TTI) ||
+ !isBlockCheapToSpeculate(ElseThenBB, ElseThenStore, TTI) ||
+ !isBlockCheapToSpeculate(ElseElseBB, ElseElseStore, TTI))
+ return false;
+
+ // MergeBB PHI nodes: all three leaf predecessors must supply the same value
+ // (since we're collapsing them into HeadBB).
+ for (PHINode &PHI : MergeBB->phis()) {
+ Value *Val = nullptr;
+ for (BasicBlock *Pred : {ThenBB, ElseThenBB, ElseElseBB}) {
+ int Idx = PHI.getBasicBlockIndex(Pred);
+ if (Idx < 0)
+ continue;
+ Value *InVal = PHI.getIncomingValue(Idx);
+ if (!Val)
+ Val = InVal;
+ else if (Val != InVal)
+ return false;
+ }
+ }
+
+ // All checks passed. Build select+store in HeadBB.
+ errs() << "[foldCondStore] firing in function: "
+ << HeadBB->getParent()->getName() << "\n";
+ IRBuilder<> Builder(BI);
+
+ // Hoist cheap ElseBB instructions (e.g. icmp for cond2) into HeadBB.
+ for (Instruction *I : ElseBBInstsToHoist)
+ I->moveBefore(BI->getIterator());
+
+ // Helper: if a value is defined inside one of the blocks being removed,
+ // hoist it into HeadBB (it must be a side-effect-free instruction whose
+ // operands are all available in HeadBB after the ElseBB hoist above).
+ auto IsInRemovedBlock = [&](Value *V) -> bool {
+ auto *I = dyn_cast<Instruction>(V);
+ if (!I)
+ return false;
+ BasicBlock *DefBB = I->getParent();
+ return DefBB == ThenBB || DefBB == ElseBB || DefBB == ElseThenBB ||
+ DefBB == ElseElseBB;
+ };
+ auto HoistIfNeeded = [&](Value *V) {
+ if (IsInRemovedBlock(V))
+ cast<Instruction>(V)->moveBefore(BI->getIterator());
+ };
+
+ // Hoist the store address and store values if they live in removed blocks.
+ HoistIfNeeded(Addr);
+ Value *VThen = ThenStore->getValueOperand();
+ Value *VElseThen = ElseThenStore->getValueOperand();
+ Value *VElseElse = ElseElseStore->getValueOperand();
+ HoistIfNeeded(VThen);
+ HoistIfNeeded(VElseThen);
+ HoistIfNeeded(VElseElse);
+
+ Value *Cond1 = BI->getCondition();
+ Value *Cond2 = ElseTerm->getCondition();
+
+ Value *SelVal;
+ if (VThen == VElseThen) {
+ // Simplify: select (cond1 || cond2), VThen, VElseElse
+ Value *CombinedCond = Builder.CreateOr(Cond1, Cond2, "cond.or");
+ SelVal = Builder.CreateSelect(CombinedCond, VThen, VElseElse, "store.sel");
+ } else {
+ // General: select cond1, VThen, (select cond2, VElseThen, VElseElse)
+ Value *InnerSel =
+ Builder.CreateSelect(Cond2, VElseThen, VElseElse, "store.sel.inner");
+ SelVal = Builder.CreateSelect(Cond1, VThen, InnerSel, "store.sel");
+ }
+
+ StoreInst *NewStore = Builder.CreateStore(SelVal, Addr);
+ NewStore->setAlignment(
+ std::min({ThenStore->getAlign(), ElseThenStore->getAlign(),
+ ElseElseStore->getAlign()}));
+
+ // Preserve metadata identical across all three original stores.
+ auto CopyIfEqual = [&](unsigned Kind) {
+ MDNode *MD = ThenStore->getMetadata(Kind);
+ if (MD && MD == ElseThenStore->getMetadata(Kind) &&
+ MD == ElseElseStore->getMetadata(Kind))
+ NewStore->setMetadata(Kind, MD);
+ };
+ CopyIfEqual(LLVMContext::MD_tbaa);
+ CopyIfEqual(LLVMContext::MD_alias_scope);
+ CopyIfEqual(LLVMContext::MD_noalias);
+ CopyIfEqual(LLVMContext::MD_nontemporal);
+ CopyIfEqual(LLVMContext::MD_access_group);
+
+ // Replace HeadBB's conditional branch with an unconditional branch to
+ // MergeBB.
+ Builder.CreateBr(MergeBB);
+ BI->eraseFromParent();
+
+ // Add HeadBB as the new incoming block for any MergeBB PHIs that had
+ // incoming values from the leaf blocks.
+ for (PHINode &PHI : MergeBB->phis()) {
+ Value *InVal = nullptr;
+ for (BasicBlock *Pred : {ThenBB, ElseThenBB, ElseElseBB}) {
+ int Idx = PHI.getBasicBlockIndex(Pred);
+ if (Idx < 0)
+ continue;
+ if (!InVal)
+ InVal = PHI.getIncomingValue(Idx);
+ }
+ if (InVal)
+ PHI.addIncoming(InVal, HeadBB);
+ }
+
+ // Detach the four now-unreachable blocks: removes their PHI contributions
+ // from successors and replaces their terminators with 'unreachable'.
+ // We do NOT erase them here — iterativelySimplifyCFG will see pred_empty
+ // and safely delete each one after BBIt has moved past it.
+ SmallVector<BasicBlock *, 4> DeadBlocks = {ThenBB, ElseBB, ElseThenBB,
+ ElseElseBB};
+ SmallVector<DominatorTree::UpdateType, 8> Updates;
+ detachDeadBlocks(DeadBlocks, DTU ? &Updates : nullptr,
+ /*KeepOneInputPHIs=*/false);
+
+ if (DTU) {
+ Updates.push_back({DominatorTree::Insert, HeadBB, MergeBB});
+ DTU->applyUpdates(Updates);
+ }
+
+ return true;
+}
+
static Value *ensureValueAvailableInSuccessor(Value *V, BasicBlock *BB,
Value *AlternativeV = nullptr) {
// PHI is going to be a PHI node that allows the value V that is defined in
@@ -8791,6 +9068,11 @@ bool SimplifyCFGOpt::simplifyCondBranch(CondBrInst *BI, IRBuilder<> &Builder) {
if (mergeNestedCondBranch(BI, DTU))
return requestResimplify();
+ // Fold triangle+diamond pattern where three branches all store to the same
+ // address into a select+store in the head block, enabling vectorization.
+ if (Options.FoldCondStoreToSelect && foldCondStoreToSelectImpl(BI, DTU, TTI))
+ return requestResimplify();
+
return false;
}
>From 31ad707a22914273529a42599de11500c3dca836 Mon Sep 17 00:00:00 2001
From: Karthika Devi C <kartc at qti.qualcomm.com>
Date: Mon, 6 Jul 2026 00:14:25 -0700
Subject: [PATCH 2/3] [SimplifyCFG] Fix deprecated BranchInst usage in
foldCondStoreToSelect
Replace deprecated BranchInst with CondBrInst/UncondBrInst in
foldCondStoreToSelectImpl to fix -Werror,-Wdeprecated-declarations
build failures.
- Change function parameter from BranchInst* to CondBrInst*
- Remove redundant isConditional() check (CondBrInst is always conditional)
- Use UncondBrInst for unconditional branch terminators (ThenTerm, leaf blocks)
- Use CondBrInst for ElseTerm conditional branch
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 374abb4a37a7b..adc361a44a951 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -4366,11 +4366,8 @@ static bool isBlockCheapToSpeculate(BasicBlock *BB, StoreInst *FoldedStore,
/// The profitability check uses TTI::isExpensiveToSpeculativelyExecute on the
/// four speculated blocks (ThenBB, ElseBB, ElseThenBB, ElseElseBB) — HeadBB
/// instructions already execute unconditionally so they are not checked.
-static bool foldCondStoreToSelectImpl(BranchInst *BI, DomTreeUpdater *DTU,
+static bool foldCondStoreToSelectImpl(CondBrInst *BI, DomTreeUpdater *DTU,
const TargetTransformInfo &TTI) {
- if (!BI->isConditional())
- return false;
-
BasicBlock *HeadBB = BI->getParent();
BasicBlock *ThenBB = BI->getSuccessor(0);
BasicBlock *ElseBB = BI->getSuccessor(1);
@@ -4379,8 +4376,8 @@ static bool foldCondStoreToSelectImpl(BranchInst *BI, DomTreeUpdater *DTU,
// branch to MergeBB.
if (ThenBB->getSinglePredecessor() != HeadBB)
return false;
- BranchInst *ThenTerm = dyn_cast<BranchInst>(ThenBB->getTerminator());
- if (!ThenTerm || ThenTerm->isConditional())
+ UncondBrInst *ThenTerm = dyn_cast<UncondBrInst>(ThenBB->getTerminator());
+ if (!ThenTerm)
return false;
BasicBlock *MergeBB = ThenTerm->getSuccessor(0);
@@ -4392,8 +4389,8 @@ static bool foldCondStoreToSelectImpl(BranchInst *BI, DomTreeUpdater *DTU,
// blocks. May contain cheap instructions before the branch (e.g. icmp).
if (ElseBB->getSinglePredecessor() != HeadBB)
return false;
- BranchInst *ElseTerm = dyn_cast<BranchInst>(ElseBB->getTerminator());
- if (!ElseTerm || !ElseTerm->isConditional())
+ CondBrInst *ElseTerm = dyn_cast<CondBrInst>(ElseBB->getTerminator());
+ if (!ElseTerm)
return false;
// Collect non-terminator instructions in ElseBB to hoist into HeadBB.
@@ -4415,8 +4412,8 @@ static bool foldCondStoreToSelectImpl(BranchInst *BI, DomTreeUpdater *DTU,
auto CheckLeafBlock = [&](BasicBlock *BB, StoreInst *&Store) -> bool {
if (BB->getSinglePredecessor() != ElseBB)
return false;
- BranchInst *Term = dyn_cast<BranchInst>(BB->getTerminator());
- if (!Term || Term->isConditional() || Term->getSuccessor(0) != MergeBB)
+ UncondBrInst *Term = dyn_cast<UncondBrInst>(BB->getTerminator());
+ if (!Term || Term->getSuccessor(0) != MergeBB)
return false;
Store = findUniqueSimpleStoreInBlock(BB);
return Store != nullptr;
>From 2b90d08bcec0547456128231028802fb6c10f012 Mon Sep 17 00:00:00 2001
From: Karthika Devi C <kartc at qti.qualcomm.com>
Date: Mon, 6 Jul 2026 05:08:35 -0700
Subject: [PATCH 3/3] [SimplifyCFG] Fix crash when folded blocks contain PHI
nodes
Bail out of foldCondStoreToSelectImpl if any of the four blocks
(ThenBB, ElseBB, ElseThenBB, ElseElseBB) contain PHI nodes.
These are single-predecessor blocks so PHIs are degenerate, but
attempting to hoist or detach them causes a verifier failure:
"PHINode should have one entry for each predecessor of its parent
basic block".
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index adc361a44a951..dbaef4a3dd21b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -4424,6 +4424,13 @@ static bool foldCondStoreToSelectImpl(CondBrInst *BI, DomTreeUpdater *DTU,
!CheckLeafBlock(ElseElseBB, ElseElseStore))
return false;
+ // Bail out if any of the blocks contain PHI nodes. These are single-
+ // predecessor blocks so PHIs here are degenerate and not expected in
+ // well-optimized IR.
+ if (!ThenBB->phis().empty() || !ElseBB->phis().empty() ||
+ !ElseThenBB->phis().empty() || !ElseElseBB->phis().empty())
+ return false;
+
// All three stores must write to the same logical address and have the same
// value type.
Value *Addr = ThenStore->getPointerOperand();
More information about the llvm-commits
mailing list