Index: include/clang/Basic/ObjCRuntime.h =================================================================== --- include/clang/Basic/ObjCRuntime.h +++ include/clang/Basic/ObjCRuntime.h @@ -173,6 +173,42 @@ 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? /// Index: lib/CodeGen/CGObjC.cpp =================================================================== --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -396,6 +396,29 @@ } break; + case OMF_autorelease: + if (ResultType->isObjCObjectPointerType() && + CGM.getLangOpts().getGC() == LangOptions::NonGC && + Runtime.shouldUseARCFunctionsForRetainRelease()) + return CGF.EmitObjCAutorelease(Receiver, CGF.ConvertType(ResultType)); + break; + + case OMF_retain: + if (ResultType->isObjCObjectPointerType() && + CGM.getLangOpts().getGC() == LangOptions::NonGC && + Runtime.shouldUseARCFunctionsForRetainRelease()) + return CGF.EmitObjCRetainNonBlock(Receiver, CGF.ConvertType(ResultType)); + break; + + case OMF_release: + if (ResultType->isVoidType() && + CGM.getLangOpts().getGC() == LangOptions::NonGC && + Runtime.shouldUseARCFunctionsForRetainRelease()) { + CGF.EmitObjCRelease(Receiver, ARCPreciseLifetime); + return nullptr; + } + break; + default: break; } @@ -2005,6 +2028,11 @@ llvm::FunctionType *fnType = llvm::FunctionType::get(CGF.Int8PtrTy, CGF.Int8PtrTy, false); fn = CGF.CGM.CreateRuntimeFunction(fnType, fnName); + + // We have Native ARC, so set nonlazybind attribute for performance + if (llvm::Function *f = dyn_cast(fn)) + if (fnName == "objc_retain") + f->addFnAttr(llvm::Attribute::NonLazyBind); } // Cast the argument to 'id'. @@ -2509,6 +2537,55 @@ CGF.EmitARCIntrinsicUse(value); } +/// Autorelease the given object. +/// call i8* \@objc_autorelease(i8* %value) +llvm::Value *CodeGenFunction::EmitObjCAutorelease(llvm::Value *value, + llvm::Type *returnType) { + return emitObjCValueOperation(*this, value, returnType, + CGM.getObjCEntrypoints().objc_autoreleaseRuntimeFunction, + "objc_autorelease"); +} + +/// Retain the given object, with normal retain semantics. +/// call i8* \@objc_retain(i8* %value) +llvm::Value *CodeGenFunction::EmitObjCRetainNonBlock(llvm::Value *value, + llvm::Type *returnType) { + return emitObjCValueOperation(*this, value, returnType, + CGM.getObjCEntrypoints().objc_retainRuntimeFunction, + "objc_retain"); +} + +/// Release the given object. +/// call void \@objc_release(i8* %value) +void CodeGenFunction::EmitObjCRelease(llvm::Value *value, + ARCPreciseLifetime_t precise) { + if (isa(value)) return; + + llvm::Constant *&fn = CGM.getObjCEntrypoints().objc_release; + if (!fn) { + if (!fn) { + llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), Int8PtrTy, false); + fn = CGM.CreateRuntimeFunction(fnType, "objc_release"); + setARCRuntimeFunctionLinkage(CGM, fn); + // We have Native ARC, so set nonlazybind attribute for performance + if (llvm::Function *f = dyn_cast(fn)) + f->addFnAttr(llvm::Attribute::NonLazyBind); + } + } + + // Cast the argument to 'id'. + value = Builder.CreateBitCast(value, Int8PtrTy); + + // Call objc_release. + llvm::CallInst *call = EmitNounwindRuntimeCall(fn, value); + + if (precise == ARCImpreciseLifetime) { + call->setMetadata("clang.imprecise_release", + llvm::MDNode::get(Builder.getContext(), None)); + } +} + namespace { struct CallObjCAutoreleasePoolObject final : EHScopeStack::Cleanup { llvm::Value *Token; Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -3798,6 +3798,11 @@ llvm::Value *EmitARCRetainAutoreleasedReturnValue(llvm::Value *value); llvm::Value *EmitARCUnsafeClaimAutoreleasedReturnValue(llvm::Value *value); + llvm::Value *EmitObjCAutorelease(llvm::Value *value, llvm::Type *returnType); + llvm::Value *EmitObjCRetainNonBlock(llvm::Value *value, + llvm::Type *returnType); + void EmitObjCRelease(llvm::Value *value, ARCPreciseLifetime_t precise); + std::pair EmitARCStoreAutoreleasing(const BinaryOperator *e); std::pair Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -138,6 +138,10 @@ /// id objc_autorelease(id); llvm::Constant *objc_autorelease; + /// id objc_autorelease(id); + /// Note this is the runtime method not the intrinsic + llvm::Constant *objc_autoreleaseRuntimeFunction; + /// id objc_autoreleaseReturnValue(id); llvm::Constant *objc_autoreleaseReturnValue; @@ -162,6 +166,10 @@ /// id objc_retain(id); llvm::Constant *objc_retain; + /// id objc_retain(id); + /// Note this is the runtime method not the intrinsic + llvm::Constant *objc_retainRuntimeFunction; + /// id objc_retainAutorelease(id); llvm::Constant *objc_retainAutorelease; @@ -177,6 +185,10 @@ /// void objc_release(id); llvm::Constant *objc_release; + /// void objc_release(id); + /// Note this is the runtime method not the intrinsic + llvm::Constant *objc_releaseRuntimeFunction; + /// void objc_storeStrong(id*, id); llvm::Constant *objc_storeStrong; Index: test/CodeGenObjC/convert-messages-to-runtime-calls.m =================================================================== --- test/CodeGenObjC/convert-messages-to-runtime-calls.m +++ test/CodeGenObjC/convert-messages-to-runtime-calls.m @@ -14,16 +14,28 @@ + (id)alloc; + (id)allocWithZone:(void*)zone; + (id)alloc2; +- (id)retain; +- (void)release; +- (id)autorelease; @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}} // MSGS: {{call.*@objc_msgSend}} // CALLS: {{call.*@objc_alloc}} // CALLS: {{call.*@objc_allocWithZone}} + // CALLS: {{call.*@objc_retain}} + // CALLS: {{call.*@objc_release}} + // CALLS: {{call.*@objc_autorelease}} [NSObject alloc]; [NSObject allocWithZone:nil]; + [x retain]; + [x release]; + [x autorelease]; } // CHECK-LABEL: define {{.*}}void @test2 @@ -43,6 +55,8 @@ @interface B + (A*) alloc; + (A*)allocWithZone:(void*)zone; +- (A*) retain; +- (A*) autorelease; @end // Make sure we get a bitcast on the return type as the @@ -65,9 +79,30 @@ return [B allocWithZone:nil]; } +// 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 + (id)allocWithZone:(int)intArg; +- (float) retain; @end // Make sure we only accept pointer types @@ -78,3 +113,37 @@ return [C allocWithZone:3]; } +// 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}} + return [c retain]; +} + +@interface NSString : NSObject ++ (void)retain_self; +- (void)retain_super; +@end + +@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}} + [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}} + [super retain]; +} + +@end +