2424#include " swift/AST/NameLookup.h"
2525#include " swift/AST/PropertyWrappers.h"
2626#include " swift/AST/ProtocolConformance.h"
27+ #include " swift/Basic/Platform.h"
2728#include " swift/Basic/Assertions.h"
2829#include " swift/Basic/ProfileCounter.h"
2930#include " swift/SIL/FormalLinkage.h"
@@ -1725,17 +1726,26 @@ emitVersionLiterals(SILLocation loc, SILGenBuilder &B, ASTContext &ctx,
17251726// / the specified version range and 0 otherwise. The returned SILValue
17261727// / (which has type Builtin.Int1) represents the result of this check.
17271728SILValue SILGenFunction::emitOSVersionRangeCheck (SILLocation loc,
1728- const VersionRange &range) {
1729+ const VersionRange &range,
1730+ bool forTargetVariant) {
17291731 // Emit constants for the checked version range.
17301732 SILValue majorValue;
17311733 SILValue minorValue;
17321734 SILValue subminorValue;
1735+
17331736 std::tie (majorValue, minorValue, subminorValue) =
17341737 emitVersionLiterals (loc, B, getASTContext (), range.getLowerEndpoint ());
17351738
17361739 // Emit call to _stdlib_isOSVersionAtLeast(major, minor, patch)
17371740 FuncDecl *versionQueryDecl =
17381741 getASTContext ().getIsOSVersionAtLeastDecl ();
1742+
1743+ // When targeting macCatalyst, the version number will be an iOS version number
1744+ // and so we call a variant of the query function that understands iOS
1745+ // versions.
1746+ if (forTargetVariant)
1747+ versionQueryDecl = getASTContext ().getIsVariantOSVersionAtLeastDecl ();
1748+
17391749 assert (versionQueryDecl);
17401750
17411751 auto silDeclRef = SILDeclRef (versionQueryDecl);
@@ -1746,6 +1756,130 @@ SILValue SILGenFunction::emitOSVersionRangeCheck(SILLocation loc,
17461756 return B.createApply (loc, availabilityGTEFn, SubstitutionMap (), args);
17471757}
17481758
1759+ SILValue SILGenFunction::emitOSVersionOrVariantVersionRangeCheck (
1760+ SILLocation loc, const VersionRange &targetRange,
1761+ const VersionRange &variantRange) {
1762+ SILValue targetMajorValue;
1763+ SILValue targetMinorValue;
1764+ SILValue targetSubminorValue;
1765+
1766+ const llvm::VersionTuple &targetVersion = targetRange.getLowerEndpoint ();
1767+ std::tie (targetMajorValue, targetMinorValue, targetSubminorValue) =
1768+ emitVersionLiterals (loc, B, getASTContext (), targetVersion);
1769+
1770+ SILValue variantMajorValue;
1771+ SILValue variantMinorValue;
1772+ SILValue variantSubminorValue;
1773+
1774+ const llvm::VersionTuple &variantVersion = variantRange.getLowerEndpoint ();
1775+ std::tie (variantMajorValue, variantMinorValue, variantSubminorValue) =
1776+ emitVersionLiterals (loc, B, getASTContext (), variantVersion);
1777+
1778+ FuncDecl *versionQueryDecl =
1779+ getASTContext ().getIsOSVersionAtLeastOrVariantVersionAtLeast ();
1780+
1781+ assert (versionQueryDecl);
1782+
1783+ auto silDeclRef = SILDeclRef (versionQueryDecl);
1784+ SILValue availabilityGTEFn = emitGlobalFunctionRef (
1785+ loc, silDeclRef, getConstantInfo (getTypeExpansionContext (), silDeclRef));
1786+
1787+ SILValue args[] = {
1788+ targetMajorValue,
1789+ targetMinorValue,
1790+ targetSubminorValue,
1791+ variantMajorValue,
1792+ variantMinorValue,
1793+ variantSubminorValue
1794+ };
1795+ return B.createApply (loc, availabilityGTEFn, SubstitutionMap (), args);
1796+ }
1797+
1798+ SILValue SILGenFunction::emitZipperedOSVersionRangeCheck (
1799+ SILLocation loc, const VersionRange &targetRange,
1800+ const VersionRange &variantRange) {
1801+ assert (getASTContext ().LangOpts .TargetVariant );
1802+
1803+ VersionRange OSVersion = targetRange;
1804+ VersionRange VariantOSVersion = variantRange;
1805+
1806+ // We're building zippered, so we need to pass both macOS and iOS
1807+ // versions to the the runtime version range check. At run time
1808+ // that check will determine what kind of process this code is loaded
1809+ // into. In a macOS process it will use the macOS version; in an
1810+ // macCatalyst process it will use the iOS version.
1811+ llvm::Triple VariantTriple = *getASTContext ().LangOpts .TargetVariant ;
1812+ llvm::Triple TargetTriple = getASTContext ().LangOpts .Target ;
1813+ assert (triplesAreValidForZippering (TargetTriple, VariantTriple));
1814+
1815+ // From perspective of the driver and most of the frontend,
1816+ // -target and -target-variant are symmetric. That is, the user
1817+ // can pass either:
1818+ // -target x86_64-apple-macosx10.15 \
1819+ // -target-variant x86_64-apple-ios13.1-macabi
1820+ // or:
1821+ // -target x86_64-apple-ios13.1-macabi \
1822+ // -target-variant x86_64-apple-macosx10.15
1823+ //
1824+ // However, the runtime availability-checking entry points need
1825+ // to compare against an actual running OS version and so can't be
1826+ // symmetric. Here we standardize on "target" means macOS version
1827+ // and "targetVariant" means iOS version.
1828+ if (tripleIsMacCatalystEnvironment (TargetTriple)) {
1829+ assert (VariantTriple.isMacOSX ());
1830+ // Normalize so that "variant" always means iOS version.
1831+ std::swap (OSVersion, VariantOSVersion);
1832+ std::swap (TargetTriple, VariantTriple);
1833+ }
1834+
1835+ // If there is no check for either the target platform
1836+ // or the target-variant platform then the condition is
1837+ // trivially true.
1838+ if (OSVersion.isAll () && VariantOSVersion.isAll ()) {
1839+ SILType i1 = SILType::getBuiltinIntegerType (1 , getASTContext ());
1840+ return B.createIntegerLiteral (loc, i1, true );
1841+ }
1842+
1843+ // The variant-only availability-checking entrypoint is not part
1844+ // of the Swift 5.0 ABI. It is only available in macOS 10.15 and above.
1845+ bool isVariantEntrypointAvailable = !TargetTriple.isMacOSXVersionLT (10 , 15 );
1846+
1847+ // If there is no check for the target but there is for the
1848+ // variant, then we only need to emit code for the variant check.
1849+ if (isVariantEntrypointAvailable && OSVersion.isAll () &&
1850+ !VariantOSVersion.isAll ())
1851+ return emitOSVersionRangeCheck (loc, VariantOSVersion,
1852+ /* forVariant*/ true );
1853+
1854+ // Similarly, if there is a check for the target but not for the
1855+ // target variant then we only to emit code for the target check.
1856+ if (!OSVersion.isAll () && VariantOSVersion.isAll ())
1857+ return emitOSVersionRangeCheck (loc, OSVersion,
1858+ /* forVariant*/ false );
1859+
1860+ if (!isVariantEntrypointAvailable ||
1861+ (!OSVersion.isAll () && !VariantOSVersion.isAll ())) {
1862+
1863+ // If the variant-only entrypoint isn't available (as is the
1864+ // case pre-macOS 10.15) we need to use the zippered entrypoint
1865+ // (which is part of the Swift 5.0 ABI) even when the macOS version
1866+ // is '*' (all).
1867+ // In this case, use the minimum macOS deployment version from
1868+ // the target triple. This ensures the check always passes on macOS.
1869+ if (!isVariantEntrypointAvailable && OSVersion.isAll ()) {
1870+ assert (TargetTriple.isMacOSX ());
1871+
1872+ llvm::VersionTuple macosVersion;
1873+ TargetTriple.getMacOSXVersion (macosVersion);
1874+ OSVersion = VersionRange::allGTE (macosVersion);
1875+ }
1876+
1877+ return emitOSVersionOrVariantVersionRangeCheck (loc, OSVersion,
1878+ VariantOSVersion);
1879+ }
1880+
1881+ llvm_unreachable (" Unhandled zippered configuration" );
1882+ }
17491883
17501884// / Emit the boolean test and/or pattern bindings indicated by the specified
17511885// / stmt condition. If the condition fails, control flow is transferred to the
@@ -1795,20 +1929,38 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest,
17951929 // specified by elt.
17961930 PoundAvailableInfo *availability = elt.getAvailability ();
17971931 VersionRange OSVersion = availability->getAvailableRange ();
1798-
1932+
17991933 // The OS version might be left empty if availability checking was
18001934 // disabled. Treat it as always-true in that case.
18011935 assert (!OSVersion.isEmpty ()
18021936 || getASTContext ().LangOpts .DisableAvailabilityChecking );
1803-
1937+
1938+ if (getASTContext ().LangOpts .TargetVariant ) {
1939+ // We're building zippered, so we need to pass both macOS and iOS
1940+ // versions to the the runtime version range check. At run time
1941+ // that check will determine what kind of process this code is loaded
1942+ // into. In a macOS process it will use the macOS version; in an
1943+ // macCatalyst process it will use the iOS version.
1944+
1945+ VersionRange VariantOSVersion =
1946+ elt.getAvailability ()->getVariantAvailableRange ();
1947+ assert (!VariantOSVersion.isEmpty ());
1948+ booleanTestValue =
1949+ emitZipperedOSVersionRangeCheck (loc, OSVersion, VariantOSVersion);
1950+ break ;
1951+ }
1952+
18041953 if (OSVersion.isEmpty () || OSVersion.isAll ()) {
18051954 // If there's no check for the current platform, this condition is
18061955 // trivially true (or false, for unavailability).
18071956 SILType i1 = SILType::getBuiltinIntegerType (1 , getASTContext ());
18081957 bool value = !availability->isUnavailability ();
18091958 booleanTestValue = B.createIntegerLiteral (loc, i1, value);
18101959 } else {
1811- booleanTestValue = emitOSVersionRangeCheck (loc, OSVersion);
1960+ bool isMacCatalyst =
1961+ tripleIsMacCatalystEnvironment (getASTContext ().LangOpts .Target );
1962+ booleanTestValue = emitOSVersionRangeCheck (loc, OSVersion,
1963+ isMacCatalyst);
18121964 if (availability->isUnavailability ()) {
18131965 // If this is an unavailability check, invert the result
18141966 // by emitting a call to Builtin.xor_Int1(lhs, -1).
0 commit comments