[darwin] add support for __isPlatformVersionAtLeast check for if (@available)
The __isPlatformVersionAtLeast routine is an implementation of `if (@available)` check
that uses the _availability_version_check API on Darwin that's supported on
macOS 10.15, iOS 13, tvOS 13 and watchOS 6.
Differential Revision: https://reviews.llvm.org/D90367
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
index a647128..89bf402 100644
--- a/clang/lib/CodeGen/CGObjC.cpp
+++ b/clang/lib/CodeGen/CGObjC.cpp
@@ -23,6 +23,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/MachO.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InlineAsm.h"
using namespace clang;
@@ -3814,9 +3815,61 @@
return Val;
}
+static unsigned getBaseMachOPlatformID(const llvm::Triple &TT) {
+ switch (TT.getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ return llvm::MachO::PLATFORM_MACOS;
+ case llvm::Triple::IOS:
+ return llvm::MachO::PLATFORM_IOS;
+ case llvm::Triple::TvOS:
+ return llvm::MachO::PLATFORM_TVOS;
+ case llvm::Triple::WatchOS:
+ return llvm::MachO::PLATFORM_WATCHOS;
+ default:
+ return /*Unknown platform*/ 0;
+ }
+}
+
+static llvm::Value *emitIsPlatformVersionAtLeast(CodeGenFunction &CGF,
+ const VersionTuple &Version) {
+ CodeGenModule &CGM = CGF.CGM;
+ // Note: we intend to support multi-platform version checks, so reserve
+ // the room for a dual platform checking invocation that will be
+ // implemented in the future.
+ llvm::SmallVector<llvm::Value *, 8> Args;
+
+ auto EmitArgs = [&](const VersionTuple &Version, const llvm::Triple &TT) {
+ Optional<unsigned> Min = Version.getMinor(), SMin = Version.getSubminor();
+ Args.push_back(
+ llvm::ConstantInt::get(CGM.Int32Ty, getBaseMachOPlatformID(TT)));
+ Args.push_back(llvm::ConstantInt::get(CGM.Int32Ty, Version.getMajor()));
+ Args.push_back(llvm::ConstantInt::get(CGM.Int32Ty, Min ? *Min : 0));
+ Args.push_back(llvm::ConstantInt::get(CGM.Int32Ty, SMin ? *SMin : 0));
+ };
+
+ assert(!Version.empty() && "unexpected empty version");
+ EmitArgs(Version, CGM.getTarget().getTriple());
+
+ if (!CGM.IsPlatformVersionAtLeastFn) {
+ llvm::FunctionType *FTy = llvm::FunctionType::get(
+ CGM.Int32Ty, {CGM.Int32Ty, CGM.Int32Ty, CGM.Int32Ty, CGM.Int32Ty},
+ false);
+ CGM.IsPlatformVersionAtLeastFn =
+ CGM.CreateRuntimeFunction(FTy, "__isPlatformVersionAtLeast");
+ }
+
+ llvm::Value *Check =
+ CGF.EmitNounwindRuntimeCall(CGM.IsPlatformVersionAtLeastFn, Args);
+ return CGF.Builder.CreateICmpNE(Check,
+ llvm::Constant::getNullValue(CGM.Int32Ty));
+}
+
llvm::Value *
-CodeGenFunction::EmitBuiltinAvailable(ArrayRef<llvm::Value *> Args) {
- assert(Args.size() == 3 && "Expected 3 argument here!");
+CodeGenFunction::EmitBuiltinAvailable(const VersionTuple &Version) {
+ // Darwin uses the new __isPlatformVersionAtLeast family of routines.
+ if (CGM.getTarget().getTriple().isOSDarwin())
+ return emitIsPlatformVersionAtLeast(*this, Version);
if (!CGM.IsOSVersionAtLeastFn) {
llvm::FunctionType *FTy =
@@ -3825,18 +3878,51 @@
CGM.CreateRuntimeFunction(FTy, "__isOSVersionAtLeast");
}
+ Optional<unsigned> Min = Version.getMinor(), SMin = Version.getSubminor();
+ llvm::Value *Args[] = {
+ llvm::ConstantInt::get(CGM.Int32Ty, Version.getMajor()),
+ llvm::ConstantInt::get(CGM.Int32Ty, Min ? *Min : 0),
+ llvm::ConstantInt::get(CGM.Int32Ty, SMin ? *SMin : 0),
+ };
+
llvm::Value *CallRes =
EmitNounwindRuntimeCall(CGM.IsOSVersionAtLeastFn, Args);
return Builder.CreateICmpNE(CallRes, llvm::Constant::getNullValue(Int32Ty));
}
+static bool isFoundationNeededForDarwinAvailabilityCheck(
+ const llvm::Triple &TT, const VersionTuple &TargetVersion) {
+ VersionTuple FoundationDroppedInVersion;
+ switch (TT.getOS()) {
+ case llvm::Triple::IOS:
+ case llvm::Triple::TvOS:
+ FoundationDroppedInVersion = VersionTuple(/*Major=*/13);
+ break;
+ case llvm::Triple::WatchOS:
+ FoundationDroppedInVersion = VersionTuple(/*Major=*/6);
+ break;
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ FoundationDroppedInVersion = VersionTuple(/*Major=*/10, /*Minor=*/15);
+ break;
+ default:
+ llvm_unreachable("Unexpected OS");
+ }
+ return TargetVersion < FoundationDroppedInVersion;
+}
+
void CodeGenModule::emitAtAvailableLinkGuard() {
- if (!IsOSVersionAtLeastFn)
+ if (!IsPlatformVersionAtLeastFn)
return;
// @available requires CoreFoundation only on Darwin.
if (!Target.getTriple().isOSDarwin())
return;
+ // @available doesn't need Foundation on macOS 10.15+, iOS/tvOS 13+, or
+ // watchOS 6+.
+ if (!isFoundationNeededForDarwinAvailabilityCheck(
+ Target.getTriple(), Target.getPlatformMinVersion()))
+ return;
// Add -framework CoreFoundation to the linker commands. We still want to
// emit the core foundation reference down below because otherwise if
// CoreFoundation is not used in the code, the linker won't link the