https://github.com/ahatanak created 
https://github.com/llvm/llvm-project/pull/183923

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

>From 942dfe1602a1b5c5bd93d9b5201f1c415ffac7a2 Mon Sep 17 00:00:00 2001
From: Ahmed Bougacha <[email protected]>
Date: Sat, 28 Feb 2026 06:18:39 -0800
Subject: [PATCH 1/2] [ObjC] Support emission of selector stubs calls instead
 of objc_msgSend.

This optimizes objc_msgSend calls by emitting "selector stubs" instead.

Usually, the linker redirects calls to external symbols to a symbol
stub it generates, which loads the target function's address from the GOT
and branches to it:

  <symbol stub for _func:>
    adrp x16, _func@GOTPAGE
    ldr x16, [x16, _func@GOTPAGEOFF]
    br x16

with msgSend selector stubs, we extend that to compute the selector as
well:

  <selector stub for "foo":>
    adrp x1, <selector ref for "foo">@PAGE
    ldr x1, [x1, <selector ref for "foo">@PAGEOFF]
    adrp x16, _objc_msgSend@GOTPAGE
    ldr x16, [x16, _objc_msgSend@GOTPAGEOFF]
    br x16

This lets us avoid loading the selector in the compiler, hopefully
leading to codesize reductions.

We're considering two kinds of stubs, the combined one above with
the objc_msgSend dispatch included, or a shorter one that forwards
to the existing stub for objc_msgSend.  That's a decision to be
made in the linker.

Concretely, instead of something like:

  %sel = load i8*, i8** @<selector ref for "foo">
  call ... @objc_msgSend(i8* self, i8* sel)

we emit a call to a newly-declared external "function":

  call ... @"objc_msgSend$foo"(i8* self, i8* undef)

where "objc_msgSend$foo" is treated as any other external symbol
reference throughout the compiler, and, at link-time, is recognized
by the linker as a selector stub.

There are other ways to implement this.  An obvious one is to combine
away the objc_msgSend(self, load(sel)) pattern at the IR level.

Both seem feasible, but the IRGen approach might save us a tiny bit
of compile-time, with the assumption that loads need more mid-level
analysis than boring external functions.

This optimization requires linker version 811.2 or newer for arm64,
arm64e, and arm64_32.

rdar://84437635
---
 clang/include/clang/Basic/CodeGenOptions.def  |   1 +
 clang/include/clang/Options/Options.td        |   3 +
 clang/lib/CodeGen/CGObjCMac.cpp               |  34 ++++-
 clang/lib/Driver/ToolChains/Clang.cpp         |   8 ++
 clang/lib/Driver/ToolChains/Darwin.cpp        |   8 ++
 clang/test/CodeGenObjC/method-selector-stub.m | 131 ++++++++++++++++++
 .../test/Driver/darwin-objc-selector-stubs.m  |  42 ++++++
 7 files changed, 225 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenObjC/method-selector-stub.m
 create mode 100644 clang/test/Driver/darwin-objc-selector-stubs.m

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index 5e174b21be466..b1e6bdbc99cae 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -214,6 +214,7 @@ 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(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..ebec192f36788 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3844,6 +3844,9 @@ 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_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..a88fb6033e066 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -863,6 +863,9 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
   llvm::DenseMap<const ObjCMethodDecl *, DirectMethodInfo>
       DirectMethodDefinitions;
 
+  /// MethodSelectorStubs - map of selector stub functions
+  llvm::DenseMap<Selector, llvm::Function*> MethodSelectorStubs;
+
   /// PropertyNames - uniqued method variable names.
   llvm::DenseMap<IdentifierInfo *, llvm::GlobalVariable *> PropertyNames;
 
@@ -1082,6 +1085,9 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
                                     const ObjCMethodDecl *OMD,
                                     const ObjCContainerDecl *CD) override;
 
+  llvm::Function *GenerateMethodSelectorStub(
+    Selector Sel, const ObjCCommonTypesHelper &ObjCTypes);
+
   void GenerateProtocol(const ObjCProtocolDecl *PD) override;
 
   /// GetOrEmitProtocolRef - Get a forward reference to the protocol
