Skip to content

Commit 3151bcb

Browse files
authored
Merge pull request #84943 from tshortli/silgen-swift-runtime-availability
stdlib/SILGen: Emit SIL for Swift runtime availability queries
2 parents 860f2db + 085f8ec commit 3151bcb

File tree

9 files changed

+224
-10
lines changed

9 files changed

+224
-10
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,9 @@ class ASTContext final {
801801
// Swift._stdlib_isOSVersionAtLeastOrVariantVersionAtLeast.
802802
FuncDecl *getIsOSVersionAtLeastOrVariantVersionAtLeast() const;
803803

804+
/// Retrieve the declaration of Swift._isSwiftRuntimeVersionAtLeast.
805+
FuncDecl *getIsSwiftRuntimeVersionAtLeast() const;
806+
804807
/// Look for the declaration with the given name within the
805808
/// passed in module.
806809
void lookupInModule(ModuleDecl *M, StringRef name,

include/swift/AST/AvailabilityQuery.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ class AvailabilityQuery final {
4747

4848
AvailabilityQuery(AvailabilityDomain domain, ResultKind kind,
4949
const std::optional<AvailabilityRange> &primaryRange,
50-
const std::optional<AvailabilityRange> &variantRange)
51-
: domain(domain), primaryRange(primaryRange), variantRange(variantRange),
52-
kind(kind), unavailable(false) {};
50+
const std::optional<AvailabilityRange> &variantRange);
5351

5452
public:
5553
/// Returns an `AvailabilityQuery` for a query that evaluates to true or

lib/AST/ASTContext.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,13 @@ struct ASTContext::Implementation {
425425
/// -> Builtin.Int1
426426
FuncDecl *IsOSVersionAtLeastOrVariantVersionAtLeastDecl = nullptr;
427427

428+
/// func _isSwiftRuntimeVersionAtLeast(
429+
/// Builtin.Word,
430+
/// Builtin.Word,
431+
/// Builtin.word)
432+
/// -> Builtin.Int1
433+
FuncDecl *IsSwiftRuntimeVersionAtLeastDecl = nullptr;
434+
428435
/// The set of known protocols, lazily populated as needed.
429436
ProtocolDecl *KnownProtocols[NumKnownProtocols] = { };
430437

@@ -1934,7 +1941,7 @@ FuncDecl *ASTContext::getIsVariantOSVersionAtLeastDecl() const {
19341941
}
19351942

19361943
FuncDecl *ASTContext::getIsOSVersionAtLeastOrVariantVersionAtLeast() const {
1937-
if (getImpl().IsOSVersionAtLeastOrVariantVersionAtLeastDecl)
1944+
if (getImpl().IsOSVersionAtLeastOrVariantVersionAtLeastDecl)
19381945
return getImpl().IsOSVersionAtLeastOrVariantVersionAtLeastDecl;
19391946

19401947
auto decl = findLibraryIntrinsic(*this,
@@ -1946,6 +1953,18 @@ if (getImpl().IsOSVersionAtLeastOrVariantVersionAtLeastDecl)
19461953
return decl;
19471954
}
19481955

1956+
FuncDecl *ASTContext::getIsSwiftRuntimeVersionAtLeast() const {
1957+
if (getImpl().IsSwiftRuntimeVersionAtLeastDecl)
1958+
return getImpl().IsSwiftRuntimeVersionAtLeastDecl;
1959+
1960+
auto decl = findLibraryIntrinsic(*this, "_isSwiftRuntimeVersionAtLeast");
1961+
if (!decl)
1962+
return nullptr;
1963+
1964+
getImpl().IsSwiftRuntimeVersionAtLeastDecl = decl;
1965+
return decl;
1966+
}
1967+
19491968
static bool isHigherPrecedenceThan(PrecedenceGroupDecl *a,
19501969
PrecedenceGroupDecl *b) {
19511970
assert(a != b && "exact match should already have been filtered");

lib/AST/AvailabilityQuery.cpp

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,54 @@
1717

1818
using namespace swift;
1919

20+
AvailabilityQuery::AvailabilityQuery(
21+
AvailabilityDomain domain, ResultKind kind,
22+
const std::optional<AvailabilityRange> &primaryRange,
23+
const std::optional<AvailabilityRange> &variantRange)
24+
: domain(domain), primaryRange(primaryRange), variantRange(variantRange),
25+
kind(kind), unavailable(false) {
26+
// Check invariants.
27+
switch (domain.getKind()) {
28+
case AvailabilityDomain::Kind::SwiftLanguageMode:
29+
case AvailabilityDomain::Kind::PackageDescription:
30+
case AvailabilityDomain::Kind::Embedded:
31+
// These domains don't support queries at all.
32+
DEBUG_ASSERT(false);
33+
break;
34+
35+
case AvailabilityDomain::Kind::Universal:
36+
// The universal domain can only support constant queries.
37+
DEBUG_ASSERT(kind != ResultKind::Dynamic);
38+
break;
39+
40+
case AvailabilityDomain::Kind::SwiftRuntime:
41+
// Dynamic Swift runtime queries take just a primary version argument.
42+
if (kind == ResultKind::Dynamic) {
43+
DEBUG_ASSERT(primaryRange);
44+
DEBUG_ASSERT(!variantRange);
45+
}
46+
break;
47+
48+
case AvailabilityDomain::Kind::Platform:
49+
// Dynamic platform version queries must have either a primary version
50+
// argument or a variant version argument (or both).
51+
if (kind == ResultKind::Dynamic) {
52+
DEBUG_ASSERT(primaryRange || variantRange);
53+
}
54+
break;
55+
56+
case AvailabilityDomain::Kind::Custom:
57+
// Custom availability domains do not support versioned queries at all yet.
58+
DEBUG_ASSERT(!primaryRange);
59+
DEBUG_ASSERT(!variantRange);
60+
61+
// A valid custom domain object is required.
62+
auto customDomain = domain.getCustomDomain();
63+
ASSERT(customDomain);
64+
break;
65+
}
66+
}
67+
2068
static void unpackVersion(const llvm::VersionTuple &version,
2169
llvm::SmallVectorImpl<unsigned> &arguments) {
2270
arguments.push_back(version.getMajor());
@@ -133,16 +181,17 @@ FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
133181
switch (domain.getKind()) {
134182
case AvailabilityDomain::Kind::Universal:
135183
case AvailabilityDomain::Kind::SwiftLanguageMode:
136-
case AvailabilityDomain::Kind::SwiftRuntime:
137184
case AvailabilityDomain::Kind::PackageDescription:
138185
case AvailabilityDomain::Kind::Embedded:
186+
// These domains don't support dynamic queries.
139187
return nullptr;
188+
189+
case AvailabilityDomain::Kind::SwiftRuntime:
190+
unpackVersion(getPrimaryArgument().value(), arguments);
191+
return ctx.getIsSwiftRuntimeVersionAtLeast();
140192
case AvailabilityDomain::Kind::Platform:
141193
return getOSAvailabilityDeclAndArguments(*this, arguments, ctx);
142194
case AvailabilityDomain::Kind::Custom:
143-
auto customDomain = domain.getCustomDomain();
144-
ASSERT(customDomain);
145-
146-
return customDomain->getPredicateFunc();
195+
return domain.getCustomDomain()->getPredicateFunc();
147196
}
148197
}

lib/AST/AvailabilityScopeBuilder.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,8 @@ class AvailabilityScopeBuilder : private ASTWalker {
870870
variantRange);
871871

872872
case AvailabilityDomain::Kind::SwiftRuntime:
873+
return AvailabilityQuery::dynamic(domain, primaryRange, std::nullopt);
874+
873875
case AvailabilityDomain::Kind::Platform:
874876
// Platform and Swift runtime checks are always dynamic. The SIL optimizer
875877
// is responsible eliminating these checks when it can prove that they can

stdlib/public/core/Availability.swift

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,22 @@ public func _stdlib_isOSVersionAtLeastOrVariantVersionAtLeast(
197197

198198
public typealias _SwiftStdlibVersion = SwiftShims._SwiftStdlibVersion
199199

200+
/// This is a magic entry point known to the compiler. It is called in
201+
/// generated code for Swift runtime availability checking, e.g.
202+
///
203+
/// if #available(Swift 6.2, *) { }
204+
///
205+
@available(SwiftStdlib 5.7, *)
206+
@_alwaysEmitIntoClient
207+
internal func _isSwiftRuntimeVersionAtLeast(
208+
_ major: Builtin.Word,
209+
_ minor: Builtin.Word,
210+
_ patch: Builtin.Word
211+
) -> Builtin.Int1 {
212+
let version = _SwiftStdlibVersion(major, minor, patch)
213+
return (_SwiftStdlibVersion.current._value <= version._value)._value
214+
}
215+
200216
/// Return true if the main executable was linked with an SDK version
201217
/// corresponding to the given Swift Stdlib release, or later. Otherwise, return
202218
/// false.
@@ -243,8 +259,36 @@ extension _SwiftStdlibVersion {
243259
@_alwaysEmitIntoClient
244260
public static var v6_3_0: Self { Self(_value: 0x060300) }
245261

262+
private static var _current: Self { .v6_3_0 }
263+
264+
#if hasFeature(Macros)
265+
@available(SwiftStdlib 5.7, *)
266+
public static var current: Self {
267+
@_noLocks
268+
@_effects(readnone)
269+
get { ._current }
270+
}
271+
#else
246272
@available(SwiftStdlib 5.7, *)
247-
public static var current: Self { .v6_3_0 }
273+
public static var current: Self {
274+
@_effects(readnone)
275+
get { ._current }
276+
}
277+
#endif
278+
279+
@_alwaysEmitIntoClient
280+
internal init(
281+
_ major: Builtin.Word,
282+
_ minor: Builtin.Word,
283+
_ patch: Builtin.Word
284+
) {
285+
let version = (Int(major), Int(minor), Int(patch))
286+
var value: UInt32 = 0x0
287+
value |= ((UInt32(truncatingIfNeeded: version.0) & 0xffff) << 16)
288+
value |= ((UInt32(truncatingIfNeeded: version.1) & 0xff) << 8)
289+
value |= ((UInt32(truncatingIfNeeded: version.2) & 0xff))
290+
self = Self(_value: value)
291+
}
248292
}
249293

250294
@available(SwiftStdlib 5.7, *)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-swift-emit-ir %s -min-swift-runtime-version 5.0 -O -enable-experimental-feature SwiftRuntimeAvailability | %FileCheck %s
2+
3+
// REQUIRES: swift_feature_SwiftRuntimeAvailability
4+
5+
@_silgen_name("callMeMaybe")
6+
public func callMeMaybe()
7+
8+
// Verify that optimized IR for "if #available(Swift X.Y, *)" is composed of a
9+
// call to the stdlib ABI that returns the current runtime version and an
10+
// integer comparison of that version and the predicate version.
11+
12+
// CHECK-LABEL: define {{.*}}swiftcc void @"$s26availability_swift_runtime15testIfAvailableyyF"()
13+
// CHECK: [[CURRENT_VERS:%.*]] = tail call swiftcc i32 @"$sSo19_SwiftStdlibVersionasE7currentABvgZ"()
14+
// CHECK: [[ICMP:%.*]] = icmp {{.*}}
15+
// CHECK: br i1 [[ICMP]], label %[[TRUE_LABEL:.*]], label %[[FALSE_LABEL:.*]]
16+
// CHECK: [[TRUE_LABEL]]:
17+
// CHECK: call {{.*}} @callMeMaybe()
18+
// CHECK: [[FALSE_LABEL]]:
19+
// CHECK: ret void
20+
public func testIfAvailable() {
21+
if #available(Swift 6.2, *) {
22+
callMeMaybe()
23+
}
24+
}
25+
26+
// In optimized IR multiple "if #available" checks for the same version should
27+
// only generate a single call to the getter for _SwiftStdlibVersion.current.
28+
29+
// CHECK-LABEL: define {{.*}}swiftcc void @"$s26availability_swift_runtime23testIfAvailableMultipleyyF"()
30+
// CHECK: call {{.*}} @"$sSo19_SwiftStdlibVersionasE7currentABvgZ"()
31+
// CHECK-NOT: call {{.*}} @"$sSo19_SwiftStdlibVersionasE7currentABvgZ"()
32+
// CHECK: icmp
33+
// CHECK: call {{.*}} @callMeMaybe()
34+
// CHECK: call {{.*}} @callMeMaybe()
35+
// CHECK: call {{.*}} @callMeMaybe()
36+
public func testIfAvailableMultiple() {
37+
if #available(Swift 5.10, *) {
38+
callMeMaybe()
39+
}
40+
if #available(Swift 5.10, *) {
41+
callMeMaybe()
42+
}
43+
if #available(Swift 5.10, *) {
44+
callMeMaybe()
45+
}
46+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-swift-emit-sil %s -min-swift-runtime-version 5.0 -verify -enable-experimental-feature SwiftRuntimeAvailability
2+
// RUN: %target-swift-emit-silgen %s -min-swift-runtime-version 5.0 -enable-experimental-feature SwiftRuntimeAvailability | %FileCheck %s
3+
4+
// REQUIRES: swift_feature_SwiftRuntimeAvailability
5+
6+
// CHECK-LABEL: sil [ossa] @$s32availability_query_swift_runtime15testIfAvailableyyF : $@convention(thin) () -> () {
7+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 6
8+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 2
9+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
10+
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
11+
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
12+
public func testIfAvailable() {
13+
if #available(Swift 6.2, *) { }
14+
}
15+
16+
// CHECK-LABEL: sil [ossa] @$s32availability_query_swift_runtime17testIfUnavailableyyF : $@convention(thin) () -> () {
17+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 5
18+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 10
19+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 1
20+
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
21+
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
22+
// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1
23+
// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]], [[MINUSONE]]) : $Builtin.Int1
24+
public func testIfUnavailable() {
25+
if #unavailable(Swift 5.10.1) { }
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-swift-emit-sil %s -target %target-cpu-apple-macosx11 -target-variant %target-cpu-apple-ios14-macabi -min-swift-runtime-version 5.0 -verify -enable-experimental-feature SwiftRuntimeAvailability
2+
// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx11 -target-variant %target-cpu-apple-ios14-macabi -min-swift-runtime-version 5.0 -enable-experimental-feature SwiftRuntimeAvailability | %FileCheck %s
3+
4+
// REQUIRES: OS=macosx || OS=maccatalyst
5+
// REQUIRES: swift_feature_SwiftRuntimeAvailability
6+
7+
// CHECK-LABEL: sil [ossa] @$s53availability_query_swift_runtime_maccatalyst_zippered15testIfAvailableyyF : $@convention(thin) () -> () {
8+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 6
9+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 2
10+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
11+
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
12+
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
13+
public func testIfAvailable() {
14+
if #available(Swift 6.2, *) { }
15+
}
16+
17+
// CHECK-LABEL: sil [ossa] @$s53availability_query_swift_runtime_maccatalyst_zippered17testIfUnavailableyyF : $@convention(thin) () -> () {
18+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 5
19+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 10
20+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 1
21+
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
22+
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
23+
// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1
24+
// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]], [[MINUSONE]]) : $Builtin.Int1
25+
public func testIfUnavailable() {
26+
if #unavailable(Swift 5.10.1) { }
27+
}

0 commit comments

Comments
 (0)