mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-05 23:52:45 +00:00
[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:
parent
40b0ab287f
commit
96ea48ff5d
@ -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
|
||||
|
@ -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 "", ""()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 [
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
;.
|
||||
|
Loading…
Reference in New Issue
Block a user