@@ -2123,8 +2129,15 @@ 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);
+    if (!IsSuper && CGM.getCodeGenOpts().ObjCMsgSendSelectorStubs) {
+      // Try to use a selector stub declaration instead of objc_msgSend.
+      Fn = GenerateMethodSelectorStub(Sel, ObjCTypes);
+      // Selector stubs synthesize `_cmd` in the stub, so we don't have to.
+      RequiresSelValue = false;
+    } else {
+      Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
+        : ObjCTypes.getSendFn(IsSuper);
+    }
   }
 
   // Cast function to proper signature
@@ -4047,6 +4060,23 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
   }
 }
 
+llvm::Function *CGObjCCommonMac::GenerateMethodSelectorStub(
+  Selector Sel, const ObjCCommonTypesHelper &ObjCTypes) {
+  auto I = MethodSelectorStubs.find(Sel);
+
+  if (I != MethodSelectorStubs.end())
+    return I->second;
+
+  auto *FnTy = llvm::FunctionType::get(
+    ObjCTypes.ObjectPtrTy, { ObjCTypes.ObjectPtrTy, ObjCTypes.SelectorPtrTy },
+    /*IsVarArg=*/true);
+  auto *Fn = cast<llvm::Function>(CGM.CreateRuntimeFunction(
+      FnTy, "objc_msgSend$" + Sel.getAsString()).getCallee());
+
+  MethodSelectorStubs.insert(std::make_pair(Sel, Fn));
+  return Fn;
+}
+
 llvm::GlobalVariable *
 CGObjCCommonMac::CreateMetadataVar(Twine Name, ConstantStructBuilder &Init,
                                    StringRef Section, CharUnits Align,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 0aa93e2e46814..7e043d78072d0 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4155,6 +4155,14 @@ static void RenderObjCOptions(const ToolChain &TC, const 
Driver &D,
           << "-fobjc-direct-precondition-thunk" << Runtime.getAsString();
     }
   }
