summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Pilkington <erik.pilkington@gmail.com>2019-02-14 19:58:37 +0000
committerErik Pilkington <erik.pilkington@gmail.com>2019-02-14 19:58:37 +0000
commited91c1fa86780ee9ce2a02031dbcef3176174a76 (patch)
tree5c83cb20af15ed396ada928f50d231e1471b3b30
parent893b8523f1e00a965d91fef0358dfc96af865bac (diff)
downloadclang-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.h16
-rw-r--r--lib/CodeGen/CGObjC.cpp44
-rw-r--r--lib/CodeGen/CodeGenFunction.h2
-rw-r--r--lib/CodeGen/CodeGenModule.h3
-rw-r--r--test/CodeGenObjC/objc-alloc-init.m28
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