Skip to content

Commit 37c461c

Browse files
committed
Serialization: Serialize shared thunks referenced from fragile functions
If a thunk is referenced from two different functions, the thunk inherits the fragile attribute from the first function that forced it to be emitted. This is wrong, in case the first function might not be fragile, while the second one is. Copying the fragile attribute to an existing thunk when checking if it has already been emitted is also wrong, because the thunk might reference another thunk, and so on. The correct fix is to have SIL serialization serialize the transitive closure of all fragile functions and thunks referenced from fragile functions. Re-work SIL function serialization to use a worklist so that we can do this. Part of https://bugs.swift.org/browse/SR-267.
1 parent 98c9a9c commit 37c461c

File tree

4 files changed

+156
-40
lines changed

4 files changed

+156
-40
lines changed

lib/SIL/SILVerifier.cpp

+13-8
Original file line numberDiff line numberDiff line change
@@ -912,28 +912,33 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
912912

913913
case SILLinkage::Shared:
914914
case SILLinkage::SharedExternal:
915-
// This handles some kind of generated functions, like constructors
916-
// of clang imported types.
917-
// TODO: check why those functions are not fragile anyway and make
918-
// a less conservative check here.
919-
return true;
915+
// This allows fragile functions to reference thunks, generated
916+
// methods on Clang types, and optimizer specializations.
917+
return true;
920918

921919
case SILLinkage::Public:
922920
case SILLinkage::PublicExternal:
923921
return true;
924922
}
925923
}
926924

