diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -294,6 +294,8 @@ CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program /// vtable optimization. +CODEGENOPT(ObjCExportDirectMethods , 1, 0) ///< Whether objc_direct methods are exported. + CODEGENOPT(VirtualFunctionElimination, 1, 0) ///< Whether to apply the dead /// virtual function elimination /// optimization. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1634,6 +1634,8 @@ def fobjc_call_cxx_cdtors : Flag<["-"], "fobjc-call-cxx-cdtors">, Group; def fobjc_exceptions: Flag<["-"], "fobjc-exceptions">, Group, HelpText<"Enable Objective-C exceptions">, Flags<[CC1Option]>; +def fobjc_export_direct_methods : + Flag<["-"], "fobjc-export-direct-methods">, Group, Flags<[CC1Option]>; def fapplication_extension : Flag<["-"], "fapplication-extension">, Group, Flags<[CC1Option]>, HelpText<"Restrict code to those available for App Extensions">; diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -689,6 +689,8 @@ const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD); if (OMD->isDirectMethod()) { Fn->setVisibility(llvm::Function::HiddenVisibility); + if (CGM.getCodeGenOpts().ObjCExportDirectMethods) + Fn->setVisibility(llvm::Function::DefaultVisibility); CGM.SetLLVMFunctionAttributes(OMD, FI, Fn); CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn); } else { diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -4068,6 +4068,21 @@ DirectMethodDefinitions.insert(std::make_pair(COMD, Fn)); } + // If we want to explicitly export objc_direct methods, we need a name fixup. + if (CGM.getCodeGenOpts().ObjCExportDirectMethods && + Fn->getName().str()[0] == '\1') { + // Drop '\1' to work with dlsym. + std::string Name = Fn->getName().str().substr(1); + + assert(Name[0] == '-' || Name[0] == '+'); + assert(Name[1] == '[' && Name[Name.length() - 1] == ']'); + + // replace "[ ]" by "< >" to avoid strip by ld64. + Name = Name.substr(0, 1) + "<" + Name.substr(2, Name.length() - 3) + ">"; + + Fn->setName(Name); + } + return Fn; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6267,6 +6267,9 @@ Input.getInputArg().renderAsInput(Args, CmdArgs); } + if (Args.hasArg(options::OPT_fobjc_export_direct_methods)) + CmdArgs.push_back("-fobjc-export-direct-methods"); + // Finally add the compile command to the compilation. if (Args.hasArg(options::OPT__SLASH_fallback) && Output.getType() == types::TY_Object && diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -811,6 +811,7 @@ Opts.DebugFwdTemplateParams = Args.hasArg(OPT_debug_forward_template_params); Opts.EmbedSource = Args.hasArg(OPT_gembed_source); Opts.ForceDwarfFrameSection = Args.hasArg(OPT_fforce_dwarf_frame); + Opts.ObjCExportDirectMethods = Args.hasArg(OPT_fobjc_export_direct_methods); for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) { auto Split = StringRef(Arg).split('='); diff --git a/clang/test/CodeGenObjC/objc-direct-wrapper.m b/clang/test/CodeGenObjC/objc-direct-wrapper.m new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenObjC/objc-direct-wrapper.m @@ -0,0 +1,44 @@ +// RUN: %clang -fobjc-arc -Wno-objc-root-class -ObjC -fobjc-runtime=ios -FFoundation \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -fobjc-export-direct-methods -c -o - %s | \ +// RUN: llvm-nm - | FileCheck -check-prefix=CHECK-WRAPPER %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s | llvm-nm - | \ +// RUN: FileCheck -check-prefix=CHECK-DEFAULT %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation \ +// RUN: -fobjc-export-direct-methods -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DEFINE %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class -DNO_OBJC_IMPL \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation \ +// RUN: -fobjc-export-direct-methods -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DECLARE %s + +// CHECK-WRAPPER: T _- + // TODO: Fix this +// CHECK-DEFAULT: T -[C testMethod:bar:] +// CHECK-WRAPPER-IR-DEFINE: define {{(dso_local )?}}void @"-" +// CHECK-WRAPPER-IR-DECLARE: declare {{(dso_local )?}}void @"-" + +@interface C +- (void)testMethod:(int)arg1 bar:(float)arg2 __attribute((objc_direct)); +@end + +#ifndef NO_OBJC_IMPL +@implementation C +- (void)testMethod:(int)arg1 bar:(float)arg2 __attribute((objc_direct)) { +} +@end +#endif + +C *c; + +void f() { + [c testMethod:1 bar:1.0]; +}