diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 14cabd275d5b1..897058ed53518 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 &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(I1); - DbgInfoIntrinsic *DBI2 = dyn_cast(I2); - if (!DBI1 || !DBI2 || !DBI1->isIdenticalToWhenDefined(DBI2)) { - while (isa(I1)) - I1 = &*BB1_Itr++; - while (isa(I2)) - I2 = &*BB2_Itr++; + // The second of pair is a SkipFlags bitmask. + using SuccIterPair = std::pair; + SmallVector SuccIterPairs; + for (auto *Succ : successors(BB)) { + BasicBlock::iterator SuccItr = Succ->begin(); + if (isa(*SuccItr)) + return false; + SuccIterPairs.push_back(SuccIterPair(SuccItr, 0)); } - if (isa(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(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(I1)) + I1 = &*++BB1ItrPair.first; + for (auto &SuccIter : OtherSuccIterRange) { + Instruction *I2 = &*SuccIter; + while (isa(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 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(I1) || isa(I2)) { - assert(isa(I1) && isa(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(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(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(I1); - DbgInfoIntrinsic *DBI2 = dyn_cast(I2); - if (!DBI1 || !DBI2 || !DBI1->isIdenticalToWhenDefined(DBI2)) { - while (isa(I1)) - I1 = &*BB1_Itr++; - while (isa(I2)) - I2 = &*BB2_Itr++; - } } +} - return Changed; +bool SimplifyCFGOpt::hoistSuccIdenticalTerminatorToSwitchOrIf( + Instruction *TI, Instruction *I1, + SmallVectorImpl &OtherSuccTIs) { + + auto *BI = dyn_cast(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(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(I1) && (!BI || !isSafeToHoistInvoke(BB1, BB2, I1, I2))) + return false; // TODO: callbr hoisting currently disabled pending further study. if (isa(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 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 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, 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, 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(PN)) - Builder.setFastMathFlags(PN.getFastMathFlags()); - - SI = cast( - 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(PN)) + Builder.setFastMathFlags(PN.getFastMathFlags()); + + SI = cast(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 0dfb5764f55b8..15657730c2cdc 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 0b634fac8c640..4088ecfc81898 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 8cb6339713d6f..d948737c16c92 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 43fb8faad7cfd..bfe31d8345d50 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 93e822b589b14..1e06abf62c9fe 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 b53224c944f11..90daf38e39d52 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} ;.