llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-driver

Author: Peter Rong (DataCorrupted)

<details>
<summary>Changes</summary>

## TL;DR

This is a stack of PRs implementing features to expose direct methods ABI.
You can see the RFC, design, and discussion 
[here](https://discourse.llvm.org/t/rfc-optimizing-code-size-of-objc-direct-by-exposing-function-symbols-and-moving-nil-checks-to-thunks/88866).

https://github.com/llvm/llvm-project/pull/170616 **Flag -fexpose-objc-direct 
set up**
https://github.com/llvm/llvm-project/pull/170617 Code refactoring to ease later 
reviews
https://github.com/llvm/llvm-project/pull/170618 Thunk generation
https://github.com/llvm/llvm-project/pull/170619 Optimizations, some class 
objects can be known to be realized

## Implementation details

1. Add a flag. I used `expose-direct-method` instead of 
`-fobjc-direct-caller-thunks` in the proposal for two reasons:
   a. It conveys our intention more clearly, that we are trying to remove the 
`\01` byte before the name of direct method so the developers can expose a 
symbol if they want
   b. Much of the code is actually handling corner cases of var_arg, which 
don't have a thunk. Thus the name `thunk` can be confusing.

2. Clean up and set up helper functions to implement later
    a. `canMessageReceiverBeNull` / `canClassObjectBeUnrealized` these two 
functions will be helpful later to determine which function (true 
implementation or nil check thunk) we should dispatch a call to. Formatting.
    b. `getSymbolNameForMethod` has a new argument `includePrefixByte`, which 
allows us to erase the prefixing `\01` when the flag is enabled
    c. `shouldExposeSymbol` is the single source of truth of what we should do. 
It not only checks for the flag, but also whether the method is qualified and 
we are in the right runtime. A method that `shouldExposeSymbol` is either 
`shouldHaveNilCheckThunk` or `shouldHaveNilCheckInline`.

## Tests

None, existing ones shouldn't break

---
Full diff: https://github.com/llvm/llvm-project/pull/170616.diff


7 Files Affected:

- (modified) clang/include/clang/AST/DeclObjC.h (+6) 
- (modified) clang/include/clang/Basic/CodeGenOptions.def (+2) 
- (modified) clang/include/clang/Options/Options.td (+5) 
- (modified) clang/lib/CodeGen/CGObjCRuntime.cpp (+15-11) 
- (modified) clang/lib/CodeGen/CGObjCRuntime.h (+14-1) 
- (modified) clang/lib/CodeGen/CodeGenModule.h (+26) 
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+4) 


``````````diff
diff --git a/clang/include/clang/AST/DeclObjC.h 
b/clang/include/clang/AST/DeclObjC.h
index 2541edba83855..e2292cbdea042 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -482,6 +482,12 @@ class ObjCMethodDecl : public NamedDecl, public 
DeclContext {
   /// True if the method is tagged as objc_direct
   bool isDirectMethod() const;
 
+  /// Check if this direct method can move nil-check to thunk.
+  /// Variadic functions cannot use thunks (musttail incompatible with va_arg)
+  bool canHaveNilCheckThunk() const {
+    return isDirectMethod() && !isVariadic();
+  }
+
   /// True if the method has a parameter that's destroyed in the callee.
   bool hasParamDestroyedInCallee() const;
 
diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index 76a6463881c6f..a7e8564c9e83c 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -210,6 +210,8 @@ ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 
2, Legacy, Benign)
 /// Replace certain message sends with calls to ObjC runtime entrypoints
 CODEGENOPT(ObjCConvertMessagesToRuntimeCalls , 1, 1, Benign)
 CODEGENOPT(ObjCAvoidHeapifyLocalBlocks, 1, 0, Benign)
+/// Expose objc_direct method symbols publicly and optimize nil checks.
+CODEGENOPT(ObjCExposeDirectMethods, 1, 0, Benign)
 
 
 // The optimization options affect frontend options, which in turn do affect 
