forked from OSchip/llvm-project
Convert some ObjC msgSends to runtime calls.
It is faster to directly call the ObjC runtime for methods such as retain/release instead of sending a message to those functions. This patch adds support for converting messages to retain/release/alloc/autorelease to their equivalent runtime calls. Tests included for the positive case of applying this transformation, negative tests that we ensure we only convert "alloc" to objc_alloc, not "alloc2", and also a driver test to ensure we enable this only for supported runtime versions. Reviewed by John McCall. Differential Revision: http://reviews.llvm.org/D14737 llvm-svn: 263607
This commit is contained in:
parent
f7cb16f7de
commit
be6c750a8e
|
@ -171,6 +171,79 @@ public:
|
|||
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()) {
|
||||
|
|
|
@ -933,6 +933,10 @@ def fno_zero_initialized_in_bss : Flag<["-"], "fno-zero-initialized-in-bss">, Gr
|
|||
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>;
|
||||
|
|
|
@ -99,6 +99,8 @@ CODEGENOPT(NoNaNsFPMath , 1, 0) ///< Assume FP arguments, results not NaN.
|
|||
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.
|
||||
|
|
|
@ -338,6 +338,69 @@ static const Expr *findWeakLValue(const Expr *E) {
|
|||
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, CGF.ConvertType(ResultType));
|
||||
break;
|
||||
|
||||
case OMF_autorelease:
|
||||
if (ResultType->isObjCObjectPointerType() &&
|
||||
CGM.getLangOpts().getGC() == LangOptions::NonGC &&
|
||||
Runtime.shouldUseARCFunctionsForRetainRelease())
|
||||
return CGF.EmitARCAutorelease(Receiver, CGF.ConvertType(ResultType));
|
||||
break;
|
||||
|
||||
case OMF_retain:
|
||||
if (ResultType->isObjCObjectPointerType() &&
|
||||
CGM.getLangOpts().getGC() == LangOptions::NonGC &&
|
||||
Runtime.shouldUseARCFunctionsForRetainRelease())
|
||||
return CGF.EmitARCRetainNonBlock(Receiver, CGF.ConvertType(ResultType));
|
||||
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 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
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
|
||||
|
@ -1814,6 +1883,7 @@ static llvm::Constant *createARCRuntimeFunction(CodeGenModule &CGM,
|
|||
/// where a null input causes a no-op and returns null.
|
||||
static llvm::Value *emitARCValueOperation(CodeGenFunction &CGF,
|
||||
llvm::Value *value,
|
||||
llvm::Type *returnType,
|
||||
llvm::Constant *&fn,
|
||||
StringRef fnName,
|
||||
bool isTailCall = false) {
|
||||
|
@ -1826,7 +1896,7 @@ static llvm::Value *emitARCValueOperation(CodeGenFunction &CGF,
|
|||
}
|
||||
|
||||
// Cast the argument to 'id'.
|
||||
llvm::Type *origType = value->getType();
|
||||
llvm::Type *origType = returnType ? returnType : value->getType();
|
||||
value = CGF.Builder.CreateBitCast(value, CGF.Int8PtrTy);
|
||||
|
||||
// Call the function.
|
||||
|
@ -1931,8 +2001,9 @@ llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) {
|
|||
|
||||
/// Retain the given object, with normal retain semantics.
|
||||
/// call i8* \@objc_retain(i8* %value)
|
||||
llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) {
|
||||
return emitARCValueOperation(*this, value,
|
||||
llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value,
|
||||
llvm::Type *returnType) {
|
||||
return emitARCValueOperation(*this, value, returnType,
|
||||
CGM.getObjCEntrypoints().objc_retain,
|
||||
"objc_retain");
|
||||
}
|
||||
|
@ -1946,7 +2017,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) {
|
|||
llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value,
|
||||
bool mandatory) {
|
||||
llvm::Value *result
|
||||
= emitARCValueOperation(*this, value,
|
||||
= emitARCValueOperation(*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_retainBlock,
|
||||
"objc_retainBlock");
|
||||
|
||||
|
@ -2016,7 +2087,7 @@ static void emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) {
|
|||
llvm::Value *
|
||||
CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) {
|
||||
emitAutoreleasedReturnValueMarker(*this);
|
||||
return emitARCValueOperation(*this, value,
|
||||
return emitARCValueOperation(*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_retainAutoreleasedReturnValue,
|
||||
"objc_retainAutoreleasedReturnValue");
|
||||
}
|
||||
|
@ -2031,7 +2102,7 @@ CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) {
|
|||
llvm::Value *
|
||||
CodeGenFunction::EmitARCUnsafeClaimAutoreleasedReturnValue(llvm::Value *value) {
|
||||
emitAutoreleasedReturnValueMarker(*this);
|
||||
return emitARCValueOperation(*this, value,
|
||||
return emitARCValueOperation(*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_unsafeClaimAutoreleasedReturnValue,
|
||||
"objc_unsafeClaimAutoreleasedReturnValue");
|
||||
}
|
||||
|
@ -2145,8 +2216,9 @@ llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst,
|
|||
|
||||
/// Autorelease the given object.
|
||||
/// call i8* \@objc_autorelease(i8* %value)
|
||||
llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) {
|
||||
return emitARCValueOperation(*this, value,
|
||||
llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value,
|
||||
llvm::Type *resultType) {
|
||||
return emitARCValueOperation(*this, value, resultType,
|
||||
CGM.getObjCEntrypoints().objc_autorelease,
|
||||
"objc_autorelease");
|
||||
}
|
||||
|
@ -2155,7 +2227,7 @@ llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) {
|
|||
/// call i8* \@objc_autoreleaseReturnValue(i8* %value)
|
||||
llvm::Value *
|
||||
CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) {
|
||||
return emitARCValueOperation(*this, value,
|
||||
return emitARCValueOperation(*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_autoreleaseReturnValue,
|
||||
"objc_autoreleaseReturnValue",
|
||||
/*isTailCall*/ true);
|
||||
|
@ -2165,7 +2237,7 @@ CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) {
|
|||
/// call i8* \@objc_retainAutoreleaseReturnValue(i8* %value)
|
||||
llvm::Value *
|
||||
CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) {
|
||||
return emitARCValueOperation(*this, value,
|
||||
return emitARCValueOperation(*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_retainAutoreleaseReturnValue,
|
||||
"objc_retainAutoreleaseReturnValue",
|
||||
/*isTailCall*/ true);
|
||||
|
@ -2194,7 +2266,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type,
|
|||
/// call i8* \@objc_retainAutorelease(i8* %value)
|
||||
llvm::Value *
|
||||
CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) {
|
||||
return emitARCValueOperation(*this, value,
|
||||
return emitARCValueOperation(*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_retainAutorelease,
|
||||
"objc_retainAutorelease");
|
||||
}
|
||||
|
@ -2338,6 +2410,15 @@ llvm::Value *CodeGenFunction::EmitObjCMRRAutoreleasePoolPush() {
|
|||
return InitRV.getScalarVal();
|
||||
}
|
||||
|
||||
/// Allocate the given objc object.
|
||||
/// call i8* \@objc_alloc(i8* %value)
|
||||
llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value,
|
||||
llvm::Type *resultType) {
|
||||
return emitARCValueOperation(*this, value, resultType,
|
||||
CGM.getObjCEntrypoints().objc_alloc,
|
||||
"objc_alloc");
|
||||
}
|
||||
|
||||
/// Produce the code to do a primitive release.
|
||||
/// [tmp drain];
|
||||
void CodeGenFunction::EmitObjCMRRAutoreleasePoolPop(llvm::Value *Arg) {
|
||||
|
|
|
@ -2821,11 +2821,13 @@ public:
|
|||
llvm::Value *EmitARCStoreStrongCall(Address addr, llvm::Value *value,
|
||||
bool resultIgnored);
|
||||
llvm::Value *EmitARCRetain(QualType type, llvm::Value *value);
|
||||
llvm::Value *EmitARCRetainNonBlock(llvm::Value *value);
|
||||
llvm::Value *EmitARCRetainNonBlock(llvm::Value *value,
|
||||
llvm::Type *returnType = nullptr);
|
||||
llvm::Value *EmitARCRetainBlock(llvm::Value *value, bool mandatory);
|
||||
void EmitARCDestroyStrong(Address addr, ARCPreciseLifetime_t precise);
|
||||
void EmitARCRelease(llvm::Value *value, ARCPreciseLifetime_t precise);
|
||||
llvm::Value *EmitARCAutorelease(llvm::Value *value);
|
||||
llvm::Value *EmitARCAutorelease(llvm::Value *value,
|
||||
llvm::Type *returnType = nullptr);
|
||||
llvm::Value *EmitARCAutoreleaseReturnValue(llvm::Value *value);
|
||||
llvm::Value *EmitARCRetainAutoreleaseReturnValue(llvm::Value *value);
|
||||
llvm::Value *EmitARCRetainAutoreleasedReturnValue(llvm::Value *value);
|
||||
|
@ -2838,6 +2840,8 @@ public:
|
|||
std::pair<LValue,llvm::Value*>
|
||||
EmitARCStoreUnsafeUnretained(const BinaryOperator *e, bool ignored);
|
||||
|
||||
llvm::Value *EmitObjCAlloc(llvm::Value *value,
|
||||
llvm::Type *returnType = nullptr);
|
||||
llvm::Value *EmitObjCThrowOperand(const Expr *expr);
|
||||
llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr);
|
||||
llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr);
|
||||
|
|
|
@ -113,7 +113,10 @@ struct OrderGlobalInits {
|
|||
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);
|
||||
|
|
|
@ -5284,6 +5284,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -707,6 +707,9 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
// 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;
|
||||
- (id)autorelease;
|
||||
@end
|
||||
|
||||
@interface NSString : NSObject
|
||||
+ (void)retain_self;
|
||||
- (void)retain_super;
|
||||
@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}}
|
||||
// GC: {{call.*@objc_alloc}}
|
||||
// GC: {{call.*@objc_msgSend}}
|
||||
// GC: {{call.*@objc_msgSend}}
|
||||
// GC: {{call.*@objc_msgSend}}
|
||||
[NSObject alloc];
|
||||
[x retain];
|
||||
[x release];
|
||||
[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];
|
||||
}
|
||||
|
||||
@class A;
|
||||
@interface B
|
||||
+ (A*) alloc;
|
||||
- (A*) retain;
|
||||
- (A*) autorelease;
|
||||
@end
|
||||
|
||||
// Make sure we get a bitcast on the return type as the
|
||||
// call will return i8* which we have to cast to A*
|
||||
// CHECK-LABEL: define {{.*}}void @test_alloc_class_ptr
|
||||
A* test_alloc_class_ptr() {
|
||||
// CALLS: {{call.*@objc_alloc}}
|
||||
// CALLS-NEXT: bitcast i8*
|
||||
// CALLS-NEXT: ret
|
||||
return [B alloc];
|
||||
}
|
||||
|
||||
// Make sure we get a bitcast on the return type as the
|
||||
// call will return i8* which we have to cast to A*
|
||||
// CHECK-LABEL: define {{.*}}void @test_retain_class_ptr
|
||||
A* test_retain_class_ptr(B *b) {
|
||||
// CALLS: {{call.*@objc_retain}}
|
||||
// CALLS-NEXT: bitcast i8*
|
||||
// CALLS-NEXT: ret
|
||||
return [b retain];
|
||||
}
|
||||
|
||||
// Make sure we get a bitcast on the return type as the
|
||||
// call will return i8* which we have to cast to A*
|
||||
// CHECK-LABEL: define {{.*}}void @test_autorelease_class_ptr
|
||||
A* test_autorelease_class_ptr(B *b) {
|
||||
// CALLS: {{call.*@objc_autorelease}}
|
||||
// CALLS-NEXT: bitcast i8*
|
||||
// CALLS-NEXT: ret
|
||||
return [b autorelease];
|
||||
}
|
||||
|
||||
@interface C
|
||||
- (float) retain;
|
||||
@end
|
||||
|
||||
// Make sure we use a message and not a call as the return type is
|
||||
// not a pointer type.
|
||||
// CHECK-LABEL: define {{.*}}void @test_cannot_message_return_float
|
||||
float test_cannot_message_return_float(C *c) {
|
||||
// MSGS: {{call.*@objc_msgSend}}
|
||||
// CALLS: {{call.*@objc_msgSend}}
|
||||
// GC: {{call.*@objc_msgSend}}
|
||||
return [c retain];
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
|
|
@ -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"
|
Loading…
Reference in New Issue