mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
stdlib/SILGen: Emit SIL for Swift runtime availability queries.
When emitting SIL for `if #available(Swift ..., *)` queries, call the new `_isSwiftRuntimeVersionAtLeast()` function in the stdlib to check the condition. To support back deployment, the implementation of `_isSwiftRuntimeVersionAtLeast()` is `@_alwaysEmitIntoClient` and performs its comparison against the result of `_SwiftStdlibVersion.current`, which is pre-existing ABI that the stdlib exposes for querying the Swift runtime version. Resolves rdar://162726037.
This commit is contained in:
@@ -801,6 +801,9 @@ public:
|
||||
// Swift._stdlib_isOSVersionAtLeastOrVariantVersionAtLeast.
|
||||
FuncDecl *getIsOSVersionAtLeastOrVariantVersionAtLeast() const;
|
||||
|
||||
/// Retrieve the declaration of Swift._isSwiftRuntimeVersionAtLeast.
|
||||
FuncDecl *getIsSwiftRuntimeVersionAtLeast() const;
|
||||
|
||||
/// Look for the declaration with the given name within the
|
||||
/// passed in module.
|
||||
void lookupInModule(ModuleDecl *M, StringRef name,
|
||||
|
||||
@@ -47,9 +47,7 @@ class AvailabilityQuery final {
|
||||
|
||||
AvailabilityQuery(AvailabilityDomain domain, ResultKind kind,
|
||||
const std::optional<AvailabilityRange> &primaryRange,
|
||||
const std::optional<AvailabilityRange> &variantRange)
|
||||
: domain(domain), primaryRange(primaryRange), variantRange(variantRange),
|
||||
kind(kind), unavailable(false) {};
|
||||
const std::optional<AvailabilityRange> &variantRange);
|
||||
|
||||
public:
|
||||
/// Returns an `AvailabilityQuery` for a query that evaluates to true or
|
||||
|
||||
@@ -425,6 +425,13 @@ struct ASTContext::Implementation {
|
||||
/// -> Builtin.Int1
|
||||
FuncDecl *IsOSVersionAtLeastOrVariantVersionAtLeastDecl = nullptr;
|
||||
|
||||
/// func _isSwiftRuntimeVersionAtLeast(
|
||||
/// Builtin.Word,
|
||||
/// Builtin.Word,
|
||||
/// Builtin.word)
|
||||
/// -> Builtin.Int1
|
||||
FuncDecl *IsSwiftRuntimeVersionAtLeastDecl = nullptr;
|
||||
|
||||
/// The set of known protocols, lazily populated as needed.
|
||||
ProtocolDecl *KnownProtocols[NumKnownProtocols] = { };
|
||||
|
||||
@@ -1946,6 +1953,18 @@ if (getImpl().IsOSVersionAtLeastOrVariantVersionAtLeastDecl)
|
||||
return decl;
|
||||
}
|
||||
|
||||
FuncDecl *ASTContext::getIsSwiftRuntimeVersionAtLeast() const {
|
||||
if (getImpl().IsSwiftRuntimeVersionAtLeastDecl)
|
||||
return getImpl().IsSwiftRuntimeVersionAtLeastDecl;
|
||||
|
||||
auto decl = findLibraryIntrinsic(*this, "_isSwiftRuntimeVersionAtLeast");
|
||||
if (!decl)
|
||||
return nullptr;
|
||||
|
||||
getImpl().IsSwiftRuntimeVersionAtLeastDecl = decl;
|
||||
return decl;
|
||||
}
|
||||
|
||||
static bool isHigherPrecedenceThan(PrecedenceGroupDecl *a,
|
||||
PrecedenceGroupDecl *b) {
|
||||
assert(a != b && "exact match should already have been filtered");
|
||||
|
||||
@@ -17,6 +17,54 @@
|
||||
|
||||
using namespace swift;
|
||||
|
||||
AvailabilityQuery::AvailabilityQuery(
|
||||
AvailabilityDomain domain, ResultKind kind,
|
||||
const std::optional<AvailabilityRange> &primaryRange,
|
||||
const std::optional<AvailabilityRange> &variantRange)
|
||||
: domain(domain), primaryRange(primaryRange), variantRange(variantRange),
|
||||
kind(kind), unavailable(false) {
|
||||
// Check invariants.
|
||||
switch (domain.getKind()) {
|
||||
case AvailabilityDomain::Kind::SwiftLanguageMode:
|
||||
case AvailabilityDomain::Kind::PackageDescription:
|
||||
case AvailabilityDomain::Kind::Embedded:
|
||||
// These domains don't support queries at all.
|
||||
DEBUG_ASSERT(false);
|
||||
break;
|
||||
|
||||
case AvailabilityDomain::Kind::Universal:
|
||||
// The universal domain can only support constant queries.
|
||||
DEBUG_ASSERT(kind != ResultKind::Dynamic);
|
||||
break;
|
||||
|
||||
case AvailabilityDomain::Kind::SwiftRuntime:
|
||||
// Dynamic Swift runtime queries take just a primary version argument.
|
||||
if (kind == ResultKind::Dynamic) {
|
||||
DEBUG_ASSERT(primaryRange);
|
||||
DEBUG_ASSERT(!variantRange);
|
||||
}
|
||||
break;
|
||||
|
||||
case AvailabilityDomain::Kind::Platform:
|
||||
// Dynamic platform version queries must have either a primary version
|
||||
// argument or a variant version argument (or both).
|
||||
if (kind == ResultKind::Dynamic) {
|
||||
DEBUG_ASSERT(primaryRange || variantRange);
|
||||
}
|
||||
break;
|
||||
|
||||
case AvailabilityDomain::Kind::Custom:
|
||||
// Custom availability domains do not support versioned queries at all yet.
|
||||
DEBUG_ASSERT(!primaryRange);
|
||||
DEBUG_ASSERT(!variantRange);
|
||||
|
||||
// A valid custom domain object is required.
|
||||
auto customDomain = domain.getCustomDomain();
|
||||
ASSERT(customDomain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void unpackVersion(const llvm::VersionTuple &version,
|
||||
llvm::SmallVectorImpl<unsigned> &arguments) {
|
||||
arguments.push_back(version.getMajor());
|
||||
@@ -133,16 +181,17 @@ FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
|
||||
switch (domain.getKind()) {
|
||||
case AvailabilityDomain::Kind::Universal:
|
||||
case AvailabilityDomain::Kind::SwiftLanguageMode:
|
||||
case AvailabilityDomain::Kind::SwiftRuntime:
|
||||
case AvailabilityDomain::Kind::PackageDescription:
|
||||
case AvailabilityDomain::Kind::Embedded:
|
||||
// These domains don't support dynamic queries.
|
||||
return nullptr;
|
||||
|
||||
case AvailabilityDomain::Kind::SwiftRuntime:
|
||||
unpackVersion(getPrimaryArgument().value(), arguments);
|
||||
return ctx.getIsSwiftRuntimeVersionAtLeast();
|
||||
case AvailabilityDomain::Kind::Platform:
|
||||
return getOSAvailabilityDeclAndArguments(*this, arguments, ctx);
|
||||
case AvailabilityDomain::Kind::Custom:
|
||||
auto customDomain = domain.getCustomDomain();
|
||||
ASSERT(customDomain);
|
||||
|
||||
return customDomain->getPredicateFunc();
|
||||
return domain.getCustomDomain()->getPredicateFunc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,6 +870,8 @@ private:
|
||||
variantRange);
|
||||
|
||||
case AvailabilityDomain::Kind::SwiftRuntime:
|
||||
return AvailabilityQuery::dynamic(domain, primaryRange, std::nullopt);
|
||||
|
||||
case AvailabilityDomain::Kind::Platform:
|
||||
// Platform and Swift runtime checks are always dynamic. The SIL optimizer
|
||||
// is responsible eliminating these checks when it can prove that they can
|
||||
|
||||
@@ -197,6 +197,22 @@ public func _stdlib_isOSVersionAtLeastOrVariantVersionAtLeast(
|
||||
|
||||
public typealias _SwiftStdlibVersion = SwiftShims._SwiftStdlibVersion
|
||||
|
||||
/// This is a magic entry point known to the compiler. It is called in
|
||||
/// generated code for Swift runtime availability checking, e.g.
|
||||
///
|
||||
/// if #available(Swift 6.2, *) { }
|
||||
///
|
||||
@available(SwiftStdlib 5.7, *)
|
||||
@_alwaysEmitIntoClient
|
||||
internal func _isSwiftRuntimeVersionAtLeast(
|
||||
_ major: Builtin.Word,
|
||||
_ minor: Builtin.Word,
|
||||
_ patch: Builtin.Word
|
||||
) -> Builtin.Int1 {
|
||||
let version = _SwiftStdlibVersion(major, minor, patch)
|
||||
return (_SwiftStdlibVersion.current._value <= version._value)._value
|
||||
}
|
||||
|
||||
/// Return true if the main executable was linked with an SDK version
|
||||
/// corresponding to the given Swift Stdlib release, or later. Otherwise, return
|
||||
/// false.
|
||||
@@ -243,8 +259,36 @@ extension _SwiftStdlibVersion {
|
||||
@_alwaysEmitIntoClient
|
||||
public static var v6_3_0: Self { Self(_value: 0x060300) }
|
||||
|
||||
private static var _current: Self { .v6_3_0 }
|
||||
|
||||
#if hasFeature(Macros)
|
||||
@available(SwiftStdlib 5.7, *)
|
||||
public static var current: Self { .v6_3_0 }
|
||||
public static var current: Self {
|
||||
@_noLocks
|
||||
@_effects(readnone)
|
||||
get { ._current }
|
||||
}
|
||||
#else
|
||||
@available(SwiftStdlib 5.7, *)
|
||||
public static var current: Self {
|
||||
@_effects(readnone)
|
||||
get { ._current }
|
||||
}
|
||||
#endif
|
||||
|
||||
@_alwaysEmitIntoClient
|
||||
internal init(
|
||||
_ major: Builtin.Word,
|
||||
_ minor: Builtin.Word,
|
||||
_ patch: Builtin.Word
|
||||
) {
|
||||
let version = (Int(major), Int(minor), Int(patch))
|
||||
var value: UInt32 = 0x0
|
||||
value |= ((UInt32(truncatingIfNeeded: version.0) & 0xffff) << 16)
|
||||
value |= ((UInt32(truncatingIfNeeded: version.1) & 0xff) << 8)
|
||||
value |= ((UInt32(truncatingIfNeeded: version.2) & 0xff))
|
||||
self = Self(_value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 5.7, *)
|
||||
|
||||
46
test/IRGen/availability_swift_runtime.swift
Normal file
46
test/IRGen/availability_swift_runtime.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
// RUN: %target-swift-emit-ir %s -min-swift-runtime-version 5.0 -O -enable-experimental-feature SwiftRuntimeAvailability | %FileCheck %s
|
||||
|
||||
// REQUIRES: swift_feature_SwiftRuntimeAvailability
|
||||
|
||||
@_silgen_name("callMeMaybe")
|
||||
public func callMeMaybe()
|
||||
|
||||
// Verify that optimized IR for "if #available(Swift X.Y, *)" is composed of a
|
||||
// call to the stdlib ABI that returns the current runtime version and an
|
||||
// integer comparison of that version and the predicate version.
|
||||
|
||||
// CHECK-LABEL: define {{.*}}swiftcc void @"$s26availability_swift_runtime15testIfAvailableyyF"()
|
||||
// CHECK: [[CURRENT_VERS:%.*]] = tail call swiftcc i32 @"$sSo19_SwiftStdlibVersionasE7currentABvgZ"()
|
||||
// CHECK: [[ICMP:%.*]] = icmp {{.*}}
|
||||
// CHECK: br i1 [[ICMP]], label %[[TRUE_LABEL:.*]], label %[[FALSE_LABEL:.*]]
|
||||
// CHECK: [[TRUE_LABEL]]:
|
||||
// CHECK: call {{.*}} @callMeMaybe()
|
||||
// CHECK: [[FALSE_LABEL]]:
|
||||
// CHECK: ret void
|
||||
public func testIfAvailable() {
|
||||
if #available(Swift 6.2, *) {
|
||||
callMeMaybe()
|
||||
}
|
||||
}
|
||||
|
||||
// In optimized IR multiple "if #available" checks for the same version should
|
||||
// only generate a single call to the getter for _SwiftStdlibVersion.current.
|
||||
|
||||
// CHECK-LABEL: define {{.*}}swiftcc void @"$s26availability_swift_runtime23testIfAvailableMultipleyyF"()
|
||||
// CHECK: call {{.*}} @"$sSo19_SwiftStdlibVersionasE7currentABvgZ"()
|
||||
// CHECK-NOT: call {{.*}} @"$sSo19_SwiftStdlibVersionasE7currentABvgZ"()
|
||||
// CHECK: icmp
|
||||
// CHECK: call {{.*}} @callMeMaybe()
|
||||
// CHECK: call {{.*}} @callMeMaybe()
|
||||
// CHECK: call {{.*}} @callMeMaybe()
|
||||
public func testIfAvailableMultiple() {
|
||||
if #available(Swift 5.10, *) {
|
||||
callMeMaybe()
|
||||
}
|
||||
if #available(Swift 5.10, *) {
|
||||
callMeMaybe()
|
||||
}
|
||||
if #available(Swift 5.10, *) {
|
||||
callMeMaybe()
|
||||
}
|
||||
}
|
||||
26
test/SILGen/availability_query_swift_runtime.swift
Normal file
26
test/SILGen/availability_query_swift_runtime.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
// RUN: %target-swift-emit-sil %s -min-swift-runtime-version 5.0 -verify -enable-experimental-feature SwiftRuntimeAvailability
|
||||
// RUN: %target-swift-emit-silgen %s -min-swift-runtime-version 5.0 -enable-experimental-feature SwiftRuntimeAvailability | %FileCheck %s
|
||||
|
||||
// REQUIRES: swift_feature_SwiftRuntimeAvailability
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s32availability_query_swift_runtime15testIfAvailableyyF : $@convention(thin) () -> () {
|
||||
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 6
|
||||
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 2
|
||||
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
|
||||
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
public func testIfAvailable() {
|
||||
if #available(Swift 6.2, *) { }
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s32availability_query_swift_runtime17testIfUnavailableyyF : $@convention(thin) () -> () {
|
||||
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 5
|
||||
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 10
|
||||
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 1
|
||||
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1
|
||||
// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]], [[MINUSONE]]) : $Builtin.Int1
|
||||
public func testIfUnavailable() {
|
||||
if #unavailable(Swift 5.10.1) { }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// REQUIRES: OS=macosx || OS=maccatalyst
|
||||
// REQUIRES: swift_feature_SwiftRuntimeAvailability
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s53availability_query_swift_runtime_maccatalyst_zippered15testIfAvailableyyF : $@convention(thin) () -> () {
|
||||
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 6
|
||||
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 2
|
||||
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
|
||||
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
public func testIfAvailable() {
|
||||
if #available(Swift 6.2, *) { }
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s53availability_query_swift_runtime_maccatalyst_zippered17testIfUnavailableyyF : $@convention(thin) () -> () {
|
||||
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 5
|
||||
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 10
|
||||
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 1
|
||||
// CHECK: [[FUNC:%.*]] = function_ref @$ss29_isSwiftRuntimeVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
|
||||
// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1
|
||||
// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]], [[MINUSONE]]) : $Builtin.Int1
|
||||
public func testIfUnavailable() {
|
||||
if #unavailable(Swift 5.10.1) { }
|
||||
}
|
||||
Reference in New Issue
Block a user