Skip to content

Commit 0f88e0f

Browse files
committed
Rewrite instruction deletion logic in many passes
Fix innumerable latent bugs with iterator invalidation and callback invocation. Removes dead code earlier and chips away at all the redundant copies the compiler generates.
1 parent dde6a37 commit 0f88e0f

22 files changed

+195
-149
lines changed

include/swift/SIL/SILInstructionWorklist.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,15 @@ class SILInstructionWorklist : SILInstructionWorklistBase {
128128
}
129129
}
130130

131-
/// All operands of \p instruction to the worklist when performing 2 stage
132-
/// instruction deletion. Meant to be used right before deleting an
133-
/// instruction in callbacks like InstModCallback::onNotifyWillBeDeleted().
131+
/// Add operands of \p instruction to the worklist. Meant to be used once it
132+
/// is certain that \p instruction will be deleted but may have operands that
133+
/// are still alive. With fewer uses, the operand definition may be
134+
/// optimizable.
135+
///
136+
/// \p instruction may still have uses because this is called before
137+
/// InstructionDeleter begins deleting it and some instructions are deleted at
138+
/// the same time as their uses.
134139
void addOperandsToWorklist(SILInstruction &instruction) {
135-
assert(!instruction.hasUsesOfAnyResult() &&
136-
"Cannot erase instruction that is used!");
137-
138140
// Make sure that we reprocess all operands now that we reduced their
139141
// use counts.
140142
if (instruction.getNumOperands() < 8) {

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,9 @@ bool SILGlobalOpt::optimizeInitializer(SILFunction *AddrF,
457457

458458
// Remove "once" call from the addressor.
459459
removeToken(CallToOnce->getOperand(0));
460-
eraseUsesOfInstruction(CallToOnce);
461-
recursivelyDeleteTriviallyDeadInstructions(CallToOnce, true);
460+
InstructionDeleter deleter;
461+
deleter.forceDeleteWithUsers(CallToOnce);
462+
deleter.cleanupDeadInstructions();
462463

463464
// Create the constant initializer of the global variable.
464465
StaticInitCloner::appendToInitializer(SILG, InitVal);
@@ -818,11 +819,15 @@ bool SILGlobalOpt::run() {
818819
for (auto &allocPair : globalAllocPairs) {
819820
HasChanged |= tryRemoveGlobalAlloc(allocPair.first, allocPair.second);
820821
}
821-
822-
// Erase the instructions that we have marked for deletion.
823-
for (auto *inst : InstToRemove) {
824-
eraseUsesOfInstruction(inst);
825-
inst->eraseFromParent();
822+
if (HasChanged) {
823+
// Erase the instructions that we have marked for deletion.
824+
InstructionDeleter deleter;
825+
for (auto *inst : InstToRemove) {
826+
deleter.forceDeleteWithUsers(inst);
827+
}
828+
deleter.cleanupDeadInstructions();
829+
} else {
830+
assert(InstToRemove.empty());
826831
}
827832

828833
for (auto &global : Module->getSILGlobals()) {

lib/SILOptimizer/IPO/LetPropertiesOpts.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ void LetPropertiesOpt::optimizeLetPropertyAccess(VarDecl *Property,
273273
return;
274274
}
275275

276+
InstructionDeleter deleter;
277+
276278
auto &Loads = AccessMap[Property];
277279

278280
unsigned NumReplaced = 0;
@@ -302,12 +304,12 @@ void LetPropertiesOpt::optimizeLetPropertyAccess(VarDecl *Property,
302304
continue;
303305

304306
replaceLoadSequence(User, clonedInit);
305-
eraseUsesOfInstruction(User);
306-
User->eraseFromParent();
307+
deleter.forceDeleteWithUsers(User);
307308
++NumReplaced;
308309
}
309310
ChangedFunctions.insert(F);
310311
}
312+
deleter.cleanupDeadInstructions();
311313

312314
LLVM_DEBUG(llvm::dbgs() << "Access to " << *Property << " was replaced "
313315
<< NumReplaced << " time(s)\n");

lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ class ForEachLoopUnroller : public SILFunctionTransform {
635635
}
636636

637637
if (changed) {
638-
deleter.cleanUpDeadInstructions();
638+
deleter.cleanupDeadInstructions();
639639
PM->invalidateAnalysis(&fun,
640640
SILAnalysis::InvalidationKind::FunctionBody);
641641
}

lib/SILOptimizer/Mandatory/IRGenPrepare.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static bool cleanFunction(SILFunction &fn) {
6363
deleter.forceDelete(bi);
6464
// StaticReport only takes trivial operands, and therefore doesn't
6565
// require fixing the lifetime of its operands.
66-
deleter.cleanUpDeadInstructions();
66+
deleter.cleanupDeadInstructions();
6767
madeChange = true;
6868
break;
6969
}

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,16 @@ static bool fixupReferenceCounts(
264264
return invalidatedStackNesting;
265265
}
266266

267-
static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) {
268-
auto *pbi = dyn_cast<ProjectBoxInst>(li->getOperand());
267+
// Handle the case where the callee of the apply is either a load or a
268+
// project_box that was used by a deleted load. If we fail to optimize,
269+
// return an invalid SILValue.
270+
static SILValue cleanupLoadedCalleeValue(SILValue calleeValue) {
271+
auto calleeSource = calleeValue;
272+
auto *li = dyn_cast<LoadInst>(calleeValue);
273+
if (li) {
274+
calleeSource = li->getOperand();
275+
}
276+
auto *pbi = dyn_cast<ProjectBoxInst>(calleeSource);
269277
if (!pbi)
270278
return SILValue();
271279
auto *abi = dyn_cast<AllocBoxInst>(pbi->getOperand());
@@ -274,17 +282,18 @@ static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) {
274282

275283
// The load instruction must have no more uses or a single destroy left to
276284
// erase it.
277-
if (li->getFunction()->hasOwnership()) {
278-
// TODO: What if we have multiple destroy_value? That should be ok as well.
279-
auto *dvi = li->getSingleUserOfType<DestroyValueInst>();
280-
if (!dvi)
285+
if (li) {
286+
if (li->getFunction()->hasOwnership()) {
287+
// TODO: What if we have multiple destroy_value? That should be ok.
288+
auto *dvi = li->getSingleUserOfType<DestroyValueInst>();
289+
if (!dvi)
290+
return SILValue();
291+
dvi->eraseFromParent();
292+
} else if (!li->use_empty()) {
281293
return SILValue();
282-
dvi->eraseFromParent();
283-
} else if (!li->use_empty()) {
284-
return SILValue();
294+
}
295+
li->eraseFromParent();
285296
}
286-
li->eraseFromParent();
287-
288297
// Look through uses of the alloc box the load is loading from to find up to
289298
// one store and up to one strong release.
290299
PointerUnion<StrongReleaseInst *, DestroyValueInst *> destroy;
@@ -356,15 +365,8 @@ static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) {
356365
/// longer necessary after inlining.
357366
static void cleanupCalleeValue(SILValue calleeValue,
358367
bool &invalidatedStackNesting) {
359-
// Handle the case where the callee of the apply is a load instruction. If we
360-
// fail to optimize, return. Otherwise, see if we can look through other
361-
// abstractions on our callee.
362-
if (auto *li = dyn_cast<LoadInst>(calleeValue)) {
363-
calleeValue = cleanupLoadedCalleeValue(calleeValue, li);
364-
if (!calleeValue) {
365-
return;
366-
}
367-
}
368+
if (auto loadedValue = cleanupLoadedCalleeValue(calleeValue))
369+
calleeValue = loadedValue;
368370

369371
calleeValue = stripCopiesAndBorrows(calleeValue);
370372

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,8 @@ static void forceDeleteAllocStack(SingleValueInstruction *inst,
12001200
forceDeleteAllocStack(cast<BeginAccessInst>(user), deleter);
12011201
continue;
12021202
}
1203+
// Notify the deletion worklist in case user's other operands become dead.
1204+
deleter.getCallbacks().notifyWillBeDeleted(user);
12031205
deleter.forceDeleteAndFixLifetimes(user);
12041206
}
12051207
deleter.forceDelete(inst);
@@ -1252,8 +1254,11 @@ static bool tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) {
12521254
if (deletedInstructions.count(inst))
12531255
continue;
12541256
deleteInstructionWithUsersAndFixLifetimes(inst, deleter);
1257+
// Call cleanupDeadInstructions incrementally because it may expose a dead
1258+
// alloc_stack, which will only be deleted by this pass via
1259+
// deleteInstructionWithUsersAndFixLifetimes().
1260+
deleter.cleanupDeadInstructions();
12551261
}
1256-
deleter.cleanUpDeadInstructions();
12571262

12581263
// If the OSLogMessage instance is not deleted, either we couldn't see the
12591264
// body of the log call or there is a bug in the library implementation.
@@ -1478,7 +1483,7 @@ suppressGlobalStringTablePointerError(SingleValueInstruction *oslogMessage) {
14781483
// many instructions, do the cleanup at the end.
14791484
deleter.trackIfDead(bi);
14801485
}
1481-
deleter.cleanUpDeadInstructions();
1486+
deleter.cleanupDeadInstructions();
14821487
}
14831488

14841489
/// If the SILInstruction is an initialization of OSLogMessage, return the

0 commit comments

Comments
 (0)