Skip to content

Commit 24aaff9

Browse files
authored
Merge pull request #82434 from tshortli/reorganize-availability
SILGen: Reorganize some availability related code
2 parents b9f73d5 + 897a74f commit 24aaff9

File tree

70 files changed

+659
-403
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+659
-403
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
/lib/SIL/IR/SILLocation* @adrian-prantl
167167
/lib/SIL/IR/SILProfiler.cpp @bnbarham @hamishknight @rintaro
168168
/lib/SILGen/ @jckarter
169+
/lib/SILGen/*Availability* @tshortli
169170
/lib/SILGen/*Distributed* @ktoso
170171
/lib/SILOptimizer/ @eeckstein
171172
/lib/SILOptimizer/**/*DebugInfo* @adrian-prantl
@@ -213,6 +214,7 @@
213214

214215
# test
215216
/test/*Demangl*/ @rjmccall
217+
/test/Availability/ @tshortli
216218
/test/ASTGen/ @bnbarham @CodaFi @hamishknight @rintaro
217219
/test/Concurrency/ @ktoso
218220
/test/Constraints/ @hborla @xedin

lib/SILGen/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ add_swift_host_library(swiftSILGen STATIC
1111
SwitchEnumBuilder.cpp
1212
SILGen.cpp
1313
SILGenApply.cpp
14-
SILGenBackDeploy.cpp
14+
SILGenAvailability.cpp
1515
SILGenBridging.cpp
1616
SILGenBuilder.cpp
1717
SILGenBuiltin.cpp

lib/SILGen/SILGenBackDeploy.cpp renamed to lib/SILGen/SILGenAvailability.cpp

Lines changed: 257 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
//===--- SILGenBackDeploy.cpp - SILGen for back deployment ----------------===//
1+
//===--- SILGenAvailability.cpp - SILGen for availability queries ---------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2022 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2022 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -13,13 +13,196 @@
1313
#include "SILGenFunction.h"
1414
#include "SILGenFunctionBuilder.h"
1515
#include "Scope.h"
16-
#include "swift/Basic/Platform.h"
1716
#include "swift/Basic/Assertions.h"
17+
#include "swift/Basic/Platform.h"
1818
#include "swift/SIL/SILDeclRef.h"
1919

2020
using namespace swift;
2121
using namespace Lowering;
2222

23+
/// Emit literals for the major, minor, and subminor components of the version
24+
/// and return a tuple of SILValues for them.
25+
static std::tuple<SILValue, SILValue, SILValue>
26+
emitVersionLiterals(SILLocation loc, SILGenBuilder &B, ASTContext &ctx,
27+
llvm::VersionTuple Vers) {
28+
unsigned major = Vers.getMajor();
29+
unsigned minor = Vers.getMinor().value_or(0);
30+
unsigned subminor = Vers.getSubminor().value_or(0);
31+
32+
SILType wordType = SILType::getBuiltinWordType(ctx);
33+
34+
SILValue majorValue = B.createIntegerLiteral(loc, wordType, major);
35+
SILValue minorValue = B.createIntegerLiteral(loc, wordType, minor);
36+
SILValue subminorValue = B.createIntegerLiteral(loc, wordType, subminor);
37+
38+
return std::make_tuple(majorValue, minorValue, subminorValue);
39+
}
40+
41+
/// Emit a check that returns 1 if the running OS version is in
42+
/// the specified version range and 0 otherwise. The returned SILValue
43+
/// (which has type Builtin.Int1) represents the result of this check.
44+
static SILValue emitOSVersionRangeCheck(SILGenFunction &SGF, SILLocation loc,
45+
const VersionRange &range,
46+
bool forTargetVariant) {
47+
auto &ctx = SGF.getASTContext();
48+
auto &B = SGF.B;
49+
50+
// Emit constants for the checked version range.
51+
SILValue majorValue;
52+
SILValue minorValue;
53+
SILValue subminorValue;
54+
55+
std::tie(majorValue, minorValue, subminorValue) =
56+
emitVersionLiterals(loc, B, ctx, range.getLowerEndpoint());
57+
58+
// Emit call to _stdlib_isOSVersionAtLeast(major, minor, patch)
59+
FuncDecl *versionQueryDecl = ctx.getIsOSVersionAtLeastDecl();
60+
61+
// When targeting macCatalyst, the version number will be an iOS version
62+
// number and so we call a variant of the query function that understands iOS
63+
// versions.
64+
if (forTargetVariant)
65+
versionQueryDecl = ctx.getIsVariantOSVersionAtLeastDecl();
66+
67+
assert(versionQueryDecl);
68+
69+
auto declRef = SILDeclRef(versionQueryDecl);
70+
SILValue availabilityGTEFn = SGF.emitGlobalFunctionRef(
71+
loc, declRef,
72+
SGF.getConstantInfo(SGF.getTypeExpansionContext(), declRef));
73+
74+
SILValue args[] = {majorValue, minorValue, subminorValue};
75+
return B.createApply(loc, availabilityGTEFn, SubstitutionMap(), args);
76+
}
77+
78+
static SILValue
79+
emitOSVersionOrVariantVersionRangeCheck(SILGenFunction &SGF, SILLocation loc,
80+
const VersionRange &targetRange,
81+
const VersionRange &variantRange) {
82+
auto &ctx = SGF.getASTContext();
83+
auto &B = SGF.B;
84+
85+
SILValue targetMajorValue;
86+
SILValue targetMinorValue;
87+
SILValue targetSubminorValue;
88+
89+
std::tie(targetMajorValue, targetMinorValue, targetSubminorValue) =
90+
emitVersionLiterals(loc, B, ctx, targetRange.getLowerEndpoint());
91+
92+
SILValue variantMajorValue;
93+
SILValue variantMinorValue;
94+
SILValue variantSubminorValue;
95+
96+
std::tie(variantMajorValue, variantMinorValue, variantSubminorValue) =
97+
emitVersionLiterals(loc, B, ctx, variantRange.getLowerEndpoint());
98+
99+
FuncDecl *versionQueryDecl =
100+
ctx.getIsOSVersionAtLeastOrVariantVersionAtLeast();
101+
102+
assert(versionQueryDecl);
103+
104+
auto declRef = SILDeclRef(versionQueryDecl);
105+
SILValue availabilityGTEFn = SGF.emitGlobalFunctionRef(
106+
loc, declRef,
107+
SGF.getConstantInfo(SGF.getTypeExpansionContext(), declRef));
108+
109+
SILValue args[] = {targetMajorValue, targetMinorValue,
110+
targetSubminorValue, variantMajorValue,
111+
variantMinorValue, variantSubminorValue};
112+
return B.createApply(loc, availabilityGTEFn, SubstitutionMap(), args);
113+
}
114+
115+
SILValue emitZipperedOSVersionRangeCheck(SILGenFunction &SGF, SILLocation loc,
116+
const VersionRange &targetRange,
117+
const VersionRange &variantRange) {
118+
auto &ctx = SGF.getASTContext();
119+
auto &B = SGF.B;
120+
121+
assert(ctx.LangOpts.TargetVariant);
122+
123+
VersionRange targetVersion = targetRange;
124+
VersionRange variantVersion = variantRange;
125+
126+
// We're building zippered, so we need to pass both macOS and iOS versions to
127+
// the runtime version range check. At run time that check will determine what
128+
// kind of process this code is loaded into. In a macOS process it will use
129+
// the macOS version; in an macCatalyst process it will use the iOS version.
130+
llvm::Triple targetTriple = ctx.LangOpts.Target;
131+
llvm::Triple variantTriple = *ctx.LangOpts.TargetVariant;
132+
133+
// From perspective of the driver and most of the frontend, -target and
134+
// -target-variant are symmetric. That is, the user can pass either:
135+
// -target x86_64-apple-macosx10.15 \
136+
// -target-variant x86_64-apple-ios13.1-macabi
137+
// or:
138+
// -target x86_64-apple-ios13.1-macabi \
139+
// -target-variant x86_64-apple-macosx10.15
140+
//
141+
// However, the runtime availability-checking entry points need to compare
142+
// against an actual running OS version and so can't be symmetric. Here we
143+
// standardize on "target" means macOS version and "targetVariant" means iOS
144+
// version.
145+
if (tripleIsMacCatalystEnvironment(targetTriple)) {
146+
assert(variantTriple.isMacOSX());
147+
// Normalize so that "variant" always means iOS version.
148+
std::swap(targetVersion, variantVersion);
149+
std::swap(targetTriple, variantTriple);
150+
}
151+
152+
// If there is no check for either the target platform or the target-variant
153+
// platform then the condition is trivially true.
154+
if (targetVersion.isAll() && variantVersion.isAll()) {
155+
SILType i1 = SILType::getBuiltinIntegerType(1, ctx);
156+
return B.createIntegerLiteral(loc, i1, true);
157+
}
158+
159+
// If either version is "never" then the check is trivially false because it
160+
// can never succeed.
161+
if (targetVersion.isEmpty() || variantVersion.isEmpty()) {
162+
SILType i1 = SILType::getBuiltinIntegerType(1, ctx);
163+
return B.createIntegerLiteral(loc, i1, false);
164+
}
165+
166+
// The variant-only availability-checking entrypoint is not part of the
167+
// Swift 5.0 ABI. It is only available in macOS 10.15 and above.
168+
bool isVariantEntrypointAvailable = !targetTriple.isMacOSXVersionLT(10, 15);
169+
170+
// If there is no check for the target but there is for the variant, then we
171+
// only need to emit code for the variant check.
172+
if (isVariantEntrypointAvailable && targetVersion.isAll() &&
173+
!variantVersion.isAll())
174+
return emitOSVersionRangeCheck(SGF, loc, variantVersion,
175+
/*forVariant*/ true);
176+
177+
// Similarly, if there is a check for the target but not for the target
178+
// variant then we only to emit code for the target check.
179+
if (!targetVersion.isAll() && variantVersion.isAll())
180+
return emitOSVersionRangeCheck(SGF, loc, targetVersion,
181+
/*forVariant*/ false);
182+
183+
if (!isVariantEntrypointAvailable ||
184+
(!targetVersion.isAll() && !variantVersion.isAll())) {
185+
186+
// If the variant-only entrypoint isn't available (as is the case
187+
// pre-macOS 10.15) we need to use the zippered entrypoint (which is part of
188+
// the Swift 5.0 ABI) even when the macOS version is '*' (all). In this
189+
// case, use the minimum macOS deployment version from the target triple.
190+
// This ensures the check always passes on macOS.
191+
if (!isVariantEntrypointAvailable && targetVersion.isAll()) {
192+
assert(targetTriple.isMacOSX());
193+
194+
llvm::VersionTuple macosVersion;
195+
targetTriple.getMacOSXVersion(macosVersion);
196+
targetVersion = VersionRange::allGTE(macosVersion);
197+
}
198+
199+
return emitOSVersionOrVariantVersionRangeCheck(SGF, loc, targetVersion,
200+
variantVersion);
201+
}
202+
203+
llvm_unreachable("Unhandled zippered configuration");
204+
}
205+
23206
/// Given a value, extracts all elements to `result` from this value if it's a
24207
/// tuple. Otherwise, add this value directly to `result`.
25208
static void extractAllElements(SILValue val, SILLocation loc,
@@ -68,7 +251,7 @@ static SILValue emitZipperedBackDeployIfAvailableBooleanTestValue(
68251
VariantOSVersion = VersionRange::allGTE(*version);
69252
}
70253

71-
return SGF.emitZipperedOSVersionRangeCheck(loc, OSVersion, VariantOSVersion);
254+
return emitZipperedOSVersionRangeCheck(SGF, loc, OSVersion, VariantOSVersion);
72255
}
73256

74257
/// Emit the following branch SIL instruction:
@@ -108,7 +291,7 @@ static void emitBackDeployIfAvailableCondition(SILGenFunction &SGF,
108291
bool isMacCatalyst =
109292
tripleIsMacCatalystEnvironment(SGF.getASTContext().LangOpts.Target);
110293
booleanTestValue =
111-
SGF.emitOSVersionRangeCheck(loc, OSVersion, isMacCatalyst);
294+
emitOSVersionRangeCheck(SGF, loc, OSVersion, isMacCatalyst);
112295
}
113296

114297
SGF.B.createCondBranch(loc, booleanTestValue, availableBB, unavailableBB);
@@ -203,6 +386,75 @@ static void emitBackDeployForwardApplyAndReturnOrThrow(
203386
SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults);
204387
}
205388

