llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Akira Hatanaka (ahatanak)

<details>
<summary>Changes</summary>

Instead of translating class messages to `objc_msgSend` calls, clang now 
emits calls to stub functions that are synthesized by the linker. Each
stub loads the class reference and the selector name and forwards them
to `objc_msgSend`.

The stub function is named using the following format:
`objc_msgSendClass$selName$_OBJC_CLASS_$_className`

Note that the optimization is disabled in the following cases:
- When the class name is unknown at compile time (e.g, `[id classMethod]`).
- The selector name contains a `$`, which serves as the delimiter in stub
   function names.
- The class is annotated with either `objc_class_stub` or
  `objc_runtime_visible`.

In such cases, clang reverts to the existing message send optimization,
where each stub loads only the selector name.

Also add class objects to llvm.used when class msgSend optimization is
enabled. This is needed as the linker currently cannot generate the stub,
which references the class object, if LTO internalizes the global variable
for the class object.

rdar://147604613

---

Patch is 24.76 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/183923.diff


7 Files Affected:

- (modified) clang/include/clang/Basic/CodeGenOptions.def (+2) 
- (modified) clang/include/clang/Options/Options.td (+7) 
- (modified) clang/lib/CodeGen/CGObjCMac.cpp (+72-3) 
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+13) 
- (modified) clang/lib/Driver/ToolChains/Darwin.cpp (+17) 
- (added) clang/test/CodeGenObjC/method-selector-stub.m (+244) 
- (added) clang/test/Driver/darwin-objc-selector-stubs.m (+65) 


``````````diff
diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index 5e174b21be466..e7461eec64e26 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -214,6 +214,8 @@ CODEGENOPT(NoZeroInitializedInBSS , 1, 0, Benign) ///< 
-fno-zero-initialized-in-
 ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy, Benign)
 /// Replace certain message sends with calls to ObjC runtime entrypoints
 CODEGENOPT(ObjCConvertMessagesToRuntimeCalls , 1, 1, Benign)
+CODEGENOPT(ObjCMsgSendSelectorStubs , 1, 0, Benign) ///< Use per-selector 
linker stubs for objc_msgSend
+CODEGENOPT(ObjCMsgSendClassSelectorStubs, 1, 0, Benign) ///< Use per-class, 
per-selector linker stubs for objc_msgSend
 CODEGENOPT(ObjCAvoidHeapifyLocalBlocks, 1, 0, Benign)
 /// Generate direct method precondition thunks to expose symbols and optimize 
nil checks.
 CODEGENOPT(ObjCDirectPreconditionThunk, 1, 0, Benign)
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index c8a1e478122e1..572f49ab3f3e6 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3844,6 +3844,13 @@ defm objc_convert_messages_to_runtime_calls : 
BoolFOption<"objc-convert-messages
   CodeGenOpts<"ObjCConvertMessagesToRuntimeCalls">, DefaultTrue,
   NegFlag<SetFalse, [], [ClangOption, CC1Option]>,
   PosFlag<SetTrue>>;
+defm objc_msgsend_selector_stubs : BoolFOption<"objc-msgsend-selector-stubs",
+  CodeGenOpts<"ObjCMsgSendSelectorStubs">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option]>, NegFlag<SetFalse>>;
+defm objc_msgsend_class_selector_stubs : 
BoolFOption<"objc-msgsend-class-selector-stubs",
+  CodeGenOpts<"ObjCMsgSendClassSelectorStubs">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option]>,
+  NegFlag<SetFalse>>;
 defm objc_arc_exceptions : BoolFOption<"objc-arc-exceptions",
   CodeGenOpts<"ObjCAutoRefCountExceptions">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option],
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index e6c244547cefd..53ac48bb6375e 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -863,6 +863,10 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
   llvm::DenseMap<const ObjCMethodDecl *, DirectMethodInfo>
       DirectMethodDefinitions;
 
+  /// MethodSelectorStubs - Map from (selector,class) to stub function.
+  llvm::DenseMap<std::pair<Selector, StringRef>, llvm::Function *>
+      MethodSelectorStubs;
+
   /// PropertyNames - uniqued method variable names.
   llvm::DenseMap<IdentifierInfo *, llvm::GlobalVariable *> PropertyNames;
 