+
+  // Pass down -fobjc-msgsend-selector-stubs if present.
+  if (types::isObjC(Input.getType())) {
+    if (Args.hasFlag(options::OPT_fobjc_msgsend_selector_stubs,
+                     options::OPT_fno_objc_msgsend_selector_stubs, false))
+      CmdArgs.push_back("-fobjc-msgsend-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..810637ef3bf05 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -3383,6 +3383,14 @@ 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");
+
   // 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..9359b70832ad3
--- /dev/null
+++ b/clang/test/CodeGenObjC/method-selector-stub.m
@@ -0,0 +1,131 @@
+// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs -triple 
arm64-apple-ios15 %s -o - | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs -triple 
arm64_32-apple-watchos8 %s -o - | FileCheck %s
+
+__attribute__((objc_root_class))
+@interface Root
+- (int)test0;
++ (int)class0;
+- (int)test1: (int)a0;
+- (int)test2: (int)a0 withA: (int)a1;
+
+@property(readonly) int intProperty;
+@end
+
+@interface Foo : Root
+@end
+
+@interface Foo ()
+- (int)testSuper0;
+- (int)methodInExtension;
+@end
+
+@interface Foo (Cat)
+- (int)methodInCategory;
+@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"
+
+@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
+
+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_class0() {
+  // CHECK-LABEL: define{{.*}} i32 @test_root_class0(
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$class0"(ptr {{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  return [Root class0];
+}
+
+// CHECK: declare ptr @"objc_msgSend$class0"(ptr, ptr, ...)
+
+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_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_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
+  // 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..47d9473bffed3
--- /dev/null
+++ b/clang/test/Driver/darwin-objc-selector-stubs.m
@@ -0,0 +1,42 @@
+// Check default enablement of Objective-C objc_msgSend selector stubs codegen.
+
+// Enabled by default with ld64-811.2+ ...
+
+// ... for arm64
+// RUN: %clang -target arm64-apple-ios15            -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-ios15            -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+// RUN: %clang -target arm64-apple-macos12          -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-macos12          -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+// ... for arm64e
+// RUN: %clang -target arm64e-apple-ios15           -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64e-apple-ios15           -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+// ... and arm64_32.
+// RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+
+// Disabled elsewhere, e.g. x86_64.
+// RUN: %clang -target x86_64-apple-macos12         -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+// RUN: %clang -target x86_64-apple-macos12         -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+// RUN: %clang -target x86_64-apple-ios15-simulator -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+// RUN: %clang -target x86_64-apple-ios15-simulator -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+// ... or armv7k.
+// RUN: %clang -target armv7k-apple-watchos6        -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+// RUN: %clang -target armv7k-apple-watchos6        -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+
+// Enabled if you ask for it.
+// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-selector-stubs       
             -### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-selector-stubs 
-mlinker-version=0 -### %s 2>&1 | FileCheck %s
+
+// Disabled if you ask for that.
+// RUN: %clang -target arm64-apple-macos12 -fno-objc-msgsend-selector-stubs 
-mlinker-version=811.2 -### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+
+
+// CHECK: "-fobjc-msgsend-selector-stubs"
+// NOSTUBS-NOT: objc-msgsend-selector-stubs

>From 3a473c1f19af8b155879ab411600c89459f095aa Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <[email protected]>
Date: Fri, 27 Feb 2026 18:10:44 -0800
Subject: [PATCH 2/2] [ObjC] Emit class msgSend stub calls

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
---
 clang/include/clang/Basic/CodeGenOptions.def  |   1 +
 clang/include/clang/Options/Options.td        |   4 +
 clang/lib/CodeGen/CGObjCMac.cpp               |  65 +++++++--
 clang/lib/Driver/ToolChains/Clang.cpp         |   7 +-
 clang/lib/Driver/ToolChains/Darwin.cpp        |   9 ++
 clang/test/CodeGenObjC/method-selector-stub.m | 137 ++++++++++++++++--
 .../test/Driver/darwin-objc-selector-stubs.m  |  39 ++++-
 7 files changed, 228 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index b1e6bdbc99cae..e7461eec64e26 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -215,6 +215,7 @@ 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 ebec192f36788..572f49ab3f3e6 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3847,6 +3847,10 @@ defm objc_convert_messages_to_runtime_calls : 
BoolFOption<"objc-convert-messages
 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 a88fb6033e066..53ac48bb6375e 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -863,8 +863,9 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
   llvm::DenseMap<const ObjCMethodDecl *, DirectMethodInfo>
       DirectMethodDefinitions;
 
-  /// MethodSelectorStubs - map of selector stub functions
-  llvm::DenseMap<Selector, llvm::Function*> MethodSelectorStubs;
+  /// 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;
@@ -1085,8 +1086,9 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
                                     const ObjCMethodDecl *OMD,
                                     const ObjCContainerDecl *CD) override;
 
-  llvm::Function *GenerateMethodSelectorStub(
-    Selector Sel, const ObjCCommonTypesHelper &ObjCTypes);
+  llvm::Function *
+  GenerateMethodSelectorStub(Selector Sel, StringRef ClassName,
+                             const ObjCCommonTypesHelper &ObjCTypes);
 
   void GenerateProtocol(const ObjCProtocolDecl *PD) override;
 
@@ -2081,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);
@@ -2103,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;
@@ -2129,10 +2133,27 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
     // must be made for it.
     if (ReceiverCanBeNull && CGM.ReturnTypeUsesSRet(MSI.CallInfo))
       RequiresNullCheck = true;
-    if (!IsSuper && CGM.getCodeGenOpts().ObjCMsgSendSelectorStubs) {
-      // Try to use a selector stub declaration instead of objc_msgSend.
-      Fn = GenerateMethodSelectorStub(Sel, ObjCTypes);
+    // 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)
@@ -2158,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);
@@ -4061,8 +4086,11 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
 }
 
 llvm::Function *CGObjCCommonMac::GenerateMethodSelectorStub(
-  Selector Sel, const ObjCCommonTypesHelper &ObjCTypes) {
-  auto I = MethodSelectorStubs.find(Sel);
+    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;
@@ -4070,10 +4098,19 @@ llvm::Function 
*CGObjCCommonMac::GenerateMethodSelectorStub(
   auto *FnTy = llvm::FunctionType::get(
     ObjCTypes.ObjectPtrTy, { ObjCTypes.ObjectPtrTy, ObjCTypes.SelectorPtrTy },
     /*IsVarArg=*/true);
-  auto *Fn = cast<llvm::Function>(CGM.CreateRuntimeFunction(
-      FnTy, "objc_msgSend$" + Sel.getAsString()).getCallee());
+  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(Sel, Fn));
+  MethodSelectorStubs.insert(std::make_pair(Key, Fn));
   return Fn;
 }
 