389+
SILValue
390+
SILGenFunction::emitIfAvailableQuery(SILLocation loc,
391+
PoundAvailableInfo *availability) {
392+
auto &ctx = getASTContext();
393+
SILValue result;
394+
395+
// Creates a boolean literal for availability conditions that have been
396+
// evaluated at compile time. Automatically inverts the value for
397+
// `#unavailable` queries.
398+
auto createBooleanTestLiteral = [&](bool value) {
399+
SILType i1 = SILType::getBuiltinIntegerType(1, ctx);
400+
if (availability->isUnavailability())
401+
value = !value;
402+
return B.createIntegerLiteral(loc, i1, value);
403+
};
404+
405+
auto versionRange = availability->getAvailableRange();
406+
407+
// The OS version might be left empty if availability checking was disabled.
408+
// Treat it as always-true in that case.
409+
assert(versionRange || ctx.LangOpts.DisableAvailabilityChecking);
410+
411+
if (ctx.LangOpts.TargetVariant && !ctx.LangOpts.DisableAvailabilityChecking) {
412+
// We're building zippered, so we need to pass both macOS and iOS versions
413+
// to the the runtime version range check. At run time that check will
414+
// determine what kind of process this code is loaded into. In a macOS
415+
// process it will use the macOS version; in an macCatalyst process it will
416+
// use the iOS version.
417+
418+
auto variantVersionRange = availability->getVariantAvailableRange();
419+
assert(variantVersionRange);
420+
421+
if (versionRange && variantVersionRange) {
422+
result = emitZipperedOSVersionRangeCheck(*this, loc, *versionRange,
423+
*variantVersionRange);
424+
} else {
425+
// Type checking did not fill in versions so as a fallback treat this
426+
// condition as trivially true.
427+
result = createBooleanTestLiteral(true);
428+
}
429+
430+
return result;
431+
}
432+
433+
if (!versionRange) {
434+
// Type checking did not fill in version so as a fallback treat this
435+
// condition as trivially true.
436+
result = createBooleanTestLiteral(true);
437+
} else if (versionRange->isAll()) {
438+
result = createBooleanTestLiteral(true);
439+
} else if (versionRange->isEmpty()) {
440+
result = createBooleanTestLiteral(false);
441+
} else {
442+
bool isMacCatalyst = tripleIsMacCatalystEnvironment(ctx.LangOpts.Target);
443+
result = emitOSVersionRangeCheck(*this, loc, versionRange.value(),
444+
isMacCatalyst);
445+
if (availability->isUnavailability()) {
446+
// If this is an unavailability check, invert the result
447+
// by emitting a call to Builtin.xor_Int1(lhs, -1).
448+
SILType i1 = SILType::getBuiltinIntegerType(1, ctx);
449+
SILValue minusOne = B.createIntegerLiteral(loc, i1, -1);
450+
result =
451+
B.createBuiltinBinaryFunction(loc, "xor", i1, i1, {result, minusOne});
452+
}
453+
}
454+
455+
return result;
456+
}
457+
206458
bool SILGenModule::requiresBackDeploymentThunk(ValueDecl *decl,
207459
ResilienceExpansion expansion) {
208460
auto &ctx = getASTContext();

0 commit comments

Comments
 (0)