Skip to content

Commit fa8d782

Browse files
committed
Serialization: include global variables in the worklist-processing
It's not sufficient to first serialize all functions and then serialize all globals, because a function can be referenced from the initializer expression of a global. Therefore the worklist processing must include both, functions and globals. This fixes a crash in the serializer, which is exposed through cross-module-optimization. https://bugs.swift.org/browse/SR-15162 rdar://82827256
1 parent 61cc7a6 commit fa8d782

File tree

2 files changed

+67
-27
lines changed

2 files changed

+67
-27
lines changed

lib/Serialization/SerializeSIL.cpp

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,9 @@ namespace {
222222
DifferentiabilityWitnessesToEmit;
223223

224224
/// Additional functions we might need to serialize.
225-
llvm::SmallVector<const SILFunction *, 16> Worklist;
225+
llvm::SmallVector<const SILFunction *, 16> functionWorklist;
226+
227+
llvm::SmallVector<const SILGlobalVariable *, 16> globalWorklist;
226228

227229
/// String storage for temporarily created strings which are referenced from
228230
/// the tables.
@@ -245,7 +247,9 @@ namespace {
245247
bool emitDeclarationsForOnoneSupport);
246248
void addReferencedSILFunction(const SILFunction *F,
247249
bool DeclOnly = false);
248-
void processSILFunctionWorklist();
250+
void addReferencedGlobalVariable(const SILGlobalVariable *gl);
251+
252+
void processWorklists();
249253

250254
/// Helper function to update ListOfValues for MethodInst. Format:
251255
/// Attr, SILDeclRef (DeclID, Kind, uncurryLevel), and an operand.
@@ -335,7 +339,7 @@ void SILSerializer::addMandatorySILFunction(const SILFunction *F,
335339
// Function body should be serialized unless it is a KeepAsPublic function
336340
// (which is typically a pre-specialization).
337341
if (!emitDeclarationsForOnoneSupport)
338-
Worklist.push_back(F);
342+
functionWorklist.push_back(F);
339343
}
340344

341345
void SILSerializer::addReferencedSILFunction(const SILFunction *F,
@@ -349,7 +353,7 @@ void SILSerializer::addReferencedSILFunction(const SILFunction *F,
349353
// serialize the body or just the declaration.
350354
if (shouldEmitFunctionBody(F)) {
351355
FuncsToEmit[F] = false;
352-
Worklist.push_back(F);
356+
functionWorklist.push_back(F);
353357
return;
354358
}
355359

@@ -358,23 +362,35 @@ void SILSerializer::addReferencedSILFunction(const SILFunction *F,
358362
F->hasForeignBody());
359363

360364
FuncsToEmit[F] = false;
361-
Worklist.push_back(F);
365+
functionWorklist.push_back(F);
362366
return;
363367
}
364368

365369
// Ok, we just need to emit a declaration.
366370
FuncsToEmit[F] = true;
367371
}
368372

369-
void SILSerializer::processSILFunctionWorklist() {
370-
while (!Worklist.empty()) {
371-
const SILFunction *F = Worklist.back();
372-
Worklist.pop_back();
373-
assert(F != nullptr);
373+
void SILSerializer::addReferencedGlobalVariable(const SILGlobalVariable *gl) {
374+
if (GlobalsToEmit.insert(gl).second)
375+
globalWorklist.push_back(gl);
376+
}
374377

375-
assert(FuncsToEmit.count(F) > 0);
376-
writeSILFunction(*F, FuncsToEmit[F]);
377-
}
378+
379+
void SILSerializer::processWorklists() {
380+
do {
381+
while (!functionWorklist.empty()) {
382+
const SILFunction *F = functionWorklist.pop_back_val();
383+
assert(F != nullptr);
384+
385+
assert(FuncsToEmit.count(F) > 0);
386+
writeSILFunction(*F, FuncsToEmit[F]);
387+
}
388+
while (!globalWorklist.empty()) {
389+
const SILGlobalVariable *gl = globalWorklist.pop_back_val();
390+
assert(GlobalsToEmit.count(gl) > 0);
391+
writeSILGlobalVar(*gl);
392+
}
393+
} while (!functionWorklist.empty());
378394
}
379395

380396
/// We enumerate all values in a SILFunction beforehand to correctly
@@ -1116,7 +1132,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
11161132
// Format: Name and type. Use SILOneOperandLayout.
11171133
const AllocGlobalInst *AGI = cast<AllocGlobalInst>(&SI);
11181134
auto *G = AGI->getReferencedGlobal();
1119-
GlobalsToEmit.insert(G);
1135+
addReferencedGlobalVariable(G);
11201136
SILOneOperandLayout::emitRecord(Out, ScratchRecord,
11211137
SILAbbrCodes[SILOneOperandLayout::Code],
11221138
(unsigned)SI.getKind(), 0, 0, 0,
@@ -1128,7 +1144,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
11281144
// Format: Name and type. Use SILOneOperandLayout.
11291145
const GlobalAccessInst *GI = cast<GlobalAccessInst>(&SI);
11301146
auto *G = GI->getReferencedGlobal();
1131-
GlobalsToEmit.insert(G);
1147+
addReferencedGlobalVariable(G);
11321148
SILOneOperandLayout::emitRecord(Out, ScratchRecord,
11331149
SILAbbrCodes[SILOneOperandLayout::Code],
11341150
(unsigned)SI.getKind(), 0,
@@ -2821,6 +2837,12 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) {
28212837
writeSILDefaultWitnessTable(wt);
28222838
}
28232839

2840+
// Add global variables that must be emitted to the list.
2841+
for (const SILGlobalVariable &g : SILMod->getSILGlobals()) {
2842+
if (g.isSerialized() || ShouldSerializeAll)
2843+
addReferencedGlobalVariable(&g);
2844+
}
2845+
28242846
// Emit only declarations if it is a module with pre-specializations.
28252847
// And only do it in optimized builds.
28262848
bool emitDeclarationsForOnoneSupport =
@@ -2840,7 +2862,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) {
28402862
}
28412863

28422864
addMandatorySILFunction(&F, emitDeclarationsForOnoneSupport);
2843-
processSILFunctionWorklist();
2865+
processWorklists();
28442866
}
28452867

28462868
// Write out differentiability witnesses.
@@ -2859,7 +2881,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) {
28592881
// Process SIL functions referenced by differentiability witnesses.
28602882
// Note: this is necessary despite processing `FuncsToEmit` below because
28612883
// `Worklist` is processed separately.
2862-
processSILFunctionWorklist();
2884+
processWorklists();
28632885

28642886
// Now write function declarations for every function we've
28652887
// emitted a reference to without emitting a function body for.
@@ -2873,16 +2895,8 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) {
28732895
}
28742896
}
28752897

2876-
// Add global variables that must be emitted to the list.
2877-
for (const SILGlobalVariable &g : SILMod->getSILGlobals())
2878-
if (g.isSerialized() || ShouldSerializeAll)
2879-
GlobalsToEmit.insert(&g);
2880-
2881-
// Now write out all referenced global variables.
2882-
for (auto *g : GlobalsToEmit)
2883-
writeSILGlobalVar(*g);
2884-
2885-
assert(Worklist.empty() && "Did not emit everything in worklist");
2898+
assert(functionWorklist.empty() && globalWorklist.empty() &&
2899+
"Did not emit everything in worklists");
28862900
}
28872901

28882902
void SILSerializer::writeSILModule(const SILModule *SILMod) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-sil-opt %s -serialize -o /dev/null
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
7+
// Check that we don't crash if a serialiable function is referenced from the
8+
// initializer of a global variable.
9+
10+
sil_global [serialized] @globalFuncPtr : $@callee_guaranteed () -> () = {
11+
%0 = function_ref @calledFromFuncPtr : $@convention(thin) () -> ()
12+
%initval = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_guaranteed () -> ()
13+
}
14+
15+
sil @caller : $@convention(thin) () -> () {
16+
bb0:
17+
%0 = global_addr @globalFuncPtr : $*@callee_guaranteed () -> ()
18+
%7 = tuple ()
19+
return %7 : $()
20+
}
21+
22+
sil shared [serializable] @calledFromFuncPtr : $@convention(thin) () -> () {
23+
bb0:
24+
%6 = tuple ()
25+
return %6 : $()
26+
}

0 commit comments

Comments
 (0)