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:
Andrew Trick
2019-09-30 13:56:17 -07:00
parent 1ca57e06d7
commit 38c29e231e
9 changed files with 373 additions and 141 deletions

View File

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