@@ -1082,6 +1086,10 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
                                     const ObjCMethodDecl *OMD,
                                     const ObjCContainerDecl *CD) override;
 
+  llvm::Function *
+  GenerateMethodSelectorStub(Selector Sel, StringRef ClassName,
+                             const ObjCCommonTypesHelper &ObjCTypes);
+
   void GenerateProtocol(const ObjCProtocolDecl *PD) override;
 
   /// GetOrEmitProtocolRef - Get a forward reference to the protocol
@@ -2075,12 +2083,13 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
     const ObjCCommonTypesHelper &ObjCTypes) {
   CodeGenTypes &Types = CGM.getTypes();
   auto selTy = CGF.getContext().getObjCSelType();
+  llvm::Value *ReceiverValue = 
llvm::UndefValue::get(Types.ConvertType(Arg0Ty));
   llvm::Value *SelValue = llvm::UndefValue::get(Types.ConvertType(selTy));
 
   CallArgList ActualArgs;
   if (!IsSuper)
     Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy);
-  ActualArgs.add(RValue::get(Arg0), Arg0Ty);
+  ActualArgs.add(RValue::get(ReceiverValue), Arg0Ty);
   if (!Method || !Method->isDirectMethod())
     ActualArgs.add(RValue::get(SelValue), selTy);
   ActualArgs.addFrom(CallArgs);
@@ -2097,6 +2106,7 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
       canMessageReceiverBeNull(CGF, Method, IsSuper, ClassReceiver, Arg0);
 
   bool RequiresNullCheck = false;
+  bool RequiresReceiverValue = true;
   bool RequiresSelValue = true;
 
   llvm::FunctionCallee Fn = nullptr;
@@ -2123,8 +2133,32 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
     // must be made for it.
     if (ReceiverCanBeNull && CGM.ReturnTypeUsesSRet(MSI.CallInfo))
       RequiresNullCheck = true;
-    Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
-                        : ObjCTypes.getSendFn(IsSuper);
+    // The class name that's used to create the class msgSend stub declaration.
+    StringRef ClassName;
+
+    // We cannot use class msgSend stubs in the following cases:
+    // 1. The class is annotated with `objc_class_stub` or
+    //    `objc_runtime_visible`.
+    // 2. The selector name contains a '$'.
+    if (CGM.getCodeGenOpts().ObjCMsgSendClassSelectorStubs && ClassReceiver &&
+        Method && Method->isClassMethod() &&
+        !ClassReceiver->hasAttr<ObjCClassStubAttr>() &&
+        !ClassReceiver->hasAttr<ObjCRuntimeVisibleAttr>() &&
+        Sel.getAsString().find('$') == std::string::npos)
+      ClassName = ClassReceiver->getObjCRuntimeNameAsString();
+
+    bool UseClassStub = ClassName.data();
+    // Try to use a selector stub declaration instead of objc_msgSend.
+    if (!IsSuper &&
+        (CGM.getCodeGenOpts().ObjCMsgSendSelectorStubs || UseClassStub)) {
+      Fn = GenerateMethodSelectorStub(Sel, ClassName, ObjCTypes);
+      // Selector stubs synthesize `_cmd` in the stub, so we don't have to.
+      RequiresReceiverValue = !UseClassStub;
+      RequiresSelValue = false;
+    } else {
+      Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
+        : ObjCTypes.getSendFn(IsSuper);
+    }
   }
 
   // Cast function to proper signature
@@ -2145,6 +2179,10 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
     nullReturn.init(CGF, Arg0);
   }
 
+  // Pass the receiver value if it's needed.
+  if (RequiresReceiverValue)
+    ActualArgs[0] = CallArg(RValue::get(Arg0), Arg0Ty);
+
   // If a selector value needs to be passed, emit the load before the call.
   if (RequiresSelValue) {
     SelValue = GetSelector(CGF, Sel);
@@ -4047,6 +4085,35 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
   }
 }
 
