[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
arphaman created this revision. After r297760, __isOSVersionAtLeast in compiler-rt loads the CoreFoundation symbols at runtime. This means that `@available` will always fail when used in a binary without a linked CoreFoundation. This patch forces Clang to emit a reference to a CoreFoundation symbol when `@available` is used to ensure that linking will fail when CoreFoundation isn't linked with the build product. Repository: rL LLVM https://reviews.llvm.org/D30977 Files: lib/CodeGen/CGObjC.cpp lib/CodeGen/CodeGenModule.cpp lib/CodeGen/CodeGenModule.h test/CodeGenObjC/availability-cf-link-guard.m 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 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: 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() {} ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
rjmccall added inline comments. Comment at: lib/CodeGen/CGObjC.cpp:3435 + CheckFTy, "__clang_at_available_requires_core_foundation_framework")); + CFLinkCheckFunc->setLinkage(llvm::GlobalValue::LinkOnceAnyLinkage); + CodeGenFunction CGF(*this); Is this a public weak symbol? Make it hidden, please. Did you consider just adding "-framework CoreFoundation" to the module link options? Repository: rL LLVM https://reviews.llvm.org/D30977 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
arphaman added inline comments. Comment at: lib/CodeGen/CGObjC.cpp:3435 + CheckFTy, "__clang_at_available_requires_core_foundation_framework")); + CFLinkCheckFunc->setLinkage(llvm::GlobalValue::LinkOnceAnyLinkage); + CodeGenFunction CGF(*this); rjmccall wrote: > Is this a public weak symbol? Make it hidden, please. > > Did you consider just adding "-framework CoreFoundation" to the module link > options? I'm not familiar with module link options, I'll look into them. Repository: rL LLVM https://reviews.llvm.org/D30977 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
arphaman updated this revision to Diff 91977. arphaman marked an inline comment as done. arphaman added a comment. The guard function is now `hidden`. I also adopted linker options as John suggested. Repository: rL LLVM https://reviews.llvm.org/D30977 Files: lib/CodeGen/CGObjC.cpp lib/CodeGen/CodeGenModule.cpp lib/CodeGen/CodeGenModule.h test/CodeGenObjC/availability-cf-link-guard.m Index: test/CodeGenObjC/availability-cf-link-guard.m === --- /dev/null +++ test/CodeGenObjC/availability-cf-link-guard.m @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D USE_BUILTIN %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D DEF_CF %s | FileCheck --check-prefixes=CHECK_CF,CHECK_LINK_OPT %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 hidden 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 + +// CHECK_LINK_OPT: !"Linker Options", ![[OPTS:[0-9]+]] +// CHECK_LINK_OPT: ![[OPTS]] = !{![[FRAMEWORK:[0-9]+]] +// CHECK_LINK_OPT: ![[FRAMEWORK]] = !{!"-framework", !"CoreFoundation"} + +// CHECK_NO_GUARD-NOT: "Linker Options" +// CHECK_NO_GUARD-NOT: CoreFoundation 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: lib/CodeGen/CGObjC.cpp === --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -3416,4 +3416,37 @@ 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; + // 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 + // framework. + auto &Context = getLLVMContext(); + llvm::Metadata *Args[2] = {llvm::MDString::get(Context, "-framework"), + llvm::MDString::get(Context, "CoreFoundation")}; + LinkerOptionsMetadata.push_back(llvm::MDNode::get(Context, Args)); + // 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::LinkOnceAn
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
rjmccall added inline comments. Comment at: lib/CodeGen/CGObjC.cpp:3423 +return; + if (!IsOSVersionAtLeastFn) +return; Reverse these checks, please; IsOSVersionAtLeastFn is much cheaper to check and will predominantly be null. Comment at: lib/CodeGen/CGObjC.cpp:3428 + // CoreFoundation is not used in the code, the linker won't link the + // framework. + auto &Context = getLLVMContext(); Can you explain why compiler-rt has to load the symbol at runtime? Is this just some compiler-rt testing thing? Because it seems like a shame to pay a code-size cost — even a negligible one — for something like that. Repository: rL LLVM https://reviews.llvm.org/D30977 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
arphaman added inline comments. Comment at: lib/CodeGen/CGObjC.cpp:3428 + // CoreFoundation is not used in the code, the linker won't link the + // framework. + auto &Context = getLLVMContext(); rjmccall wrote: > Can you explain why compiler-rt has to load the symbol at runtime? Is this > just some compiler-rt testing thing? Because it seems like a shame to pay a > code-size cost — even a negligible one — for something like that. Because compiler-rt is linked into some internal projects that use the `-all_load` flag, so the linker loads `__isOSVersionAtLeast` that references the CoreFoundation symbols even when it's not used in code. Repository: rL LLVM https://reviews.llvm.org/D30977 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
arphaman updated this revision to Diff 92028. arphaman marked an inline comment as done. arphaman added a comment. Reverse the early exit checks. Repository: rL LLVM https://reviews.llvm.org/D30977 Files: lib/CodeGen/CGObjC.cpp lib/CodeGen/CodeGenModule.cpp lib/CodeGen/CodeGenModule.h test/CodeGenObjC/availability-cf-link-guard.m Index: test/CodeGenObjC/availability-cf-link-guard.m === --- /dev/null +++ test/CodeGenObjC/availability-cf-link-guard.m @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D USE_BUILTIN %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D DEF_CF %s | FileCheck --check-prefixes=CHECK_CF,CHECK_LINK_OPT %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 hidden 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 + +// CHECK_LINK_OPT: !"Linker Options", ![[OPTS:[0-9]+]] +// CHECK_LINK_OPT: ![[OPTS]] = !{![[FRAMEWORK:[0-9]+]] +// CHECK_LINK_OPT: ![[FRAMEWORK]] = !{!"-framework", !"CoreFoundation"} + +// CHECK_NO_GUARD-NOT: "Linker Options" +// CHECK_NO_GUARD-NOT: CoreFoundation 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: lib/CodeGen/CGObjC.cpp === --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -3416,4 +3416,37 @@ return Builder.CreateICmpNE(CallRes, llvm::Constant::getNullValue(Int32Ty)); } +void CodeGenModule::emitAtAvailableLinkGuard() { + if (!IsOSVersionAtLeastFn) +return; + // @available requires CoreFoundation only on Darwin. + if (!Target.getTriple().isOSDarwin()) +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 + // framework. + auto &Context = getLLVMContext(); + llvm::Metadata *Args[2] = {llvm::MDString::get(Context, "-framework"), + llvm::MDString::get(Context, "CoreFoundation")}; + LinkerOptionsMetadata.push_back(llvm::MDNode::get(Context, Args)); + // 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); + CFLinkCheckFunc->setVisibility(llvm::Glob
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
rjmccall accepted this revision. rjmccall added a comment. This revision is now accepted and ready to land. LGTM. Comment at: lib/CodeGen/CGObjC.cpp:3428 + // CoreFoundation is not used in the code, the linker won't link the + // framework. + auto &Context = getLLVMContext(); arphaman wrote: > rjmccall wrote: > > Can you explain why compiler-rt has to load the symbol at runtime? Is this > > just some compiler-rt testing thing? Because it seems like a shame to pay > > a code-size cost — even a negligible one — for something like that. > Because compiler-rt is linked into some internal projects that use the > `-all_load` flag, so the linker loads `__isOSVersionAtLeast` that references > the CoreFoundation symbols even when it's not used in code. Well, that is terrible. Repository: rL LLVM https://reviews.llvm.org/D30977 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D30977: [CodeGen] Emit a CoreFoundation link guard when @available is used
This revision was automatically updated to reflect the committed changes. Closed by commit rL298588: [CodeGen] Emit a CoreFoundation link guard when @available is used (authored by arphaman). Changed prior to commit: https://reviews.llvm.org/D30977?vs=92028&id=92780#toc Repository: rL LLVM https://reviews.llvm.org/D30977 Files: cfe/trunk/lib/CodeGen/CGObjC.cpp cfe/trunk/lib/CodeGen/CodeGenModule.cpp cfe/trunk/lib/CodeGen/CodeGenModule.h cfe/trunk/test/CodeGenObjC/availability-cf-link-guard.m Index: cfe/trunk/test/CodeGenObjC/availability-cf-link-guard.m === --- cfe/trunk/test/CodeGenObjC/availability-cf-link-guard.m +++ cfe/trunk/test/CodeGenObjC/availability-cf-link-guard.m @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D USE_BUILTIN %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D DEF_CF %s | FileCheck --check-prefixes=CHECK_CF,CHECK_LINK_OPT %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 hidden 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 + +// CHECK_LINK_OPT: !"Linker Options", ![[OPTS:[0-9]+]] +// CHECK_LINK_OPT: ![[OPTS]] = !{![[FRAMEWORK:[0-9]+]] +// CHECK_LINK_OPT: ![[FRAMEWORK]] = !{!"-framework", !"CoreFoundation"} + +// CHECK_NO_GUARD-NOT: "Linker Options" +// CHECK_NO_GUARD-NOT: CoreFoundation Index: cfe/trunk/lib/CodeGen/CodeGenModule.cpp === --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp @@ -414,6 +414,7 @@ CoverageMapping->emit(); if (CodeGenOpts.SanitizeCfiCrossDso) CodeGenFunction(*this).EmitCfiCheckFail(); + emitAtAvailableLinkGuard(); emitLLVMUsed(); if (SanStats) SanStats->finish(); Index: cfe/trunk/lib/CodeGen/CodeGenModule.h === --- cfe/trunk/lib/CodeGen/CodeGenModule.h +++ cfe/trunk/lib/CodeGen/CodeGenModule.h @@ -1286,6 +1286,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: cfe/trunk/lib/CodeGen/CGObjC.cpp === --- cfe/trunk/lib/CodeGen/CGObjC.cpp +++ cfe/trunk/lib/CodeGen/CGObjC.cpp @@ -3416,4 +3416,37 @@ return Builder.CreateICmpNE(CallRes, llvm::Constant::getNullValue(Int32Ty)); } +void CodeGenModule::emitAtAvailableLinkGuard() { + if (!IsOSVersionAtLeastFn) +return; + // @available requires CoreFoundation only on Darwin. + if (!Target.getTriple().isOSDarwin()) +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 + // framework. + auto &Context = getLLVMContext(); + llvm::Metadata *Args[2] = {llvm::MDString::get(Context, "-framework"), + llvm::MDString::get(Context, "CoreFoundation")}; + LinkerOptionsMetadata.push_back(llvm::MDNode::get(Context, Args)); + // 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::Func