@@ -6404,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 7e043d78072d0..90b11c9e95997 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4156,11 +4156,16 @@ static void RenderObjCOptions(const ToolChain &TC, 
const Driver &D,
     }
   }
 
-  // Pass down -fobjc-msgsend-selector-stubs if present.
   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
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp 
b/clang/lib/Driver/ToolChains/Darwin.cpp
index 810637ef3bf05..bc8e3cc32389a 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -3391,6 +3391,15 @@ void Darwin::addClangTargetOptions(
       (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
index 9359b70832ad3..57bdca1c1f69b 100644
--- a/clang/test/CodeGenObjC/method-selector-stub.m
+++ b/clang/test/CodeGenObjC/method-selector-stub.m
@@ -1,39 +1,54 @@
-// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs -triple 
arm64-apple-ios15 %s -o - | FileCheck %s
-// RUN: %clang_cc1 -emit-llvm -fobjc-msgsend-selector-stubs -triple 
arm64_32-apple-watchos8 %s -o - | FileCheck %s
+// 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]])
+  // CHECK: %{{[^ ]*}} = call i32 @objc_msgSendSuper2(ptr {{[^,]*}}%{{[^,]+}}, 
ptr {{[^,]+}}[[SEL]])
 
   return [super test0];
 }
@@ -55,25 +70,84 @@ - (int)methodInCategoryNoDecl {
 }
 @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)
+  // 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(
-  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$class0"(ptr {{[^,]+}}, ptr 
{{[^,)]*}}undef)
+  // 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];
 }
 
-// CHECK: declare ptr @"objc_msgSend$class0"(ptr, ptr, ...)
+// 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)
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$test1:"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef, i32 {{[^,)]*}}42)
   return [r test1: 42];
 }
 
@@ -81,7 +155,7 @@ int test_root_test1(Root *r) {
 
 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)
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$test2:withA:"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef, i32 {{[^,)]*}}42, i32 {{[^,)]*}}84)
   return [r test2: 42 withA: 84];
 
 }
@@ -90,23 +164,62 @@ int test_root_test2(Root *r) {
 
 int test_extension(Foo *f) {
   // CHECK-LABEL: define{{.*}} i32 @test_extension
-  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$methodInExtension"(ptr 
{{[^,]+}}, ptr {{[^,)]*}}undef)
+  // 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)
+  // 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)
+  // CHECK: %{{[^ ]*}} = call i32 @"objc_msgSend$methodInCategoryNoDecl"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef)
   return [f methodInCategoryNoDecl];
 }
 
