Skip to content

Commit 2dd809c

Browse files
committed
SILGen: Fix 'multiple definitions of symbol' error for functions with @_backDeploy containing defer blocks.
If the emission of a function is delayed via `emitOrDelayFunction()`, then the function is recorded on a list of delayed functions. If the delayed function is later referenced, then the function moves from the delayed list to the "forced" list which will cause it to actually be emitted later. The implementation of `emitOrDelayFunction()` had a bug when called twice for the same delayable function - it would emit that function prematurely if the function moved to the forced list between the two invocations. Later, during forced function emission a multiple definitions of symbol diagnostic would be emitted since the function was not empty. This issue could be triggered by `@_backDeploy` functions that have auxilary delayable helper functions (e.g. defer blocks). SILGen visits `@_backDeploy` functions twice (once for the copy of the function emitted into the client and once for the resilient copy of the function) so `emitOrDelayFunction()` is called twice for each of the helper functions and the helper functions could become referenced between the two calls. The fix is to check for an existing entry in the forced functions list before delaying or emitting a delayable function. Resolves rdar://102909684
1 parent 2a7b55c commit 2dd809c

File tree

4 files changed

+53
-27
lines changed

4 files changed

+53
-27
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -732,15 +732,16 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
732732

733733
emittedFunctions[constant] = F;
734734