925+
bool isValidLinkageForFragileRef(const SILFunction *RefF) {
926+
if (RefF->isFragile() ||
927+
RefF->isExternalDeclaration()) {
928+
return true;
929+
}
930+
931+
return isValidLinkageForFragileRef(RefF->getLinkage());
932+
}
933+
927934
void checkFunctionRefInst(FunctionRefInst *FRI) {
928935
auto fnType = requireObjectType(SILFunctionType, FRI,
929936
"result of function_ref");
930937
require(!fnType->getExtInfo().hasContext(),
931938
"function_ref should have a context-free function result");
932939
if (F.isFragile()) {
933940
SILFunction *RefF = FRI->getReferencedFunction();
934-
require(RefF->isFragile()
935-
|| isValidLinkageForFragileRef(RefF->getLinkage())
936-
|| RefF->isExternalDeclaration(),
941+
require(isValidLinkageForFragileRef(RefF),
937942
"function_ref inside fragile function cannot "
938943
"reference a private or hidden symbol");
939944
}

lib/Serialization/SerializeSIL.cpp

+107-28
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,14 @@ namespace {
157157
uint32_t /*DeclID*/ NextDefaultWitnessTableID = 1;
158158

159159
/// Give each SILBasicBlock a unique ID.
160-
llvm::DenseMap<const SILBasicBlock*, unsigned> BasicBlockMap;
160+
llvm::DenseMap<const SILBasicBlock *, unsigned> BasicBlockMap;
161161

162-
/// Functions that we've emitted a reference to.
163-
llvm::SmallSet<const SILFunction *, 16> FuncsToDeclare;
162+
/// Functions that we've emitted a reference to. If the key maps
163+
/// to true, we want to emit a declaration only.
164+
llvm::DenseMap<const SILFunction *, bool> FuncsToEmit;
165+
166+
/// Additional functions we might need to serialize.
167+
llvm::SmallVector<const SILFunction *, 16> Worklist;
164168

165169
std::array<unsigned, 256> SILAbbrCodes;
166170
template <typename Layout>
@@ -175,6 +179,12 @@ namespace {
175179

176180
bool ShouldSerializeAll;
177181

182+
void addMandatorySILFunction(const SILFunction *F,
183+
bool emitDeclarationsForOnoneSupport);
184+
void addReferencedSILFunction(const SILFunction *F,
185+
bool DeclOnly = false);
186+
void processSILFunctionWorklist();
187+
178188
/// Helper function to update ListOfValues for MethodInst. Format:
179189
/// Attr, SILDeclRef (DeclID, Kind, uncurryLevel, IsObjC), and an operand.
180190
void handleMethodInst(const MethodInst *MI, SILValue operand,
@@ -207,7 +217,7 @@ namespace {
207217

208218
/// Helper function to determine if given the current state of the
209219
/// deserialization if the function body for F should be deserialized.
210-
bool shouldEmitFunctionBody(const SILFunction &F);
220+
bool shouldEmitFunctionBody(const SILFunction *F);
211221

212222
public:
213223
SILSerializer(Serializer &S, ASTContext &Ctx,
@@ -218,6 +228,69 @@ namespace {
218228
};
219229
} // end anonymous namespace
220230

231+
void SILSerializer::addMandatorySILFunction(const SILFunction *F,
232+
bool emitDeclarationsForOnoneSupport) {
233+
// If this function is not fragile, don't do anything.
234+
if (!shouldEmitFunctionBody(F))
235+
return;
236+
237+
auto iter = FuncsToEmit.find(F);
238+
if (iter != FuncsToEmit.end()) {
239+
// We've already visited this function. Make sure that we decided
240+
// to emit its body the first time around.
241+
assert(iter->second == emitDeclarationsForOnoneSupport
242+
&& "Already emitting declaration");
243+
return;
244+
}
245+
246+
// We haven't seen this function before. Record that we want to
247+
// emit its body, and add it to the worklist.
248+
FuncsToEmit[F] = emitDeclarationsForOnoneSupport;
249+
if (!emitDeclarationsForOnoneSupport)
250+
Worklist.push_back(F);
251+
}
252+
253+
void SILSerializer::addReferencedSILFunction(const SILFunction *F,
254+
bool DeclOnly) {
255+
assert(F != nullptr);
256+
257+
if (FuncsToEmit.count(F) > 0)
258+
return;
259+
260+
// We haven't seen this function before. Let's see if we should
261+
// serialize the body or just the declaration.
262+
if (shouldEmitFunctionBody(F)) {
263+
FuncsToEmit[F] = false;
264+
Worklist.push_back(F);
265+
return;
266+
}
267+
268+
// If we referenced a non-fragile shared function from a fragile
269+
// function, serialize it too. In practice, it will either be a
270+
// thunk, or an optimizer specialization. In both cases, we don't
271+
// have enough information at the time we emit the function to
272+
// know if it should be marked fragile or not.
273+
if (F->getLinkage() == SILLinkage::Shared && !DeclOnly) {
274+
FuncsToEmit[F] = false;
275+
Worklist.push_back(F);
276+
return;
277+
}
278+
279+
// Ok, we just need to emit a declaration.
280+
FuncsToEmit[F] = true;
281+
}
282+
283+
void SILSerializer::processSILFunctionWorklist() {
284+
while(Worklist.size() > 0) {
285+
const SILFunction *F = Worklist.back();
286+
Worklist.pop_back();
287+
assert(F != nullptr);
288+
289+
assert(FuncsToEmit.count(F) > 0);
290+
writeSILFunction(*F, FuncsToEmit[F]);
291+
}
292+
}
293+
221294
/// We enumerate all values in a SILFunction beforehand to correctly
222295
/// handle forward references of values.
223296
ValueID SILSerializer::addValueRef(const ValueBase *Val) {
@@ -943,7 +1016,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
9431016
S.addIdentifierRef(Ctx.getIdentifier(ReferencedFunction->getName())));
9441017

9451018
// Make sure we declare the referenced function.
946-
FuncsToDeclare.insert(ReferencedFunction);
1019+
addReferencedSILFunction(ReferencedFunction);
9471020
break;
9481021
}
9491022
case ValueKind::DeallocPartialRefInst:
@@ -1558,7 +1631,7 @@ void SILSerializer::writeSILVTable(const SILVTable &vt) {
15581631
for (auto &entry : vt.getEntries()) {
15591632
SmallVector<ValueID, 4> ListOfValues;
15601633
handleSILDeclRef(S, entry.first, ListOfValues);
1561-
FuncsToDeclare.insert(entry.second);
1634+
addReferencedSILFunction(entry.second, true);
15621635
// Each entry is a pair of SILDeclRef and SILFunction.
15631636
VTableEntryLayout::emitRecord(Out, ScratchRecord,
15641637
SILAbbrCodes[VTableEntryLayout::Code],
@@ -1619,9 +1692,9 @@ void SILSerializer::writeSILWitnessTable(const SILWitnessTable &wt) {
16191692
auto &methodWitness = entry.getMethodWitness();
16201693
SmallVector<ValueID, 4> ListOfValues;
16211694
handleSILDeclRef(S, methodWitness.Requirement, ListOfValues);
1622-
FuncsToDeclare.insert(methodWitness.Witness);
16231695
IdentifierID witnessID = 0;
16241696
if (SILFunction *witness = methodWitness.Witness) {
1697+
addReferencedSILFunction(witness, true);
16251698
witnessID = S.addIdentifierRef(Ctx.getIdentifier(witness->getName()));
16261699
}
16271700
WitnessMethodEntryLayout::emitRecord(Out, ScratchRecord,
@@ -1656,7 +1729,7 @@ writeSILDefaultWitnessTable(const SILDefaultWitnessTable &wt) {
16561729
SmallVector<ValueID, 4> ListOfValues;
16571730
handleSILDeclRef(S, entry.getRequirement(), ListOfValues);
16581731
SILFunction *witness = entry.getWitness();
1659-
FuncsToDeclare.insert(witness);
1732+
addReferencedSILFunction(witness, true);
16601733
IdentifierID witnessID = S.addIdentifierRef(
16611734
Ctx.getIdentifier(witness->getName()));
16621735
DefaultWitnessTableEntryLayout::emitRecord(Out, ScratchRecord,
@@ -1668,17 +1741,19 @@ writeSILDefaultWitnessTable(const SILDefaultWitnessTable &wt) {
16681741
}
16691742

16701743
/// Helper function for whether to emit a function body.
1671-
bool SILSerializer::shouldEmitFunctionBody(const SILFunction &F) {
1744+
bool SILSerializer::shouldEmitFunctionBody(const SILFunction *F) {
1745+
// If we are asked to serialize everything, go ahead and do it.
1746+
if (ShouldSerializeAll)
1747+
return true;
1748+
16721749
// If F is a declaration, it has no body to emit...
1673-
if (F.isExternalDeclaration())
1750+
if (F->isExternalDeclaration())
16741751
return false;
16751752

16761753
// If F is transparent, we should always emit its body.
1677-
if (F.isFragile())
1754+
if (F->isFragile())
16781755
return true;
16791756

1680-
// Otherwise serialize the body of the function only if we are asked to
1681-
// serialize everything.
16821757
return false;
16831758
}
16841759

@@ -1769,29 +1844,33 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) {
17691844
// Go through all the SILFunctions in SILMod and write out any
17701845
// mandatory function bodies.
17711846
for (const SILFunction &F : *SILMod) {
1772-
if (shouldEmitFunctionBody(F) || ShouldSerializeAll) {
1773-
if (emitDeclarationsForOnoneSupport) {
1774-
// Only declarations of whitelisted pre-specializations from with
1775-
// public linkage need to be serialized as they will be used
1776-
// by UsePrespecializations pass during -Onone compilation to
1777-
// check for availability of concrete pre-specializations.
1778-
if (!hasPublicVisibility(F.getLinkage()) ||
1779-
!isWhitelistedSpecialization(F.getName()))
1780-
continue;
1781-
}
1782-
writeSILFunction(F, emitDeclarationsForOnoneSupport);
1847+
if (emitDeclarationsForOnoneSupport) {
1848+
// Only declarations of whitelisted pre-specializations from with
1849+
// public linkage need to be serialized as they will be used
1850+
// by UsePrespecializations pass during -Onone compilation to
1851+
// check for availability of concrete pre-specializations.
1852+
if (!hasPublicVisibility(F.getLinkage()) ||
1853+
!isWhitelistedSpecialization(F.getName()))
1854+
continue;
17831855
}
1784-
}
17851856

1786-
if (ShouldSerializeAll)
1787-
return;
1857+
addMandatorySILFunction(&F, emitDeclarationsForOnoneSupport);
1858+
processSILFunctionWorklist();
1859+
}
17881860

17891861
// Now write function declarations for every function we've
17901862
// emitted a reference to without emitting a function body for.
17911863
for (const SILFunction &F : *SILMod) {
1792-
if (!shouldEmitFunctionBody(F) && FuncsToDeclare.count(&F))
1864+
auto iter = FuncsToEmit.find(&F);
1865+
if (iter != FuncsToEmit.end() && iter->second) {
1866+
assert((emitDeclarationsForOnoneSupport ||
1867+
!shouldEmitFunctionBody(&F)) &&
1868+
"Should have emitted function body earlier");
17931869
writeSILFunction(F, true);
1870+
}
17941871
}
1872+
1873+
assert(Worklist.empty() && "Did not emit everything in worklist");
17951874
}
17961875

17971876
void SILSerializer::writeSILModule(const SILModule *SILMod) {
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11

2+
public func getVersion() -> Int {
3+
#if BEFORE
4+
return 0
5+
#else
6+
return 1
7+
#endif
8+
}
9+
210
@_transparent public func getBuildVersion() -> Int {
311
#if BEFORE
412
return 0
@@ -7,15 +15,29 @@
715
#endif
816
}
917

10-
@_transparent public func getFunction(x: Int) -> Int -> Int {
18+
public func getFunction(x: Int) -> Int -> Int {
19+
// Force a re-abstraction thunk for (T -> T) => (Int -> Int) to be
20+
// emitted from a non-transparent context first
21+
22+
#if BEFORE
23+
func id(y: Int) -> Int { return x * y }
24+
#else
25+
func id<T>(t: T) -> T { return t }
26+
#endif
27+
28+
return id
29+
}
30+
31+
@_transparent public func getTransparentFunction(x: Int) -> Int -> Int {
1132
// The mangled name and calling convention of the local function
1233
// will change -- so we must serialize it and inline it into
1334
// the calling module
1435

1536
#if BEFORE
1637
func id(y: Int) -> Int { return x * y }
1738
#else
18-
func id(y: Int) -> Int { return y }
39+
func id<T>(t: T) -> T { return t }
1940
#endif
41+
2042
return id
2143
}

validation-test/Evolution/test_function_change_transparent_body.swift

+12-2
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,22 @@ ChangeTransparentBodyTest.test("ChangeTransparentBody") {
2828

2929
}
3030

31+
ChangeTransparentBodyTest.test("ChangeNonTransparentClosure") {
32+
33+
if getVersion() == 0 {
34+
expectEqual(202, getFunction(2)(101))
35+
} else {
36+
expectEqual(101, getFunction(2)(101))
37+
}
38+
39+
}
40+
3141
ChangeTransparentBodyTest.test("ChangeTransparentClosure") {
3242

3343
#if BEFORE
34-
expectEqual(202, getFunction(2)(101))
44+
expectEqual(202, getTransparentFunction(2)(101))
3545
#else
36-
expectEqual(101, getFunction(2)(101))
46+
expectEqual(101, getTransparentFunction(2)(101))
3747
#endif
3848

3949
}

0 commit comments

Comments
 (0)