the AST.
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 756d6deed7130..ddc04b30cbaa2 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3775,6 +3775,11 @@ defm objc_avoid_heapify_local_blocks : 
BoolFOption<"objc-avoid-heapify-local-blo
   PosFlag<SetTrue, [], [ClangOption], "Try">,
   NegFlag<SetFalse, [], [ClangOption], "Don't try">,
   BothFlags<[], [CC1Option], " to avoid heapifying local blocks">>;
+defm objc_expose_direct_methods : BoolFOption<"objc-expose-direct-methods",
+  CodeGenOpts<"ObjCExposeDirectMethods">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Expose direct method symbols and move nil checks to caller-side 
thunks">,
+  NegFlag<SetFalse>>;
 defm disable_block_signature_string : 
BoolFOption<"disable-block-signature-string",
   CodeGenOpts<"DisableBlockSignatureString">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption], "Disable">,
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp 
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index 76e0054f4c9da..a4b4460fdc49c 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -382,11 +382,9 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl 
*method,
   return MessageSendInfo(argsInfo, signatureType);
 }
 
-bool CGObjCRuntime::canMessageReceiverBeNull(CodeGenFunction &CGF,
-                                             const ObjCMethodDecl *method,
-                                             bool isSuper,
-                                       const ObjCInterfaceDecl *classReceiver,
-                                             llvm::Value *receiver) {
+bool CGObjCRuntime::canMessageReceiverBeNull(
+    CodeGenFunction &CGF, const ObjCMethodDecl *method, bool isSuper,
+    const ObjCInterfaceDecl *classReceiver, llvm::Value *receiver) {
   // Super dispatch assumes that self is non-null; even the messenger
   // doesn't have a null check internally.
   if (isSuper)
@@ -399,8 +397,7 @@ bool 
CGObjCRuntime::canMessageReceiverBeNull(CodeGenFunction &CGF,
 
   // If we're emitting a method, and self is const (meaning just ARC, for now),
   // and the receiver is a load of self, then self is a valid object.
-  if (auto curMethod =
-               dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {
+  if (auto curMethod = dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {
     auto self = curMethod->getSelfDecl();
     if (self->getType().isConstQualified()) {
       if (auto LI = dyn_cast<llvm::LoadInst>(receiver->stripPointerCasts())) {
@@ -416,6 +413,13 @@ bool 
CGObjCRuntime::canMessageReceiverBeNull(CodeGenFunction &CGF,
   return true;
 }
 
+bool CGObjCRuntime::canClassObjectBeUnrealized(
+    const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
+  // TODO
+  // Otherwise, assume it can be unrealized.
+  return true;
+}
+
 bool CGObjCRuntime::isWeakLinkedClass(const ObjCInterfaceDecl *ID) {
   do {
     if (ID->isWeakImported())
@@ -466,11 +470,11 @@ clang::CodeGen::emitObjCProtocolObject(CodeGenModule &CGM,
 }
 
 std::string CGObjCRuntime::getSymbolNameForMethod(const ObjCMethodDecl *OMD,
-                                                  bool includeCategoryName) {
+                                                  bool includeCategoryName,
+                                                  bool includePrefixByte) {
   std::string buffer;
   llvm::raw_string_ostream out(buffer);
-  CGM.getCXXABI().getMangleContext().mangleObjCMethodName(OMD, out,
-                                       /*includePrefixByte=*/true,
-                                       includeCategoryName);
+  CGM.getCXXABI().getMangleContext().mangleObjCMethodName(
+      OMD, out, includePrefixByte, includeCategoryName);
   return buffer;
 }
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h 
b/clang/lib/CodeGen/CGObjCRuntime.h
index 72997bf6348ae..0f0cc6ba09200 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.h
+++ b/clang/lib/CodeGen/CGObjCRuntime.h
@@ -117,7 +117,8 @@ class CGObjCRuntime {
   virtual ~CGObjCRuntime();
 
   std::string getSymbolNameForMethod(const ObjCMethodDecl *method,
-                                     bool includeCategoryName = true);
+                                     bool includeCategoryName = true,
+                                     bool includePrefixByte = true);
 
   /// Generate the function required to register all Objective-C components in
   /// this compilation unit with the runtime library.
@@ -322,10 +323,22 @@ class CGObjCRuntime {
   MessageSendInfo getMessageSendInfo(const ObjCMethodDecl *method,
                                      QualType resultType,
                                      CallArgList &callArgs);
+
   bool canMessageReceiverBeNull(CodeGenFunction &CGF,
                                 const ObjCMethodDecl *method, bool isSuper,
                                 const ObjCInterfaceDecl *classReceiver,
                                 llvm::Value *receiver);
+
+  /// Check if a class object can be unrealized (not yet initialized).
+  /// Returns true if the class may be unrealized, false if provably realized.
+  ///
+  /// TODO: Returns false if:
+  /// - An instance method on the same class was called in a dominating path
+  /// - The class was explicitly realized earlier in control flow
+  /// - Note: [Parent foo] does NOT realize Child (inheritance care needed)
+  virtual bool canClassObjectBeUnrealized(const ObjCInterfaceDecl *ClassDecl,
+                                          CodeGenFunction &CGF) const;
+
   static bool isWeakLinkedClass(const ObjCInterfaceDecl *cls);
 
   /// Destroy the callee-destroyed arguments of the given method,
diff --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index a253bcda2d06c..f65739de10957 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -717,6 +717,32 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Return true iff an Objective-C runtime has been configured.
   bool hasObjCRuntime() { return !!ObjCRuntime; }
 
+  /// Check if a direct method should have its symbol exposed (no \01 prefix).
+  /// This applies to ALL direct methods (including variadic).
+  /// Returns false if OMD is null or not a direct method.
+  bool shouldExposeSymbol(const ObjCMethodDecl *OMD) const {
+    return OMD && OMD->isDirectMethod() &&
+           getLangOpts().ObjCRuntime.isNeXTFamily() &&
+           getCodeGenOpts().ObjCExposeDirectMethods;
+  }
+
+  /// Check if a direct method should use nil-check thunks at call sites.
+  /// This applies only to non-variadic direct methods.
+  /// Variadic methods cannot use thunks (musttail incompatible with va_arg).
+  /// Returns false if OMD is null or not eligible for thunks.
+  bool shouldHaveNilCheckThunk(const ObjCMethodDecl *OMD) const {
+    return OMD && shouldExposeSymbol(OMD) && OMD->canHaveNilCheckThunk();
+  }
+
+  /// Check if a direct method should have inline nil checks at call sites.
+  /// This applies to direct methods that cannot use thunks (e.g., variadic
+  /// methods). These methods get exposed symbols but need inline nil checks
+  /// instead of thunks. Returns false if OMD is null or not eligible for 
inline
+  /// nil checks.
+  bool shouldHaveNilCheckInline(const ObjCMethodDecl *OMD) const {
+    return OMD && shouldExposeSymbol(OMD) && !OMD->canHaveNilCheckThunk();
+  }
+
   const std::string &getModuleNameHash() const { return ModuleNameHash; }
 
   /// Return a reference to the configured OpenCL runtime.
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 0380568412e62..2c3beb94f2ef8 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4096,6 +4096,10 @@ static void RenderObjCOptions(const ToolChain &TC, const 
Driver &D,
     }
   }
 
+  // Forward -fobjc-expose-direct-methods to cc1
+  if (Args.hasArg(options::OPT_fobjc_expose_direct_methods))
+    CmdArgs.push_back("-fobjc-expose-direct-methods");
+
   // When ObjectiveC legacy runtime is in effect on MacOSX, turn on the option
   // to do Array/Dictionary subscripting by default.
   if (Arch == llvm::Triple::x86 && T.isMacOSX() &&

``````````

</details>


https://github.com/llvm/llvm-project/pull/170616
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to