mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Generalize and fix SinkAddressProjections.
Fixes a potential real bug in the case that SinkAddressProjections moves
projections without notifying SimplifyCFG of the change. This could
fail to update Analyses (probably won't break anything in practice).
Introduce SILInstruction::isPure. Among other things, this can tell
you if it's safe to duplicate instructions at their
uses. SinkAddressProjections should check this before sinking uses. I
couldn't find a way to expose this as a real bug, but it is a
theoretical bug.
Add the SinkAddressProjections functionality to the BasicBlockCloner
utility. Enable address projection sinking for all BasicBlockCloner
clients (the four different kinds of jump-threading that use it). This
brings the compiler much closer to banning all address phis.
The "bugs" were originally introduced a week ago here:
commit f22371bf0b (fork/fix-address-phi, fix-address-phi)
Author: Andrew Trick <atrick@apple.com>
Date: Tue Sep 17 16:45:51 2019
Add SIL SinkAddressProjections utility to avoid address phis.
Enable this utility during jump-threading in SimplifyCFG.
Ultimately, the SIL verifier should prevent all address-phis and we'll
need to use this utility in a few more places.
Fixes <rdar://problem/55320867> SIL verification failed: Unknown
formal access pattern: storage
This commit is contained in:
@@ -274,12 +274,15 @@ public:
|
||||
|
||||
ThreadInfo() = default;
|
||||
|
||||
void threadEdge() {
|
||||
bool threadEdge() {
|
||||
LLVM_DEBUG(llvm::dbgs() << "thread edge from bb" << Src->getDebugID()
|
||||
<< " to bb" << Dest->getDebugID() << '\n');
|
||||
auto *SrcTerm = cast<BranchInst>(Src->getTerminator());
|
||||
|
||||
BasicBlockCloner Cloner(SrcTerm->getDestBB());
|
||||
if (!Cloner.canCloneBlock())
|
||||
return false;
|
||||
|
||||
Cloner.cloneBranchTarget(SrcTerm);
|
||||
|
||||
// We have copied the threaded block into the edge.
|
||||
@@ -329,7 +332,8 @@ public:
|
||||
// After rewriting the cloned branch, split the critical edge.
|
||||
// This does not currently update DominanceInfo.
|
||||
Cloner.splitCriticalEdges(nullptr, nullptr);
|
||||
updateSSAAfterCloning(Cloner, Src, Dest);
|
||||
Cloner.updateSSAAfterCloning();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -551,8 +555,8 @@ bool SimplifyCFG::dominatorBasedSimplifications(SILFunction &Fn,
|
||||
return Changed;
|
||||
|
||||
for (auto &ThreadInfo : JumpThreadableEdges) {
|
||||
ThreadInfo.threadEdge();
|
||||
Changed = true;
|
||||
if (ThreadInfo.threadEdge())
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
return Changed;
|
||||
@@ -922,16 +926,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
|
||||
if (DestBB->getTerminator()->isFunctionExiting())
|
||||
return false;
|
||||
|
||||
// We need to update SSA if a value duplicated is used outside of the
|
||||
// duplicated block.
|
||||
bool NeedToUpdateSSA = false;
|
||||
|
||||
// Are the arguments to this block used outside of the block.
|
||||
for (auto Arg : DestBB->getArguments())
|
||||
if ((NeedToUpdateSSA |= isUsedOutsideOfBlock(Arg))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We don't have a great cost model at the SIL level, so we don't want to
|
||||
// blissly duplicate tons of code with a goal of improved performance (we'll
|
||||
// leave that to LLVM). However, doing limited code duplication can lead to
|
||||
@@ -978,36 +972,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
|
||||
if (ThreadingBudget <= 0)
|
||||
return false;
|
||||
|
||||
// If it looks potentially interesting, decide whether we *can* do the
|
||||
// operation and whether the block is small enough to be worth duplicating.
|
||||
int copyCosts = 0;
|
||||
SinkAddressProjections sinkProj;
|
||||
for (auto ii = DestBB->begin(), ie = DestBB->end(); ii != ie;) {
|
||||
copyCosts += getThreadingCost(&*ii);
|
||||
if (ThreadingBudget <= copyCosts)
|
||||
return false;
|
||||
|
||||
// If this is an address projection with outside uses, sink it before
|
||||
// checking for SSA update.
|
||||
if (!sinkProj.analyzeAddressProjections(&*ii))
|
||||
return false;
|
||||
|
||||
sinkProj.cloneProjections();
|
||||
// After cloning check if any of the non-address defs in the cloned block
|
||||
// (including the current instruction) now have uses outside the
|
||||
// block. Do this even if nothing was cloned.
|
||||
if (!sinkProj.getInBlockDefs().empty())
|
||||
NeedToUpdateSSA = true;
|
||||
|
||||
auto nextII = std::next(ii);
|
||||
recursivelyDeleteTriviallyDeadInstructions(
|
||||
&*ii, false, [&nextII](SILInstruction *deadInst) {
|
||||
if (deadInst->getIterator() == nextII)
|
||||
++nextII;
|
||||
});
|
||||
ii = nextII;
|
||||
}
|
||||
|
||||
// Don't jump thread through a potential header - this can produce irreducible
|
||||
// control flow. Still, we make an exception for switch_enum.
|
||||
bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0);
|
||||
@@ -1016,19 +980,31 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it looks potentially interesting, decide whether we *can* do the
|
||||
// operation and whether the block is small enough to be worth duplicating.
|
||||
int copyCosts = 0;
|
||||
BasicBlockCloner Cloner(DestBB);
|
||||
for (auto &inst : *DestBB) {
|
||||
copyCosts += getThreadingCost(&inst);
|
||||
if (ThreadingBudget <= copyCosts)
|
||||
return false;
|
||||
|
||||
// If this is an address projection with outside uses, sink it before
|
||||
// checking for SSA update.
|
||||
if (!Cloner.canCloneInstruction(&inst))
|
||||
return false;
|
||||
}
|
||||
LLVM_DEBUG(llvm::dbgs() << "jump thread from bb" << SrcBB->getDebugID()
|
||||
<< " to bb" << DestBB->getDebugID() << '\n');
|
||||
|
||||
JumpThreadingCost[DestBB] += copyCosts;
|
||||
|
||||
// Okay, it looks like we want to do this and we can. Duplicate the
|
||||
// destination block into this one, rewriting uses of the BBArgs to use the
|
||||
// branch arguments as we go.
|
||||
BasicBlockCloner Cloner(DestBB);
|
||||
// Duplicate the destination block into this one, rewriting uses of the BBArgs
|
||||
// to use the branch arguments as we go.
|
||||
Cloner.cloneBranchTarget(BI);
|
||||
|
||||
// Does not currently update DominanceInfo.
|
||||
Cloner.splitCriticalEdges(nullptr, nullptr);
|
||||
Cloner.updateSSAAfterCloning();
|
||||
|
||||
// Once all the instructions are copied, we can nuke BI itself. We also add
|
||||
// the threaded and edge block to the worklist now that they (likely) can be
|
||||
@@ -1036,9 +1012,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
|
||||
addToWorklist(SrcBB);
|
||||
addToWorklist(Cloner.getNewBB());
|
||||
|
||||
if (NeedToUpdateSSA)
|
||||
updateSSAAfterCloning(Cloner, Cloner.getNewBB(), DestBB);
|
||||
|
||||
// We may be able to simplify DestBB now that it has one fewer predecessor.
|
||||
simplifyAfterDroppingPredecessor(DestBB);
|
||||
|
||||
@@ -2742,21 +2715,23 @@ bool SimplifyCFG::tailDuplicateObjCMethodCallSuccessorBlocks() {
|
||||
for (auto *BB : ObjCBlocks) {
|
||||
auto *Branch = cast<BranchInst>(BB->getTerminator());
|
||||
auto *DestBB = Branch->getDestBB();
|
||||
Changed = true;
|
||||
|
||||
// Okay, it looks like we want to do this and we can. Duplicate the
|
||||
// destination block into this one, rewriting uses of the BBArgs to use the
|
||||
// branch arguments as we go.
|
||||
BasicBlockCloner Cloner(DestBB);
|
||||
if (!Cloner.canCloneBlock())
|
||||
continue;
|
||||
|
||||
Cloner.cloneBranchTarget(Branch);
|
||||
|
||||
// Does not currently update DominanceInfo.
|
||||
Cloner.splitCriticalEdges(nullptr, nullptr);
|
||||
Cloner.updateSSAAfterCloning();
|
||||
|
||||
updateSSAAfterCloning(Cloner, Cloner.getNewBB(), DestBB);
|
||||
Changed = true;
|
||||
addToWorklist(Cloner.getNewBB());
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user