https://github.com/DataCorrupted updated https://github.com/llvm/llvm-project/pull/170616
>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 1/2] [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() && >From b5fd161368aafff8323866990bd351a2d08e2dc6 Mon Sep 17 00:00:00 2001 From: Peter Rong <[email protected]> Date: Wed, 3 Dec 2025 23:15:45 -0800 Subject: [PATCH 2/2] format --- clang/lib/CodeGen/CGObjCRuntime.cpp | 2 +- clang/lib/CodeGen/CGObjCRuntime.h | 22 +++------------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index 38efd4d865284..a4b4460fdc49c 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -415,7 +415,7 @@ bool CGObjCRuntime::canMessageReceiverBeNull( bool CGObjCRuntime::canClassObjectBeUnrealized( const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const { - + // TODO // Otherwise, assume it can be unrealized. return true; } diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h index 1ee3b85e8a779..0f0cc6ba09200 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -324,31 +324,15 @@ class CGObjCRuntime { QualType resultType, CallArgList &callArgs); - /// 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, + 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: + /// 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) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
