Skip to content

Commit 9fb7769

Browse files
authored
Merge pull request swiftlang#33888 from eeckstein/fix-leaks
SIL: fix some memory leaks and add verification for leaked instructions
2 parents e9650cf + 56c857a commit 9fb7769

17 files changed

+236
-96
lines changed

include/swift/SIL/SILBasicBlock.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
7676
/// This method unlinks 'self' from the containing SILFunction and deletes it.
7777
void eraseFromParent();
7878

79+
/// Remove all instructions of a SILGlobalVariable's static initializer block.
80+
void clearStaticInitializerBlock(SILModule &module);
81+
7982
//===--------------------------------------------------------------------===//
8083
// SILInstruction List Inspection and Manipulation
8184
//===--------------------------------------------------------------------===//

include/swift/SIL/SILFunction.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ class SILFunction
141141
CanSILFunctionType LoweredType;
142142

143143
/// The context archetypes of the function.
144-
GenericEnvironment *GenericEnv;
144+
GenericEnvironment *GenericEnv = nullptr;
145145

146146
/// The information about specialization.
147147
/// Only set if this function is a specialization of another function.
148-
const GenericSpecializationInformation *SpecializationInfo;
148+
const GenericSpecializationInformation *SpecializationInfo = nullptr;
149149

150150
/// The forwarding substitution map, lazily computed.
151151
SubstitutionMap ForwardingSubMap;
@@ -158,10 +158,10 @@ class SILFunction
158158
ValueDecl *ClangNodeOwner = nullptr;
159159

160160
/// The source location and scope of the function.
161-
const SILDebugScope *DebugScope;
161+
const SILDebugScope *DebugScope = nullptr;
162162

163163
/// The AST decl context of the function.
164-
DeclContext *DeclCtxt;
164+
DeclContext *DeclCtxt = nullptr;
165165

166166
/// The profiler for instrumentation based profiling, or null if profiling is
167167
/// disabled.
@@ -315,8 +315,7 @@ class SILFunction
315315
IsTransparent_t isTrans, IsSerialized_t isSerialized,
316316
ProfileCounter entryCount, IsThunk_t isThunk,
317317
SubclassScope classSubclassScope, Inline_t inlineStrategy,
318-
EffectsKind E, SILFunction *insertBefore,
319-
const SILDebugScope *debugScope,
318+
EffectsKind E, const SILDebugScope *debugScope,
320319
IsDynamicallyReplaceable_t isDynamic,
321320
IsExactSelfClass_t isExactSelfClass);
322321

@@ -334,6 +333,18 @@ class SILFunction
334333
SILFunction *InsertBefore = nullptr,
335334
const SILDebugScope *DebugScope = nullptr);
336335

336+
void init(SILLinkage Linkage, StringRef Name,
337+
CanSILFunctionType LoweredType,
338+
GenericEnvironment *genericEnv,
339+
Optional<SILLocation> Loc, IsBare_t isBareSILFunction,
340+
IsTransparent_t isTrans, IsSerialized_t isSerialized,
341+
ProfileCounter entryCount, IsThunk_t isThunk,
342+
SubclassScope classSubclassScope,
343+
Inline_t inlineStrategy, EffectsKind E,
344+
const SILDebugScope *DebugScope,
345+
IsDynamicallyReplaceable_t isDynamic,
346+
IsExactSelfClass_t isExactSelfClass);
347+
337348
/// Set has ownership to the given value. True means that the function has
338349
/// ownership, false means it does not.
339350
///

