pete updated this revision to Diff 40719.
pete added a comment.
Thanks for all the feedback!
I've addressed all of your previous feedback and also applied clang-format to
the whole patch as some parts were looking questionable.
BTW, each of the implementations of EmitObjC* ultimately called to
emitARCValueOperation which handled the bit cast insertion when the dest type
was not an id. I've added a check for this in the test case, and verified that
the IR looked good.
Let me know what you think. Thanks!
http://reviews.llvm.org/D14737
Files:
include/clang/Basic/ObjCRuntime.h
include/clang/Driver/Options.td
include/clang/Frontend/CodeGenOptions.def
lib/CodeGen/CGObjC.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/CodeGenModule.h
lib/Driver/Tools.cpp
lib/Frontend/CompilerInvocation.cpp
test/CodeGenObjC/convert-messages-to-runtime-calls.m
test/Driver/objc-convert-messages-to-runtime-calls.m
Index: test/Driver/objc-convert-messages-to-runtime-calls.m
===================================================================
--- /dev/null
+++ test/Driver/objc-convert-messages-to-runtime-calls.m
@@ -0,0 +1,7 @@
+// RUN: %clang %s -### -o %t.o 2>&1 -fsyntax-only -fobjc-convert-messages-to-runtime-calls -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 -fsyntax-only -fno-objc-convert-messages-to-runtime-calls -fobjc-convert-messages-to-runtime-calls -target x86_64-apple-macosx10.10.0 | FileCheck %s --check-prefix=ENABLE
+
+// Check that we pass fobjc-convert-messages-to-runtime-calls only when supported, and not explicitly disabled.
+
+// DISABLE: "-fno-objc-convert-messages-to-runtime-calls"
+// ENABLE-NOT: "-fno-objc-convert-messages-to-runtime-calls"
Index: test/CodeGenObjC/convert-messages-to-runtime-calls.m
===================================================================
--- /dev/null
+++ test/CodeGenObjC/convert-messages-to-runtime-calls.m
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -fobjc-runtime=macosx-10.10.0 -emit-llvm -o - %s -fno-objc-convert-messages-to-runtime-calls | FileCheck %s --check-prefix=MSGS
+// RUN: %clang_cc1 -fobjc-runtime=macosx-10.10.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=CALLS
+// RUN: %clang_cc1 -fobjc-runtime=macosx-10.9.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSGS
+// RUN: %clang_cc1 -fobjc-runtime=macosx-fragile-10.10.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSGS
+// Make sure we don't do calls to retain/release when using GC.
+// RUN: %clang_cc1 -fobjc-runtime=macosx-10.10.0 -emit-llvm -o - %s -fobjc-gc | FileCheck %s --check-prefix=GC
+// RUN: %clang_cc1 -fobjc-runtime=ios-8.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=CALLS
+// RUN: %clang_cc1 -fobjc-runtime=ios-7.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=MSGS
+// Note: This line below is for tvos for which the driver passes through to use the ios9.0 runtime.
+// RUN: %clang_cc1 -fobjc-runtime=ios-9.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=CALLS
+// RUN: %clang_cc1 -fobjc-runtime=watchos-2.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=CALLS
+
+@interface NSObject
++ (id)alloc;
++ (id)alloc2;
+- (id)init;
+- (id)retain;
+- (void)release;
+- (NSObject *)autorelease;
+@end
+
+@interface NSString : NSObject
+- (void)retain_self;
+- (void)retain_super;
+@end
+
+// CHECK-LABEL: define {{.*}}void @test1
+NSObject *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}}
+ // CALLS-NEXT: bitcast i8*
+ // CALLS-NEXT: ret
+ // GC: {{call.*@objc_alloc}}
+ // GC: {{call.*@objc_msgSend}}
+ // GC: {{call.*@objc_msgSend}}
+ // GC: {{call.*@objc_msgSend}}
+ [NSObject alloc];
+ [x retain];
+ [x release];
+ // The auto release returns its value here as then we can ensure we insert
+ // the bitcasts as needed.
+ return [x autorelease];
+}
+
+// CHECK-LABEL: define {{.*}}void @test2
+void test2() {
+ // MSGS: {{call.*@objc_msgSend}}
+ // CALLS: {{call.*@objc_msgSend}}
+ // GC: {{call.*@objc_msgSend}}
+ // Make sure alloc has the correct name and number of types.
+ [NSObject alloc2];
+}
+
+@implementation NSString
+
+// Make sure we can convert a message to a dynamic receiver to a call
+// CHECK-LABEL: define {{.*}}void @retain_self
+- (void)retain_self {
+ // MSGS: {{call.*@objc_msgSend}}
+ // CALLS: {{call.*@objc_retain}}
+ // GC: {{call.*@objc_msgSend}}
+ [self retain];
+}
+
+// Make sure we never convert a message to super to a call
+// CHECK-LABEL: define {{.*}}void @retain_super
+- (void)retain_super {
+ // MSGS: {{call.*@objc_msgSend}}
+ // CALLS: {{call.*@objc_msgSend}}
+ // GC: {{call.*@objc_msgSend}}
+ [super retain];
+}
+
+@end
\ No newline at end of file
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -609,6 +609,9 @@
}
}
+ if (Args.hasArg(OPT_fno_objc_convert_messages_to_runtime_calls))
+ Opts.ObjCConvertMessagesToRuntimeCalls = 0;
+
Opts.EmulatedTLS =
Args.hasFlag(OPT_femulated_tls, OPT_fno_emulated_tls, false);
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -4901,6 +4901,18 @@
}
+ // Allow the user to control whether messages can be converted to runtime
+ // functions.
+ if (types::isObjC(InputType)) {
+ auto *Arg = Args.getLastArg(
+ options::OPT_fobjc_convert_messages_to_runtime_calls,
+ options::OPT_fno_objc_convert_messages_to_runtime_calls);
+ if (Arg &&
+ Arg->getOption().matches(
+ options::OPT_fno_objc_convert_messages_to_runtime_calls))
+ CmdArgs.push_back("-fno-objc-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/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -111,7 +111,10 @@
struct ObjCEntrypoints {
ObjCEntrypoints() { memset(this, 0, sizeof(*this)); }
- /// void objc_autoreleasePoolPop(void*);
+ /// void objc_alloc(id);
+ llvm::Constant *objc_alloc;
+
+ /// void objc_autoreleasePoolPop(void*);
llvm::Constant *objc_autoreleasePoolPop;
/// void *objc_autoreleasePoolPush(void);
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -2799,6 +2799,7 @@
std::pair<LValue,llvm::Value*>
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/CGObjC.cpp
===================================================================
--- lib/CodeGen/CGObjC.cpp
+++ lib/CodeGen/CGObjC.cpp
@@ -338,6 +338,69 @@
return nullptr;
}
+/// The ObjC runtime may provide entrypoints that are likely to be faster
+/// than an ordinary message send of the appropriate selector.
+///
+/// The entrypoints are guaranteed to be equivalent to just sending the
+/// corresponding message. If the entrypoint is implemented naively as just a
+/// message send, using it is a trade-off: it sacrifices a few cycles of
+/// overhead to save a small amount of code. However, it's possible for
+/// runtimes to detect and special-case classes that use "standard"
+/// retain/release behavior; if that's dynamically a large proportion of all
+/// retained objects, using the entrypoint will also be faster than using a
+/// message send.
+///
+/// If the runtime does support a required entrypoint, then this method will
+/// generate a call and return the resulting value. Otherwise it will return
+/// None and the caller can generate a msgSend instead.
+static Optional<llvm::Value *>
+tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
+ llvm::Value *Receiver, Selector Sel,
+ const ObjCMethodDecl *method) {
+ auto &CGM = CGF.CGM;
+ if (!CGM.getCodeGenOpts().ObjCConvertMessagesToRuntimeCalls)
+ return None;
+
+ auto &Runtime = CGM.getLangOpts().ObjCRuntime;
+ switch (Sel.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.
+ if (Runtime.shouldUseRuntimeFunctionsForAlloc() && Sel.isUnarySelector() &&
+ Sel.getNameForSlot(0) == "alloc" &&
+ ResultType->isObjCObjectPointerType())
+ return CGF.EmitObjCAlloc(Receiver);
+ break;
+
+ case OMF_autorelease:
+ if (ResultType->isObjCObjectPointerType() &&
+ CGM.getLangOpts().getGC() == LangOptions::NonGC &&
+ Runtime.shouldUseARCFunctionsForRetainRelease())
+ return CGF.EmitARCAutorelease(Receiver);
+ break;
+
+ case OMF_retain:
+ if (ResultType->isObjCObjectPointerType() &&
+ CGM.getLangOpts().getGC() == LangOptions::NonGC &&
+ Runtime.shouldUseARCFunctionsForRetainRelease())
+ return CGF.EmitARCRetainNonBlock(Receiver);
+ break;
+
+ case OMF_release:
+ if (ResultType->isVoidType() &&
+ CGM.getLangOpts().getGC() == LangOptions::NonGC &&
+ Runtime.shouldUseARCFunctionsForRetainRelease()) {
+ CGF.EmitARCRelease(Receiver, ARCPreciseLifetime);
+ return nullptr;
+ }
+ break;
+ default:
+ break;
+ }
+ return None;
+}
+
RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
ReturnValueSlot Return) {
// Only the lookup mechanism and first two arguments of the method
@@ -460,10 +523,16 @@
Args,
method);
} else {
- result = Runtime.GenerateMessageSend(*this, Return, ResultType,
- E->getSelector(),
- Receiver, Args, OID,
- method);
+ // Call runtime methods directly if we can.
+ if (Optional<llvm::Value *> SpecializedResult =
+ tryGenerateSpecializedMessageSend(*this, ResultType, Receiver,
+ E->getSelector(), method)) {
+ result = RValue::get(SpecializedResult.getValue());
+ } else {
+ result = Runtime.GenerateMessageSend(*this, Return, ResultType,
+ E->getSelector(), Receiver, Args,
+ OID, method);
+ }
}
// For delegate init calls in ARC, implicitly store the result of
@@ -2333,6 +2402,13 @@
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: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -95,6 +95,8 @@
CODEGENOPT(NoZeroInitializedInBSS , 1, 0) ///< -fno-zero-initialized-in-bss.
/// \brief Method of Objective-C dispatch to use.
ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy)
+/// Replace certain message sends with calls to ObjC runtime entrypoints
+CODEGENOPT(ObjCConvertMessagesToRuntimeCalls , 1, 1)
CODEGENOPT(OmitLeafFramePointer , 1, 0) ///< Set when -momit-leaf-frame-pointer is
///< enabled.
VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified.
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -892,6 +892,10 @@
def fobjc_arc : Flag<["-"], "fobjc-arc">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Synthesize retain and release calls for Objective-C pointers">;
def fno_objc_arc : Flag<["-"], "fno-objc-arc">, Group<f_Group>;
+def fobjc_convert_messages_to_runtime_calls :
+ Flag<["-"], "fobjc-convert-messages-to-runtime-calls">, Group<f_Group>;
+def fno_objc_convert_messages_to_runtime_calls :
+ Flag<["-"], "fno-objc-convert-messages-to-runtime-calls">, Group<f_Group>, Flags<[CC1Option]>;
def fobjc_arc_exceptions : Flag<["-"], "fobjc-arc-exceptions">, Group<f_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<f_Group>;
Index: include/clang/Basic/ObjCRuntime.h
===================================================================
--- include/clang/Basic/ObjCRuntime.h
+++ include/clang/Basic/ObjCRuntime.h
@@ -171,6 +171,79 @@
llvm_unreachable("bad kind");
}
+ /// Does this runtime provide ARC entrypoints that are likely to be faster
+ /// than an ordinary message send of the appropriate selector?
+ ///
+ /// The ARC entrypoints are guaranteed to be equivalent to just sending the
+ /// corresponding message. If the entrypoint is implemented naively as just a
+ /// message send, using it is a trade-off: it sacrifices a few cycles of
+ /// overhead to save a small amount of code. However, it's possible for
+ /// runtimes to detect and special-case classes that use "standard"
+ /// retain/release behavior; if that's dynamically a large proportion of all
+ /// retained objects, using the entrypoint will also be faster than using a
+ /// message send.
+ ///
+ /// When this method returns true, Clang will turn non-super message sends of
+ /// certain selectors into calls to the correspond entrypoint:
+ /// retain => objc_retain
+ /// release => objc_release
+ 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");
+ }
+
+ /// Does this runtime provide entrypoints that are likely to be faster
+ /// than an ordinary message send of the "alloc" selector?
+ ///
+ /// The "alloc" entrypoint is guaranteed to be equivalent to just sending the
+ /// corresponding message. If the entrypoint is implemented naively as just a
+ /// message send, using it is a trade-off: it sacrifices a few cycles of
+ /// overhead to save a small amount of code. However, it's possible for
+ /// runtimes to detect and special-case classes that use "standard"
+ /// alloc behavior; if that's dynamically a large proportion of all
+ /// objects, using the entrypoint will also be faster than using a message
+ /// send.
+ ///
+ /// When this method returns true, Clang will turn non-super message sends of
+ /// certain selectors into calls to the correspond entrypoint:
+ /// alloc => objc_alloc
+ bool shouldUseRuntimeFunctionsForAlloc() 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()) {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits