Skip to content

Commit 6cb1411

Browse files
committed
SILOptimizer: Allow inlining of transparent functions in @backDeployed thunks.
In order for availability checks in iOS apps to be evaluated correctly when running on macOS, the application binary must call a copy of `_stdlib_isOSVersionAtLeast_AEIC()` that was emitted into the app, instead of calling the `_stdlib_isOSVersionAtLeast()` function provided by the standard library. This is because the call to the underlying compiler-rt function `__isPlatformVersionAtLeast()` must be given the correct platform identifier argument; if the call is not emitted into the client, then the macOS platform identifier is used and the iOS version number will be mistakenly interpreted as a macOS version number at runtime. The `_stdlib_isOSVersionAtLeast()` function in the standard library is marked `@_transparent` on iOS so that its call to `_stdlib_isOSVersionAtLeast_AEIC()` is always inlined into the client. This works for the code generated by normal `if #available` checks, but for the `@backDeployed` function thunks, the calls to `_stdlib_isOSVersionAtLeast()` were not being inlined and that was causing calls to `@backDeployed` functions to crash in iOS apps running on macOS since their availability checks were being misevaluated. The SIL optimizer has a heuristic which inhibits mandatory inlining in functions that are classified as thunks, in order to save code size. This heuristic needs to be relaxed in `@backDeployed` thunks, so that mandatory inlining of `_stdlib_isOSVersionAtLeast()` can behave as expected. The change should be safe since the only `@_transparent` function a `@backDeployed` thunk is ever expected to call is `_stdlib_isOSVersionAtLeast()`. Resolves rdar://134793410.
1 parent fdad092 commit 6cb1411

File tree

9 files changed

+65
-7
lines changed

9 files changed

+65
-7
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ enum IsThunk_t {
5656
IsNotThunk,
5757
IsThunk,
5858
IsReabstractionThunk,
59-
IsSignatureOptimizedThunk
59+
IsSignatureOptimizedThunk,
60+
IsBackDeployedThunk,
6061
};
6162
enum IsDynamicallyReplaceable_t {
6263
IsNotDynamic,
@@ -368,7 +369,7 @@ class SILFunction
368369
///
369370
/// The inliner uses this information to avoid inlining (non-trivial)
370371
/// functions into the thunk.
371-
unsigned Thunk : 2;
372+
unsigned Thunk : 3;
372373

373374
/// The scope in which the parent class can be subclassed, if this is a method
374375
/// which is contained in the vtable of that class.
@@ -488,6 +489,7 @@ class SILFunction
488489
break;
489490
case IsThunk:
490491
case IsReabstractionThunk:
492+
case IsBackDeployedThunk:
491493
thunkCanHaveSubclassScope = false;
492494
break;
493495
}

lib/SIL/IR/SILPrinter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,6 +3355,7 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
33553355

