plotfi created this revision. plotfi added reviewers: rjmccall, manmanren, MadCoder. Herald added subscribers: cfe-commits, dang. Herald added a project: clang. plotfi requested review of this revision.
Hi @rjmccall @MadCoder I'd like to preface this diff: I mostly want to discuss the prospects of exporting generated wrappers that call objc_direct methods, or alternately exporting the objc_direct methods themselves when an optional CodeGenOpts command-line flag is specified. This diff implements generating wrappers when a CodeGenOpts command-line flag is provided since C wrappers were mentioned in the original objc_direct diff. However I do think it might be possible to export the methods directly provided the implicit '_' underbar prefix is prepended to satisfy C naming on Darwin, and if thats the case I can post a diff that does that instead. Anyways, thats all I wanted to preface with. Thanks. Motivated by the potential benefit of using the objc_direct attribute, this diff aims to expand support to cross dylib objc_direct method calls by automatically generating exportable wrapper functions that call objc_direct methods internal to a dylib. In the original patch landed in https://reviews.llvm.org/D69991 it mentions: "The symbol for the method has enforced hidden visibility and such direct calls are hence unreachable cross image. An explicit C function must be made if so desired to wrap them." This change provides a codegen options flag to clang `-fobjc-export-direct-method-wrappers` to generate the wrapper functions that begin with the prefix __objc_direct_wrapper and are marked as __attribute__((alwaysinline)). This way within a link unit the wrapper functions should be inlined away at their call sites, but across a dylib boundary the wrapper call is used. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D86049 Files: clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Driver/Options.td clang/lib/CodeGen/CGObjC.cpp clang/lib/CodeGen/CGObjCMac.cpp clang/lib/Driver/ToolChains/Clang.cpp clang/lib/Frontend/CompilerInvocation.cpp
Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ 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; Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ 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) Index: clang/lib/CodeGen/CGObjCMac.cpp =================================================================== --- clang/lib/CodeGen/CGObjCMac.cpp +++ 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); Index: clang/lib/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ 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<llvm::Value *, 8> 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. Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1622,6 +1622,8 @@ Flag<["-"], "fobjc-convert-messages-to-runtime-calls">, Group<f_Group>; def fno_objc_convert_messages_to_runtime_calls : Flag<["-"], "fno-objc-convert-messages-to-runtime-calls">, Group<f_Group>, Flags<[CC1Option]>; +def fobjc_export_direct_method_wrappers : + Flag<["-"], "fobjc-export-direct-method-wrappers">, Group<f_Group>, Flags<[CC1Option]>; def fobjc_arc_exceptions : Flag<["-"], "fobjc-arc-exceptions">, Group<f_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<f_Group>; Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ 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.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits