Skip to content

Commit 24799e1

Browse files
committed
SIL: defer instruction deletion to the end of a pass run.
When an instruction is "deleted" from the SIL, it is put into the SILModule::scheduledForDeletion list. The instructions in this list are eventually deleted for real in SILModule::flushDeletedInsts(), which is called by the pass manager after each pass run. In other words: instruction deletion is deferred to the end of a pass. This avoids dangling instruction pointers within the run of a pass and in analysis caches. Note that the analysis invalidation mechanism ensures that analysis caches are invalidated before flushDeletedInsts().
1 parent ef306ca commit 24799e1

File tree

14 files changed

+120
-81
lines changed

14 files changed

+120
-81
lines changed

include/swift/SIL/SILBasicBlock.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
127127
void push_back(SILInstruction *I);
128128
void push_front(SILInstruction *I);
129129
void remove(SILInstruction *I);
130-
iterator erase(SILInstruction *I);
130+
void erase(SILInstruction *I);
131+
void erase(SILInstruction *I, SILModule &module);
132+
133+
void eraseAllInstructions(SILModule &module);
131134

132135
SILInstruction &back() { return InstList.back(); }
133136
const SILInstruction &back() const {
@@ -439,8 +442,6 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
439442
I.dropAllReferences();
440443
}
441444

442-
void eraseInstructions();
443-
444445
private:
445446
friend class SILArgument;
446447

include/swift/SIL/SILFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,10 @@ class SILFunction
981981

982982
void clear();
983983

984+
/// Like `clear`, but does not call `dropAllReferences`, which is the
985+
/// responsibility of the caller.
986+
void eraseAllBlocks();
987+
984988
/// Return the identity substitutions necessary to forward this call if it is
985989
/// generic.
986990
SubstitutionMap getForwardingSubstitutionMap();

include/swift/SIL/SILGlobalVariable.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ class SILGlobalVariable
181181
StaticInitializerBlock.dropAllReferences();
182182
}
183183

184+
void clear() {
185+
dropAllReferences();
186+
StaticInitializerBlock.eraseAllInstructions(Module);
187+
}
188+
184189
/// Return whether this variable corresponds to a Clang node.
185190
bool hasClangNode() const;
186191

include/swift/SIL/SILInstruction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
320320
friend llvm::ilist_traits<SILInstruction>;
321321
friend llvm::ilist_traits<SILBasicBlock>;
322322
friend SILBasicBlock;
323+
friend SILModule;
323324

324325
/// A backreference to the containing basic block. This is maintained by
325326
/// ilist_traits<SILInstruction>.
@@ -381,6 +382,10 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
381382
return C.allocateInst(Bytes, Alignment);
382383
}
383384

