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

Reply via email to