diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 60de8cdd66405..3a2c0869f1cb6 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -732,15 +732,16 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant, emittedFunctions[constant] = F; - if (!delayedFunctions.count(constant)) { + auto foundDelayed = delayedFunctions.find(constant); + if (foundDelayed == delayedFunctions.end()) { if (isEmittedOnDemand(M, constant)) { - forcedFunctions.push_back(constant); + if (forcedFunctions.insert(constant).second) + pendingForcedFunctions.push_back(constant); return F; } } // If we delayed emitting this function previously, we need it now. - auto foundDelayed = delayedFunctions.find(constant); if (foundDelayed != delayedFunctions.end()) { // Move the function to its proper place within the module. M.functions.remove(F); @@ -752,7 +753,8 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant, M.functions.insertAfter(insertAfter->getIterator(), F); } - forcedFunctions.push_back(constant); + if (forcedFunctions.insert(constant).second) + pendingForcedFunctions.push_back(constant); delayedFunctions.erase(foundDelayed); } else { // We would have registered a delayed function as "last emitted" when we @@ -770,7 +772,6 @@ bool SILGenModule::hasFunction(SILDeclRef constant) { void SILGenModule::visitFuncDecl(FuncDecl *fd) { emitFunction(fd); } void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { - if (!f->empty()) { diagnose(constant.getAsRegularLocation(), diag::sil_function_redefinition, f->getName()); @@ -1150,24 +1151,28 @@ static void emitOrDelayFunction(SILGenModule &SGM, SILDeclRef constant) { !constant.isDynamicallyReplaceable() && !isPossiblyUsedExternally(linkage, SGM.M.isWholeModule()); - // Avoid emitting a delayable definition if it hasn't already been referenced. - SILFunction *f = nullptr; - if (mayDelay) - f = SGM.getEmittedFunction(constant, ForDefinition); - else - f = SGM.getFunction(constant, ForDefinition); - - // If we don't want to emit now, remember how for later. - if (!f) { - SGM.delayedFunctions.insert({constant, emitAfter}); - // Even though we didn't emit the function now, update the - // lastEmittedFunction so that we preserve the original ordering that - // the symbols would have been emitted in. - SGM.lastEmittedFunction = constant; + if (!mayDelay) { + SGM.emitFunctionDefinition(constant, SGM.getFunction(constant, ForDefinition)); + return; + } + + // If the function is already forced then it was previously delayed and then + // referenced. We don't need to emit or delay it again. + if (SGM.forcedFunctions.contains(constant)) + return; + + if (auto *f = SGM.getEmittedFunction(constant, ForDefinition)) { + SGM.emitFunctionDefinition(constant, f); return; } - SGM.emitFunctionDefinition(constant, f); + // This is a delayable function so remember how to emit it in case it gets + // referenced later. + SGM.delayedFunctions.insert({constant, emitAfter}); + // Even though we didn't emit the function now, update the + // lastEmittedFunction so that we preserve the original ordering that + // the symbols would have been emitted in. + SGM.lastEmittedFunction = constant; } void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F, @@ -2222,13 +2227,13 @@ class SILGenModuleRAII { // Emit any delayed definitions that were forced. // Emitting these may in turn force more definitions, so we have to take // care to keep pumping the queues. - while (!SGM.forcedFunctions.empty() + while (!SGM.pendingForcedFunctions.empty() || !SGM.pendingConformances.empty()) { - while (!SGM.forcedFunctions.empty()) { - auto &front = SGM.forcedFunctions.front(); + while (!SGM.pendingForcedFunctions.empty()) { + auto &front = SGM.pendingForcedFunctions.front(); SGM.emitFunctionDefinition( front, SGM.getEmittedFunction(front, ForDefinition)); - SGM.forcedFunctions.pop_front(); + SGM.pendingForcedFunctions.pop_front(); } while (!SGM.pendingConformances.empty()) { SGM.getWitnessTable(SGM.pendingConformances.front()); diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 122b757405778..ac4e66c71e736 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -66,7 +66,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { llvm::DenseMap delayedFunctions; /// Queue of delayed SILFunctions that need to be forced. - std::deque forcedFunctions; + std::deque pendingForcedFunctions; + + /// Delayed SILFunctions that need to be forced. + llvm::DenseSet forcedFunctions; /// Mapping global VarDecls to their onceToken and onceFunc, respectively. llvm::DenseMap () +// CHECK: bb0: +// CHECK: [[FNREF:%.*]] = function_ref @$s11back_deploy9otherFuncyyYaF : $@convention(thin) @async () -> () +// CHECK: [[APPLY:%.*]] = apply [[FNREF]]() : $@convention(thin) @async () -> () +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Back deployment thunk for trivialFunc() +// CHECK-LABEL: sil non_abi [serialized] [thunk] [ossa] @$s11back_deploy9asyncFuncyyYaFTwb : $@convention(thin) @async () -> () +// CHECK: bb0: +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 52 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]] +// +// CHECK: [[UNAVAIL_BB]]: +// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy9asyncFuncyyYaFTwB : $@convention(thin) @async () -> () +// CHECK: {{%.*}} = apply [[FALLBACKFN]]() : $@convention(thin) @async () -> () +// CHECK: br [[RETURN_BB:bb[0-9]+]] +// +// CHECK: [[AVAIL_BB]]: +// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy9asyncFuncyyYaF : $@convention(thin) @async () -> () +// CHECK: {{%.*}} = apply [[ORIGFN]]() : $@convention(thin) @async () -> () +// CHECK: br [[RETURN_BB]] +// +// CHECK: [[RETURN_BB]] +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Original definition of trivialFunc() +// CHECK-LABEL: sil [available 10.52] [ossa] @$s11back_deploy9asyncFuncyyYaF : $@convention(thin) @async () -> () +@available(macOS 10.51, *) +@_backDeploy(before: macOS 10.52) +public func asyncFunc() async { + await otherFunc() +} + +// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyYaF : $@convention(thin) @async () -> () +@available(macOS 10.51, *) +func caller() async { + // -- Verify the thunk is called + // CHECK: {{%.*}} = function_ref @$s11back_deploy9asyncFuncyyYaFTwb : $@convention(thin) @async () -> () + await asyncFunc() +} + diff --git a/test/attr/Inputs/BackDeployHelper.swift b/test/attr/Inputs/BackDeployHelper.swift index 257ace9c2e22b..7aa5b7916f4e7 100644 --- a/test/attr/Inputs/BackDeployHelper.swift +++ b/test/attr/Inputs/BackDeployHelper.swift @@ -151,6 +151,13 @@ extension IntArray { public var rawValues: [Int] { _read { yield _values } } + + @available(BackDeploy 1.0, *) + @_backDeploy(before: BackDeploy 2.0) + public mutating func removeLast() -> Int? { + defer { _values.removeLast() } + return _values.last + } } extension ReferenceIntArray { @@ -192,6 +199,13 @@ extension ReferenceIntArray { public final var rawValues: [Int] { _read { yield _values } } + + @available(BackDeploy 1.0, *) + @_backDeploy(before: BackDeploy 2.0) + public final func removeLast() -> Int? { + defer { _values.removeLast() } + return _values.last + } } extension Array { diff --git a/test/attr/attr_backDeploy_evolution.swift b/test/attr/attr_backDeploy_evolution.swift index e90cbdb8e7f08..9bf1c78a92fea 100644 --- a/test/attr/attr_backDeploy_evolution.swift +++ b/test/attr/attr_backDeploy_evolution.swift @@ -162,7 +162,9 @@ do { // CHECK-ABI: library: [5, 43, 3] // CHECK-BD: client: [5, 43, 3] - print(array.rawValues.print()) + array.rawValues.print() + + precondition(array.removeLast() == 3) } do { @@ -194,5 +196,7 @@ do { // CHECK-ABI: library: [7, 40, 1] // CHECK-BD: client: [7, 40, 1] - print(array.rawValues.print()) + array.rawValues.print() + + precondition(array.removeLast() == 1) }