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 @@ -173,6 +173,8 @@ ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy) /// Replace certain message sends with calls to ObjC runtime entrypoints CODEGENOPT(ObjCConvertMessagesToRuntimeCalls , 1, 1) +/// Generate and export wrapper functions for objc_direct methods. +CODEGENOPT(ObjCExportDirectMethodWrappers , 1, 0) VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified. VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is specified. 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 @@ -1622,6 +1622,8 @@ Flag<["-"], "fobjc-convert-messages-to-runtime-calls">, Group; def fno_objc_convert_messages_to_runtime_calls : Flag<["-"], "fno-objc-convert-messages-to-runtime-calls">, Group, Flags<[CC1Option]>; +def fobjc_export_direct_method_wrappers : + Flag<["-"], "fobjc-export-direct-method-wrappers">, Group, Flags<[CC1Option]>; def fobjc_arc_exceptions : Flag<["-"], "fobjc-arc-exceptions">, Group, Flags<[CC1Option]>, HelpText<"Use EH-safe code when synthesizing retains and releases in -fobjc-arc">; def fno_objc_arc_exceptions : Flag<["-"], "fno-objc-arc-exceptions">, Group; 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 @@ -673,6 +673,33 @@ }; } +/// Populates the body of a objc_direct wrapper function. +/// This should be done in the same TU that we are populating the definition of +/// the objc_direct method it is wrapping. +static void populateObjCDirectWrapperContent(llvm::Function &Wrapper, + llvm::Function &F) { + assert(Wrapper.isDeclaration() && + "Expected empty objc_direct wrapper function."); + llvm::Module &M = *F.getParent(); + llvm::LLVMContext &Ctx = M.getContext(); + llvm::BasicBlock *EntryBB = llvm::BasicBlock::Create(Ctx, "entry", &Wrapper); + + llvm::SmallVector Args; + auto *FArgIt = F.arg_begin(); + for (llvm::Argument &Arg : Wrapper.args()) { + Args.push_back(&Arg); + Arg.setName((FArgIt++)->getName()); + } + + llvm::CallInst *CI = llvm::CallInst::Create(&F, Args, "", EntryBB); + CI->setTailCall(true); + CI->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoInline); + llvm::ReturnInst::Create(Ctx, CI->getType()->isVoidTy() ? nullptr : CI, + EntryBB); +} +llvm::Function *getObjCDirectWrapper(llvm::Function &F); + /// StartObjCMethod - Begin emission of an ObjCMethod. This generates /// the LLVM function and sets the other context used by /// CodeGenFunction. @@ -712,6 +739,10 @@ // // TODO: possibly have several entry points to elide the check CGM.getObjCRuntime().GenerateDirectMethodPrologue(*this, Fn, OMD, CD); + if (CGM.getCodeGenOpts().ObjCExportDirectMethodWrappers) { + llvm::Function *Wrapper = getObjCDirectWrapper(*Fn); + populateObjCDirectWrapperContent(*Wrapper, *Fn); + } } // In ARC, certain methods get an extra cleanup. 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 @@ -2143,6 +2143,36 @@ return false; } +/// Create or look up the declaration of an objc_direct method wrapper. +llvm::Function *getObjCDirectWrapper(llvm::Function &F) { + std::string NewName = "__objc_direct_wrapper"; + for (auto C : F.getName().str()) { + if (C == '\1') + continue; + NewName += C; + } + + auto WI = llvm::find_if(*F.getParent(), [NewName](const llvm::Function &F) { + return F.getName() == NewName; + }); + + llvm::Function *Wrapper = nullptr; + if (WI == F.getParent()->end()) { + llvm::Module &M = *F.getParent(); + llvm::FunctionType *FnTy = F.getFunctionType(); + Wrapper = llvm::Function::Create(FnTy, F.getLinkage(), F.getAddressSpace(), + NewName); + Wrapper->setLinkage(llvm::GlobalValue::ExternalLinkage); + Wrapper->addFnAttr(llvm::Attribute::AlwaysInline); + Wrapper->setVisibility(llvm::Function::DefaultVisibility); + M.getFunctionList().insert(F.getIterator(), Wrapper); + } else { + Wrapper = &*WI; + } + + return Wrapper; +} + CodeGen::RValue CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, @@ -2214,7 +2244,13 @@ llvm::FunctionCallee Fn = nullptr; if (Method && Method->isDirectMethod()) { - Fn = GenerateDirectMethod(Method, Method->getClassInterface()); + if (CGM.getCodeGenOpts().ObjCExportDirectMethodWrappers) { + llvm::Function *FnDirect = + GenerateDirectMethod(Method, Method->getClassInterface()); + Fn = getObjCDirectWrapper(*FnDirect); + } else { + Fn = GenerateDirectMethod(Method, Method->getClassInterface()); + } } else if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) { if (ReceiverCanBeNull) RequiresNullCheck = true; Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper) @@ -4006,6 +4042,9 @@ if (OMD->isDirectMethod()) { Method = GenerateDirectMethod(OMD, CD); + if (CGM.getCodeGenOpts().ObjCExportDirectMethodWrappers) { + getObjCDirectWrapper(*Method); + } } else { SmallString<256> Name; GetNameForMethod(OMD, CD, Name); 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 @@ -3462,6 +3462,9 @@ CmdArgs.push_back("-fno-objc-convert-messages-to-runtime-calls"); } + if (Args.hasArg(options::OPT_fobjc_export_direct_method_wrappers)) + CmdArgs.push_back("-fobjc-export-direct-method-wrappers"); + // -fobjc-infer-related-result-type is the default, except in the Objective-C // rewriter. if (InferCovariantReturns) 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 @@ -1283,6 +1283,8 @@ } } + if (Args.hasArg(OPT_fobjc_export_direct_method_wrappers)) + Opts.ObjCExportDirectMethodWrappers = 1; if (Args.hasArg(OPT_fno_objc_convert_messages_to_runtime_calls)) Opts.ObjCConvertMessagesToRuntimeCalls = 0; 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,43 @@ +// 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-method-wrappers -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-method-wrappers -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-method-wrappers -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DECLARE %s + +// CHECK-WRAPPER: T ___objc_direct_wrapper-[C testMethod:bar:] +// CHECK-DEFAULT-NOT: ___objc_direct_wrapper +// CHECK-WRAPPER-IR-DEFINE: define {{(dso_local )?}}{{(dso_local )?}}void @"__objc_direct_wrapper-[C testMethod:bar:]" +// CHECK-WRAPPER-IR-DECLARE: declare {{(dso_local )?}}void @"__objc_direct_wrapper-[C testMethod:bar:]" + +@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]; +}