@@ -122,7 +235,7 @@ @interface NSArray
 
 void test_fastenum_rawsel(NSArray *array) {
   // CHECK-LABEL: define{{.*}} void @test_fastenum_rawsel
-  // CHECK: %{{[^ ]*}} = call {{i32|i64}} 
@"objc_msgSend$countByEnumeratingWithState:objects:count:"(ptr {{[^,]+}}, ptr
+  // CHECK: %{{[^ ]*}} = call {{i32|i64}} 
@"objc_msgSend$countByEnumeratingWithState:objects:count:"(ptr 
{{[^,]*}}%{{[^,]+}}, ptr {{[^,)]*}}undef,
   // CHECK-NOT: @objc_msgSend to
   for (id x in array)
     use(x);
diff --git a/clang/test/Driver/darwin-objc-selector-stubs.m 
b/clang/test/Driver/darwin-objc-selector-stubs.m
index 47d9473bffed3..b4a23ce768586 100644
--- a/clang/test/Driver/darwin-objc-selector-stubs.m
+++ b/clang/test/Driver/darwin-objc-selector-stubs.m
@@ -3,40 +3,63 @@
 // Enabled by default with ld64-811.2+ ...
 
 // ... for arm64
-// RUN: %clang -target arm64-apple-ios15            -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-ios15            -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-ios15            -mlinker-version=1249  
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
+// RUN: %clang -target arm64-apple-ios15            -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
 // RUN: %clang -target arm64-apple-ios15            -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
-// RUN: %clang -target arm64-apple-macos12          -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-macos12          -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64-apple-macos12          -mlinker-version=1249  
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
+// RUN: %clang -target arm64-apple-macos12          -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
 // RUN: %clang -target arm64-apple-macos12          -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
 // ... for arm64e
-// RUN: %clang -target arm64e-apple-ios15           -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64e-apple-ios15           -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64e-apple-ios15           -mlinker-version=1249  
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
+// RUN: %clang -target arm64e-apple-ios15           -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
 // RUN: %clang -target arm64e-apple-ios15           -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
 // ... and arm64_32.
-// RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s
+// RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=1249  
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
+// RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=INST_STUB_ONLY
 // RUN: %clang -target arm64_32-apple-watchos8      -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
 
 // Disabled elsewhere, e.g. x86_64.
+// RUN: %clang -target x86_64-apple-macos12         -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 // RUN: %clang -target x86_64-apple-macos12         -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 // RUN: %clang -target x86_64-apple-macos12         -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
+// RUN: %clang -target x86_64-apple-ios15-simulator -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 // RUN: %clang -target x86_64-apple-ios15-simulator -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 // RUN: %clang -target x86_64-apple-ios15-simulator -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
 // ... or armv7k.
+// RUN: %clang -target armv7k-apple-watchos6        -mlinker-version=1250  
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 // RUN: %clang -target armv7k-apple-watchos6        -mlinker-version=811.2 
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 // RUN: %clang -target armv7k-apple-watchos6        -mlinker-version=811   
-### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
 
 
 // Enabled if you ask for it.
-// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-selector-stubs       
             -### %s 2>&1 | FileCheck %s
-// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-selector-stubs 
-mlinker-version=0 -### %s 2>&1 | FileCheck %s
+// If the linker version isn't specified on the command line, the cmake 
default version is used.
+// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-selector-stubs       
             -### %s 2>&1 | FileCheck %s -check-prefix=INST_STUB
+
+// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-selector-stubs 
-mlinker-version=0 -### %s 2>&1 | FileCheck %s -check-prefix=INST_STUB_ONLY
+// RUN: %clang -target arm64-apple-macos12 -fobjc-msgsend-class-selector-stubs 
-mlinker-version=0 -### %s 2>&1 | FileCheck %s -check-prefix=CLASS_STUB_ONLY
 
 // Disabled if you ask for that.
 // RUN: %clang -target arm64-apple-macos12 -fno-objc-msgsend-selector-stubs 
-mlinker-version=811.2 -### %s 2>&1 | FileCheck %s --check-prefix=NOSTUBS
+// RUN: %clang -target arm64-apple-macos12 -fno-objc-msgsend-selector-stubs 
-mlinker-version=1250 -### %s 2>&1  | FileCheck %s 
--check-prefix=CLASS_STUB_ONLY
+// RUN: %clang -target arm64-apple-macos12 
-fno-objc-msgsend-class-selector-stubs -mlinker-version=1250 -### %s 2>&1 | 
FileCheck %s --check-prefix=INST_STUB_ONLY
 
 
-// CHECK: "-fobjc-msgsend-selector-stubs"
-// NOSTUBS-NOT: objc-msgsend-selector-stubs
+// CHECK: "-fobjc-msgsend-selector-stubs" "-fobjc-msgsend-class-selector-stubs"
+// INST_STUB_ONLY-NOT: objc-msgsend-class-selector-stubs
+// INST_STUB_ONLY: objc-msgsend-selector-stubs
+// INST_STUB_ONLY-NOT: objc-msgsend-class-selector-stubs
+// INST_STUB: objc-msgsend-selector-stubs
+// CLASS_STUB_ONLY-NOT: objc-msgsend-selector-stubs
+// CLASS_STUB_ONLY: objc-msgsend-class-selector-stubs
+// CLASS_STUB_ONLY-NOT: objc-msgsend-selector-stubs
+// NOSTUBS-NOT: objc-msgsend-{{.*}}selector-stubs

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

Reply via email to