Skip to content

Commit 6e08b89

Browse files
committed
SILGen: Refactor emission of availability checks.
Generalize SILGen for `if #available` checks by delegating the determination of the query function and its arguments to `AvailabilityQuery`. Update SILGen for `@backDeployed` thunks to use the same infrastructure. NFC.
1 parent a4f59f9 commit 6e08b89

File tree

4 files changed

+230
-230
lines changed

4 files changed

+230
-230
lines changed

include/swift/AST/AvailabilityQuery.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#include "llvm/Support/VersionTuple.h"
2424

2525
namespace swift {
26+
class ASTContext;
27+
class FuncDecl;
28+
2629
/// Represents the information needed to evaluate an `#if available` query
2730
/// (either at runtime or compile-time).
2831
class AvailabilityQuery final {
@@ -121,6 +124,14 @@ class AvailabilityQuery final {
121124
return std::nullopt;
122125
return variantRange->getRawMinimumVersion();
123126
}
127+
128+
/// Returns the `FuncDecl *` that should be invoked at runtime to evaluate
129+
/// the query, and populates `arguments` with the arguments to invoke it with
130+
/// (the integer components of the version tuples that are being tested). If
131+
/// the query does not have a dynamic result, returns `nullptr`.
132+
FuncDecl *
133+
getDynamicQueryDeclAndArguments(llvm::SmallVectorImpl<unsigned> &arguments,
134+
ASTContext &ctx) const;
124135
};
125136

126137
} // end namespace swift

