https://github.com/DataCorrupted created 
https://github.com/llvm/llvm-project/pull/170616

1. Add a flag
2. Clean up and set up helper functions to implement later

>From 76489044b1150f846af5fe86f07d8d8f32e56179 Mon Sep 17 00:00:00 2001
From: Peter Rong <[email protected]>
Date: Wed, 3 Dec 2025 22:18:02 -0800
Subject: [PATCH] [ExposeObjCDirect] Adding a flag to allow new objc direct ABI

1. Add a flag
2. Clean up and set up helper functions to implement later

Signed-off-by: Peter Rong <[email protected]>
---
 clang/include/clang/AST/DeclObjC.h           |  6 ++++
 clang/include/clang/Basic/CodeGenOptions.def |  2 ++
 clang/include/clang/Options/Options.td       |  5 +++
 clang/lib/CodeGen/CGObjCRuntime.cpp          | 26 +++++++++------
 clang/lib/CodeGen/CGObjCRuntime.h            | 35 ++++++++++++++++++--
 clang/lib/CodeGen/CodeGenModule.h            | 26 +++++++++++++++
 clang/lib/Driver/ToolChains/Clang.cpp        |  4 +++
 7 files changed, 90 insertions(+), 14 deletions(-)

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..38efd4d865284 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 {
+
+  // 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..1ee3b85e8a779 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,38 @@ class CGObjCRuntime {
   MessageSendInfo getMessageSendInfo(const ObjCMethodDecl *method,
                                      QualType resultType,
                                      CallArgList &callArgs);
-  bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-                                const ObjCMethodDecl *method, bool isSuper,
+
+  /// Check if the receiver of an ObjC message send can be null.
+  /// Returns true if the receiver may be null, false if provably non-null.
+  ///
+  /// This can be overridden by subclasses to add runtime-specific heuristics.
+  /// Base implementation checks:
+  /// - Super dispatch (always non-null)
+  /// - Self in const-qualified methods (ARC)
+  /// - Weak-linked classes
+  ///
+  /// Future enhancements in CGObjCCommonMac override:
+  /// - _Nonnull attributes
+  /// - Results of alloc, new, ObjC literals
+  virtual 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.
+  ///
+  /// STUB IMPLEMENTATION: Base class always returns true (conservative).
+  /// Subclasses can override to add runtime-specific dominating-call analysis.
+  ///
+  /// Future: 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() &&

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

Reply via email to