mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +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:
@@ -68,11 +68,19 @@ bool swift::removeUnreachableBlocks(SILFunction &f) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// Helper function to perform SSA updates in case of jump threading.
|
||||
void swift::updateSSAAfterCloning(BasicBlockCloner &cloner,
|
||||
SILBasicBlock *srcBB, SILBasicBlock *destBB) {
|
||||
void BasicBlockCloner::updateSSAAfterCloning() {
|
||||
// All instructions should have been checked by canCloneInstruction. But we
|
||||
// still need to check the arguments.
|
||||
for (auto arg : origBB->getArguments()) {
|
||||
if ((needsSSAUpdate |= isUsedOutsideOfBlock(arg))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!needsSSAUpdate)
|
||||
return;
|
||||
|
||||
SILSSAUpdater ssaUpdater;
|
||||
for (auto availValPair : cloner.AvailVals) {
|
||||
for (auto availValPair : availVals) {
|
||||
ValueBase *inst = availValPair.first;
|
||||
if (inst->use_empty())
|
||||
continue;
|
||||
@@ -85,20 +93,20 @@ void swift::updateSSAAfterCloning(BasicBlockCloner &cloner,
|
||||
useList.push_back(UseWrapper(use));
|
||||
|
||||
ssaUpdater.Initialize(inst->getType());
|
||||
ssaUpdater.AddAvailableValue(destBB, inst);
|
||||
ssaUpdater.AddAvailableValue(srcBB, newResult);
|
||||
ssaUpdater.AddAvailableValue(origBB, inst);
|
||||
ssaUpdater.AddAvailableValue(getNewBB(), newResult);
|
||||
|
||||
if (useList.empty())
|
||||
continue;
|
||||
|
||||
// Update all the uses.
|
||||
for (auto useWrapper : useList) {
|
||||
Operand *use = useWrapper;
|
||||
Operand *use = useWrapper; // unwrap
|
||||
SILInstruction *user = use->getUser();
|
||||
assert(user && "Missing user");
|
||||
|
||||
// Ignore uses in the same basic block.
|
||||
if (user->getParent() == destBB)
|
||||
if (user->getParent() == origBB)
|
||||
continue;
|
||||
|
||||
ssaUpdater.RewriteUse(*use);
|
||||
@@ -128,6 +136,29 @@ bool BasicBlockCloner::splitCriticalEdges(DominanceInfo *domInfo,
|
||||
return changed;
|
||||
}
|
||||
|
||||
void BasicBlockCloner::sinkAddressProjections() {
|
||||
// Because the address projections chains will be disjoint (an instruction
|
||||
// in one chain cannot use the result of an instruction in another chain),
|
||||
// the order they are sunk does not matter.
|
||||
for (auto ii = origBB->begin(), ie = origBB->end(); ii != ie;) {
|
||||
bool canSink = sinkProj.analyzeAddressProjections(&*ii);
|
||||
(void)canSink;
|
||||
assert(canSink && "canCloneInstruction should catch this.");
|
||||
|
||||
sinkProj.cloneProjections();
|
||||
assert((sinkProj.getInBlockDefs().empty() || needsSSAUpdate)
|
||||
&& "canCloneInstruction should catch this.");
|
||||
|
||||
auto nextII = std::next(ii);
|
||||
recursivelyDeleteTriviallyDeadInstructions(
|
||||
&*ii, false, [&nextII](SILInstruction *deadInst) {
|
||||
if (deadInst->getIterator() == nextII)
|
||||
++nextII;
|
||||
});
|
||||
ii = nextII;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate 'projections' with the chain of address projections leading
|
||||
// to and including 'inst'.
|
||||
//
|
||||
@@ -149,7 +180,7 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) {
|
||||
return true;
|
||||
}
|
||||
if (auto *addressProj = dyn_cast<SingleValueInstruction>(def)) {
|
||||
if (addressProj->isTriviallyDuplicatable()) {
|
||||
if (addressProj->isPure()) {
|
||||
projections.push_back(addressProj);
|
||||
return true;
|
||||
}
|
||||
@@ -183,19 +214,40 @@ bool SinkAddressProjections::cloneProjections() {
|
||||
return false;
|
||||
|
||||
SILBasicBlock *bb = projections.front()->getParent();
|
||||
SmallVector<Operand *, 4> usesToReplace;
|
||||
// Clone projections in last-to-first order.
|
||||
for (unsigned idx = 0; idx < projections.size(); ++idx) {
|
||||
auto *oldProj = projections[idx];
|
||||
assert(oldProj->getParent() == bb);
|
||||
// Reset transient per-projection sets.
|
||||
usesToReplace.clear();
|
||||
firstBlockUse.clear();
|
||||
// Gather uses.
|
||||
for (Operand *use : oldProj->getUses()) {
|
||||
if (use->getUser()->getParent() != bb)
|
||||
auto *useBB = use->getUser()->getParent();
|
||||
if (useBB != bb) {
|
||||
firstBlockUse.try_emplace(useBB, use);
|
||||
usesToReplace.push_back(use);
|
||||
}
|
||||
}
|
||||
// Replace uses. Uses must be handled in the same order they were discovered
|
||||
// above.
|
||||
//
|
||||
// Avoid cloning a projection multiple times per block. This avoids extra
|
||||
// projections, but also prevents the removal of DebugValue. If a
|
||||
// projection's only remaining is DebugValue, then it is deleted along with
|
||||
// the DebugValue.
|
||||
for (Operand *use : usesToReplace) {
|
||||
auto *newProj = oldProj->clone(use->getUser());
|
||||
use->set(cast<SingleValueInstruction>(newProj));
|
||||
auto *useBB = use->getUser()->getParent();
|
||||
auto *firstUse = firstBlockUse.lookup(useBB);
|
||||
SingleValueInstruction *newProj;
|
||||
if (use == firstUse)
|
||||
newProj = cast<SingleValueInstruction>(oldProj->clone(use->getUser()));
|
||||
else {
|
||||
newProj = cast<SingleValueInstruction>(firstUse->get());
|
||||
assert(newProj->getParent() == useBB);
|
||||
newProj->moveFront(useBB);
|
||||
}
|
||||
use->set(newProj);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user