[llvm] d200bd1 - Reland "[SimplifyCFG] Hoist common instructions on switch" (#67077)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 22 03:30:03 PDT 2023
Author: DianQK
Date: 2023-09-22T18:29:59+08:00
New Revision: d200bd1a7d355f220816408f2c7443da6f28588e
URL: https://github.com/llvm/llvm-project/commit/d200bd1a7d355f220816408f2c7443da6f28588e
DIFF: https://github.com/llvm/llvm-project/commit/d200bd1a7d355f220816408f2c7443da6f28588e.diff
LOG: Reland "[SimplifyCFG] Hoist common instructions on switch" (#67077)
This relands commit 96ea48ff5dcba46af350f5300eafd7f7394ba606.
Added:
Modified:
llvm/lib/Transforms/Utils/SimplifyCFG.cpp
llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll
llvm/test/Transforms/SimplifyCFG/HoistCode.ll
llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll
llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 14cabd275d5b111..35fead111aa9666 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -271,7 +271,10 @@ class SimplifyCFGOpt {
bool tryToSimplifyUncondBranchWithICmpInIt(ICmpInst *ICI,
IRBuilder<> &Builder);
- bool HoistThenElseCodeToIf(BranchInst *BI, bool EqTermsOnly);
+ bool hoistCommonCodeFromSuccessors(BasicBlock *BB, bool EqTermsOnly);
+ bool hoistSuccIdenticalTerminatorToSwitchOrIf(
+ Instruction *TI, Instruction *I1,
+ SmallVectorImpl<Instruction *> &OtherSuccTIs);
bool SpeculativelyExecuteBB(BranchInst *BI, BasicBlock *ThenBB);
bool SimplifyTerminatorOnSelect(Instruction *OldTerm, Value *Cond,
BasicBlock *TrueBB, BasicBlock *FalseBB,
@@ -1408,8 +1411,9 @@ bool SimplifyCFGOpt::FoldValueComparisonIntoPredecessors(Instruction *TI,
}
// If we would need to insert a select that uses the value of this invoke
-// (comments in HoistThenElseCodeToIf explain why we would need to do this), we
-// can't hoist the invoke, as there is nowhere to put the select in this case.
+// (comments in hoistSuccIdenticalTerminatorToSwitchOrIf explain why we would
+// need to do this), we can't hoist the invoke, as there is nowhere to put the
+// select in this case.
static bool isSafeToHoistInvoke(BasicBlock *BB1, BasicBlock *BB2,
Instruction *I1, Instruction *I2) {
for (BasicBlock *Succ : successors(BB1)) {
@@ -1424,9 +1428,9 @@ static bool isSafeToHoistInvoke(BasicBlock *BB1, BasicBlock *BB2,
return true;
}
-// Get interesting characteristics of instructions that `HoistThenElseCodeToIf`
-// didn't hoist. They restrict what kind of instructions can be reordered
-// across.
+// Get interesting characteristics of instructions that
+// `hoistCommonCodeFromSuccessors` didn't hoist. They restrict what kind of
+// instructions can be reordered across.
enum SkipFlags {
SkipReadMem = 1,
SkipSideEffect = 2,
@@ -1484,7 +1488,7 @@ static bool isSafeToHoistInstr(Instruction *I, unsigned Flags) {
static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified = false);
-/// Helper function for HoistThenElseCodeToIf. Return true if identical
+/// Helper function for hoistCommonCodeFromSuccessors. Return true if identical
/// instructions \p I1 and \p I2 can and should be hoisted.
static bool shouldHoistCommonInstructions(Instruction *I1, Instruction *I2,
const TargetTransformInfo &TTI) {
@@ -1515,62 +1519,51 @@ static bool shouldHoistCommonInstructions(Instruction *I1, Instruction *I2,
return true;
}
-/// Given a conditional branch that goes to BB1 and BB2, hoist any common code
-/// in the two blocks up into the branch block. The caller of this function
-/// guarantees that BI's block dominates BB1 and BB2. If EqTermsOnly is given,
-/// only perform hoisting in case both blocks only contain a terminator. In that
-/// case, only the original BI will be replaced and selects for PHIs are added.
-bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI, bool EqTermsOnly) {
+/// Hoist any common code in the successor blocks up into the block. This
+/// function guarantees that BB dominates all successors. If EqTermsOnly is
+/// given, only perform hoisting in case both blocks only contain a terminator.
+/// In that case, only the original BI will be replaced and selects for PHIs are
+/// added.
+bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(BasicBlock *BB,
+ bool EqTermsOnly) {
// This does very trivial matching, with limited scanning, to find identical
- // instructions in the two blocks. In particular, we don't want to get into
- // O(M*N) situations here where M and N are the sizes of BB1 and BB2. As
+ // instructions in the two blocks. In particular, we don't want to get into
+ // O(N1*N2*...) situations here where Ni are the sizes of these successors. As
// such, we currently just scan for obviously identical instructions in an
// identical order, possibly separated by the same number of non-identical
// instructions.
- BasicBlock *BB1 = BI->getSuccessor(0); // The true destination.
- BasicBlock *BB2 = BI->getSuccessor(1); // The false destination
+ unsigned int SuccSize = succ_size(BB);
+ if (SuccSize < 2)
+ return false;
// If either of the blocks has it's address taken, then we can't do this fold,
// because the code we'd hoist would no longer run when we jump into the block
// by it's address.
- if (BB1->hasAddressTaken() || BB2->hasAddressTaken())
- return false;
+ for (auto *Succ : successors(BB))
+ if (Succ->hasAddressTaken() || !Succ->getSinglePredecessor())
+ return false;
- BasicBlock::iterator BB1_Itr = BB1->begin();
- BasicBlock::iterator BB2_Itr = BB2->begin();
+ auto *TI = BB->getTerminator();
- Instruction *I1 = &*BB1_Itr++, *I2 = &*BB2_Itr++;
- // Skip debug info if it is not identical.
- DbgInfoIntrinsic *DBI1 = dyn_cast<DbgInfoIntrinsic>(I1);
- DbgInfoIntrinsic *DBI2 = dyn_cast<DbgInfoIntrinsic>(I2);
- if (!DBI1 || !DBI2 || !DBI1->isIdenticalToWhenDefined(DBI2)) {
- while (isa<DbgInfoIntrinsic>(I1))
- I1 = &*BB1_Itr++;
- while (isa<DbgInfoIntrinsic>(I2))
- I2 = &*BB2_Itr++;
+ // The second of pair is a SkipFlags bitmask.
+ using SuccIterPair = std::pair<BasicBlock::iterator, unsigned>;
+ SmallVector<SuccIterPair, 8> SuccIterPairs;
+ for (auto *Succ : successors(BB)) {
+ BasicBlock::iterator SuccItr = Succ->begin();
+ if (isa<PHINode>(*SuccItr))
+ return false;
+ SuccIterPairs.push_back(SuccIterPair(SuccItr, 0));
}
- if (isa<PHINode>(I1))
- return false;
-
- BasicBlock *BIParent = BI->getParent();
-
- bool Changed = false;
-
- auto _ = make_scope_exit([&]() {
- if (Changed)
- ++NumHoistCommonCode;
- });
// Check if only hoisting terminators is allowed. This does not add new
// instructions to the hoist location.
if (EqTermsOnly) {
// Skip any debug intrinsics, as they are free to hoist.
- auto *I1NonDbg = &*skipDebugIntrinsics(I1->getIterator());
- auto *I2NonDbg = &*skipDebugIntrinsics(I2->getIterator());
- if (!I1NonDbg->isIdenticalToWhenDefined(I2NonDbg))
- return false;
- if (!I1NonDbg->isTerminator())
- return false;
+ for (auto &SuccIter : make_first_range(SuccIterPairs)) {
+ auto *INonDbg = &*skipDebugIntrinsics(SuccIter);
+ if (!INonDbg->isTerminator())
+ return false;
+ }
// Now we know that we only need to hoist debug intrinsics and the
// terminator. Let the loop below handle those 2 cases.
}
@@ -1579,154 +1572,235 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI, bool EqTermsOnly) {
// many instructions we skip, serving as a compilation time control as well as
// preventing excessive increase of life ranges.
unsigned NumSkipped = 0;
+ // If we find an unreachable instruction at the beginning of a basic block, we
+ // can still hoist instructions from the rest of the basic blocks.
+ if (SuccIterPairs.size() > 2) {
+ erase_if(SuccIterPairs,
+ [](const auto &Pair) { return isa<UnreachableInst>(Pair.first); });
+ if (SuccIterPairs.size() < 2)
+ return false;
+ }
- // Record any skipped instuctions that may read memory, write memory or have
- // side effects, or have implicit control flow.
- unsigned SkipFlagsBB1 = 0;
- unsigned SkipFlagsBB2 = 0;
+ bool Changed = false;
for (;;) {
+ auto *SuccIterPairBegin = SuccIterPairs.begin();
+ auto &BB1ItrPair = *SuccIterPairBegin++;
+ auto OtherSuccIterPairRange =
+ iterator_range(SuccIterPairBegin, SuccIterPairs.end());
+ auto OtherSuccIterRange = make_first_range(OtherSuccIterPairRange);
+
+ Instruction *I1 = &*BB1ItrPair.first;
+ auto *BB1 = I1->getParent();
+
+ // Skip debug info if it is not identical.
+ bool AllDbgInstsAreIdentical = all_of(OtherSuccIterRange, [I1](auto &Iter) {
+ Instruction *I2 = &*Iter;
+ return I1->isIdenticalToWhenDefined(I2);
+ });
+ if (!AllDbgInstsAreIdentical) {
+ while (isa<DbgInfoIntrinsic>(I1))
+ I1 = &*++BB1ItrPair.first;
+ for (auto &SuccIter : OtherSuccIterRange) {
+ Instruction *I2 = &*SuccIter;
+ while (isa<DbgInfoIntrinsic>(I2))
+ I2 = &*++SuccIter;
+ }
+ }
+
+ bool AllInstsAreIdentical = true;
+ bool HasTerminator = I1->isTerminator();
+ for (auto &SuccIter : OtherSuccIterRange) {
+ Instruction *I2 = &*SuccIter;
+ HasTerminator |= I2->isTerminator();
+ if (AllInstsAreIdentical && !I1->isIdenticalToWhenDefined(I2))
+ AllInstsAreIdentical = false;
+ }
+
// If we are hoisting the terminator instruction, don't move one (making a
// broken BB), instead clone it, and remove BI.
- if (I1->isTerminator() || I2->isTerminator()) {
+ if (HasTerminator) {
+ // Even if BB, which contains only one unreachable instruction, is ignored
+ // at the beginning of the loop, we can hoist the terminator instruction.
// If any instructions remain in the block, we cannot hoist terminators.
- if (NumSkipped || !I1->isIdenticalToWhenDefined(I2))
+ if (NumSkipped || !AllInstsAreIdentical)
return Changed;
- goto HoistTerminator;
+ SmallVector<Instruction *, 8> Insts;
+ for (auto &SuccIter : OtherSuccIterRange)
+ Insts.push_back(&*SuccIter);
+ return hoistSuccIdenticalTerminatorToSwitchOrIf(TI, I1, Insts) || Changed;
}
- if (I1->isIdenticalToWhenDefined(I2) &&
- // Even if the instructions are identical, it may not be safe to hoist
- // them if we have skipped over instructions with side effects or their
- // operands weren't hoisted.
- isSafeToHoistInstr(I1, SkipFlagsBB1) &&
- isSafeToHoistInstr(I2, SkipFlagsBB2) &&
- shouldHoistCommonInstructions(I1, I2, TTI)) {
- if (isa<DbgInfoIntrinsic>(I1) || isa<DbgInfoIntrinsic>(I2)) {
- assert(isa<DbgInfoIntrinsic>(I1) && isa<DbgInfoIntrinsic>(I2));
+ if (AllInstsAreIdentical) {
+ unsigned SkipFlagsBB1 = BB1ItrPair.second;
+ AllInstsAreIdentical =
+ isSafeToHoistInstr(I1, SkipFlagsBB1) &&
+ all_of(OtherSuccIterPairRange, [=](const auto &Pair) {
+ Instruction *I2 = &*Pair.first;
+ unsigned SkipFlagsBB2 = Pair.second;
+ // Even if the instructions are identical, it may not
+ // be safe to hoist them if we have skipped over
+ // instructions with side effects or their operands
+ // weren't hoisted.
+ return isSafeToHoistInstr(I2, SkipFlagsBB2) &&
+ shouldHoistCommonInstructions(I1, I2, TTI);
+ });
+ }
+
+ if (AllInstsAreIdentical) {
+ BB1ItrPair.first++;
+ if (isa<DbgInfoIntrinsic>(I1)) {
// The debug location is an integral part of a debug info intrinsic
// and can't be separated from it or replaced. Instead of attempting
// to merge locations, simply hoist both copies of the intrinsic.
- I1->moveBeforePreserving(BI);
- I2->moveBeforePreserving(BI);
- Changed = true;
+ I1->moveBeforePreserving(TI);
+ for (auto &SuccIter : OtherSuccIterRange) {
+ auto *I2 = &*SuccIter++;
+ assert(isa<DbgInfoIntrinsic>(I2));
+ I2->moveBeforePreserving(TI);
+ }
} else {
// For a normal instruction, we just move one to right before the
// branch, then replace all uses of the other with the first. Finally,
// we remove the now redundant second instruction.
- I1->moveBeforePreserving(BI);
- if (!I2->use_empty())
- I2->replaceAllUsesWith(I1);
- I1->andIRFlags(I2);
- combineMetadataForCSE(I1, I2, true);
-
- // I1 and I2 are being combined into a single instruction. Its debug
- // location is the merged locations of the original instructions.
- I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
-
- I2->eraseFromParent();
+ I1->moveBeforePreserving(TI);
+ BB->splice(TI->getIterator(), BB1, I1->getIterator());
+ for (auto &SuccIter : OtherSuccIterRange) {
+ Instruction *I2 = &*SuccIter++;
+ assert(I2 != I1);
+ if (!I2->use_empty())
+ I2->replaceAllUsesWith(I1);
+ I1->andIRFlags(I2);
+ combineMetadataForCSE(I1, I2, true);
+ // I1 and I2 are being combined into a single instruction. Its debug
+ // location is the merged locations of the original instructions.
+ I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
+ I2->eraseFromParent();
+ }
}
+ if (!Changed)
+ NumHoistCommonCode += SuccIterPairs.size();
Changed = true;
- ++NumHoistCommonInstrs;
+ NumHoistCommonInstrs += SuccIterPairs.size();
} else {
if (NumSkipped >= HoistCommonSkipLimit)
return Changed;
// We are about to skip over a pair of non-identical instructions. Record
// if any have characteristics that would prevent reordering instructions
// across them.
- SkipFlagsBB1 |= skippedInstrFlags(I1);
- SkipFlagsBB2 |= skippedInstrFlags(I2);
+ for (auto &SuccIterPair : SuccIterPairs) {
+ Instruction *I = &*SuccIterPair.first++;
+ SuccIterPair.second |= skippedInstrFlags(I);
+ }
++NumSkipped;
}
-
- I1 = &*BB1_Itr++;
- I2 = &*BB2_Itr++;
- // Skip debug info if it is not identical.
- DbgInfoIntrinsic *DBI1 = dyn_cast<DbgInfoIntrinsic>(I1);
- DbgInfoIntrinsic *DBI2 = dyn_cast<DbgInfoIntrinsic>(I2);
- if (!DBI1 || !DBI2 || !DBI1->isIdenticalToWhenDefined(DBI2)) {
- while (isa<DbgInfoIntrinsic>(I1))
- I1 = &*BB1_Itr++;
- while (isa<DbgInfoIntrinsic>(I2))
- I2 = &*BB2_Itr++;
- }
}
+}
- return Changed;
+bool SimplifyCFGOpt::hoistSuccIdenticalTerminatorToSwitchOrIf(
+ Instruction *TI, Instruction *I1,
+ SmallVectorImpl<Instruction *> &OtherSuccTIs) {
+
+ auto *BI = dyn_cast<BranchInst>(TI);
+
+ bool Changed = false;
+ BasicBlock *TIParent = TI->getParent();
+ BasicBlock *BB1 = I1->getParent();
-HoistTerminator:
- // It may not be possible to hoist an invoke.
+ // Use only for an if statement.
+ auto *I2 = *OtherSuccTIs.begin();
+ auto *BB2 = I2->getParent();
+ if (BI) {
+ assert(OtherSuccTIs.size() == 1);
+ assert(BI->getSuccessor(0) == I1->getParent());
+ assert(BI->getSuccessor(1) == I2->getParent());
+ }
+
+ // In the case of an if statement, we try to hoist an invoke.
// FIXME: Can we define a safety predicate for CallBr?
- if (isa<InvokeInst>(I1) && !isSafeToHoistInvoke(BB1, BB2, I1, I2))
- return Changed;
+ // FIXME: Test case llvm/test/Transforms/SimplifyCFG/2009-06-15-InvokeCrash.ll
+ // removed in 4c923b3b3fd0ac1edebf0603265ca3ba51724937 commit?
+ if (isa<InvokeInst>(I1) && (!BI || !isSafeToHoistInvoke(BB1, BB2, I1, I2)))
+ return false;
// TODO: callbr hoisting currently disabled pending further study.
if (isa<CallBrInst>(I1))
- return Changed;
+ return false;
for (BasicBlock *Succ : successors(BB1)) {
for (PHINode &PN : Succ->phis()) {
Value *BB1V = PN.getIncomingValueForBlock(BB1);
- Value *BB2V = PN.getIncomingValueForBlock(BB2);
- if (BB1V == BB2V)
- continue;
+ for (Instruction *OtherSuccTI : OtherSuccTIs) {
+ Value *BB2V = PN.getIncomingValueForBlock(OtherSuccTI->getParent());
+ if (BB1V == BB2V)
+ continue;
- // Check for passingValueIsAlwaysUndefined here because we would rather
- // eliminate undefined control flow then converting it to a select.
- if (passingValueIsAlwaysUndefined(BB1V, &PN) ||
- passingValueIsAlwaysUndefined(BB2V, &PN))
- return Changed;
+ // In the case of an if statement, check for
+ // passingValueIsAlwaysUndefined here because we would rather eliminate
+ // undefined control flow then converting it to a select.
+ if (!BI || passingValueIsAlwaysUndefined(BB1V, &PN) ||
+ passingValueIsAlwaysUndefined(BB2V, &PN))
+ return false;
+ }
}
}
// Okay, it is safe to hoist the terminator.
Instruction *NT = I1->clone();
- NT->insertInto(BIParent, BI->getIterator());
+ NT->insertInto(TIParent, TI->getIterator());
if (!NT->getType()->isVoidTy()) {
I1->replaceAllUsesWith(NT);
- I2->replaceAllUsesWith(NT);
+ for (Instruction *OtherSuccTI : OtherSuccTIs)
+ OtherSuccTI->replaceAllUsesWith(NT);
NT->takeName(I1);
}
Changed = true;
- ++NumHoistCommonInstrs;
+ NumHoistCommonInstrs += OtherSuccTIs.size() + 1;
// Ensure terminator gets a debug location, even an unknown one, in case
// it involves inlinable calls.
- NT->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
+ SmallVector<DILocation *, 4> Locs;
+ Locs.push_back(I1->getDebugLoc());
+ for (auto *OtherSuccTI : OtherSuccTIs)
+ Locs.push_back(OtherSuccTI->getDebugLoc());
+ NT->setDebugLoc(DILocation::getMergedLocations(Locs));
// PHIs created below will adopt NT's merged DebugLoc.
IRBuilder<NoFolder> Builder(NT);
- // Hoisting one of the terminators from our successor is a great thing.
- // Unfortunately, the successors of the if/else blocks may have PHI nodes in
- // them. If they do, all PHI entries for BB1/BB2 must agree for all PHI
- // nodes, so we insert select instruction to compute the final result.
- std::map<std::pair<Value *, Value *>, SelectInst *> InsertedSelects;
- for (BasicBlock *Succ : successors(BB1)) {
- for (PHINode &PN : Succ->phis()) {
- Value *BB1V = PN.getIncomingValueForBlock(BB1);
- Value *BB2V = PN.getIncomingValueForBlock(BB2);
- if (BB1V == BB2V)
- continue;
+ // In the case of an if statement, hoisting one of the terminators from our
+ // successor is a great thing. Unfortunately, the successors of the if/else
+ // blocks may have PHI nodes in them. If they do, all PHI entries for BB1/BB2
+ // must agree for all PHI nodes, so we insert select instruction to compute
+ // the final result.
+ if (BI) {
+ std::map<std::pair<Value *, Value *>, SelectInst *> InsertedSelects;
+ for (BasicBlock *Succ : successors(BB1)) {
+ for (PHINode &PN : Succ->phis()) {
+ Value *BB1V = PN.getIncomingValueForBlock(BB1);
+ Value *BB2V = PN.getIncomingValueForBlock(BB2);
+ if (BB1V == BB2V)
+ continue;
- // These values do not agree. Insert a select instruction before NT
- // that determines the right value.
- SelectInst *&SI = InsertedSelects[std::make_pair(BB1V, BB2V)];
- if (!SI) {
- // Propagate fast-math-flags from phi node to its replacement select.
- IRBuilder<>::FastMathFlagGuard FMFGuard(Builder);
- if (isa<FPMathOperator>(PN))
- Builder.setFastMathFlags(PN.getFastMathFlags());
-
- SI = cast<SelectInst>(
- Builder.CreateSelect(BI->getCondition(), BB1V, BB2V,
- BB1V->getName() + "." + BB2V->getName(), BI));
- }
+ // These values do not agree. Insert a select instruction before NT
+ // that determines the right value.
+ SelectInst *&SI = InsertedSelects[std::make_pair(BB1V, BB2V)];
+ if (!SI) {
+ // Propagate fast-math-flags from phi node to its replacement select.
+ IRBuilder<>::FastMathFlagGuard FMFGuard(Builder);
+ if (isa<FPMathOperator>(PN))
+ Builder.setFastMathFlags(PN.getFastMathFlags());
+
+ SI = cast<SelectInst>(Builder.CreateSelect(
+ BI->getCondition(), BB1V, BB2V,
+ BB1V->getName() + "." + BB2V->getName(), BI));
+ }
- // Make the PHI node use the select for all incoming values for BB1/BB2
- for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i)
- if (PN.getIncomingBlock(i) == BB1 || PN.getIncomingBlock(i) == BB2)
- PN.setIncomingValue(i, SI);
+ // Make the PHI node use the select for all incoming values for BB1/BB2
+ for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i)
+ if (PN.getIncomingBlock(i) == BB1 || PN.getIncomingBlock(i) == BB2)
+ PN.setIncomingValue(i, SI);
+ }
}
}
@@ -1734,16 +1808,16 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI, bool EqTermsOnly) {
// Update any PHI nodes in our new successors.
for (BasicBlock *Succ : successors(BB1)) {
- AddPredecessorToBlock(Succ, BIParent, BB1);
+ AddPredecessorToBlock(Succ, TIParent, BB1);
if (DTU)
- Updates.push_back({DominatorTree::Insert, BIParent, Succ});
+ Updates.push_back({DominatorTree::Insert, TIParent, Succ});
}
if (DTU)
- for (BasicBlock *Succ : successors(BI))
- Updates.push_back({DominatorTree::Delete, BIParent, Succ});
+ for (BasicBlock *Succ : successors(TI))
+ Updates.push_back({DominatorTree::Delete, TIParent, Succ});
- EraseTerminatorAndDCECond(BI);
+ EraseTerminatorAndDCECond(TI);
if (DTU)
DTU->applyUpdates(Updates);
return Changed;
@@ -2777,8 +2851,8 @@ static bool validateAndCostRequiredSelects(BasicBlock *BB, BasicBlock *ThenBB,
Value *OrigV = PN.getIncomingValueForBlock(BB);
Value *ThenV = PN.getIncomingValueForBlock(ThenBB);
- // FIXME: Try to remove some of the duplication with HoistThenElseCodeToIf.
- // Skip PHIs which are trivial.
+ // FIXME: Try to remove some of the duplication with
+ // hoistCommonCodeFromSuccessors. Skip PHIs which are trivial.
if (ThenV == OrigV)
continue;
@@ -6815,6 +6889,10 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
if (ReduceSwitchRange(SI, Builder, DL, TTI))
return requestResimplify();
+ if (HoistCommon &&
+ hoistCommonCodeFromSuccessors(SI->getParent(), !Options.HoistCommonInsts))
+ return requestResimplify();
+
return false;
}
@@ -7081,7 +7159,8 @@ bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) {
// can hoist it up to the branching block.
if (BI->getSuccessor(0)->getSinglePredecessor()) {
if (BI->getSuccessor(1)->getSinglePredecessor()) {
- if (HoistCommon && HoistThenElseCodeToIf(BI, !Options.HoistCommonInsts))
+ if (HoistCommon && hoistCommonCodeFromSuccessors(
+ BI->getParent(), !Options.HoistCommonInsts))
return requestResimplify();
} else {
// If Successor #1 has multiple preds, we may be able to conditionally
diff --git a/llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll b/llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll
index 0dfb5764f55b8cc..15657730c2cdcb6 100644
--- a/llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll
+++ b/llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll
@@ -70,7 +70,7 @@ entry:
i64 4, label %sw.bb4
]
sw.bb0:
- call void asm sideeffect "", ""()
+ call void asm sideeffect "nop", ""()
ret void
sw.bb1:
call void asm sideeffect "", ""()
diff --git a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll
index 0b634fac8c640ae..4088ecfc818982f 100644
--- a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll
+++ b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll
@@ -19,21 +19,9 @@ F: ; preds = %0
define void @foo_switch(i64 %C, ptr %P) {
; CHECK-LABEL: @foo_switch(
-; CHECK-NEXT: switch i64 [[C:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: common.ret:
-; CHECK-NEXT: ret void
-; CHECK: bb0:
+; CHECK-NEXT: common.ret:
; CHECK-NEXT: store i32 7, ptr [[P:%.*]], align 4
-; CHECK-NEXT: br label [[COMMON_RET:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: store i32 7, ptr [[P]], align 4
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: bb2:
-; CHECK-NEXT: store i32 7, ptr [[P]], align 4
-; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK-NEXT: ret void
;
switch i64 %C, label %bb0 [
i64 1, label %bb1
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll
index 8cb6339713d6f6a..27c3a97375bc5af 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll
@@ -4,25 +4,8 @@
define i1 @common_instr_with_unreachable(i64 %a, i64 %b, i64 %c) {
; CHECK-LABEL: @common_instr_with_unreachable(
; CHECK-NEXT: start:
-; CHECK-NEXT: switch i64 [[A:%.*]], label [[UNREACHABLE:%.*]] [
-; CHECK-NEXT: i64 0, label [[BB0:%.*]]
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: unreachable:
-; CHECK-NEXT: unreachable
-; CHECK: bb0:
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
-; CHECK-NEXT: br label [[EXIT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
-; CHECK-NEXT: br label [[EXIT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
-; CHECK-NEXT: br label [[EXIT]]
-; CHECK: exit:
-; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
-; CHECK-NEXT: ret i1 [[RESULT]]
+; CHECK-NEXT: ret i1 [[TMP0]]
;
start:
switch i64 %a, label %unreachable [
@@ -54,46 +37,126 @@ exit: ; preds = %bb2, %bb1, %bb0
define i1 @common_instr_with_unreachable_2(i64 %a, i64 %b, i64 %c) {
; CHECK-LABEL: @common_instr_with_unreachable_2(
; CHECK-NEXT: start:
-; CHECK-NEXT: switch i64 [[A:%.*]], label [[BB1:%.*]] [
+; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: ret i1 [[TMP0]]
+;
+start:
+ switch i64 %a, label %bb1 [
+ i64 0, label %bb0
+ i64 1, label %unreachable
+ i64 2, label %bb2
+ ]
+
+unreachable:
+ unreachable
+
+bb0: ; preds = %start
+ %0 = icmp eq i64 %b, %c
+ br label %exit
+
+bb1: ; preds = %start
+ %1 = icmp eq i64 %b, %c
+ br label %exit
+
+bb2: ; preds = %start
+ %2 = icmp eq i64 %b, %c
+ br label %exit
+
+exit: ; preds = %bb2, %bb1, %bb0
+ %result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
+ ret i1 %result
+}
+
+define i1 @not_only_unreachable(i64 %a, i64 %b, i64 %c) {
+; CHECK-LABEL: @not_only_unreachable(
+; CHECK-NEXT: start:
+; CHECK-NEXT: switch i64 [[A:%.*]], label [[UNREACHABLE:%.*]] [
; CHECK-NEXT: i64 0, label [[BB0:%.*]]
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
+; CHECK: unreachable:
+; CHECK-NEXT: call void @no_return()
+; CHECK-NEXT: unreachable
; CHECK: bb0:
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
+; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT]]
; CHECK: bb2:
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
+; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
; CHECK-NEXT: ret i1 [[RESULT]]
;
start:
- switch i64 %a, label %bb1 [
+ switch i64 %a, label %unreachable [
i64 0, label %bb0
- i64 1, label %unreachable
+ i64 1, label %bb1
i64 2, label %bb2
]
unreachable:
+ call void @no_return()
unreachable
bb0: ; preds = %start
%0 = icmp eq i64 %b, %c
+ call void @foo()
br label %exit
bb1: ; preds = %start
%1 = icmp eq i64 %b, %c
+ call void @foo()
br label %exit
bb2: ; preds = %start
%2 = icmp eq i64 %b, %c
+ call void @foo()
br label %exit
exit: ; preds = %bb2, %bb1, %bb0
%result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
ret i1 %result
}
+
+; If we can hoist a musttail call,
+; we can and have to hoist subsequent bitcast and ret instructions.
+define ptr @switch_musttail_call(ptr %arg) {
+; CHECK-LABEL: @switch_musttail_call(
+; CHECK-NEXT: bb:
+; CHECK-NEXT: [[P0:%.*]] = musttail call ptr @musttail_call(ptr [[ARG:%.*]])
+; CHECK-NEXT: ret ptr [[P0]]
+;
+bb:
+ %load = load i16, ptr %arg, align 2
+ switch i16 %load, label %unreachable [
+ i16 0, label %bb0
+ i16 1, label %bb1
+ i16 2, label %bb2
+ ]
+
+unreachable:
+ unreachable
+
+bb0:
+ %p0 = musttail call ptr @musttail_call(ptr %arg)
+ ret ptr %p0
+
+bb1:
+ %p1 = musttail call ptr @musttail_call(ptr %arg)
+ ret ptr %p1
+
+bb2:
+ %p2 = musttail call ptr @musttail_call(ptr %arg)
+ ret ptr %p2
+}
+
+declare void @no_return()
+declare void @foo()
+declare ptr @musttail_call(ptr)
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
index 43fb8faad7cfd94..bfe31d8345d506c 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
@@ -26,27 +26,11 @@ F: ; preds = %0
define void @test_switch(i64 %i, ptr %Q) {
; CHECK-LABEL: @test_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: common.ret:
-; CHECK-NEXT: ret void
-; CHECK: bb0:
+; CHECK-NEXT: common.ret:
; CHECK-NEXT: store i32 1, ptr [[Q:%.*]], align 4
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[Q]], align 4
; CHECK-NEXT: call void @bar(i32 [[A]])
-; CHECK-NEXT: br label [[COMMON_RET:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: store i32 1, ptr [[Q]], align 4
-; CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Q]], align 4
-; CHECK-NEXT: call void @bar(i32 [[B]])
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: bb2:
-; CHECK-NEXT: store i32 1, ptr [[Q]], align 4
-; CHECK-NEXT: [[C:%.*]] = load i32, ptr [[Q]], align 4
-; CHECK-NEXT: call void @bar(i32 [[C]])
-; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
i64 1, label %bb1
@@ -69,25 +53,41 @@ bb2: ; preds = %0
ret void
}
-define i1 @common_instr_on_switch(i64 %a, i64 %b, i64 %c) unnamed_addr {
-; CHECK-LABEL: @common_instr_on_switch(
-; CHECK-NEXT: start:
-; CHECK-NEXT: switch i64 [[A:%.*]], label [[BB0:%.*]] [
+; We ensure that we examine all instructions during each iteration to confirm the presence of a terminating one.
+define void @test_switch_reach_terminator(i64 %i, ptr %p) {
+; CHECK-LABEL: @test_switch_reach_terminator(
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: i64 2, label [[COMMON_RET:%.*]]
; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
; CHECK: bb0:
-; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
-; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK-NEXT: store i32 1, ptr [[P:%.*]], align 4
+; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: bb1:
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
-; CHECK-NEXT: br label [[EXIT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
-; CHECK-NEXT: br label [[EXIT]]
-; CHECK: exit:
-; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
-; CHECK-NEXT: ret i1 [[RESULT]]
+; CHECK-NEXT: store i32 2, ptr [[P]], align 4
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+bb0: ; preds = %0
+ store i32 1, ptr %p
+ ret void
+bb1: ; preds = %0
+ store i32 2, ptr %p
+ ret void
+bb2: ; preds = %0
+ ret void
+}
+
+define i1 @common_instr_on_switch(i64 %a, i64 %b, i64 %c) unnamed_addr {
+; CHECK-LABEL: @common_instr_on_switch(
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: ret i1 [[TMP0]]
;
start:
switch i64 %a, label %bb0 [
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
index 93e822b589b1424..1e06abf62c9fe3e 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
@@ -48,6 +48,68 @@ if.end:
ret void
}
+define void @f0_switch(i64 %i, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
+; CHECK-LABEL: @f0_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
+; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = add i16 [[SUB]], 3
+; CHECK-NEXT: [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: [[SUB2:%.*]] = sub nsw i16 [[TMP0]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = add i16 [[SUB2]], 3
+; CHECK-NEXT: [[W:%.*]] = add i16 [[SUB2]], [[TMP3]]
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[U]], [[BB0]] ], [ [[V]], [[BB1]] ], [ [[W]], [[BB2]] ]
+; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
+; CHECK-NEXT: ret void
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %0 = load i16, ptr %b, align 2
+ %add = add nsw i16 %0, 1
+ %1 = load i16, ptr %m, align 2
+ %u = add i16 %add, %1
+ br label %end
+
+bb1:
+ %2 = load i16, ptr %b, align 2
+ %sub = sub nsw i16 %2, 1
+ %3 = load i16, ptr %m, align 2
+ %4 = add i16 %sub, 3
+ %v = add i16 %sub, %4
+ br label %end
+
+bb2:
+ %5 = load i16, ptr %b, align 2
+ %sub2 = sub nsw i16 %5, 1
+ %6 = load i16, ptr %m, align 2
+ %7 = add i16 %sub2, 3
+ %w = add i16 %sub2, %7
+ br label %end
+
+end:
+ %uv = phi i16 [ %u, %bb0 ], [ %v, %bb1 ], [ %w, %bb2 ]
+ store i16 %uv, ptr %d, align 2
+ ret void
+}
;; Check some instructions (e.g. add) can be reordered across instructions with side
;; effects, while others (e.g. load) can't.
@@ -97,6 +159,70 @@ if.end:
ret void
}
+define void @f2_switch(i64 %i, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
+; CHECK-LABEL: @f2_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: call void @side_effects0()
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
+; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: call void @no_side_effects0()
+; CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr [[M]], align 2
+; CHECK-NEXT: [[V:%.*]] = add i16 [[ADD_0]], [[TMP2]]
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: call void @no_side_effects0()
+; CHECK-NEXT: [[TMP3:%.*]] = load i16, ptr [[M]], align 2
+; CHECK-NEXT: [[W:%.*]] = add i16 [[ADD_0]], [[TMP3]]
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[U]], [[BB0]] ], [ [[V]], [[BB1]] ], [ [[W]], [[BB2]] ]
+; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
+; CHECK-NEXT: ret void
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %0 = load i16, ptr %b, align 2
+ call void @side_effects0()
+ %add.0 = add nsw i16 %0, 1
+ %1 = load i16, ptr %m, align 2
+ %u = add i16 %add.0, %1
+ br label %end
+
+bb1:
+ %2 = load i16, ptr %b, align 2
+ call void @no_side_effects0()
+ %add.1 = add nsw i16 %2, 1
+ %3 = load i16, ptr %m, align 2
+ %v = add i16 %add.1, %3
+ br label %end
+
+bb2:
+ %4 = load i16, ptr %b, align 2
+ call void @no_side_effects0()
+ %add.2 = add nsw i16 %4, 1
+ %5 = load i16, ptr %m, align 2
+ %w = add i16 %add.2, %5
+ br label %end
+
+end:
+ %uv = phi i16 [ %u, %bb0 ], [ %v, %bb1 ], [ %w, %bb2 ]
+ store i16 %uv, ptr %d, align 2
+ ret void
+}
;; Check indeed it was the side effects that prevented hoisting the load
;; in the previous test.
@@ -143,6 +269,67 @@ if.end:
ret void
}
+define void @f3_switch(i64 %i, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
+; CHECK-LABEL: @f3_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
+; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: call void @no_side_effects0()
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: call void @no_side_effects1()
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: call void @no_side_effects1()
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[U]], [[BB0]] ], [ [[U]], [[BB1]] ], [ [[U]], [[BB2]] ]
+; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
+; CHECK-NEXT: ret void
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %0 = load i16, ptr %b, align 2
+ call void @no_side_effects0()
+ %add.0 = add nsw i16 %0, 1
+ %1 = load i16, ptr %m, align 2
+ %u = add i16 %add.0, %1
+ br label %end
+
+bb1:
+ %2 = load i16, ptr %b, align 2
+ call void @no_side_effects1()
+ %add.1 = add nsw i16 %2, 1
+ %3 = load i16, ptr %m, align 2
+ %v = add i16 %add.1, %3
+ br label %end
+
+bb2:
+ %4 = load i16, ptr %b, align 2
+ call void @no_side_effects1()
+ %add.2 = add nsw i16 %4, 1
+ %5 = load i16, ptr %m, align 2
+ %w = add i16 %add.2, %5
+ br label %end
+
+end:
+ %uv = phi i16 [ %u, %bb0 ], [ %v, %bb1 ], [ %w, %bb2 ]
+ store i16 %uv, ptr %d, align 2
+ ret void
+}
+
;; Check some instructions (e.g. sdiv) are not speculatively executed.
;; Division by non-zero constant OK to speculate ...
@@ -186,6 +373,63 @@ if.end:
ret void
}
+define void @f4_switch(i64 %i, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
+; CHECK-LABEL: @f4_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 [[TMP0]], 2
+; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: call void @side_effects0()
+; CHECK-NEXT: br label [[IF_END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: call void @side_effects1()
+; CHECK-NEXT: br label [[IF_END]]
+; CHECK: bb2:
+; CHECK-NEXT: call void @side_effects1()
+; CHECK-NEXT: br label [[IF_END]]
+; CHECK: if.end:
+; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[U]], [[BB0]] ], [ [[U]], [[BB1]] ], [ [[U]], [[BB2]] ]
+; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
+; CHECK-NEXT: ret void
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %0 = load i16, ptr %b, align 2
+ call void @side_effects0()
+ %div.0 = sdiv i16 %0, 2
+ %u = add i16 %div.0, %0
+ br label %if.end
+
+bb1:
+ %1 = load i16, ptr %b, align 2
+ call void @side_effects1()
+ %div.1 = sdiv i16 %1, 2
+ %v = add i16 %div.1, %1
+ br label %if.end
+
+bb2:
+ %2 = load i16, ptr %b, align 2
+ call void @side_effects1()
+ %div.2 = sdiv i16 %2, 2
+ %w = add i16 %div.2, %2
+ br label %if.end
+
+if.end:
+ %uv = phi i16 [ %u, %bb0 ], [ %v, %bb1 ], [ %w, %bb2 ]
+ store i16 %uv, ptr %d, align 2
+ ret void
+}
+
;; ... but not a general division ...
define void @f5(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f5(
@@ -230,6 +474,67 @@ if.end:
ret void
}
+define void @f5_switch(i64 %i, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
+; CHECK-LABEL: @f5_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: call void @side_effects0()
+; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
+; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: call void @side_effects1()
+; CHECK-NEXT: [[DIV_1:%.*]] = sdiv i16 211, [[TMP0]]
+; CHECK-NEXT: [[V:%.*]] = add i16 [[DIV_1]], [[TMP0]]
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: call void @side_effects1()
+; CHECK-NEXT: [[DIV_2:%.*]] = sdiv i16 211, [[TMP0]]
+; CHECK-NEXT: [[W:%.*]] = add i16 [[DIV_2]], [[TMP0]]
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[U]], [[BB0]] ], [ [[V]], [[BB1]] ], [ [[W]], [[BB2]] ]
+; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
+; CHECK-NEXT: ret void
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %0 = load i16, ptr %b, align 2
+ call void @side_effects0()
+ %div.0 = sdiv i16 211, %0
+ %u = add i16 %div.0, %0
+ br label %end
+
+bb1:
+ %1 = load i16, ptr %b, align 2
+ call void @side_effects1()
+ %div.1 = sdiv i16 211, %1
+ %v = add i16 %div.1, %1
+ br label %end
+
+bb2:
+ %2 = load i16, ptr %b, align 2
+ call void @side_effects1()
+ %div.2 = sdiv i16 211, %2
+ %w = add i16 %div.2, %2
+ br label %end
+
+end:
+ %uv = phi i16 [ %u, %bb0 ], [ %v, %bb1 ], [ %w, %bb2 ]
+ store i16 %uv, ptr %d, align 2
+ ret void
+}
+
;; ... and it's also OK to hoist the division when there's no speculation happening.
define void @f6(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f6(
@@ -271,6 +576,63 @@ if.end:
ret void
}
+define void @f6_switch(i64 %i, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
+; CHECK-LABEL: @f6_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
+; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: call void @no_side_effects0()
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: call void @no_side_effects1()
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: call void @no_side_effects1()
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[U]], [[BB0]] ], [ [[U]], [[BB1]] ], [ [[U]], [[BB2]] ]
+; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
+; CHECK-NEXT: ret void
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %0 = load i16, ptr %b, align 2
+ call void @no_side_effects0()
+ %div.0 = sdiv i16 211, %0
+ %u = add i16 %div.0, %0
+ br label %end
+
+bb1:
+ %1 = load i16, ptr %b, align 2
+ call void @no_side_effects1()
+ %div.1 = sdiv i16 211, %1
+ %v = add i16 %div.1, %1
+ br label %end
+
+bb2:
+ %2 = load i16, ptr %b, align 2
+ call void @no_side_effects1()
+ %div.2 = sdiv i16 211, %2
+ %w = add i16 %div.2, %2
+ br label %end
+
+end:
+ %uv = phi i16 [ %u, %bb0 ], [ %v, %bb1 ], [ %w, %bb2 ]
+ store i16 %uv, ptr %d, align 2
+ ret void
+}
+
;; No reorder of store over a load.
define i16 @f7(i1 %c, ptr %a, ptr %b) {
; CHECK-LABEL: @f7(
@@ -306,6 +668,55 @@ if.end:
ret i16 %v
}
+define i16 @f7_switch(i64 %i, ptr %a, ptr %b) {
+; CHECK-LABEL: @f7_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: [[VA:%.*]] = load i16, ptr [[A:%.*]], align 2
+; CHECK-NEXT: store i16 0, ptr [[B:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[VB:%.*]] = load i16, ptr [[B]], align 2
+; CHECK-NEXT: store i16 0, ptr [[B]], align 2
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: [[VC:%.*]] = load i16, ptr [[B]], align 2
+; CHECK-NEXT: store i16 0, ptr [[B]], align 2
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[V:%.*]] = phi i16 [ [[VA]], [[BB0]] ], [ [[VB]], [[BB1]] ], [ [[VC]], [[BB2]] ]
+; CHECK-NEXT: ret i16 [[V]]
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %va = load i16, ptr %a, align 2
+ store i16 0, ptr %b, align 2
+ br label %end
+
+bb1:
+ %vb = load i16, ptr %b, align 2
+ store i16 0, ptr %b, align 2
+ br label %end
+
+bb2:
+ %vc = load i16, ptr %b, align 2
+ store i16 0, ptr %b, align 2
+ br label %end
+
+end:
+ %v = phi i16 [ %va, %bb0 ], [ %vb, %bb1 ], [ %vc, %bb2 ]
+ ret i16 %v
+}
+
;; Can reorder load over another load
define i16 @f8(i1 %cond, ptr %a, ptr %b, ptr %c) {
; CHECK-LABEL: @f8(
@@ -346,6 +757,59 @@ if.end:
ret i16 %w
}
+define i16 @f8_switch(i64 %i, ptr %a, ptr %b, ptr %c) {
+; CHECK-LABEL: @f8_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[C_0:%.*]] = load i16, ptr [[C:%.*]], align 2
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: [[VA:%.*]] = load i16, ptr [[A:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[VB:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: [[VC:%.*]] = load i16, ptr [[B]], align 2
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[V:%.*]] = phi i16 [ [[VA]], [[BB0]] ], [ [[VB]], [[BB1]] ], [ [[VC]], [[BB2]] ]
+; CHECK-NEXT: [[U:%.*]] = phi i16 [ [[C_0]], [[BB0]] ], [ [[C_0]], [[BB1]] ], [ [[C_0]], [[BB2]] ]
+; CHECK-NEXT: [[W:%.*]] = add i16 [[V]], [[U]]
+; CHECK-NEXT: ret i16 [[W]]
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %va = load i16, ptr %a, align 2
+ %c.0 = load i16, ptr %c
+ br label %end
+
+bb1:
+ %vb = load i16, ptr %b, align 2
+ %c.1 = load i16, ptr %c
+ br label %end
+
+bb2:
+ %vc = load i16, ptr %b, align 2
+ %c.2 = load i16, ptr %c
+ br label %end
+
+end:
+ %v = phi i16 [ %va, %bb0 ], [ %vb, %bb1 ], [ %vc, %bb2 ]
+ %u = phi i16 [ %c.0, %bb0 ], [ %c.1, %bb1 ], [ %c.2, %bb2 ]
+
+ %w = add i16 %v, %u
+
+ ret i16 %w
+}
+
;; Currently won't reorder volatile and non-volatile loads.
define i16 @f9(i1 %cond, ptr %a, ptr %b, ptr %c) {
; CHECK-LABEL: @f9(
@@ -387,6 +851,61 @@ if.end:
ret i16 %w
}
+define i16 @f9_switch(i64 %i, ptr %a, ptr %b, ptr %c) {
+; CHECK-LABEL: @f9_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: [[VA:%.*]] = load volatile i16, ptr [[A:%.*]], align 2
+; CHECK-NEXT: [[C_0:%.*]] = load i16, ptr [[C:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[VB:%.*]] = load i16, ptr [[B:%.*]], align 2
+; CHECK-NEXT: [[C_1:%.*]] = load i16, ptr [[C]], align 2
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: [[VC:%.*]] = load i16, ptr [[B]], align 2
+; CHECK-NEXT: [[C_2:%.*]] = load i16, ptr [[C]], align 2
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[V:%.*]] = phi i16 [ [[VA]], [[BB0]] ], [ [[VB]], [[BB1]] ], [ [[VC]], [[BB2]] ]
+; CHECK-NEXT: [[U:%.*]] = phi i16 [ [[C_0]], [[BB0]] ], [ [[C_1]], [[BB1]] ], [ [[C_2]], [[BB2]] ]
+; CHECK-NEXT: [[W:%.*]] = add i16 [[V]], [[U]]
+; CHECK-NEXT: ret i16 [[W]]
+;
+entry:
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %va = load volatile i16, ptr %a, align 2
+ %c.0 = load i16, ptr %c
+ br label %end
+
+bb1:
+ %vb = load i16, ptr %b, align 2
+ %c.1 = load i16, ptr %c
+ br label %end
+
+bb2:
+ %vc = load i16, ptr %b, align 2
+ %c.2 = load i16, ptr %c
+ br label %end
+
+end:
+ %v = phi i16 [ %va, %bb0 ], [ %vb, %bb1 ], [ %vc, %bb2 ]
+ %u = phi i16 [ %c.0, %bb0 ], [ %c.1, %bb1 ], [ %c.2, %bb2 ]
+
+ %w = add i16 %v, %u
+
+ ret i16 %w
+}
+
;; Don't hoist stacksaves across inalloca allocas
define void @f10(i1 %cond) {
; CHECK-LABEL: @f10(
@@ -438,6 +957,79 @@ end:
ret void
}
+define void @f10_switch(i64 %i) {
+; CHECK-LABEL: @f10_switch(
+; CHECK-NEXT: [[SS:%.*]] = call ptr @llvm.stacksave.p0()
+; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
+; CHECK-NEXT: i64 1, label [[BB1:%.*]]
+; CHECK-NEXT: i64 2, label [[BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb0:
+; CHECK-NEXT: [[I1:%.*]] = alloca inalloca i32, align 4
+; CHECK-NEXT: [[SS2:%.*]] = call ptr @llvm.stacksave.p0()
+; CHECK-NEXT: [[I2:%.*]] = alloca inalloca i64, align 8
+; CHECK-NEXT: call void @inalloca_i64(ptr inalloca(i64) [[I2]])
+; CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[SS2]])
+; CHECK-NEXT: call void @inalloca_i32(ptr inalloca(i32) [[I1]])
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[I3:%.*]] = alloca inalloca i64, align 8
+; CHECK-NEXT: [[SS3:%.*]] = call ptr @llvm.stacksave.p0()
+; CHECK-NEXT: [[I4:%.*]] = alloca inalloca i64, align 8
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @inalloca_i64(ptr inalloca(i64) [[I4]])
+; CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[SS3]])
+; CHECK-NEXT: [[TMP2:%.*]] = call ptr @inalloca_i64(ptr inalloca(i64) [[I3]])
+; CHECK-NEXT: br label [[END]]
+; CHECK: bb2:
+; CHECK-NEXT: [[I5:%.*]] = alloca inalloca i64, align 8
+; CHECK-NEXT: [[SS4:%.*]] = call ptr @llvm.stacksave.p0()
+; CHECK-NEXT: [[I6:%.*]] = alloca inalloca i64, align 8
+; CHECK-NEXT: [[TMP3:%.*]] = call ptr @inalloca_i64(ptr inalloca(i64) [[I6]])
+; CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[SS4]])
+; CHECK-NEXT: [[TMP4:%.*]] = call ptr @inalloca_i64(ptr inalloca(i64) [[I5]])
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[SS]])
+; CHECK-NEXT: ret void
+;
+ %ss = call ptr @llvm.stacksave()
+ switch i64 %i, label %bb0 [
+ i64 1, label %bb1
+ i64 2, label %bb2
+ ]
+
+bb0:
+ %i1 = alloca inalloca i32
+ %ss2 = call ptr @llvm.stacksave()
+ %i2 = alloca inalloca i64
+ call void @inalloca_i64(ptr inalloca(i64) %i2)
+ call void @llvm.stackrestore(ptr %ss2)
+ call void @inalloca_i32(ptr inalloca(i32) %i1)
+ br label %end
+
+bb1:
+ %i3 = alloca inalloca i64
+ %ss3 = call ptr @llvm.stacksave()
+ %i4 = alloca inalloca i64
+ call ptr @inalloca_i64(ptr inalloca(i64) %i4)
+ call void @llvm.stackrestore(ptr %ss3)
+ call ptr @inalloca_i64(ptr inalloca(i64) %i3)
+ br label %end
+
+bb2:
+ %i5 = alloca inalloca i64
+ %ss4 = call ptr @llvm.stacksave()
+ %i6 = alloca inalloca i64
+ call ptr @inalloca_i64(ptr inalloca(i64) %i6)
+ call void @llvm.stackrestore(ptr %ss4)
+ call ptr @inalloca_i64(ptr inalloca(i64) %i5)
+ br label %end
+
+end:
+ call void @llvm.stackrestore(ptr %ss)
+ ret void
+}
+
declare void @side_effects0()
declare void @side_effects1()
declare void @no_side_effects0() readonly nounwind willreturn
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll b/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
index b53224c944f1101..90daf38e39d529e 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-with-metadata.ll
@@ -21,20 +21,8 @@ out:
define void @hoist_range_switch(i64 %i, ptr %p) {
; CHECK-LABEL: @hoist_range_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: bb0:
+; CHECK-NEXT: out:
; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1, !range [[RNG1:![0-9]+]]
-; CHECK-NEXT: br label [[OUT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[E:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG2:![0-9]+]]
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[F:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG3:![0-9]+]]
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: out:
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
@@ -57,7 +45,7 @@ out:
define void @hoist_both_noundef(i1 %c, ptr %p) {
; CHECK-LABEL: @hoist_both_noundef(
; CHECK-NEXT: if:
-; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1, !noundef !4
+; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1, !noundef !2
; CHECK-NEXT: ret void
;
if:
@@ -78,20 +66,8 @@ out:
define void @hoist_both_noundef_switch(i64 %i, ptr %p) {
; CHECK-LABEL: @hoist_both_noundef_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: bb0:
-; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1, !noundef !4
-; CHECK-NEXT: br label [[OUT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[E:%.*]] = load i8, ptr [[P]], align 1, !noundef !4
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[F:%.*]] = load i8, ptr [[P]], align 1, !noundef !4
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: out:
+; CHECK-NEXT: out:
+; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1, !noundef !2
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
@@ -134,20 +110,8 @@ out:
define void @hoist_one_noundef_switch(i64 %i, ptr %p) {
; CHECK-LABEL: @hoist_one_noundef_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: bb0:
-; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1, !noundef !4
-; CHECK-NEXT: br label [[OUT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[E:%.*]] = load i8, ptr [[P]], align 1
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[F:%.*]] = load i8, ptr [[P]], align 1, !noundef !4
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: out:
+; CHECK-NEXT: out:
+; CHECK-NEXT: [[T:%.*]] = load i8, ptr [[P:%.*]], align 1
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
@@ -170,7 +134,7 @@ out:
define void @hoist_dereferenceable(i1 %c, ptr %p) {
; CHECK-LABEL: @hoist_dereferenceable(
; CHECK-NEXT: if:
-; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable !5
+; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable !3
; CHECK-NEXT: ret void
;
if:
@@ -187,20 +151,8 @@ out:
define void @hoist_dereferenceable_switch(i64 %i, ptr %p) {
; CHECK-LABEL: @hoist_dereferenceable_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: bb0:
-; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable !5
-; CHECK-NEXT: br label [[OUT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[E:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable !6
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[F:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable !7
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: out:
+; CHECK-NEXT: out:
+; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable !3
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
@@ -223,7 +175,7 @@ out:
define void @hoist_dereferenceable_or_null(i1 %c, ptr %p) {
; CHECK-LABEL: @hoist_dereferenceable_or_null(
; CHECK-NEXT: if:
-; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable_or_null !5
+; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable_or_null !3
; CHECK-NEXT: ret void
;
if:
@@ -240,20 +192,8 @@ out:
define void @hoist_dereferenceable_or_null_switch(i64 %i, ptr %p) {
; CHECK-LABEL: @hoist_dereferenceable_or_null_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: bb0:
-; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable_or_null !6
-; CHECK-NEXT: br label [[OUT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[E:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable_or_null !5
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[F:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable_or_null !7
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: out:
+; CHECK-NEXT: out:
+; CHECK-NEXT: [[T:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable_or_null !3
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
@@ -277,7 +217,7 @@ out:
define i32 @speculate_range(i1 %c, ptr dereferenceable(8) align 8 %p) {
; CHECK-LABEL: @speculate_range(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4, !range [[RNG8:![0-9]+]]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4, !range [[RNG4:![0-9]+]]
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[C:%.*]], i32 [[V]], i32 0
; CHECK-NEXT: ret i32 [[SPEC_SELECT]]
;
@@ -298,7 +238,7 @@ join:
define ptr @speculate_nonnull(i1 %c, ptr dereferenceable(8) align 8 %p) {
; CHECK-LABEL: @speculate_nonnull(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[V:%.*]] = load ptr, ptr [[P:%.*]], align 8, !nonnull !4
+; CHECK-NEXT: [[V:%.*]] = load ptr, ptr [[P:%.*]], align 8, !nonnull !2
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[C:%.*]], ptr [[V]], ptr null
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
;
@@ -319,7 +259,7 @@ join:
define ptr @speculate_align(i1 %c, ptr dereferenceable(8) align 8 %p) {
; CHECK-LABEL: @speculate_align(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[V:%.*]] = load ptr, ptr [[P:%.*]], align 8, !align !9
+; CHECK-NEXT: [[V:%.*]] = load ptr, ptr [[P:%.*]], align 8, !align !5
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[C:%.*]], ptr [[V]], ptr null
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
;
@@ -338,7 +278,7 @@ join:
define void @hoist_fpmath(i1 %c, double %x) {
; CHECK-LABEL: @hoist_fpmath(
; CHECK-NEXT: if:
-; CHECK-NEXT: [[T:%.*]] = fadd double [[X:%.*]], 1.000000e+00, !fpmath !10
+; CHECK-NEXT: [[T:%.*]] = fadd double [[X:%.*]], 1.000000e+00, !fpmath !6
; CHECK-NEXT: ret void
;
if:
@@ -355,20 +295,8 @@ out:
define void @hoist_fpmath_switch(i64 %i, double %x) {
; CHECK-LABEL: @hoist_fpmath_switch(
-; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
-; CHECK-NEXT: i64 1, label [[BB1:%.*]]
-; CHECK-NEXT: i64 2, label [[BB2:%.*]]
-; CHECK-NEXT: ]
-; CHECK: bb0:
-; CHECK-NEXT: [[T:%.*]] = fadd double [[X:%.*]], 1.000000e+00, !fpmath !10
-; CHECK-NEXT: br label [[OUT:%.*]]
-; CHECK: bb1:
-; CHECK-NEXT: [[E:%.*]] = fadd double [[X]], 1.000000e+00, !fpmath !11
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: bb2:
-; CHECK-NEXT: [[F:%.*]] = fadd double [[X]], 1.000000e+00, !fpmath !12
-; CHECK-NEXT: br label [[OUT]]
-; CHECK: out:
+; CHECK-NEXT: out:
+; CHECK-NEXT: [[T:%.*]] = fadd double [[X:%.*]], 1.000000e+00, !fpmath !6
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
@@ -394,16 +322,10 @@ out:
!3 = !{ i8 7, i8 9 }
;.
; CHECK: [[RNG0]] = !{i8 0, i8 1, i8 3, i8 5}
-; CHECK: [[RNG1]] = !{i8 0, i8 1}
-; CHECK: [[RNG2]] = !{i8 3, i8 5}
-; CHECK: [[RNG3]] = !{i8 7, i8 9}
-; CHECK: [[META4:![0-9]+]] = !{}
-; CHECK: [[META5:![0-9]+]] = !{i64 10}
-; CHECK: [[META6:![0-9]+]] = !{i64 20}
-; CHECK: [[META7:![0-9]+]] = !{i64 30}
-; CHECK: [[RNG8]] = !{i32 0, i32 10}
-; CHECK: [[META9:![0-9]+]] = !{i64 4}
-; CHECK: [[META10:![0-9]+]] = !{float 2.500000e+00}
-; CHECK: [[META11:![0-9]+]] = !{float 5.000000e+00}
-; CHECK: [[META12:![0-9]+]] = !{float 7.500000e+00}
+; CHECK: [[RNG1]] = !{i8 0, i8 1, i8 3, i8 5, i8 7, i8 9}
+; CHECK: [[META2:![0-9]+]] = !{}
+; CHECK: [[META3:![0-9]+]] = !{i64 10}
+; CHECK: [[RNG4]] = !{i32 0, i32 10}
+; CHECK: [[META5:![0-9]+]] = !{i64 4}
+; CHECK: [[META6:![0-9]+]] = !{float 2.500000e+00}
;.
More information about the llvm-commits
mailing list