[llvm] 96ea48f - [SimplifyCFG] Hoist common instructions on Switch.

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 19 16:22:28 PDT 2023


Author: DianQK
Date: 2023-09-20T07:21:49+08:00
New Revision: 96ea48ff5dcba46af350f5300eafd7f7394ba606

URL: https://github.com/llvm/llvm-project/commit/96ea48ff5dcba46af350f5300eafd7f7394ba606
DIFF: https://github.com/llvm/llvm-project/commit/96ea48ff5dcba46af350f5300eafd7f7394ba606.diff

LOG: [SimplifyCFG] Hoist common instructions on Switch.

Sink common instructions are not always performance friendly. We need to implement hoist common instructions on switch instruction to solve the following problem:
```
define i1 @foo(i64 %a, i64 %b, i64 %c, i64 %d) {
start:
  %test = icmp eq i64 %a, %d
  br i1 %test, label %switch_bb, label %exit

switch_bb:                                        ; preds = %start
  switch i64 %a, label %bb0 [
    i64 1, label %bb1
    i64 2, label %bb2
  ]

bb0:                                              ; preds = %switch_bb
  %0 = icmp eq i64 %b, %c
  br label %exit

bb1:                                              ; preds = %switch_bb
  %1 = icmp eq i64 %b, %c
  br label %exit

bb2:                                              ; preds = %switch_bb
  %2 = icmp eq i64 %b, %c
  br label %exit

exit:                                             ; preds = %bb2, %bb1, %bb0, %start
  %result = phi i1 [ false, %start ], [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
  ret i1 %result
}
```
The pre-commit test is D156617.

Reviewed By: XChy, nikic

Differential Revision: https://reviews.llvm.org/D155711

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..897058ed5351836 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,234 @@ 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) {
       // If any instructions remain in the block, we cannot hoist terminators.
-      if (NumSkipped || !I1->isIdenticalToWhenDefined(I2))
+      if (NumSkipped || SuccSize != SuccIterPairs.size() ||
+          !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 +1807,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 +2850,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 +6888,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 +7158,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..d948737c16c9266 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,43 +37,90 @@ 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
+}
+
+declare void @no_return()
+declare void @foo()
+
+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

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