33563356
switch (isThunk()) {
33573357
case IsNotThunk: break;
3358+
case IsBackDeployedThunk: // FIXME: Give this a distinct label
33583359
case IsThunk: OS << "[thunk] "; break;
33593360
case IsSignatureOptimizedThunk:
33603361
OS << "[signature_optimized_thunk] ";

lib/SILGen/SILGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
909909
preEmitFunction(constant, f, loc);
910910
PrettyStackTraceSILFunction X("silgen emitBackDeploymentThunk", f);
911911
f->setBare(IsBare);
912-
f->setThunk(IsThunk);
912+
f->setThunk(IsBackDeployedThunk);
913913

914914
SILGenFunction(*this, *f, dc).emitBackDeploymentThunk(constant);
915915

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,10 +1027,22 @@ class MandatoryInlining : public SILModuleTransform {
10271027

10281028
SILOptFunctionBuilder FuncBuilder(*this);
10291029
for (auto &F : *M) {
1030-
// Don't inline into thunks, even transparent callees.
1031-
if (F.isThunk())
1030+
switch (F.isThunk()) {
1031+
case IsThunk_t::IsThunk:
1032+
case IsThunk_t::IsReabstractionThunk:
1033+
case IsThunk_t::IsSignatureOptimizedThunk:
1034+
// Don't inline into most thunks, even transparent callees.
10321035
continue;
10331036

1037+
case IsThunk_t::IsNotThunk:
1038+
case IsThunk_t::IsBackDeployedThunk:
1039+
// For correctness, inlining _stdlib_isOSVersionAtLeast() when it is
1040+
// declared transparent is mandatory in the thunks of @backDeployed
1041+
// functions. These thunks will not contain calls to other transparent
1042+
// functions.
1043+
break;
1044+
}
1045+
10341046
// Skip deserialized functions.
10351047
if (F.wasDeserializedCanonical())
10361048
continue;

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 870; // SerializePackageEnabled / [serialized_for_package] for SILFunctionLayout / package field in SerializedKind_t
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 871; // SIL function thunk kind
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///

lib/Serialization/SILFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ namespace sil_block {
292292
BCRecordLayout<SIL_FUNCTION, SILLinkageField,
293293
BCFixed<1>, // transparent
294294
BCFixed<2>, // serializedKind
295-
BCFixed<2>, // thunks: signature optimized/reabstraction
295+
BCFixed<3>, // thunk kind
296296
BCFixed<1>, // without_actually_escaping
297297
BCFixed<3>, // specialPurpose
298298
BCFixed<2>, // inlineStrategy

test/IRGen/Inputs/back_deployed.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@backDeployed(before: SwiftStdlib 6.0)
2+
public func backDeployedFunc() {
3+
otherFunc()
4+
}
5+
6+
@usableFromInline internal func otherFunc() {}

test/IRGen/back_deployed_Onone.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module %S/Inputs/back_deployed.swift -o %t/ -swift-version 5 -enable-library-evolution
3+
// RUN: %target-swift-frontend -emit-ir %s -I %t -Onone | %FileCheck %s
4+
5+
// _stdlib_isOSVersionAtLeast() is not @_transparent on macOS, watchOS, and tvOS
6+
// REQUIRES: OS=macosx || OS=watchos || OS=tvos
7+
8+
import back_deployed
9+
10+
public func test() {
11+
backDeployedFunc()
12+
}
13+
14+
// CHECK: define{{.*}} hidden swiftcc void @"$s13back_deployed0A12DeployedFuncyyFTwb"
15+
// CHECK: call swiftcc i1 @"$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF"
16+
// CHECK: call swiftcc void @"$s13back_deployed0A12DeployedFuncyyFTwB"
17+
// CHECK: call swiftcc void @"$s13back_deployed0A12DeployedFuncyyF"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module %S/Inputs/back_deployed.swift -o %t/ -swift-version 5 -enable-library-evolution
3+
// RUN: %target-swift-frontend -emit-ir %s -I %t -Onone | %FileCheck %s
4+
5+
// _stdlib_isOSVersionAtLeast() is @_transparent on iOS
6+
// REQUIRES: OS=ios
7+
8+
import back_deployed
9+
10+
public func test() {
11+
backDeployedFunc()
12+
}
13+
14+
// CHECK: define{{.*}} hidden swiftcc void @"$s13back_deployed0A12DeployedFuncyyFTwb"
15+
// CHECK: call swiftcc i1 @"$ss31_stdlib_isOSVersionAtLeast_AEICyBi1_Bw_BwBwtF"
16+
// CHECK: call swiftcc void @"$s13back_deployed0A12DeployedFuncyyFTwB"
17+
// CHECK: call swiftcc void @"$s13back_deployed0A12DeployedFuncyyF"
18+
19+
// CHECK: define{{.*}} hidden swiftcc i1 @"$ss31_stdlib_isOSVersionAtLeast_AEICyBi1_Bw_BwBwtF"
20+
// CHECK: call i32 @__isPlatformVersionAtLeast

0 commit comments

Comments
 (0)