385+
/// Returns true if this instruction is removed from its function and
386+
/// scheduled to be deleted.
387+
bool isDeleted() const { return !ParentBB; }
388+
384389
enum class MemoryBehavior {
385390
None,
386391
/// The instruction may read memory.

include/swift/SIL/SILModule.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,17 @@ class SILModule {
189189
/// For consistency checking.
190190
size_t numAllocatedSlabs = 0;
191191

192+
/// When an instruction is "deleted" from the SIL, it is put into this list.
193+
/// The instructions in this list are eventually deleted for real in
194+
/// flushDeletedInsts(), which is called by the pass manager after each pass
195+
/// run.
196+
/// In other words: instruction deletion is deferred to the end of a pass.
197+
///
198+
/// This avoids dangling instruction pointers within the run of a pass and in
199+
/// analysis caches. Note that the analysis invalidation mechanism ensures
200+
/// that analysis caches are invalidated before flushDeletedInsts().
201+
llvm::iplist<SILInstruction> scheduledForDeletion;
202+
192203
/// The swift Module associated with this SILModule.
193204
ModuleDecl *TheSwiftModule;
194205

@@ -849,8 +860,17 @@ class SILModule {
849860
/// Allocate memory for an instruction using the module's internal allocator.
850861
void *allocateInst(unsigned Size, unsigned Align) const;
851862

852-
/// Deallocate memory of an instruction.
853-
void deallocateInst(SILInstruction *I);
863+
/// Called before \p I is removed from its basic block and scheduled for
864+
/// deletion.
865+
void willDeleteInstruction(SILInstruction *I);
866+
867+
/// Schedules the (already removed) instruction \p I for deletion.
868+
/// See scheduledForDeletion for details.
869+
void scheduleForDeletion(SILInstruction *I);
870+
871+
/// Deletes all scheuled instructions for real.
872+
/// See scheduledForDeletion for details.
873+
void flushDeletedInsts();
854874

855875
/// Looks up the llvm intrinsic ID and type for the builtin function.
856876
///

lib/SIL/IR/SILBasicBlock.cpp

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,35 +41,8 @@ SILBasicBlock::~SILBasicBlock() {
4141
return;
4242
}
4343

44-
SILModule &M = getModule();
45-
46-
// Invalidate all of the basic block arguments.
47-
for (auto *Arg : ArgumentList) {
48-
M.notifyDeleteHandlers(Arg);
49-
}
50-
5144
dropAllReferences();
52-
53-
for (auto I = begin(), E = end(); I != E;) {
54-
auto Inst = &*I;
55-
++I;
56-
erase(Inst);
57-
}
58-
assert(InstList.empty());
59-
}
60-
61-
void SILBasicBlock::clearStaticInitializerBlock(SILModule &module) {
62-
assert(!getParent() && "not a global variable's static initializer block");
63-
assert(ArgumentList.empty() &&
64-
"a static initializer block must not have arguments");
65-
66-
for (auto I = begin(), E = end(); I != E;) {
67-
SILInstruction *Inst = &*I;
68-
++I;
69-
InstList.erase(Inst);
70-
module.deallocateInst(Inst);
71-
}
72-
assert(InstList.empty());
45+
eraseAllInstructions(getModule());
7346
}
7447

7548
int SILBasicBlock::getDebugID() const {
@@ -104,22 +77,21 @@ void SILBasicBlock::remove(SILInstruction *I) {
10477
InstList.remove(I);
10578
}
10679

107-
void SILBasicBlock::eraseInstructions() {
108-
for (auto It = begin(); It != end();) {
109-
auto *Inst = &*It++;
110-
Inst->replaceAllUsesOfAllResultsWithUndef();
111-
Inst->eraseFromParent();
80+
void SILBasicBlock::eraseAllInstructions(SILModule &module) {
81+
while (!empty()) {
82+
erase(&*begin(), module);
11283
}
11384
}
11485

11586
/// Returns the iterator following the erased instruction.
116-
SILBasicBlock::iterator SILBasicBlock::erase(SILInstruction *I) {
117-
// Notify the delete handlers that this instruction is going away.
118-
SILModule &module = getModule();
119-
module.notifyDeleteHandlers(I->asSILNode());
120-
auto nextIter = InstList.erase(I);
121-
module.deallocateInst(I);
122-
return nextIter;
87+
void SILBasicBlock::erase(SILInstruction *I) {
88+
erase(I, getModule());
89+
}
90+
91+
void SILBasicBlock::erase(SILInstruction *I, SILModule &module) {
92+
module.willDeleteInstruction(I);
93+
InstList.remove(I);
94+
module.scheduleForDeletion(I);
12395
}
12496

12597
/// This method unlinks 'self' from the containing SILFunction and deletes it.

lib/SIL/IR/SILFunction.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,7 @@ SILFunction::~SILFunction() {
201201

202202
auto &M = getModule();
203203
for (auto &BB : *this) {
204-
for (auto I = BB.begin(), E = BB.end(); I != E;) {
205-
auto Inst = &*I;
206-
++I;
207-
SILInstruction::destroy(Inst);
208-
// TODO: It is only safe to directly deallocate an
209-
// instruction if this BB is being removed in scope
210-
// of destructing a SILFunction.
211-
M.deallocateInst(Inst);
212-
}
213-
BB.InstList.clearAndLeakNodesUnsafely();
204+
BB.eraseAllInstructions(M);
214205
}
215206

216207
assert(RefCount == 0 &&
@@ -689,6 +680,10 @@ bool SILFunction::isExternallyUsedSymbol() const {
689680

690681
void SILFunction::clear() {
691682
dropAllReferences();
683+
eraseAllBlocks();
684+
}
685+
686+
void SILFunction::eraseAllBlocks() {
692687
BlockList.clear();
693688
}
694689

lib/SIL/IR/SILGlobalVariable.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ SILGlobalVariable::SILGlobalVariable(SILModule &Module, SILLinkage Linkage,
5858
}
5959

6060
SILGlobalVariable::~SILGlobalVariable() {
61-
StaticInitializerBlock.dropAllReferences();
62-
StaticInitializerBlock.clearStaticInitializerBlock(Module);
61+
clear();
6362
}
6463

6564
bool SILGlobalVariable::isPossiblyUsedExternally() const {

lib/SIL/IR/SILInstruction.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ void llvm::ilist_traits<SILInstruction>::addNodeToList(SILInstruction *I) {
6868

6969
void llvm::ilist_traits<SILInstruction>::removeNodeFromList(SILInstruction *I) {
7070
// When an instruction is removed from a BB, clear the parent pointer.
71-
assert(I->ParentBB && "Not in a list!");
7271
I->ParentBB = nullptr;
7372
}
7473

@@ -147,6 +146,12 @@ void SILInstruction::dropAllReferences() {
147146
OpI->drop();
148147
}
149148

149+
if (auto *termInst = dyn_cast<TermInst>(this)) {
150+
for (SILSuccessor &succ : termInst->getSuccessors()) {
151+
succ = nullptr;
152+
}
153+
}
154+
150155
// If we have a function ref inst, we need to especially drop its function
151156
// argument so that it gets a proper ref decrement.
152157
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(this)) {

lib/SIL/IR/SILModule.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,9 @@ SILModule::~SILModule() {
126126
assert(!hasUnresolvedOpenedArchetypeDefinitions());
127127

128128
// Decrement ref count for each SILGlobalVariable with static initializers.
129-
for (SILGlobalVariable &v : silGlobals)
130-
v.dropAllReferences();
129+
for (SILGlobalVariable &v : silGlobals) {
130+
v.clear();
131+
}
131132

132133
for (auto vt : vtables)
133134
vt->~SILVTable();
@@ -143,10 +144,16 @@ SILModule::~SILModule() {
143144
F.dropDynamicallyReplacedFunction();
144145
F.clearSpecializeAttrs();
145146
}
147+
148+
for (SILFunction &F : *this) {
149+
F.eraseAllBlocks();
150+
}
151+
flushDeletedInsts();
146152
}
147153

148154
void SILModule::checkForLeaks() const {
149-
int instsInModule = 0;
155+
int instsInModule = std::distance(scheduledForDeletion.begin(),
156+
scheduledForDeletion.end());
150157
for (const SILFunction &F : *this) {
151158
for (const SILBasicBlock &block : F) {
152159
instsInModule += std::distance(block.begin(), block.end());
@@ -231,8 +238,30 @@ void *SILModule::allocateInst(unsigned Size, unsigned Align) const {
231238
return AlignedAlloc(Size, Align);
232239
}
233240

234-
void SILModule::deallocateInst(SILInstruction *I) {
235-
AlignedFree(I);
241+
void SILModule::willDeleteInstruction(SILInstruction *I) {
242+
// Update openedArchetypeDefs.
243+
if (auto *svi = dyn_cast<SingleValueInstruction>(I)) {
244+
if (CanArchetypeType archeTy = svi->getOpenedArchetype()) {
245+
OpenedArchetypeKey key = {archeTy, svi->getFunction()};
246+
assert(openedArchetypeDefs.lookup(key) == svi &&
247+
"archetype def was not registered");
248+
openedArchetypeDefs.erase(key);
249+
}
250+
}
251+
}
252+
253+
void SILModule::scheduleForDeletion(SILInstruction *I) {
254+
I->dropAllReferences();
255+
scheduledForDeletion.push_back(I);
256+
I->ParentBB = nullptr;
257+
}
258+
259+
void SILModule::flushDeletedInsts() {
260+
while (!scheduledForDeletion.empty()) {
261+
SILInstruction *inst = &*scheduledForDeletion.begin();
262+
scheduledForDeletion.erase(inst);
263+
AlignedFree(inst);
264+
}
236265
}
237266

238267
SILWitnessTable *

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,9 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) {
17671767
op->set(beginAccess->getSource());
17681768
}
17691769
}
1770-
return beginAccess->getParent()->erase(beginAccess);
1770+
auto nextIter = std::next(beginAccess->getIterator());
1771+
beginAccess->getParent()->erase(beginAccess);
1772+
return nextIter;
17711773
}
17721774

17731775
//===----------------------------------------------------------------------===//

lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ struct AccessMarkerElimination {
5353
}
5454

5555
SILBasicBlock::iterator eraseInst(SILInstruction *inst) {
56+
auto nextIter = std::next(inst->getIterator());
5657
notifyErased(inst);
57-
return inst->getParent()->erase(inst);
58+
return nextIter;
5859
};
5960

6061
bool shouldPreserveAccess(SILAccessEnforcement enforcement);

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) {
473473
}
474474
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
475475
Mod->removeDeleteNotificationHandler(SFT);
476+
Mod->flushDeletedInsts();
476477

477478
auto Delta = (std::chrono::system_clock::now() - StartTime).count();
478479
if (SILPrintPassTime) {
@@ -619,6 +620,7 @@ void SILPassManager::runModulePass(unsigned TransIdx) {
619620
SMT->run();
620621
Mod->removeDeleteNotificationHandler(SMT);
621622
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
623+
Mod->flushDeletedInsts();
622624

623625
auto Delta = (std::chrono::system_clock::now() - StartTime).count();
624626
if (SILPrintPassTime) {

lib/SILOptimizer/Transforms/Outliner.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) {
306306
return FunctionType;
307307
}
308308

309+
static void eraseBlock(SILBasicBlock *block) {
310+
for (SILInstruction &inst : *block) {
311+
inst.replaceAllUsesOfAllResultsWithUndef();
312+
}
313+
block->eraseFromParent();
314+
}
315+
309316
std::pair<SILFunction *, SILBasicBlock::iterator>
310317
BridgedProperty::outline(SILModule &M) {
311318
// Get the function type.
@@ -362,14 +369,10 @@ BridgedProperty::outline(SILModule &M) {
362369
// Delete the outlined instructions/blocks.
363370
if (Release)
364371
Release->eraseFromParent();
365-
OutlinedEntryBB->eraseInstructions();
366-
OutlinedEntryBB->eraseFromParent();
367-
switchInfo.NoneBB->eraseInstructions();
368-
switchInfo.NoneBB->eraseFromParent();
369-
switchInfo.SomeBB->eraseInstructions();
370-
switchInfo.SomeBB->eraseFromParent();
371-
OldMergeBB->eraseInstructions();
372-
OldMergeBB->eraseFromParent();
372+
eraseBlock(OutlinedEntryBB);
373+
eraseBlock(switchInfo.NoneBB);
374+
eraseBlock(switchInfo.SomeBB);
375+
eraseBlock(OldMergeBB);
373376
return std::make_pair(nullptr, std::prev(StartBB->end()));
374377
}
375378

@@ -904,14 +907,10 @@ void BridgedReturn::outline(SILFunction *Fun, ApplyInst *NewOutlinedCall) {
904907
// Outlined function already existed. Just delete instructions and wire up
905908
// blocks.
906909
if (!Fun) {
907-
OutlinedEntryBB->eraseInstructions();
908-
OutlinedEntryBB->eraseFromParent();
909-
switchInfo.NoneBB->eraseInstructions();
910-
switchInfo.NoneBB->eraseFromParent();
911-
switchInfo.SomeBB->eraseInstructions();
912-
switchInfo.SomeBB->eraseFromParent();
913-
OldMergeBB->eraseInstructions();
914-
OldMergeBB->eraseFromParent();
910+
eraseBlock(OutlinedEntryBB);
911+
eraseBlock(switchInfo.NoneBB);
912+
eraseBlock(switchInfo.SomeBB);
913+
eraseBlock(OldMergeBB);
915914
return;
916915
}
917916

0 commit comments

Comments
 (0)