[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
This commit is contained in:
DianQK 2023-09-19 17:57:30 +08:00
parent 40b0ab287f
commit 96ea48ff5d
No known key found for this signature in database
GPG Key ID: 46BDB1AC96C48912
7 changed files with 942 additions and 332 deletions

View File

@ -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 (;;) {
// 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 any instructions remain in the block, we cannot hoist terminators.
if (NumSkipped || !I1->isIdenticalToWhenDefined(I2))
return Changed;
goto HoistTerminator;
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;
}
}
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));
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 (HasTerminator) {
// If any instructions remain in the block, we cannot hoist terminators.
if (NumSkipped || SuccSize != SuccIterPairs.size() ||
!AllInstsAreIdentical)
return Changed;
SmallVector<Instruction *, 8> Insts;
for (auto &SuccIter : OtherSuccIterRange)
Insts.push_back(&*SuccIter);
return hoistSuccIdenticalTerminatorToSwitchOrIf(TI, I1, Insts) || Changed;
}
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++;
}
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();
// 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());
}
return Changed;
HoistTerminator:
// It may not be possible to hoist an invoke.
// 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());
// 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));
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 @@ HoistTerminator:
// 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

View File

@ -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 "", ""()

View File

@ -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

View File

@ -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,22 +37,8 @@ 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: i64 0, label [[BB0:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
; 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 %bb1 [
@ -97,3 +66,64 @@ 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 %unreachable [
i64 0, label %bb0
i64 1, label %bb1
i64 2, label %bb2
]
unreachable:
call void @no_return()
unreachable
bb0: ; preds = %start
%0 = icmp eq i64 %b, %c
call void @foo()
br label %exit
bb1: ; preds = %start
%1 = icmp eq i64 %b, %c
call void @foo()
br label %exit
bb2: ; preds = %start
%2 = icmp eq i64 %b, %c
call void @foo()
br label %exit
exit: ; preds = %bb2, %bb1, %bb0
%result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
ret i1 %result
}

View File

@ -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
}
; 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 [[COMMON_RET:%.*]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: bb0:
; CHECK-NEXT: store i32 1, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: bb1:
; 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: switch i64 [[A:%.*]], label [[BB0:%.*]] [
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
; 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 %bb0 [

View File

@ -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

View File

@ -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}
;.