Index: lib/CodeGen/CGObjC.cpp =================================================================== --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -3416,4 +3416,28 @@ return Builder.CreateICmpNE(CallRes, llvm::Constant::getNullValue(Int32Ty)); } +void CodeGenModule::emitAtAvailableLinkGuard() { + // @available requires CoreFoundation only on Darwin. + if (!Target.getTriple().isOSDarwin()) + return; + if (!IsOSVersionAtLeastFn) + return; + // Emit a reference to a symbol from CoreFoundation to ensure that + // CoreFoundation is linked into the final binary. + llvm::FunctionType *FTy = + llvm::FunctionType::get(Int32Ty, {VoidPtrTy}, false); + llvm::Constant *CFFunc = + CreateRuntimeFunction(FTy, "CFBundleGetVersionNumber"); + + llvm::FunctionType *CheckFTy = llvm::FunctionType::get(VoidTy, {}, false); + llvm::Function *CFLinkCheckFunc = cast(CreateBuiltinFunction( + CheckFTy, "__clang_at_available_requires_core_foundation_framework")); + CFLinkCheckFunc->setLinkage(llvm::GlobalValue::LinkOnceAnyLinkage); + CodeGenFunction CGF(*this); + CGF.Builder.SetInsertPoint(CGF.createBasicBlock("", CFLinkCheckFunc)); + CGF.EmitNounwindRuntimeCall(CFFunc, llvm::Constant::getNullValue(VoidPtrTy)); + CGF.Builder.CreateUnreachable(); + addCompilerUsedGlobal(CFLinkCheckFunc); +} + CGObjCRuntime::~CGObjCRuntime() {} Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -1288,6 +1288,10 @@ /// Emit any vtables which we deferred and still have a use for. void EmitDeferredVTables(); + /// Emit a dummy function that reference a CoreFoundation symbol when + /// @available is used on Darwin. + void emitAtAvailableLinkGuard(); + /// Emit the llvm.used and llvm.compiler.used metadata. void emitLLVMUsed(); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -408,6 +408,7 @@ CoverageMapping->emit(); if (CodeGenOpts.SanitizeCfiCrossDso) CodeGenFunction(*this).EmitCfiCheckFail(); + emitAtAvailableLinkGuard(); emitLLVMUsed(); if (SanStats) SanStats->finish(); Index: test/CodeGenObjC/availability-cf-link-guard.m =================================================================== --- /dev/null +++ test/CodeGenObjC/availability-cf-link-guard.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D USE_BUILTIN %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D DEF_CF %s | FileCheck --check-prefix=CHECK_CF %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK_NO_GUARD %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -emit-llvm -o - %s | FileCheck --check-prefix=CHECK_NO_GUARD %s + +#ifdef DEF_CF +struct CFBundle; +typedef struct CFBundle *CFBundleRef; +unsigned CFBundleGetVersionNumber(CFBundleRef bundle); +// CHECK_CF: declare i32 @CFBundleGetVersionNumber(%struct.CFBundle*) +// CHECK_CF: @__clang_at_available_requires_core_foundation_framework +// CHECK_CF-NEXT: call {{.*}}@CFBundleGetVersionNumber +#endif + +void use_at_available() { +#ifdef DEF_CF + CFBundleGetVersionNumber(0); +#endif +#ifdef USE_BUILTIN + if (__builtin_available(macos 10.12, *)) + ; +#else + if (@available(macos 10.12, *)) + ; +#endif +} + +// CHECK: @llvm.compiler.used{{.*}}@__clang_at_available_requires_core_foundation_framework + +// CHECK: declare i32 @CFBundleGetVersionNumber(i8*) + +// CHECK-LABEL: linkonce void @__clang_at_available_requires_core_foundation_framework +// CHECK: call i32 @CFBundleGetVersionNumber(i8* null) +// CHECK-NEXT: unreachable + +// CHECK_NO_GUARD-NOT: __clang_at_available_requires_core_foundation_framework +// CHECK_NO_GUARD-NOT: CFBundleGetVersionNumber