+llvm::Function *CGObjCCommonMac::GenerateMethodSelectorStub(
+    Selector Sel, StringRef ClassName, const ObjCCommonTypesHelper &ObjCTypes) 
{
+  assert((!ClassName.data() || !ClassName.empty()) &&
+         "class name cannot be an empty string");
+  auto Key = std::make_pair(Sel, ClassName);
+  auto I = MethodSelectorStubs.find(Key);
+
+  if (I != MethodSelectorStubs.end())
+    return I->second;
+
+  auto *FnTy = llvm::FunctionType::get(
+    ObjCTypes.ObjectPtrTy, { ObjCTypes.ObjectPtrTy, ObjCTypes.SelectorPtrTy },
+    /*IsVarArg=*/true);
+  std::string FnName;
+
+  if (ClassName.data())
+    FnName = ("objc_msgSendClass$" + Sel.getAsString() + "$_OBJC_CLASS_$_" +
+              llvm::Twine(ClassName))
+                 .str();
+  else
+    FnName = "objc_msgSend$" + Sel.getAsString();
+
+  auto *Fn =
+      cast<llvm::Function>(CGM.CreateRuntimeFunction(FnTy, 
FnName).getCallee());
+
+  MethodSelectorStubs.insert(std::make_pair(Key, Fn));
+  return Fn;
+}
+
 llvm::GlobalVariable *
 CGObjCCommonMac::CreateMetadataVar(Twine Name, ConstantStructBuilder &Init,
                                    StringRef Section, CharUnits Align,
@@ -6374,6 +6441,8 @@ llvm::GlobalVariable 
*CGObjCNonFragileABIMac::BuildClassObject(
   if (!CGM.getTriple().isOSBinFormatCOFF())
     if (HiddenVisibility)
       GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  if (CGM.getCodeGenOpts().ObjCMsgSendClassSelectorStubs && !isMetaclass)
+    CGM.addUsedGlobal(GV);
   return GV;
 }
 
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 0aa93e2e46814..90b11c9e95997 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4155,6 +4155,19 @@ static void RenderObjCOptions(const ToolChain &TC, const 
Driver &D,
           << "-fobjc-direct-precondition-thunk" << Runtime.getAsString();
     }
   }
+
+  if (types::isObjC(Input.getType())) {
+    // Pass down -fobjc-msgsend-selector-stubs if present.
+    if (Args.hasFlag(options::OPT_fobjc_msgsend_selector_stubs,
+                     options::OPT_fno_objc_msgsend_selector_stubs, false))
+      CmdArgs.push_back("-fobjc-msgsend-selector-stubs");
+
+    // Pass down -fobjc-msgsend-class-selector-stubs if present.
+    if (Args.hasFlag(options::OPT_fobjc_msgsend_class_selector_stubs,
+                     options::OPT_fno_objc_msgsend_class_selector_stubs, 
false))
+      CmdArgs.push_back("-fobjc-msgsend-class-selector-stubs");
+  }
+
   // 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/lib/Driver/ToolChains/Darwin.cpp 
b/clang/lib/Driver/ToolChains/Darwin.cpp
index aec1ad7d2f155..bc8e3cc32389a 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -3383,6 +3383,23 @@ void Darwin::addClangTargetOptions(
       isAlignedAllocationUnavailable())
     CC1Args.push_back("-faligned-alloc-unavailable");
 
+  // Enable objc_msgSend selector stubs by default if the linker supports it.
+  // ld64-811.2+ does, for arm64, arm64e, and arm64_32.
+  if (!DriverArgs.hasArgNoClaim(options::OPT_fobjc_msgsend_selector_stubs,
+                                options::OPT_fno_objc_msgsend_selector_stubs) 
&&
+      getTriple().isAArch64() &&
+      (getLinkerVersion(DriverArgs) >= VersionTuple(811, 2)))
+    CC1Args.push_back("-fobjc-msgsend-selector-stubs");
+
+  // Enable objc_msgSend class selector stubs by default if the linker supports
+  // it. ld64-1250+ does, for arm64, arm64e, and arm64_32.
+  if (!DriverArgs.hasArgNoClaim(
+          options::OPT_fobjc_msgsend_class_selector_stubs,
+          options::OPT_fno_objc_msgsend_class_selector_stubs) &&
+      getTriple().isAArch64() &&
+      (getLinkerVersion(DriverArgs) >= VersionTuple(1250, 0)))
+    CC1Args.push_back("-fobjc-msgsend-class-selector-stubs");
+
   // Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
   // or disabled sized deallocations.
   if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