lib/AST/AvailabilityQuery.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//===--- AvailabilityQuery.cpp - Swift Availability Queries ---------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/AST/AvailabilityQuery.h"
14+
#include "swift/AST/ASTContext.h"
15+
#include "swift/AST/Decl.h"
16+
#include "swift/Basic/Platform.h"
17+
18+
using namespace swift;
19+
20+
static void unpackVersion(const llvm::VersionTuple &version,
21+
llvm::SmallVectorImpl<unsigned> &arguments) {
22+
arguments.push_back(version.getMajor());
23+
arguments.push_back(version.getMinor().value_or(0));
24+
arguments.push_back(version.getSubminor().value_or(0));
25+
}
26+
27+
static FuncDecl *
28+
getOSVersionRangeCheck(const llvm::VersionTuple &version,
29+
llvm::SmallVectorImpl<unsigned> &arguments,
30+
ASTContext &ctx, bool forTargetVariant) {
31+
unpackVersion(version, arguments);
32+
return forTargetVariant ? ctx.getIsVariantOSVersionAtLeastDecl()
33+
: ctx.getIsOSVersionAtLeastDecl();
34+
}
35+
36+
static FuncDecl *getOSVersionOrVariantVersionRangeCheck(
37+
const llvm::VersionTuple &targetVersion,
38+
const llvm::VersionTuple &variantVersion,
39+
llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) {
40+
unpackVersion(targetVersion, arguments);
41+
unpackVersion(variantVersion, arguments);
42+
return ctx.getIsOSVersionAtLeastOrVariantVersionAtLeast();
43+
}
44+
45+
static FuncDecl *
46+
getZipperedOSVersionRangeCheck(const AvailabilityQuery &query,
47+
llvm::SmallVectorImpl<unsigned> &arguments,
48+
ASTContext &ctx) {
49+
50+
auto targetVersion = query.getPrimaryArgument();
51+
auto variantVersion = query.getVariantArgument();
52+
DEBUG_ASSERT(targetVersion || variantVersion);
53+
54+
// We're building zippered, so we need to pass both macOS and iOS versions to
55+
// the runtime version range check. At run time that check will determine what
56+
// kind of process this code is loaded into. In a macOS process it will use
57+
// the macOS version; in an macCatalyst process it will use the iOS version.
58+
llvm::Triple targetTriple = ctx.LangOpts.Target;
59+
llvm::Triple variantTriple = *ctx.LangOpts.TargetVariant;
60+
61+
// From perspective of the driver and most of the frontend, -target and
62+
// -target-variant are symmetric. That is, the user can pass either:
63+
// -target x86_64-apple-macosx10.15 \
64+
// -target-variant x86_64-apple-ios13.1-macabi
65+
// or:
66+
// -target x86_64-apple-ios13.1-macabi \
67+
// -target-variant x86_64-apple-macosx10.15
68+
//
69+
// However, the runtime availability-checking entry points need to compare
70+
// against an actual running OS version and so can't be symmetric. Here we
71+
// standardize on "target" means macOS version and "targetVariant" means iOS
72+
// version.
73+
if (tripleIsMacCatalystEnvironment(targetTriple)) {
74+
DEBUG_ASSERT(variantTriple.isMacOSX());
75+
// Normalize so that "variant" always means iOS version.
76+
std::swap(targetVersion, variantVersion);
77+
std::swap(targetTriple, variantTriple);
78+
}
79+
80+
// The variant-only availability-checking entrypoint is not part of the
81+
// Swift 5.0 ABI. It is only available in macOS 10.15 and above.
82+
bool isVariantEntrypointAvailable = !targetTriple.isMacOSXVersionLT(10, 15);
83+
84+
// If there is no check for the target but there is for the variant, then we
85+
// only need to emit code for the variant check.
86+
if (isVariantEntrypointAvailable && !targetVersion && variantVersion)
87+
return getOSVersionRangeCheck(*variantVersion, arguments, ctx,
88+
/*forVariant=*/true);
89+
90+
// Similarly, if there is a check for the target but not for the target
91+
// variant then we only to emit code for the target check.
92+
if (targetVersion && !variantVersion)
93+
return getOSVersionRangeCheck(*targetVersion, arguments, ctx,
94+
/*forTargetVariant=*/false);
95+
96+
if (!isVariantEntrypointAvailable || (targetVersion && variantVersion)) {
97+
98+
// If the variant-only entrypoint isn't available (as is the case
99+
// pre-macOS 10.15) we need to use the zippered entrypoint (which is part of
100+
// the Swift 5.0 ABI) even when the macOS version is '*' (all). In this
101+
// case, use the minimum macOS deployment version from the target triple.
102+
// This ensures the check always passes on macOS.
103+
if (!isVariantEntrypointAvailable && !targetVersion) {
104+
DEBUG_ASSERT(targetTriple.isMacOSX());
105+
106+
llvm::VersionTuple macosVersion;
107+
targetTriple.getMacOSXVersion(macosVersion);
108+
targetVersion = macosVersion;
109+
}
110+
111+
return getOSVersionOrVariantVersionRangeCheck(
112+
*targetVersion, *variantVersion, arguments, ctx);
113+
}
114+
115+
llvm_unreachable("Unhandled zippered configuration");
116+
}
117+
118+
static FuncDecl *
119+
getOSAvailabilityDeclAndArguments(const AvailabilityQuery &query,
120+
llvm::SmallVectorImpl<unsigned> &arguments,
121+
ASTContext &ctx) {
122+
if (ctx.LangOpts.TargetVariant)
123+
return getZipperedOSVersionRangeCheck(query, arguments, ctx);
124+
125+
bool isMacCatalyst = tripleIsMacCatalystEnvironment(ctx.LangOpts.Target);
126+
return getOSVersionRangeCheck(query.getPrimaryArgument().value(), arguments,
127+
ctx, isMacCatalyst);
128+
}
129+
130+
FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
131+
llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) const {
132+
switch (getDomain().getKind()) {
133+
case AvailabilityDomain::Kind::Universal:
134+
case AvailabilityDomain::Kind::SwiftLanguage:
135+
case AvailabilityDomain::Kind::PackageDescription:
136+
case AvailabilityDomain::Kind::Embedded:
137+
return nullptr;
138+
case AvailabilityDomain::Kind::Platform:
139+
return getOSAvailabilityDeclAndArguments(*this, arguments, ctx);
140+
case AvailabilityDomain::Kind::Custom:
141+
// FIXME: [availability] Support custom domains.
142+
return nullptr;
143+
}
144+
}

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_swift_host_library(swiftAST STATIC
3030
AvailabilityConstraint.cpp
3131
AvailabilityContext.cpp
3232
AvailabilityDomain.cpp
33+
AvailabilityQuery.cpp
3334
AvailabilityScope.cpp
3435
AvailabilityScopeBuilder.cpp
3536
AvailabilitySpec.cpp

0 commit comments

Comments
 (0)