Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -191,6 +191,7 @@ LANGOPT(NoBitFieldTypeAlign , 1, 0, "bit-field type alignment") LANGOPT(HexagonQdsp6Compat , 1, 0, "hexagon-qdsp6 backward compatibility") LANGOPT(ObjCAutoRefCount , 1, 0, "Objective-C automated reference counting") +LANGOPT(ObjCConvertMessagesToRuntimeCalls , 1, 0, "objc_* support for retain/release in the runtime") LANGOPT(ObjCWeakRuntime , 1, 0, "__weak support in the ARC runtime") LANGOPT(ObjCWeak , 1, 0, "Objective-C __weak in ARC and MRC files") LANGOPT(ObjCSubscriptingLegacyRuntime , 1, 0, "Subscripting support in legacy ObjectiveC runtime") Index: include/clang/Basic/ObjCRuntime.h =================================================================== --- include/clang/Basic/ObjCRuntime.h +++ include/clang/Basic/ObjCRuntime.h @@ -171,6 +171,27 @@ llvm_unreachable("bad kind"); } + /// Does this runtime have entrypoints which can handle messaging + /// retain/release. + /// + /// It is faster to call objc_retain(x) than [x retain]. Newer versions of + /// the runtime are aware of custom retain/release methods and so can send + /// the message if required, but otherwise are able to take a fast-path. + bool shouldUseARCFunctionsForRetainRelease() const { + switch (getKind()) { + case FragileMacOSX: return false; + case MacOSX: return getVersion() >= VersionTuple(10, 10); + case iOS: return getVersion() >= VersionTuple(8); + case WatchOS: + return true; + + case GCC: return false; + case GNUstep: return false; + case ObjFW: return false; + } + llvm_unreachable("bad kind"); + } + /// \brief Does this runtime supports optimized setter entrypoints? bool hasOptimizedSetter() const { switch (getKind()) { Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -892,6 +892,12 @@ def fobjc_arc : Flag<["-"], "fobjc-arc">, Group, Flags<[CC1Option]>, HelpText<"Synthesize retain and release calls for Objective-C pointers">; def fno_objc_arc : Flag<["-"], "fno-objc-arc">, Group; +def fobjc_convert_messages_to_runtime_calls : + Flag<["-"], "fobjc-convert-messages-to-runtime-calls">, + Group, Flags<[CC1Option]>, + HelpText<"Convert eligible message sends to runtime calls">; +def fno_objc_convert_messages_to_runtime_calls : + Flag<["-"], "fno-objc-convert-messages-to-runtime-calls">, Group; def fobjc_arc_exceptions : Flag<["-"], "fobjc-arc-exceptions">, Group, Flags<[CC1Option]>, HelpText<"Use EH-safe code when synthesizing retains and releases in -fobjc-arc">; def fno_objc_arc_exceptions : Flag<["-"], "fno-objc-arc-exceptions">, Group; Index: lib/CodeGen/CGObjC.cpp =================================================================== --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -2333,6 +2333,14 @@ return InitRV.getScalarVal(); } +/// Allocate the given objc object. +/// call i8* \@objc_alloc(i8* %value) +llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getObjCEntrypoints().objc_alloc, + "objc_alloc"); +} + /// Produce the code to do a primitive release. /// [tmp drain]; void CodeGenFunction::EmitObjCMRRAutoreleasePoolPop(llvm::Value *Arg) { Index: lib/CodeGen/CGObjCMac.cpp =================================================================== --- lib/CodeGen/CGObjCMac.cpp +++ lib/CodeGen/CGObjCMac.cpp @@ -1867,6 +1867,41 @@ const ObjCMethodDecl *Method, const ObjCInterfaceDecl *ClassReceiver, const ObjCCommonTypesHelper &ObjCTypes) { + // Call runtime methods directly if we can. + if (Method && CGM.getLangOpts().ObjCConvertMessagesToRuntimeCalls) { + switch (Method->getMethodFamily()) { + case OMF_alloc: { + // Make sure the name is exactly 'alloc'. All methods with that prefix + // are identified as OMF_alloc but we only want to call the runtime for + // this version. + Selector S = Method->getSelector(); + if (S.isUnarySelector() && S.getNameForSlot(0) == "alloc" && + ResultType->isObjCIdType() && Arg0Ty->isObjCIdType()) + return RValue::get(CGF.EmitObjCAlloc(Arg0)); + break; + } + case OMF_autorelease: + if (ResultType->isObjCIdType() && Arg0Ty->isObjCIdType()) + return RValue::get(CGF.EmitARCAutorelease(Arg0)); + break; + + case OMF_retain: + if (ResultType->isObjCIdType() && Arg0Ty->isObjCIdType()) + return RValue::get(CGF.EmitARCRetainNonBlock(Arg0)); + break; + + case OMF_release: + if (ResultType->isVoidType() && Arg0Ty->isObjCIdType()) { + CGF.EmitARCRelease(Arg0, ARCImpreciseLifetime); + return RValue::get(nullptr); + } + break; + + default: + break; + } + } + CallArgList ActualArgs; if (!IsSuper) Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -2799,6 +2799,7 @@ std::pair EmitARCStoreStrong(const BinaryOperator *e, bool ignored); + llvm::Value *EmitObjCAlloc(llvm::Value *value); llvm::Value *EmitObjCThrowOperand(const Expr *expr); llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr); llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr); Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -111,6 +111,9 @@ struct ObjCEntrypoints { ObjCEntrypoints() { memset(this, 0, sizeof(*this)); } + /// void objc_alloc(id); + llvm::Constant *objc_alloc; + /// void objc_autoreleasePoolPop(void*); llvm::Constant *objc_autoreleasePoolPop; Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -4842,6 +4842,15 @@ } + // Allow the user to control whether messages can be converted to runtime + // functions. + if (types::isObjC(InputType) && + Args.hasFlag(options::OPT_fobjc_convert_messages_to_runtime_calls, + options::OPT_fno_objc_convert_messages_to_runtime_calls, + true) && + objcRuntime.shouldUseARCFunctionsForRetainRelease()) + CmdArgs.push_back("-fobjc-convert-messages-to-runtime-calls"); + // -fobjc-infer-related-result-type is the default, except in the Objective-C // rewriter. if (rewriteKind != RK_None) Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1519,6 +1519,9 @@ if (Args.hasArg(OPT_fobjc_subscripting_legacy_runtime)) Opts.ObjCSubscriptingLegacyRuntime = (Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX); + + if (Args.hasArg(OPT_fobjc_convert_messages_to_runtime_calls)) + Opts.ObjCConvertMessagesToRuntimeCalls = 1; } if (Args.hasArg(OPT_fgnu89_inline)) { Index: test/CodeGenObjC/convert-messages-to-runtime-calls.m =================================================================== --- /dev/null +++ test/CodeGenObjC/convert-messages-to-runtime-calls.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.10.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSGS +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.10.0 -emit-llvm -o - %s -fobjc-convert-messages-to-runtime-calls | FileCheck %s --check-prefix=CALLS + +@interface NSObject ++ (id)alloc; ++ (id)alloc2; +- (id)init; +- (id)retain; +- (void)release; +- (id)autorelease; +@end + +@interface NSString : NSObject +@end + +// CHECK-LABEL: define {{.*}}void @test1 +void test1(id x) { + // MSGS: {{call.*@objc_msgSend}} + // MSGS: {{call.*@objc_msgSend}} + // MSGS: {{call.*@objc_msgSend}} + // MSGS: {{call.*@objc_msgSend}} + // CALLS: {{call.*@objc_alloc}} + // CALLS: {{call.*@objc_retain}} + // CALLS: {{call.*@objc_release}} + // CALLS: {{call.*@objc_autorelease}} + [NSObject alloc]; + [x retain]; + [x release]; + [x autorelease]; +} + +// CHECK-LABEL: define {{.*}}void @test2 +void test2() { + // MSGS: {{call.*@objc_msgSend}} + // CALLS: {{call.*@objc_msgSend}} + // Make sure alloc has the correct name and number of types. + [NSObject alloc2]; +} Index: test/Driver/objc-convert-messages-to-runtime-calls.m =================================================================== --- /dev/null +++ test/Driver/objc-convert-messages-to-runtime-calls.m @@ -0,0 +1,17 @@ +// RUN: %clang %s -### -o %t.o 2>&1 -fobjc-convert-messages-to-runtime-calls -fsyntax-only -fno-objc-convert-messages-to-runtime-calls -target x86_64-apple-macosx10.10.0 | FileCheck %s --check-prefix=DISABLE +// RUN: %clang %s -### -o %t.o 2>&1 -fobjc-convert-messages-to-runtime-calls -fsyntax-only -target x86_64-apple-macosx10.10.0 | FileCheck %s --check-prefix=ENABLE +// RUN: %clang %s -### -o %t.o 2>&1 -fsyntax-only -target x86_64-apple-macosx10.10.0 | FileCheck %s --check-prefix=SUPPORTED_MACOS +// RUN: %clang %s -### -o %t.o 2>&1 -fsyntax-only -target x86_64-apple-macosx10.9.0 | FileCheck %s --check-prefix=UNSUPPORTED_MACOS +// RUN: %clang %s -### -o %t.o 2>&1 -fsyntax-only -target armv7-apple-ios8.0 | FileCheck %s --check-prefix=SUPPORTED_IOS +// RUN: %clang %s -### -o %t.o 2>&1 -fsyntax-only -target armv7-apple-ios7.0 | FileCheck %s --check-prefix=UNSUPPORTED_IOS + +// Check that we pass fobjc-convert-messages-to-runtime-calls only when supported, and not explicitly disabled. + +// DISABLE-NOT: "-fobjc-convert-messages-to-runtime-calls" +// ENABLE: "-fobjc-convert-messages-to-runtime-calls" +// SUPPORTED_MACOS: "-fobjc-convert-messages-to-runtime-calls" +// UNSUPPORTED_MACOS-NOT: "-fobjc-convert-messages-to-runtime-calls" +// SUPPORTED_IOS: "-fobjc-convert-messages-to-runtime-calls" +// UNSUPPORTED_IOS-NOT: "-fobjc-convert-messages-to-runtime-calls" + +