diff --git a/clang/test/CodeGenObjC/method-selector-stub.m 
b/clang/test/CodeGenObjC/method-selector-stub.m
new file mode 100644
index 0000000000000..57bdca1c1f69b
--- /dev/null
+++ b/clang/test/CodeGenObjC/method-selector-stub.m
@@ -0,0 +1,244 @@
+// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs -triple 
arm64-apple-ios15 %s -o - | FileCheck -check-prefix=CHECK 
-check-prefix=INST_STUB %s
+// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs -triple 
arm64_32-apple-watchos8 %s -o - | FileCheck -check-prefix=CHECK 
-check-prefix=INST_STUB %s
+// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs 
-fobjc-msgsend-class-selector-stubs -triple arm64-apple-ios15 %s -o - | 
FileCheck -check-prefix=CHECK -check-prefix=CLASS_STUB %s
+// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs 
-fobjc-msgsend-class-selector-stubs -triple arm64_32-apple-watchos8 %s -o - | 
FileCheck -check-prefix=CHECK -check-prefix=CLASS_STUB %s
+
+__attribute__((objc_root_class))
+@interface Root
+- (int)test0;
+- (int)test$0;
++ (int)class0;
+- (int)class0;
++ (int)class0$;
+- (int)test1: (int)a0;
+- (int)test2: (int)a0 withA: (int)a1;
+
+@property(readonly) int intProperty;
+@end
+
+__attribute__((objc_root_class))
+@interface Root2
++ (int)class0;
+@end
+
+@interface Foo : Root
+@end
+
+@interface Foo ()
+- (int)testSuper0;
+- (int)methodInExtension;
++ (int)classMethodInExtension;
+@end
+
+@interface Foo (Cat)
+- (int)methodInCategory;
++ (int)classMethodInCategory;
+@end
+
+
+// CHECK: [[TEST0_METHNAME:@OBJC_METH_VAR_NAME_[^ ]*]] = private unnamed_addr 
constant [6 x i8] c"test0\00", section "__TEXT,__objc_methname,cstring_literals"
+// CHECK: [[TEST0_SELREF:@OBJC_SELECTOR_REFERENCES_[^ ]*]] = internal 
externally_initialized global ptr [[TEST0_METHNAME]], section 
"__DATA,__objc_selrefs,literal_pointers,no_dead_strip"
+
+// INST_STUB-NOT: @llvm.used =
+// CLASS_STUB: @llvm.used = appending global [1 x ptr] [ptr 
@"OBJC_CLASS_$_Foo"],
+
+@implementation Foo
+
+- (int)testSuper0 {
+  // Super calls don't have stubs.
+  // CHECK-LABEL: define{{.*}} i32 @"\01-[Foo testSuper0]"(
+  // CHECK: [[SEL:%[^ ]]] = load ptr, ptr [[TEST0_SELREF]]
+  // CHECK: %{{[^ ]*}} = call i32 @objc_msgSendSuper2(ptr {{[^,]*}}%{{[^,]+}}, 
ptr {{[^,]+}}[[SEL]])
+
+  return [super test0];
+}
+
+// CHECK-LABEL: define internal i32 @"\01-[Foo methodInExtension]"(
+- (int)methodInExtension {
+  return 42;
+}
+@end
+
+@implementation Foo (Cat)
+// CHECK-LABEL: define internal i32 @"\01-[Foo(Cat) methodInCategory]"(
+- (int)methodInCategory {
+  return 42;
+}
+// CHECK-LABEL: define internal i32 @"\01-[Foo(Cat) methodInCategoryNoDecl]"(
+- (int)methodInCategoryNoDecl {
+  return 42;
+}
+@end
+
+__attribute__((objc_root_class,objc_runtime_name("_Foo1234")))
+@interface Foo2
++(void)m0;
+@end
+
+__attribute__((objc_class_stub, objc_subclassing_restricted))
+@interface Foo3
++(void)m0;
+@end
+
+__attribute__((objc_runtime_visible))
+@interface Foo4
++(void)m0;
+@end
+
+int test_root_test0(Root *r) {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_test0(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$test0"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [r test0];
+}
+
+// CHECK: declare ptr @"objc_msgSend$test0"(ptr, ptr, ...)
+
+int test_root_test0_dollar(Root *r) {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_test0_dollar(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$test$0"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [r test$0];
+}
+
+// CHECK: declare ptr @"objc_msgSend$test$0"(ptr, ptr, ...)
+
+int test_root_class0() {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_class0(
+  // INST_STUB: %{{[^ ]*}} = call i32 @"objc_msgSend$class0"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  // CLASS_STUB: %{{[^ ]*}} = call i32 
@"objc_msgSendClass$class0$_OBJC_CLASS_$_Root"(ptr {{[^,)]*}}undef, ptr 
{{[^,)]*}}undef)
+  return [Root class0];
+}
+
+// INST_STUB: declare ptr @"objc_msgSend$class0"(ptr, ptr, ...)
+// CLASS_STUB: declare ptr @"objc_msgSendClass$class0$_OBJC_CLASS_$_Root"(ptr, 
ptr, ...)
+
+int test_root2_class0() {
+  // CHECK-LABEL: define{{.*}} i32 @test_root2_class0(
+  // INST_STUB: %{{[^ ]*}} = call i32 @"objc_msgSend$class0"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  // CLASS_STUB: %{{[^ ]*}} = call i32 
@"objc_msgSendClass$class0$_OBJC_CLASS_$_Root2"(ptr {{[^,)]*}}undef, ptr 
{{[^,)]*}}undef)
+  return [Root2 class0];
+}
+
+// CLASS_STUB: declare ptr 
@"objc_msgSendClass$class0$_OBJC_CLASS_$_Root2"(ptr, ptr, ...)
+
+int test_root_class0_inst(Root *r) {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_class0_inst(
+  // CHECK: %[[R_ADDR:.*]] = alloca ptr,
+  // CHECK: store ptr %{{.*}}, ptr %[[R_ADDR]],
+  // CHECK: %[[V0:.*]] = load ptr, ptr %[[R_ADDR]],
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$class0"(ptr {{[^,]*}}%[[V0]], 
ptr {{[^,)]*}}undef)
+  return [r class0];
+}
+
+// CLASS_STUB: declare ptr @"objc_msgSend$class0"(ptr, ptr, ...)
+
+int test_root_class0_dollar() {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_class0_dollar(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$class0$"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [Root class0$];
+}
+
+// CHECK: declare ptr @"objc_msgSend$class0$"(ptr, ptr, ...)
+
+int test_id_class0(id r) {
+  // CHECK-LABEL: define{{.*}} i32 @test_id_class0(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$class0"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [r class0];
+}
+
+int test_root_test1(Root *r) {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_test1(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$test1:"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef, i32 {{[^,)]*}}42)
+  return [r test1: 42];
+}
+
+// CHECK: declare ptr @"objc_msgSend$test1:"(ptr, ptr, ...)
+
+int test_root_test2(Root *r) {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_test2(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$test2:withA:"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef, i32 {{[^,)]*}}42, i32 {{[^,)]*}}84)
+  return [r test2: 42 withA: 84];
+
+}
+
+// CHECK: declare ptr @"objc_msgSend$test2:withA:"(ptr, ptr, ...)
+
+int test_extension(Foo *f) {
+  // CHECK-LABEL: define{{.*}} i32 @test_extension
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$methodInExtension"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [f methodInExtension];
+}
+
+// CHECK: declare ptr @"objc_msgSend$methodInExtension"(ptr, ptr, ...)
+
+int test_class_method_extension(void) {
+  // CHECK-LABEL: define{{.*}} i32 @test_class_method_extension
+  // INST_STUB: %{{[^ ]*}} = call i32 
@"objc_msgSend$classMethodInExtension"(ptr {{[^,]*}}%{{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  // CLASS_STUB: %{{[^ ]*}} = call i32 
@"objc_msgSendClass$classMethodInExtension$_OBJC_CLASS_$_Foo"(ptr 
{{[^,)]*}}undef, ptr {{[^,)]*}}undef)
+  return [Foo classMethodInExtension];
+}
+
+// INST_STUB: declare ptr @"objc_msgSend$classMethodInExtension"(ptr, ptr, ...)
+// CLASS_STUB: declare ptr 
@"objc_msgSendClass$classMethodInExtension$_OBJC_CLASS_$_Foo"(ptr, ptr, ...)
+
+int test_category(Foo *f) {
+  // CHECK-LABEL: define{{.*}} i32 @test_category
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$methodInCategory"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [f methodInCategory];
+}
+
+// CHECK: declare ptr @"objc_msgSend$methodInCategory"(ptr, ptr, ...)
+
+int test_class_method_category(void) {
+  // CHECK-LABEL: define{{.*}} i32 @test_class_method_category
+  // INST_STUB: %{{[^ ]*}} = call i32 
@"objc_msgSend$classMethodInCategory"(ptr {{[^,]*}}%{{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  // CLASS_STUB: %{{[^ ]*}} = call i32 
@"objc_msgSendClass$classMethodInCategory$_OBJC_CLASS_$_Foo"(ptr 
{{[^,]*}}undef, ptr {{[^,)]*}}undef)
+  return [Foo classMethodInCategory];
+}
+
+// INST_STUB: declare ptr @"objc_msgSend$classMethodInCategory"(ptr, ptr, ...)
+// CLASS_STUB: declare ptr 
@"objc_msgSendClass$classMethodInCategory$_OBJC_CLASS_$_Foo"(ptr, ptr, ...)
+
+void test_class_method_objc_runtime_name(void) {
+  // CHECK-LABEL: define{{.*}} void @test_class_method_objc_runtime_name(
+  // INST_STUB: call void @"objc_msgSend$m0"(ptr {{[^,]*}}%{{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  // CLASS_STUB: call void @"objc_msgSendClass$m0$_OBJC_CLASS_$__Foo1234"(ptr 
{{[^,]*}}undef, ptr {{[^,)]*}}undef)
+  [Foo2 m0];
+}
+
+void test_class_method_class_stub(void) {
+  // CHECK-LABEL: define{{.*}} void @test_class_method_class_stub(
+  // CHECK: call void @"objc_msgSend$m0"(ptr {{[^,]*}}%{{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  [Foo3 m0];
+}
+
+void test_class_method_objc_runtime_visible(void) {
+  // CHECK-LABEL: define{{.*}} void @test_class_method_objc_runtime_visible(
+  // CHECK: call void @"objc_msgSend$m0"(ptr {{[^,]*}}%{{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  [Foo4 m0];
+}
+
+int test_category_nodecl(Foo *f) {
+  // CHECK-LABEL: define{{.*}} i32 @test_category_nodecl
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$methodInCategoryNoDecl"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
+  return [f methodInCategoryNoDecl];
+}
+
+// CHECK: declare ptr @"objc_msgSend$methodInCategoryNoDecl"(ptr, ptr, ...)
+
+
+// === Test the special case where there's no method, but only a selector.
+
+@interface NSArray
+@end;
+
+extern void use(id);
+
+void test_fastenum_rawsel(NSArray *array) {
+  // CHECK-LABEL: define{{.*}} void @test_fastenum_rawsel
+  // CHECK: %{{[^ ]*}} = call {{i32|i64}} 
@"objc_msgSend$countByEnumeratingWithState:objects:count:"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef,
+  // CHECK-NOT: @objc_msgSend to
+  for (id x in array)
+    use(x);
+}
+
+// CHECK: declare ptr 
@"objc_msgSend$countByEnumeratingWithState:objects:count:"(ptr, ptr, ...)
diff --git a/clang/test/Driver/darwin-objc-selector-stubs.m 
b/clang/test/Driver/darwin-objc-selector-stubs.m
new file mode 100644
index 0000000000000..b4a23ce768586
--- /dev/null
+++ b/clang/test/Driver/darwin-objc-selector-stubs.m
@@ -0,0 +1,65 @@
+// Check default enablement of Objective-C objc_msgSend selector stubs codegen.
+
+// Enabled by default with ld64-811.2...
[truncated]

``````````

</details>


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

Reply via email to