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, NegFlag, BothFlags<[], [CC1Option], " to avoid heapifying local blocks">>; +defm objc_expose_direct_methods : BoolFOption<"objc-expose-direct-methods", + CodeGenOpts<"ObjCExposeDirectMethods">, DefaultFalse, + PosFlag, + NegFlag>; defm disable_block_signature_string : BoolFOption<"disable-block-signature-string", CodeGenOpts<"DisableBlockSignatureString">, DefaultFalse, PosFlag, 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(CGF.CurCodeDecl)) { + if (auto curMethod = dyn_cast_or_null(CGF.CurCodeDecl)) { auto self = curMethod->getSelfDecl(); if (self->getType().isConstQualified()) { if (auto LI = dyn_cast(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..193a756cc5f86 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4096,6 +4096,11 @@ static void RenderObjCOptions(const ToolChain &TC, const Driver &D, } } + // Forward -fobjc-expose-direct-methods to cc1 + if (Args.hasFlag(options::OPT_fobjc_expose_direct_methods, + options::OPT_fno_objc_expose_direct_methods, false)) + 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() && diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c index 765f9d6ae3212..2088ceaa3e8f0 100644 --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -599,6 +599,14 @@ // CHECK_DISABLE_DIRECT: -fobjc-disable-direct-methods-for-testing // CHECK_NO_DISABLE_DIRECT-NOT: -fobjc-disable-direct-methods-for-testing +// RUN: %clang -### -xobjective-c -fobjc-expose-direct-methods %s 2>&1 | FileCheck -check-prefix=CHECK_EXPOSE_DIRECT %s +// RUN: %clang -### -xobjective-c -fno-objc-expose-direct-methods %s 2>&1 | FileCheck -check-prefix=CHECK_NO_EXPOSE_DIRECT %s +// RUN: %clang -### -xobjective-c -fobjc-expose-direct-methods -fno-objc-expose-direct-methods %s 2>&1 | FileCheck -check-prefix=CHECK_NO_EXPOSE_DIRECT %s +// RUN: %clang -### -xobjective-c -fno-objc-expose-direct-methods -fobjc-expose-direct-methods %s 2>&1 | FileCheck -check-prefix=CHECK_EXPOSE_DIRECT %s +// RUN: %clang -### -xobjective-c %s 2>&1 | FileCheck -check-prefix=CHECK_NO_EXPOSE_DIRECT %s +// CHECK_EXPOSE_DIRECT: "-fobjc-expose-direct-methods" +// CHECK_NO_EXPOSE_DIRECT-NOT: -fobjc-expose-direct-methods + // RUN: %clang -### -S -fjmc --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefixes=CHECK_JMC_WARN,CHECK_NOJMC %s // RUN: %clang -### -S -fjmc --target=x86_64-pc-windows-msvc %s 2>&1 | FileCheck -check-prefixes=CHECK_JMC_WARN,CHECK_NOJMC %s // RUN: %clang -### -S -fjmc -g --target=x86_64-pc-windows-msvc %s 2>&1 | FileCheck -check-prefix=CHECK_JMC %s