diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 95ac6e08e4d2f..92d30827df824 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -206,6 +206,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) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 0bd1622d99f80..eca2569df29bb 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3824,6 +3824,13 @@ defm objc_convert_messages_to_runtime_calls : BoolFOption<"objc-convert-messages CodeGenOpts<"ObjCConvertMessagesToRuntimeCalls">, DefaultTrue, NegFlag, PosFlag>; +defm objc_msgsend_selector_stubs : BoolFOption<"objc-msgsend-selector-stubs", + CodeGenOpts<"ObjCMsgSendSelectorStubs">, DefaultFalse, + PosFlag, NegFlag>; +defm objc_msgsend_class_selector_stubs : BoolFOption<"objc-msgsend-class-selector-stubs", + CodeGenOpts<"ObjCMsgSendClassSelectorStubs">, DefaultFalse, + PosFlag, + NegFlag>; defm objc_arc_exceptions : BoolFOption<"objc-arc-exceptions", CodeGenOpts<"ObjCAutoRefCountExceptions">, DefaultFalse, PosFlag DirectMethodDefinitions; + /// MethodSelectorStubs - Map from (selector,class) to stub function. + llvm::DenseMap, llvm::Function *> + MethodSelectorStubs; + /// PropertyNames - uniqued method variable names. llvm::DenseMap PropertyNames; @@ -1061,6 +1065,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 @@ -2057,12 +2065,14 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend( const ObjCCommonTypesHelper &ObjCTypes) { CodeGenTypes &Types = CGM.getTypes(); auto selTy = CGF.getContext().getObjCSelType(); + llvm::Value *ReceiverValue = + llvm::PoisonValue::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); @@ -2079,6 +2089,7 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend( canMessageReceiverBeNull(CGF, Method, IsSuper, ClassReceiver, Arg0); bool RequiresNullCheck = false; + bool RequiresReceiverValue = true; bool RequiresSelValue = true; llvm::FunctionCallee Fn = nullptr; @@ -2104,8 +2115,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() && + !ClassReceiver->hasAttr() && + 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 @@ -2126,6 +2161,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); @@ -4013,6 +4052,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(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, @@ -6353,6 +6421,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 5e708f6156d6c..0a91d5c46e673 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4244,6 +4244,18 @@ static void RenderObjCOptions(const ToolChain &TC, const Driver &D, } } + 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 84ed31f950270..9b4379159002f 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -3526,6 +3526,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..1f908d6120d5d --- /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 {{[^,)]*}}poison, 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 {{[^,)]*}}poison, 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 {{[^,)]*}}poison, 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 {{[^,]*}}poison, 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 {{[^,]*}}poison, 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+ ... + +// ... for arm64 +// 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=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=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=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. +// 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" "-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