From dad52660449aca2ba77e61a06afcc9cb52fd44fd Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Sat, 15 Apr 2017 05:31:35 +0000 Subject: [PATCH] [ObjC] Use empty Objective-C collection literal constants when available. Original patch by Douglas Gregor with minor modifications. rdar://problem/20689633 llvm-svn: 300389 --- clang/include/clang/Basic/ObjCRuntime.h | 14 ++++++ clang/lib/CodeGen/CGObjC.cpp | 16 ++++++- clang/lib/Sema/SemaExprCXX.cpp | 12 +++++ clang/test/CodeGenObjC/empty-collection-literals.m | 51 ++++++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 clang/test/CodeGenObjC/empty-collection-literals.m diff --git a/clang/include/clang/Basic/ObjCRuntime.h b/clang/include/clang/Basic/ObjCRuntime.h index 78fc899..8dc259c 100644 --- a/clang/include/clang/Basic/ObjCRuntime.h +++ b/clang/include/clang/Basic/ObjCRuntime.h @@ -326,6 +326,20 @@ public: } } + /// Are the empty collection symbols available? + bool hasEmptyCollections() const { + switch (getKind()) { + default: + return false; + case MacOSX: + return getVersion() >= VersionTuple(10, 11); + case iOS: + return getVersion() >= VersionTuple(9); + case WatchOS: + return getVersion() >= VersionTuple(2); + } + } + /// \brief Try to parse an Objective-C runtime specification from the given /// string. /// diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index c011ab4..067718e 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -117,10 +117,22 @@ llvm::Value *CodeGenFunction::EmitObjCCollectionLiteral(const Expr *E, const ObjCArrayLiteral *ALE = dyn_cast(E); if (!ALE) DLE = cast(E); - - // Compute the type of the array we're initializing. + + // Optimize empty collections by referencing constants, when available. uint64_t NumElements = ALE ? ALE->getNumElements() : DLE->getNumElements(); + if (NumElements == 0 && CGM.getLangOpts().ObjCRuntime.hasEmptyCollections()) { + StringRef ConstantName = ALE ? "__NSArray0__" : "__NSDictionary0__"; + QualType IdTy(CGM.getContext().getObjCIdType()); + llvm::Constant *Constant = + CGM.CreateRuntimeVariable(ConvertType(IdTy), ConstantName); + LValue LV = LValue::MakeAddr(Constant, IdTy, + Context.getTypeAlignInChars(IdTy), Context); + return Builder.CreateBitCast(EmitLoadOfScalar(LV, E->getLocStart()), + ConvertType(E->getType())); + } + + // Compute the type of the array we're initializing. llvm::APInt APNumElements(Context.getTypeSize(Context.getSizeType()), NumElements); QualType ElementType = Context.getObjCIdType().withConst(); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 485d012..d65570f 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5980,9 +5980,21 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { } else if (ObjCBoxedExpr *BoxedExpr = dyn_cast(E)) { D = BoxedExpr->getBoxingMethod(); } else if (ObjCArrayLiteral *ArrayLit = dyn_cast(E)) { + // Don't do reclaims if we're using the zero-element array + // constant. + if (ArrayLit->getNumElements() == 0 && + Context.getLangOpts().ObjCRuntime.hasEmptyCollections()) + return E; + D = ArrayLit->getArrayWithObjectsMethod(); } else if (ObjCDictionaryLiteral *DictLit = dyn_cast(E)) { + // Don't do reclaims if we're using the zero-element dictionary + // constant. + if (DictLit->getNumElements() == 0 && + Context.getLangOpts().ObjCRuntime.hasEmptyCollections()) + return E; + D = DictLit->getDictWithObjectsMethod(); } diff --git a/clang/test/CodeGenObjC/empty-collection-literals.m b/clang/test/CodeGenObjC/empty-collection-literals.m new file mode 100644 index 0000000..0f9715f --- /dev/null +++ b/clang/test/CodeGenObjC/empty-collection-literals.m @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -I %S/Inputs -triple x86_64-apple-macosx10.10.0 -fobjc-runtime=macosx-10.10.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITHOUT-EMPTY-COLLECTIONS %s +// RUN: %clang_cc1 -I %S/Inputs -triple x86_64-apple-macosx10.11.0 -fobjc-runtime=macosx-10.11.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITH-EMPTY-COLLECTIONS %s + +// RUN: %clang_cc1 -I %S/Inputs -triple arm64-apple-ios8.0 -fobjc-runtime=ios-8.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITHOUT-EMPTY-COLLECTIONS %s +// RUN: %clang_cc1 -I %S/Inputs -triple arm64-apple-ios9.0 -fobjc-runtime=ios-9.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITH-EMPTY-COLLECTIONS %s + +// RUN: %clang_cc1 -I %S/Inputs -triple armv7k-apple-watchos2.0 -fobjc-runtime=watchos-1.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITHOUT-EMPTY-COLLECTIONS %s +// RUN: %clang_cc1 -I %S/Inputs -triple armv7k-apple-watchos2.0 -fobjc-runtime=watchos-2.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITH-EMPTY-COLLECTIONS %s + +// RUN: %clang_cc1 -I %S/Inputs -triple arm64-apple-tvos8.0 -fobjc-runtime=ios-8.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITHOUT-EMPTY-COLLECTIONS %s +// RUN: %clang_cc1 -I %S/Inputs -triple arm64-apple-tvos9.0 -fobjc-runtime=ios-9.0 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-WITH-EMPTY-COLLECTIONS %s + +#include "literal-support.h" + +void test_empty_array() { + // CHECK-WITHOUT-EMPTY-COLLECTIONS-LABEL: define void @test_empty_array + // CHECK-WITHOUT-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITHOUT-EMPTY-COLLECTIONS: {{call.*objc_msgSend}} + // CHECK-WITHOUT-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITHOUT-EMPTY-COLLECTIONS: {{call.*objc_retainAutoreleasedReturnValue}} + // CHECK-WITHOUT-EMPTY-COLLECTIONS: ret void + + // CHECK-WITH-EMPTY-COLLECTIONS-LABEL: define void @test_empty_array + // CHECK-WITH-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITH-EMPTY-COLLECTIONS: load {{.*}} @__NSArray0__ + // CHECK-WITH-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITH-EMPTY-COLLECTIONS: {{call.*objc_retain\(}} + // CHECK-WITH-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITH-EMPTY-COLLECTIONS: call void @objc_storeStrong + // CHECK-WITH-EMPTY-COLLECTIONS-NEXT: ret void + NSArray *arr = @[]; +} + +void test_empty_dictionary() { + // CHECK-WITHOUT-EMPTY-COLLECTIONS-LABEL: define void @test_empty_dictionary + // CHECK-WITHOUT-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITHOUT-EMPTY-COLLECTIONS: {{call.*objc_msgSend}} + // CHECK-WITHOUT-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITHOUT-EMPTY-COLLECTIONS: {{call.*objc_retainAutoreleasedReturnValue}} + // CHECK-WITHOUT-EMPTY-COLLECTIONS: ret void + + // CHECK-WITH-EMPTY-COLLECTIONS-LABEL: define void @test_empty_dictionary + // CHECK-WITH-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITH-EMPTY-COLLECTIONS: load {{.*}} @__NSDictionary0__ + // CHECK-WITH-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITH-EMPTY-COLLECTIONS: {{call.*objc_retain\(}} + // CHECK-WITH-EMPTY-COLLECTIONS-NOT: ret void + // CHECK-WITH-EMPTY-COLLECTIONS: call void @objc_storeStrong + // CHECK-WITH-EMPTY-COLLECTIONS-NEXT: ret void + NSDictionary *dict = @{}; +} -- 2.7.4