735-
if (!delayedFunctions.count(constant)) {
735+
auto foundDelayed = delayedFunctions.find(constant);
736+
if (foundDelayed == delayedFunctions.end()) {
736737
if (isEmittedOnDemand(M, constant)) {
737-
forcedFunctions.push_back(constant);
738+
if (forcedFunctions.insert(constant).second)
739+
pendingForcedFunctions.push_back(constant);
738740
return F;
739741
}
740742
}
741743

742744
// If we delayed emitting this function previously, we need it now.
743-
auto foundDelayed = delayedFunctions.find(constant);
744745
if (foundDelayed != delayedFunctions.end()) {
745746
// Move the function to its proper place within the module.
746747
M.functions.remove(F);
@@ -752,7 +753,8 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
752753
M.functions.insertAfter(insertAfter->getIterator(), F);
753754
}
754755

755-
forcedFunctions.push_back(constant);
756+
if (forcedFunctions.insert(constant).second)
757+
pendingForcedFunctions.push_back(constant);
756758
delayedFunctions.erase(foundDelayed);
757759
} else {
758760
// We would have registered a delayed function as "last emitted" when we
@@ -770,7 +772,6 @@ bool SILGenModule::hasFunction(SILDeclRef constant) {
770772
void SILGenModule::visitFuncDecl(FuncDecl *fd) { emitFunction(fd); }
771773

772774
void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
773-
774775
if (!f->empty()) {
775776
diagnose(constant.getAsRegularLocation(), diag::sil_function_redefinition,
776777
f->getName());
@@ -1150,24 +1151,28 @@ static void emitOrDelayFunction(SILGenModule &SGM, SILDeclRef constant) {
11501151
!constant.isDynamicallyReplaceable() &&
11511152
!isPossiblyUsedExternally(linkage, SGM.M.isWholeModule());
11521153

1153-
// Avoid emitting a delayable definition if it hasn't already been referenced.
1154-
SILFunction *f = nullptr;
1155-
if (mayDelay)
1156-
f = SGM.getEmittedFunction(constant, ForDefinition);
1157-
else
1158-
f = SGM.getFunction(constant, ForDefinition);
1159-
1160-
// If we don't want to emit now, remember how for later.
1161-
if (!f) {
1162-
SGM.delayedFunctions.insert({constant, emitAfter});
1163-
// Even though we didn't emit the function now, update the
1164-
// lastEmittedFunction so that we preserve the original ordering that
1165-
// the symbols would have been emitted in.
1166-
SGM.lastEmittedFunction = constant;
1154+
if (!mayDelay) {
1155+
SGM.emitFunctionDefinition(constant, SGM.getFunction(constant, ForDefinition));
1156+
return;
1157+
}
1158+
1159+
// If the function is already forced then it was previously delayed and then
1160+
// referenced. We don't need to emit or delay it again.
1161+
if (SGM.forcedFunctions.contains(constant))
1162+
return;
1163+
1164+
if (auto *f = SGM.getEmittedFunction(constant, ForDefinition)) {
1165+
SGM.emitFunctionDefinition(constant, f);
11671166
return;
11681167
}
11691168

1170-
SGM.emitFunctionDefinition(constant, f);
1169+
// This is a delayable function so remember how to emit it in case it gets
1170+
// referenced later.
1171+
SGM.delayedFunctions.insert({constant, emitAfter});
1172+
// Even though we didn't emit the function now, update the
1173+
// lastEmittedFunction so that we preserve the original ordering that
1174+
// the symbols would have been emitted in.
1175+
SGM.lastEmittedFunction = constant;
11711176
}
11721177

11731178
void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F,
@@ -2222,13 +2227,13 @@ class SILGenModuleRAII {
22222227
// Emit any delayed definitions that were forced.
22232228
// Emitting these may in turn force more definitions, so we have to take
22242229
// care to keep pumping the queues.
2225-
while (!SGM.forcedFunctions.empty()
2230+
while (!SGM.pendingForcedFunctions.empty()
22262231
|| !SGM.pendingConformances.empty()) {
2227-
while (!SGM.forcedFunctions.empty()) {
2228-
auto &front = SGM.forcedFunctions.front();
2232+
while (!SGM.pendingForcedFunctions.empty()) {
2233+
auto &front = SGM.pendingForcedFunctions.front();
22292234
SGM.emitFunctionDefinition(
22302235
front, SGM.getEmittedFunction(front, ForDefinition));
2231-
SGM.forcedFunctions.pop_front();
2236+
SGM.pendingForcedFunctions.pop_front();
22322237
}
22332238
while (!SGM.pendingConformances.empty()) {
22342239
SGM.getWitnessTable(SGM.pendingConformances.front());

lib/SILGen/SILGen.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
6666
llvm::DenseMap<SILDeclRef, SILDeclRef> delayedFunctions;
6767

6868
/// Queue of delayed SILFunctions that need to be forced.
69-
std::deque<SILDeclRef> forcedFunctions;
69+
std::deque<SILDeclRef> pendingForcedFunctions;
70+
71+
/// Delayed SILFunctions that need to be forced.
72+
llvm::DenseSet<SILDeclRef> forcedFunctions;
7073

7174
/// Mapping global VarDecls to their onceToken and onceFunc, respectively.
7275
llvm::DenseMap<VarDecl *, std::pair<SILGlobalVariable *,

test/attr/Inputs/BackDeployHelper.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ extension IntArray {
151151
public var rawValues: [Int] {
152152
_read { yield _values }
153153
}
154+
155+
@available(BackDeploy 1.0, *)
156+
@_backDeploy(before: BackDeploy 2.0)
157+
public mutating func removeLast() -> Int? {
158+
defer { _values.removeLast() }
159+
return _values.last
160+
}
154161
}
155162

156163
extension ReferenceIntArray {
@@ -192,6 +199,13 @@ extension ReferenceIntArray {
192199
public final var rawValues: [Int] {
193200
_read { yield _values }
194201
}
202+
203+
@available(BackDeploy 1.0, *)
204+
@_backDeploy(before: BackDeploy 2.0)
205+
public final func removeLast() -> Int? {
206+
defer { _values.removeLast() }
207+
return _values.last
208+
}
195209
}
196210

197211
extension Array {

test/attr/attr_backDeploy_evolution.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ do {
162162

163163
// CHECK-ABI: library: [5, 43, 3]
164164
// CHECK-BD: client: [5, 43, 3]
165-
print(array.rawValues.print())
165+
array.rawValues.print()
166+
167+
precondition(array.removeLast() == 3)
166168
}
167169

168170
do {
@@ -194,5 +196,7 @@ do {
194196

195197
// CHECK-ABI: library: [7, 40, 1]
196198
// CHECK-BD: client: [7, 40, 1]
197-
print(array.rawValues.print())
199+
array.rawValues.print()
200+
201+
precondition(array.removeLast() == 1)
198202
}

0 commit comments

Comments
 (0)