24
24
#include " swift/AST/NameLookup.h"
25
25
#include " swift/AST/PropertyWrappers.h"
26
26
#include " swift/AST/ProtocolConformance.h"
27
+ #include " swift/Basic/Platform.h"
27
28
#include " swift/Basic/Assertions.h"
28
29
#include " swift/Basic/ProfileCounter.h"
29
30
#include " swift/SIL/FormalLinkage.h"
@@ -1725,17 +1726,26 @@ emitVersionLiterals(SILLocation loc, SILGenBuilder &B, ASTContext &ctx,
1725
1726
// / the specified version range and 0 otherwise. The returned SILValue
1726
1727
// / (which has type Builtin.Int1) represents the result of this check.
1727
1728
SILValue SILGenFunction::emitOSVersionRangeCheck (SILLocation loc,
1728
- const VersionRange &range) {
1729
+ const VersionRange &range,
1730
+ bool forTargetVariant) {
1729
1731
// Emit constants for the checked version range.
1730
1732
SILValue majorValue;
1731
1733
SILValue minorValue;
1732
1734
SILValue subminorValue;
1735
+
1733
1736
std::tie (majorValue, minorValue, subminorValue) =
1734
1737
emitVersionLiterals (loc, B, getASTContext (), range.getLowerEndpoint ());
1735
1738
1736
1739
// Emit call to _stdlib_isOSVersionAtLeast(major, minor, patch)
1737
1740
FuncDecl *versionQueryDecl =
1738
1741
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
+
1739
1749
assert (versionQueryDecl);
1740
1750
1741
1751
auto silDeclRef = SILDeclRef (versionQueryDecl);
@@ -1746,6 +1756,130 @@ SILValue SILGenFunction::emitOSVersionRangeCheck(SILLocation loc,
1746
1756
return B.createApply (loc, availabilityGTEFn, SubstitutionMap (), args);
1747
1757
}
1748
1758
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
+ }
1749
1883
1750
1884
// / Emit the boolean test and/or pattern bindings indicated by the specified
1751
1885
// / stmt condition. If the condition fails, control flow is transferred to the
@@ -1795,20 +1929,38 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest,
1795
1929
// specified by elt.
1796
1930
PoundAvailableInfo *availability = elt.getAvailability ();
1797
1931
VersionRange OSVersion = availability->getAvailableRange ();
1798
-
1932
+
1799
1933
// The OS version might be left empty if availability checking was
1800
1934
// disabled. Treat it as always-true in that case.
1801
1935
assert (!OSVersion.isEmpty ()
1802
1936
|| 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
+
1804
1953
if (OSVersion.isEmpty () || OSVersion.isAll ()) {
1805
1954
// If there's no check for the current platform, this condition is
1806
1955
// trivially true (or false, for unavailability).
1807
1956
SILType i1 = SILType::getBuiltinIntegerType (1 , getASTContext ());
1808
1957
bool value = !availability->isUnavailability ();
1809
1958
booleanTestValue = B.createIntegerLiteral (loc, i1, value);
1810
1959
} else {
1811
- booleanTestValue = emitOSVersionRangeCheck (loc, OSVersion);
1960
+ bool isMacCatalyst =
1961
+ tripleIsMacCatalystEnvironment (getASTContext ().LangOpts .Target );
1962
+ booleanTestValue = emitOSVersionRangeCheck (loc, OSVersion,
1963
+ isMacCatalyst);
1812
1964
if (availability->isUnavailability ()) {
1813
1965
// If this is an unavailability check, invert the result
1814
1966
// by emitting a call to Builtin.xor_Int1(lhs, -1).
0 commit comments