include/swift/SIL/SILGlobalVariable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public ilist_node_traits<::swift::SILGlobalVariable> {
233233
using SILGlobalVariable = ::swift::SILGlobalVariable;
234234

235235
public:
236-
static void deleteNode(SILGlobalVariable *V) {}
236+
static void deleteNode(SILGlobalVariable *V) { V->~SILGlobalVariable(); }
237237

238238
private:
239239
void createNode(const SILGlobalVariable &);

include/swift/SIL/SILModule.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,6 @@ class SILModule {
171171
/// but kept alive for debug info generation.
172172
FunctionListType zombieFunctions;
173173

174-
/// Stores the names of zombie functions.
175-
llvm::BumpPtrAllocator zombieFunctionNames;
176-
177174
/// Lookup table for SIL vtables from class decls.
178175
llvm::DenseMap<const ClassDecl *, SILVTable *> VTableMap;
179176

@@ -340,7 +337,7 @@ class SILModule {
340337
/// Specialization can cause a function that was erased before by dead function
341338
/// elimination to become alive again. If this happens we need to remove it
342339
/// from the list of zombies.
343-
void removeFromZombieList(StringRef Name);
340+
SILFunction *removeFromZombieList(StringRef Name);
344341

345342
/// Erase a global SIL variable from the module.
346343
void eraseGlobalVariable(SILGlobalVariable *G);
@@ -653,6 +650,19 @@ class SILModule {
653650
/// invariants.
654651
void verify() const;
655652

653+
/// Check if there are any leaking instructions.
654+
///
655+
/// Aborts with an error if more instructions are allocated than contained in
656+
/// the module.
657+
void checkForLeaks() const;
658+
659+
/// Check if there are any leaking instructions after the SILModule is
660+
/// destructed.
661+
///
662+
/// The SILModule destructor already calls checkForLeaks(). This function is
663+
/// useful to check if the destructor itself destroys all data structures.
664+
static void checkForLeaksAfterDestruction();
665+
656666
/// Pretty-print the module.
657667
void dump(bool Verbose = false) const;
658668

lib/IRGen/IRGen.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,10 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
10091009

10101010
// Free the memory occupied by the SILModule.
10111011
// Execute this task in parallel to the embedding of bitcode.
1012-
auto SILModuleRelease = [&SILMod]() { SILMod.reset(nullptr); };
1012+
auto SILModuleRelease = [&SILMod]() {
1013+
SILMod.reset(nullptr);
1014+
SILModule::checkForLeaksAfterDestruction();
1015+
};
10131016
auto Thread = std::thread(SILModuleRelease);
10141017
// Wait for the thread to terminate.
10151018
SWIFT_DEFER { Thread.join(); };
@@ -1307,7 +1310,10 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {
13071310

13081311
// Free the memory occupied by the SILModule.
13091312
// Execute this task in parallel to the LLVM compilation.
1310-
auto SILModuleRelease = [&SILMod]() { SILMod.reset(nullptr); };
1313+
auto SILModuleRelease = [&SILMod]() {
1314+
SILMod.reset(nullptr);
1315+
SILModule::checkForLeaksAfterDestruction();
1316+
};
13111317
auto releaseModuleThread = std::thread(SILModuleRelease);
13121318

13131319
codeGenThreads.runMainThread();

lib/SIL/IR/SILBasicBlock.cpp

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,43 @@ SILBasicBlock::SILBasicBlock(SILFunction *parent, SILBasicBlock *relativeToBB,
4343
}
4444
}
4545
SILBasicBlock::~SILBasicBlock() {
46+
if (!getParent()) {
47+
assert(ArgumentList.empty() &&
48+
"a static initializer block must not have arguments");
49+
assert(InstList.empty() &&
50+
"a static initializer block must be cleared before the destructor");
51+
return;
52+
}
53+
54+
SILModule &M = getModule();
55+
4656
// Invalidate all of the basic block arguments.
4757
for (auto *Arg : ArgumentList) {
48-
getModule().notifyDeleteHandlers(Arg);
58+
M.notifyDeleteHandlers(Arg);
4959
}
5060

5161
dropAllReferences();
5262

53-
SILModule *M = nullptr;
54-
if (getParent())
55-
M = &getParent()->getModule();
56-
5763
for (auto I = begin(), E = end(); I != E;) {
5864
auto Inst = &*I;
5965
++I;
60-
if (M) {
61-
// Notify the delete handlers that the instructions in this block are
62-
// being deleted.
63-
M->notifyDeleteHandlers(Inst);
64-
}
6566
erase(Inst);
6667
}
68+
assert(InstList.empty());
69+
}
70+
71+
void SILBasicBlock::clearStaticInitializerBlock(SILModule &module) {
72+
assert(!getParent() && "not a global variable's static initializer block");
73+
assert(ArgumentList.empty() &&
74+
"a static initializer block must not have arguments");
6775

68-
// iplist's destructor is going to destroy the InstList.
69-
InstList.clearAndLeakNodesUnsafely();
76+
for (auto I = begin(), E = end(); I != E;) {
77+
SILInstruction *Inst = &*I;
78+
++I;
79+
InstList.erase(Inst);
80+
module.deallocateInst(Inst);
81+
}
82+
assert(InstList.empty());
7083
}
7184

7285
int SILBasicBlock::getDebugID() const {
@@ -112,10 +125,10 @@ void SILBasicBlock::eraseInstructions() {
112125
/// Returns the iterator following the erased instruction.
113126
SILBasicBlock::iterator SILBasicBlock::erase(SILInstruction *I) {
114127
// Notify the delete handlers that this instruction is going away.
115-
getModule().notifyDeleteHandlers(&*I);
116-
auto *F = getParent();
128+
SILModule &module = getModule();
129+
module.notifyDeleteHandlers(&*I);
117130
auto nextIter = InstList.erase(I);
118-
F->getModule().deallocateInst(I);
131+
module.deallocateInst(I);
119132
return nextIter;
120133
}
121134

lib/SIL/IR/SILFunction.cpp

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,29 @@ SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name,
7272
name = entry->getKey();
7373
}
7474

75-
auto fn = new (M) SILFunction(M, linkage, name, loweredType, genericEnv, loc,
75+
SILFunction *fn = M.removeFromZombieList(name);
76+
if (fn) {
77+
// Resurrect a zombie function.
78+
// This happens for example if a specialized function gets dead and gets
79+
// deleted. And afterwards the same specialization is created again.
80+
fn->init(linkage, name, loweredType, genericEnv, loc, isBareSILFunction,
81+
isTrans, isSerialized, entryCount, isThunk, classSubclassScope,
82+
inlineStrategy, E, debugScope, isDynamic, isExactSelfClass);
83+
assert(fn->empty());
84+
} else {
85+
fn = new (M) SILFunction(M, linkage, name, loweredType, genericEnv, loc,
7686
isBareSILFunction, isTrans, isSerialized,
7787
entryCount, isThunk, classSubclassScope,
78-
inlineStrategy, E, insertBefore, debugScope,
88+
inlineStrategy, E, debugScope,
7989
isDynamic, isExactSelfClass);
80-
90+
}
8191
if (entry) entry->setValue(fn);
92+
93+
if (insertBefore)
94+
M.functions.insert(SILModule::iterator(insertBefore), fn);
95+
else
96+
M.functions.push_back(fn);
97+
8298
return fn;
8399
}
84100

@@ -90,41 +106,60 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name,
90106
ProfileCounter entryCount, IsThunk_t isThunk,
91107
SubclassScope classSubclassScope,
92108
Inline_t inlineStrategy, EffectsKind E,
93-
SILFunction *InsertBefore,
94109
const SILDebugScope *DebugScope,
95110
IsDynamicallyReplaceable_t isDynamic,
96111
IsExactSelfClass_t isExactSelfClass)
97-
: Module(Module), Name(Name), LoweredType(LoweredType),
98-
GenericEnv(genericEnv), SpecializationInfo(nullptr),
99-
EntryCount(entryCount),
100-
Availability(AvailabilityContext::alwaysAvailable()),
101-
Bare(isBareSILFunction), Transparent(isTrans),
102-
Serialized(isSerialized), Thunk(isThunk),
103-
ClassSubclassScope(unsigned(classSubclassScope)), GlobalInitFlag(false),
104-
InlineStrategy(inlineStrategy), Linkage(unsigned(Linkage)),
105-
HasCReferences(false), IsWeakImported(false),
106-
IsDynamicReplaceable(isDynamic),
107-
ExactSelfClass(isExactSelfClass),
108-
Inlined(false), Zombie(false), HasOwnership(true),
109-
WasDeserializedCanonical(false), IsWithoutActuallyEscapingThunk(false),
110-
OptMode(unsigned(OptimizationMode::NotSet)),
111-
EffectsKindAttr(unsigned(E)) {
112-
assert(!Transparent || !IsDynamicReplaceable);
113-
validateSubclassScope(classSubclassScope, isThunk, nullptr);
114-
setDebugScope(DebugScope);
115-
116-
if (InsertBefore)
117-
Module.functions.insert(SILModule::iterator(InsertBefore), this);
118-
else
119-
Module.functions.push_back(this);
120-
121-
Module.removeFromZombieList(Name);
122-
112+
: Module(Module), Availability(AvailabilityContext::alwaysAvailable()) {
113+
init(Linkage, Name, LoweredType, genericEnv, Loc, isBareSILFunction, isTrans,
114+
isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy,
115+
E, DebugScope, isDynamic, isExactSelfClass);
116+
123117
// Set our BB list to have this function as its parent. This enables us to
124118
// splice efficiently basic blocks in between functions.
125119
BlockList.Parent = this;
126120
}
127121

122+
void SILFunction::init(SILLinkage Linkage, StringRef Name,
123+
CanSILFunctionType LoweredType,
124+
GenericEnvironment *genericEnv,
125+
Optional<SILLocation> Loc, IsBare_t isBareSILFunction,
126+
IsTransparent_t isTrans, IsSerialized_t isSerialized,
127+
ProfileCounter entryCount, IsThunk_t isThunk,
128+
SubclassScope classSubclassScope,
129+
Inline_t inlineStrategy, EffectsKind E,
130+
const SILDebugScope *DebugScope,
131+
IsDynamicallyReplaceable_t isDynamic,
132+
IsExactSelfClass_t isExactSelfClass) {
133+
this->Name = Name;
134+
this->LoweredType = LoweredType;
135+
this->GenericEnv = genericEnv;
136+
this->SpecializationInfo = nullptr;
137+
this->EntryCount = entryCount;
138+
this->Availability = AvailabilityContext::alwaysAvailable();
139+
this->Bare = isBareSILFunction;
140+
this->Transparent = isTrans;
141+
this->Serialized = isSerialized;
142+
this->Thunk = isThunk;
143+
this->ClassSubclassScope = unsigned(classSubclassScope);
144+
this->GlobalInitFlag = false;
145+
this->InlineStrategy = inlineStrategy;
146+
this->Linkage = unsigned(Linkage);
147+
this->HasCReferences = false;
148+
this->IsWeakImported = false;
149+
this->IsDynamicReplaceable = isDynamic;
150+
this->ExactSelfClass = isExactSelfClass;
151+
this->Inlined = false;
152+
this->Zombie = false;
153+
this->HasOwnership = true,
154+
this->WasDeserializedCanonical = false;
155+
this->IsWithoutActuallyEscapingThunk = false;
156+
this->OptMode = unsigned(OptimizationMode::NotSet);
157+
this->EffectsKindAttr = unsigned(E);
158+
assert(!Transparent || !IsDynamicReplaceable);
159+
validateSubclassScope(classSubclassScope, isThunk, nullptr);
160+
setDebugScope(DebugScope);
161+
}
162+
128163
SILFunction::~SILFunction() {
129164
// If the function is recursive, a function_ref inst inside of the function
130165
// will give the function a non-zero ref count triggering the assertion. Thus

lib/SIL/IR/SILGlobalVariable.cpp

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

6060
SILGlobalVariable::~SILGlobalVariable() {
61-
getModule().GlobalVariableMap.erase(Name);
61+
StaticInitializerBlock.dropAllReferences();
62+
StaticInitializerBlock.clearStaticInitializerBlock(Module);
6263
}
6364

6465
/// Get this global variable's fragile attribute.

0 commit comments

Comments
 (0)