diff options
author | Erik Pilkington <erik.pilkington@gmail.com> | 2019-02-14 19:58:37 +0000 |
---|---|---|
committer | Erik Pilkington <erik.pilkington@gmail.com> | 2019-02-14 19:58:37 +0000 |
commit | ed91c1fa86780ee9ce2a02031dbcef3176174a76 (patch) | |
tree | 5c83cb20af15ed396ada928f50d231e1471b3b30 | |
parent | 893b8523f1e00a965d91fef0358dfc96af865bac (diff) | |
download | clang-ed91c1fa86780ee9ce2a02031dbcef3176174a76.tar.gz |
[CodeGenObjC] Emit [[X alloc] init] as objc_alloc_init(X) when available
This provides a code size win on the caller side, since the init
message send is done in the runtime function.
rdar://44987038
Differential revision: https://reviews.llvm.org/D57936
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@354056 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/ObjCRuntime.h | 16 | ||||
-rw-r--r-- | lib/CodeGen/CGObjC.cpp | 44 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 2 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.h | 3 | ||||
-rw-r--r-- | test/CodeGenObjC/objc-alloc-init.m | 28 |
5 files changed, 93 insertions, 0 deletions
diff --git a/include/clang/Basic/ObjCRuntime.h b/include/clang/Basic/ObjCRuntime.h index 921df689e4..fc87f20d56 100644 --- a/include/clang/Basic/ObjCRuntime.h +++ b/include/clang/Basic/ObjCRuntime.h @@ -246,6 +246,22 @@ public: llvm_unreachable("bad kind"); } + /// Does this runtime provide the objc_alloc_init entrypoint? This can apply + /// the same optimization as objc_alloc, but also sends an -init message, + /// reducing code size on the caller. + bool shouldUseRuntimeFunctionForCombinedAllocInit() const { + switch (getKind()) { + case MacOSX: + return getVersion() >= VersionTuple(10, 14, 4); + case iOS: + return getVersion() >= VersionTuple(12, 2); + case WatchOS: + return getVersion() >= VersionTuple(5, 2); + default: + return false; + } + } + /// Does this runtime supports optimized setter entrypoints? bool hasOptimizedSetter() const { switch (getKind()) { diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 785a12665b..1b3e71b436 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -422,6 +422,40 @@ tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType, return None; } +/// Instead of '[[MyClass alloc] init]', try to generate +/// 'objc_alloc_init(MyClass)'. This provides a code size improvement on the +/// caller side, as well as the optimized objc_alloc. +static Optional<llvm::Value *> +tryEmitSpecializedAllocInit(CodeGenFunction &CGF, const ObjCMessageExpr *OME) { + auto &Runtime = CGF.getLangOpts().ObjCRuntime; + if (!Runtime.shouldUseRuntimeFunctionForCombinedAllocInit()) + return None; + + // Match the exact pattern '[[MyClass alloc] init]'. + Selector Sel = OME->getSelector(); + if (!OME->isInstanceMessage() || !OME->getType()->isObjCObjectPointerType() || + !Sel.isUnarySelector() || Sel.getNameForSlot(0) != "init") + return None; + + // Okay, this is '[receiver init]', check if 'receiver' is '[cls alloc]'. + auto *SubOME = + dyn_cast<ObjCMessageExpr>(OME->getInstanceReceiver()->IgnoreParens()); + if (!SubOME) + return None; + Selector SubSel = SubOME->getSelector(); + if (SubOME->getReceiverKind() != ObjCMessageExpr::Class || + !SubOME->getType()->isObjCObjectPointerType() || + !SubSel.isUnarySelector() || SubSel.getNameForSlot(0) != "alloc") + return None; + + QualType ReceiverType = SubOME->getClassReceiver(); + const ObjCObjectType *ObjTy = ReceiverType->getAs<ObjCObjectType>(); + const ObjCInterfaceDecl *ID = ObjTy->getInterface(); + assert(ID && "null interface should be impossible here"); + llvm::Value *Receiver = CGF.CGM.getObjCRuntime().GetClass(CGF, ID); + return CGF.EmitObjCAllocInit(Receiver, CGF.ConvertType(OME->getType())); +} + RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, ReturnValueSlot Return) { // Only the lookup mechanism and first two arguments of the method @@ -443,6 +477,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, } } + if (Optional<llvm::Value *> Val = tryEmitSpecializedAllocInit(*this, E)) + return AdjustObjCObjectType(*this, E->getType(), RValue::get(*Val)); + // We don't retain the receiver in delegate init calls, and this is // safe because the receiver value is always loaded from 'self', // which we zero out. We don't want to Block_copy block receivers, @@ -2503,6 +2540,13 @@ llvm::Value *CodeGenFunction::EmitObjCAllocWithZone(llvm::Value *value, "objc_allocWithZone", /*MayThrow=*/true); } +llvm::Value *CodeGenFunction::EmitObjCAllocInit(llvm::Value *value, + llvm::Type *resultType) { + return emitObjCValueOperation(*this, value, resultType, + CGM.getObjCEntrypoints().objc_alloc_init, + "objc_alloc_init", /*MayThrow=*/true); +} + /// Produce the code to do a primitive release. /// [tmp drain]; void CodeGenFunction::EmitObjCMRRAutoreleasePoolPop(llvm::Value *Arg) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 9452de899f..37b62470af 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -3831,6 +3831,8 @@ public: llvm::Type *returnType); llvm::Value *EmitObjCAllocWithZone(llvm::Value *value, llvm::Type *returnType); + llvm::Value *EmitObjCAllocInit(llvm::Value *value, llvm::Type *resultType); + llvm::Value *EmitObjCThrowOperand(const Expr *expr); llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr); llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 4139a0e031..a652f143d0 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -124,6 +124,9 @@ struct ObjCEntrypoints { /// void objc_allocWithZone(id); llvm::FunctionCallee objc_allocWithZone; + /// void objc_alloc_init(id); + llvm::FunctionCallee objc_alloc_init; + /// void objc_autoreleasePoolPop(void*); llvm::FunctionCallee objc_autoreleasePoolPop; diff --git a/test/CodeGenObjC/objc-alloc-init.m b/test/CodeGenObjC/objc-alloc-init.m new file mode 100644 index 0000000000..e56303c7fe --- /dev/null +++ b/test/CodeGenObjC/objc-alloc-init.m @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 %s -fobjc-runtime=macosx-10.14.4 -emit-llvm -O0 -o - | FileCheck %s --check-prefix=OPTIMIZED --check-prefix=EITHER +// RUN: %clang_cc1 %s -fobjc-runtime=macosx-10.14.3 -emit-llvm -O0 -o - | FileCheck %s --check-prefix=NOT_OPTIMIZED --check-prefix=EITHER +// RUN: %clang_cc1 %s -fobjc-runtime=ios-12.2 -emit-llvm -O0 -o - | FileCheck %s --check-prefix=OPTIMIZED --check-prefix=EITHER +// RUN: %clang_cc1 %s -fobjc-runtime=ios-12.1 -emit-llvm -O0 -o - | FileCheck %s --check-prefix=NOT_OPTIMIZED --check-prefix=EITHER + +@interface X ++(X *)alloc; +-(X *)init; +@end + +void f() { + [[X alloc] init]; + // OPTIMIZED: call i8* @objc_alloc_init( + // NOT_OPTIMIZED: call i8* @objc_alloc( +} + +@interface Y : X ++(void)meth; +@end + +@implementation Y ++(void)meth { + [[self alloc] init]; + // EITHER-NOT: call i8* @objc_alloc + // EITHER: call {{.*}} @objc_msgSend + // EITHER: call {{.*}} @objc_msgSend +} +@end |