From 23290b0570e085739f7e783dabefc5fef120657a Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Thu, 1 Nov 2012 18:32:55 +0000 Subject: [PATCH] objective-C block meta-data. This patch completes meta-data generation for captured block variables in arc mode. This includes inlined version of the meta-data when it can be done. It also includes severat tests. This is wip. // rdar://12184410. llvm-svn: 167241 --- clang/lib/CodeGen/CGBlocks.cpp | 9 +- clang/lib/CodeGen/CGBlocks.h | 5 + clang/lib/CodeGen/CGObjCMac.cpp | 178 ++++++++- clang/test/CodeGenObjC/arc-block-ivar-layout.m | 60 --- clang/test/CodeGenObjC/arc-blocks.m | 16 +- .../arc-captured-block-var-inlined-layout.m | 102 +++++ .../CodeGenObjC/arc-captured-block-var-layout.m | 425 +++++++++++++++++++++ 7 files changed, 709 insertions(+), 86 deletions(-) delete mode 100644 clang/test/CodeGenObjC/arc-block-ivar-layout.m create mode 100644 clang/test/CodeGenObjC/arc-captured-block-var-inlined-layout.m create mode 100644 clang/test/CodeGenObjC/arc-captured-block-var-layout.m diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 00203be..6742f36 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -27,7 +27,8 @@ using namespace CodeGen; CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name) : Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false), - HasCXXObject(false), UsesStret(false), StructureType(0), Block(block), + HasCXXObject(false), UsesStret(false), HasCapturedVariableLayout(false), + StructureType(0), Block(block), DominatingIP(0) { // Skip asm prefix, if any. 'name' is usually taken directly from @@ -308,7 +309,10 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, info.CanBeGlobal = true; return; } - + else if (C.getLangOpts().ObjC1 && + CGM.getLangOpts().getGC() == LangOptions::NonGC) + info.HasCapturedVariableLayout = true; + // Collect the layout chunks. SmallVector layout; layout.reserve(block->capturesCXXThis() + @@ -667,6 +671,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { // Compute the initial on-stack block flags. BlockFlags flags = BLOCK_HAS_SIGNATURE; + if (blockInfo.HasCapturedVariableLayout) flags |= BLOCK_HAS_EXTENDED_LAYOUT; if (blockInfo.NeedsCopyDispose) flags |= BLOCK_HAS_COPY_DISPOSE; if (blockInfo.HasCXXObject) flags |= BLOCK_HAS_CXX_OBJ; if (blockInfo.UsesStret) flags |= BLOCK_USE_STRET; diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h index 690fa8b..6263562 100644 --- a/clang/lib/CodeGen/CGBlocks.h +++ b/clang/lib/CodeGen/CGBlocks.h @@ -59,6 +59,7 @@ enum BlockByrefFlags { }; enum BlockLiteralFlags { + BLOCK_HAS_EXTENDED_LAYOUT = (1 << 23), BLOCK_HAS_COPY_DISPOSE = (1 << 25), BLOCK_HAS_CXX_OBJ = (1 << 26), BLOCK_IS_GLOBAL = (1 << 28), @@ -193,6 +194,10 @@ public: /// UsesStret : True if the block uses an stret return. Mutable /// because it gets set later in the block-creation process. mutable bool UsesStret : 1; + + /// HasCapturedVariableLayout : True if block has captured variables + /// and their layout meta-data has been generated. + bool HasCapturedVariableLayout : 1; /// The mapping of allocated indexes within the block. llvm::DenseMap Captures; diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 12f5e0d..c67e3fd 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -955,6 +955,8 @@ protected: ArrayRef RecFields, unsigned int BytePos, bool &HasUnion); + uint64_t InlineLayoutInstruction(SmallVectorImpl &Layout); + /// GetIvarLayoutName - Returns a unique constant for the given /// ivar layout bitmap. @@ -2124,11 +2126,114 @@ void CGObjCCommonMac::BuildRCBlockVarRecordLayout(const RecordType *RT, BuildRCRecordLayout(RecLayout, RD, Fields, BytePos, HasUnion); } +/// InlineLayoutInstruction - This routine produce an inline instruction for the +/// block variable layout if it can. If not, it returns 0. Rules are as follow: +/// If ((uintptr_t) layout) < (1 << 12), the layout is inline. In the 64bit world, +/// an inline layout of value 0x0000000000000xyz is interpreted as follows: +/// x captured object pointers of BLOCK_LAYOUT_STRONG. Followed by +/// y captured object of BLOCK_LAYOUT_BYREF. Followed by +/// z captured object of BLOCK_LAYOUT_WEAK. If any of the above is missing, zero +/// replaces it. For example, 0x00000x00 means x BLOCK_LAYOUT_STRONG and no +/// BLOCK_LAYOUT_BYREF and no BLOCK_LAYOUT_WEAK objects are captured. +uint64_t CGObjCCommonMac::InlineLayoutInstruction( + SmallVectorImpl &Layout) { + uint64_t Result = 0; + if (Layout.size() <= 3) { + unsigned size = Layout.size(); + unsigned strong_word_count = 0, byref_word_count=0, weak_word_count=0; + unsigned char inst; + enum BLOCK_LAYOUT_OPCODE opcode ; + switch (size) { + case 3: + inst = Layout[0]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_STRONG) + strong_word_count = (inst & 0xF)+1; + else + return 0; + inst = Layout[1]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_BYREF) + byref_word_count = (inst & 0xF)+1; + else + return 0; + inst = Layout[2]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_WEAK) + weak_word_count = (inst & 0xF)+1; + else + return 0; + break; + + case 2: + inst = Layout[0]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_STRONG) { + strong_word_count = (inst & 0xF)+1; + inst = Layout[1]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_BYREF) + byref_word_count = (inst & 0xF)+1; + else if (opcode == BLOCK_LAYOUT_WEAK) + weak_word_count = (inst & 0xF)+1; + else + return 0; + } + else if (opcode == BLOCK_LAYOUT_BYREF) { + byref_word_count = (inst & 0xF)+1; + inst = Layout[1]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_WEAK) + weak_word_count = (inst & 0xF)+1; + else + return 0; + } + else + return 0; + break; + + case 1: + inst = Layout[0]; + opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_STRONG) + strong_word_count = (inst & 0xF)+1; + else if (opcode == BLOCK_LAYOUT_BYREF) + byref_word_count = (inst & 0xF)+1; + else if (opcode == BLOCK_LAYOUT_WEAK) + weak_word_count = (inst & 0xF)+1; + else + return 0; + break; + + default: + return 0; + } + + // Cannot inline when any of the word counts is 15. Because this is one less + // than the actual work count (so 15 means 16 actual word counts), + // and we can only display 0 thru 15 word counts. + if (strong_word_count == 16 || byref_word_count == 16 || weak_word_count == 16) + return 0; + + unsigned count = + (strong_word_count != 0) + (byref_word_count != 0) + (weak_word_count != 0); + + if (size == count) { + if (strong_word_count) + Result = strong_word_count; + Result <<= 4; + if (byref_word_count) + Result += byref_word_count; + Result <<= 4; + if (weak_word_count) + Result += weak_word_count; + } + } + return Result; +} + llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, const CGBlockInfo &blockInfo) { - // FIXME. Temporary call the GC layout routine. - return BuildGCBlockLayout(CGM, blockInfo); - assert(CGM.getLangOpts().getGC() == LangOptions::NonGC); llvm::Constant *nullPtr = llvm::Constant::getNullValue(CGM.Int8PtrTy); @@ -2167,7 +2272,8 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, BuildRCBlockVarRecordLayout(record, fieldOffset, hasUnion); continue; } - unsigned fieldSize = CGM.getContext().getTypeSize(type); + unsigned fieldSize = ci->isByRef() ? WordSizeInBits + : CGM.getContext().getTypeSize(type); UpdateRunSkipBlockVars(ci->isByRef(), type.getObjCLifetime(), fieldOffset, fieldSize); } @@ -2178,7 +2284,7 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, // Sort on byte position; captures might not be allocated in order, // and unions can do funny things. llvm::array_pod_sort(RunSkipBlockVars.begin(), RunSkipBlockVars.end()); - std::string Layout; + SmallVector Layout; unsigned size = RunSkipBlockVars.size(); unsigned int shift = (WordSizeInBytes == 8) ? 3 : 2; @@ -2214,35 +2320,70 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, unsigned size_in_words = size_in_bytes >> shift; while (size_in_words >= 16) { // Note that value in imm. is one less that the actual - // value. So, 0xff means 16 words follow! - unsigned char inst = (opcode << 4) | 0xff; - Layout += inst; + // value. So, 0xf means 16 words follow! + unsigned char inst = (opcode << 4) | 0xf; + Layout.push_back(inst); size_in_words -= 16; } if (size_in_words > 0) { // Note that value in imm. is one less that the actual // value. So, we subtract 1 away! unsigned char inst = (opcode << 4) | (size_in_words-1); - Layout += inst; + Layout.push_back(inst); } if (residue_in_bytes > 0) { - unsigned char inst = (BLOCK_LAYOUT_NON_OBJECT_BYTES << 4) | residue_in_bytes; - Layout += inst; + unsigned char inst = + (BLOCK_LAYOUT_NON_OBJECT_BYTES << 4) | (residue_in_bytes-1); + Layout.push_back(inst); + } + } + + int e = Layout.size()-1; + while (e >= 0) { + unsigned char inst = Layout[e--]; + enum BLOCK_LAYOUT_OPCODE opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); + if (opcode == BLOCK_LAYOUT_NON_OBJECT_BYTES || opcode == BLOCK_LAYOUT_NON_OBJECT_WORDS) + Layout.pop_back(); + else + break; + } + + uint64_t Result = InlineLayoutInstruction(Layout); + if (Result != 0) { + // Block variable layout instruction has been inlined. + if (CGM.getLangOpts().ObjCGCBitmapPrint) { + printf("\n Inline instruction for block variable layout: "); + printf("0x0%llx\n", Result); + } + if (WordSizeInBytes == 8) { + const llvm::APInt Instruction(64, Result); + return llvm::Constant::getIntegerValue(CGM.Int64Ty, Instruction); + } + else { + const llvm::APInt Instruction(32, Result); + return llvm::Constant::getIntegerValue(CGM.Int32Ty, Instruction); } } + + unsigned char inst = (BLOCK_LAYOUT_OPERATOR << 4) | 0; + Layout.push_back(inst); + std::string BitMap; + for (unsigned i = 0, e = Layout.size(); i != e; i++) + BitMap += Layout[i]; + if (CGM.getLangOpts().ObjCGCBitmapPrint) { printf("\n block variable layout: "); - for (unsigned i = 0, e = Layout.size(); i != e; i++) { - unsigned char inst = Layout[i]; + for (unsigned i = 0, e = BitMap.size(); i != e; i++) { + unsigned char inst = BitMap[i]; enum BLOCK_LAYOUT_OPCODE opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4); unsigned delta = 1; switch (opcode) { case BLOCK_LAYOUT_OPERATOR: printf("BL_OPERATOR:"); + delta = 0; break; case BLOCK_LAYOUT_NON_OBJECT_BYTES: printf("BL_NON_OBJECT_BYTES:"); - delta = 0; break; case BLOCK_LAYOUT_NON_OBJECT_WORDS: printf("BL_NON_OBJECT_WORD:"); @@ -2257,7 +2398,7 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, printf("BL_WEAK:"); break; case BLOCK_LAYOUT_UNRETAINED: - printf("BL_UNRETAINE:"); + printf("BL_UNRETAINED:"); break; } // Actual value of word count is one more that what is in the imm. @@ -2269,7 +2410,12 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, printf("\n"); } } - return nullPtr; + + llvm::GlobalVariable * Entry = + CreateMetadataVar("\01L_OBJC_CLASS_NAME_", + llvm::ConstantDataArray::getString(VMContext, BitMap,false), + "__TEXT,__objc_classname,cstring_literals", 1, true); + return getConstantGEP(VMContext, Entry, 0, 0); } llvm::Value *CGObjCMac::GenerateProtocolRef(CGBuilderTy &Builder, diff --git a/clang/test/CodeGenObjC/arc-block-ivar-layout.m b/clang/test/CodeGenObjC/arc-block-ivar-layout.m deleted file mode 100644 index 6c82f29..0000000 --- a/clang/test/CodeGenObjC/arc-block-ivar-layout.m +++ /dev/null @@ -1,60 +0,0 @@ -// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s -// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s -// rdar://8991729 - -__weak id wid; -void x(id y) {} -void y(int a) {} - -extern id opaque_id(); - -void f() { - __block int byref_int = 0; - char ch = 'a'; - char ch1 = 'b'; - char ch2 = 'c'; - short sh = 2; - const id bar = (id) opaque_id(); - id baz = 0; - __strong id strong_void_sta; - __block id byref_bab = (id)0; - __block id bl_var1; - int i; double dob; - -// The patterns here are a sequence of bytes, each saying first how -// many sizeof(void*) chunks to skip (high nibble) and then how many -// to scan (low nibble). A zero byte says that we've reached the end -// of the pattern. -// -// All of these patterns start with 01 3x because the block header on -// LP64 consists of an isa pointer (which we're supposed to scan for -// some reason) followed by three words (2 ints, a function pointer, -// and a descriptor pointer). - -// Test 1 -// byref int, short, char, char, char, id, id, strong id, byref id -// 01 35 10 00 -// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\015\10\00" - void (^b)() = ^{ - byref_int = sh + ch+ch1+ch2 ; - x(bar); - x(baz); - x((id)strong_void_sta); - x(byref_bab); - }; - b(); - -// Test 2 -// byref int, short, char, char, char, id, id, strong id, byref void*, byref id -// 01 36 10 00 -// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\016\10\00" - void (^c)() = ^{ - byref_int = sh + ch+ch1+ch2 ; - x(bar); - x(baz); - x((id)strong_void_sta); - x(wid); - bl_var1 = 0; - x(byref_bab); - }; -} diff --git a/clang/test/CodeGenObjC/arc-blocks.m b/clang/test/CodeGenObjC/arc-blocks.m index 38c88af..58919a9 100644 --- a/clang/test/CodeGenObjC/arc-blocks.m +++ b/clang/test/CodeGenObjC/arc-blocks.m @@ -118,8 +118,8 @@ void test4(void) { // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) // CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]] // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 - // 0x42000000 - has signature, copy/dispose helpers - // CHECK: store i32 1107296256, + // 0x42800000 - has signature, copy/dispose helpers, as well as BLOCK_HAS_EXTENDED_LAYOUT + // CHECK: store i32 1115684864, // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* // CHECK-NEXT: store i8* [[T0]], i8** // CHECK: call void @test4_helper( @@ -170,8 +170,8 @@ void test5(void) { // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) // CHECK-NEXT: store i8* [[T1]], i8** [[VAR]], // CHECK-NEXT: call void @objc_release(i8* [[T1]]) - // 0x40000000 - has signature but no copy/dispose - // CHECK: store i32 1073741824, i32* + // 0x40800000 - has signature but no copy/dispose, as well as BLOCK_HAS_EXTENDED_LAYOUT + // CHECK: store i32 1082130432, i32* // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-NEXT: [[T0:%.*]] = load i8** [[VAR]] // CHECK-NEXT: store i8* [[T0]], i8** [[CAPTURE]] @@ -198,8 +198,8 @@ void test6(void) { // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T1]]) // CHECK-NEXT: call void @objc_release(i8* [[T1]]) // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 - // 0x42000000 - has signature, copy/dispose helpers - // CHECK: store i32 1107296256, + // 0x42800000 - has signature, copy/dispose helpers, as well as BLOCK_HAS_EXTENDED_LAYOUT + // CHECK: store i32 1115684864, // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* // CHECK-NEXT: store i8* [[T0]], i8** // CHECK: call void @test6_helper( @@ -247,8 +247,8 @@ void test7(void) { // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) // CHECK-NEXT: call i8* @objc_initWeak(i8** [[VAR]], i8* [[T1]]) // CHECK-NEXT: call void @objc_release(i8* [[T1]]) - // 0x42000000 - has signature, copy/dispose helpers - // CHECK: store i32 1107296256, + // 0x42800000 - has signature, copy/dispose helpers, as well as BLOCK_HAS_EXTENDED_LAYOUT + // CHECK: store i32 1115684864, // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[VAR]]) // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T0]]) diff --git a/clang/test/CodeGenObjC/arc-captured-block-var-inlined-layout.m b/clang/test/CodeGenObjC/arc-captured-block-var-inlined-layout.m new file mode 100644 index 0000000..a5400c5 --- /dev/null +++ b/clang/test/CodeGenObjC/arc-captured-block-var-inlined-layout.m @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s +// rdar://12184410 + +void x(id y) {} +void y(int a) {} + +extern id opaque_id(); + +void f() { + __block int byref_int = 0; + const id bar = (id) opaque_id(); + id baz = 0; + __strong id strong_void_sta; + __block id byref_bab = (id)0; + __block id bl_var1; + +// Inline instruction for block variable layout: 0x0100 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 256 } + void (^b)() = ^{ + x(bar); + }; + +// Inline instruction for block variable layout: 0x0210 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 528 } + void (^c)() = ^{ + x(bar); + x(baz); + byref_int = 1; + }; + +// Inline instruction for block variable layout: 0x0230 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 560 } + void (^d)() = ^{ + x(bar); + x(baz); + byref_int = 1; + bl_var1 = 0; + byref_bab = 0; + }; + +// Inline instruction for block variable layout: 0x0231 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 561 } + __weak id wid; + id (^e)() = ^{ + x(bar); + x(baz); + byref_int = 1; + bl_var1 = 0; + byref_bab = 0; + return wid; + }; + +// Inline instruction for block variable layout: 0x0235 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 565 } + __weak id wid1, wid2, wid3, wid4; + id (^f)() = ^{ + x(bar); + x(baz); + byref_int = 1; + bl_var1 = 0; + byref_bab = 0; + x(wid1); + x(wid2); + x(wid3); + x(wid4); + return wid; + }; + +// Inline instruction for block variable layout: 0x035 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 53 } + id (^g)() = ^{ + byref_int = 1; + bl_var1 = 0; + byref_bab = 0; + x(wid1); + x(wid2); + x(wid3); + x(wid4); + return wid; + }; + +// Inline instruction for block variable layout: 0x01 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 1 } + id (^h)() = ^{ + return wid; + }; + +// Inline instruction for block variable layout: 0x020 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 32 } + void (^ii)() = ^{ + byref_int = 1; + byref_bab = 0; + }; + +// Inline instruction for block variable layout: 0x0102 +// CKECK-LP64: i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i64 258 } + void (^jj)() = ^{ + x(bar); + x(wid1); + x(wid2); + }; +} diff --git a/clang/test/CodeGenObjC/arc-captured-block-var-layout.m b/clang/test/CodeGenObjC/arc-captured-block-var-layout.m new file mode 100644 index 0000000..77f042e --- /dev/null +++ b/clang/test/CodeGenObjC/arc-captured-block-var-layout.m @@ -0,0 +1,425 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://12184410 + +void x(id y) {} +void y(int a) {} + +extern id opaque_id(); + +void f() { + __weak id wid; + __block int byref_int = 0; + char ch = 'a'; + char ch1 = 'b'; + char ch2 = 'c'; + short sh = 2; + const id bar = (id) opaque_id(); + id baz = 0; + __strong id strong_void_sta; + __block id byref_bab = (id)0; + __block id bl_var1; + int i; double dob; + +// The patterns here are a sequence of bytes, each saying first how +// many sizeof(void*) chunks to skip (high nibble) and then how many +// to scan (low nibble). A zero byte says that we've reached the end +// of the pattern. +// +// All of these patterns start with 01 3x because the block header on +// LP64 consists of an isa pointer (which we're supposed to scan for +// some reason) followed by three words (2 ints, a function pointer, +// and a descriptor pointer). + +// Test 1 +// block variable layout: BL_BYREF:1, BL_STRONG:3, BL_BYREF:1, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"@2@\00" + void (^b)() = ^{ + byref_int = sh + ch+ch1+ch2 ; + x(bar); + x(baz); + x((id)strong_void_sta); + x(byref_bab); + }; + b(); + +// Test 2 +// block variable layout: BL_BYREF:1, BL_STRONG:3, BL_WEAK:1, BL_BYREF:2, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [5 x i8] c"@2PA\00" + void (^c)() = ^{ + byref_int = sh + ch+ch1+ch2 ; + x(bar); + x(baz); + x((id)strong_void_sta); + x(wid); + bl_var1 = 0; + x(byref_bab); + }; +} + +@class NSString, NSNumber; +void g() { + NSString *foo; + NSNumber *bar; + unsigned int bletch; + __weak id weak_delegate; + unsigned int i; + NSString *y; + NSString *z; +// block variable layout: BL_STRONG:2, BL_WEAK:1, BL_STRONG:2, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"1P1\00" + void (^c)() = ^{ + int j = i + bletch; + x(foo); + x(bar); + x(weak_delegate); + x(y); + x(z); + }; + c(); +} + +// Test 5 (unions/structs and their nesting): +void h() { + struct S5 { + int i1; + __unsafe_unretained id o1; + struct V { + int i2; + __unsafe_unretained id o2; + } v1; + int i3; + union UI { + void * i1; + __unsafe_unretained id o1; + int i3; + __unsafe_unretained id o3; + }ui; + }; + + union U { + void * i1; + __unsafe_unretained id o1; + int i3; + __unsafe_unretained id o3; + }ui; + + struct S5 s2; + union U u2; + __block id block_id; + +/** +block variable layout: BL_NON_OBJECT_WORD:1, BL_UNRETAINE:1, BL_NON_OBJECT_WORD:1, + BL_UNRETAINE:1, BL_NON_OBJECT_WORD:3, BL_BYREF:1, BL_OPERATOR:0 +*/ +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [7 x i8] c" ` `\22@\00" + void (^c)() = ^{ + x(s2.ui.o1); + x(u2.o1); + block_id = 0; + }; + c(); +} + +// Test for array of stuff. +void arr1() { + struct S { + __unsafe_unretained id unsafe_unretained_var[4]; + } imported_s; + +// block variable layout: BL_UNRETAINE:4, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [2 x i8] c"c\00" + void (^c)() = ^{ + x(imported_s.unsafe_unretained_var[2]); + }; + + c(); +} + +// Test2 for array of stuff. +void arr2() { + struct S { + int a; + __unsafe_unretained id unsafe_unretained_var[4]; + } imported_s; + +// block variable layout: BL_NON_OBJECT_WORD:1, BL_UNRETAINE:4, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [3 x i8] c" c\00" + void (^c)() = ^{ + x(imported_s.unsafe_unretained_var[2]); + }; + + c(); +} + +// Test3 for array of stuff. +void arr3() { + struct S { + int a; + __unsafe_unretained id unsafe_unretained_var[0]; + } imported_s; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + void (^c)() = ^{ + int i = imported_s.a; + }; + + c(); +} + + +// Test4 for array of stuff. +@class B; +void arr4() { + struct S { + struct s0 { + __unsafe_unretained id s_f0; + __unsafe_unretained id s_f1; + } f0; + + __unsafe_unretained id f1; + + struct s1 { + int *f0; + __unsafe_unretained B *f1; + } f4[2][2]; + } captured_s; + +/** +block variable layout: BL_UNRETAINE:3, + BL_NON_OBJECT_WORD:1, BL_UNRETAINE:1, + BL_NON_OBJECT_WORD:1, BL_UNRETAINE:1, + BL_NON_OBJECT_WORD:1, BL_UNRETAINE:1, + BL_NON_OBJECT_WORD:1, BL_UNRETAINE:1, + BL_OPERATOR:0 +*/ +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [10 x i8] + void (^c)() = ^{ + id i = captured_s.f0.s_f1; + }; + + c(); +} + +// Test1 bitfield in cpatured aggregate. +void bf1() { + struct S { + int flag : 25; + int flag1: 7; + int flag2 :1; + int flag3: 7; + int flag4: 24; + } s; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + int (^c)() = ^{ + return s.flag; + }; + c(); +} + +// Test2 bitfield in cpatured aggregate. +void bf2() { + struct S { + int flag : 1; + } s; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + int (^c)() = ^{ + return s.flag; + }; + c(); +} + +// Test3 bitfield in cpatured aggregate. +void bf3() { + + struct { + unsigned short _reserved : 16; + + unsigned char _draggedNodesAreDeletable: 1; + unsigned char _draggedOutsideOutlineView : 1; + unsigned char _adapterRespondsTo_addRootPaths : 1; + unsigned char _adapterRespondsTo_moveDataNodes : 1; + unsigned char _adapterRespondsTo_removeRootDataNode : 1; + unsigned char _adapterRespondsTo_doubleClickDataNode : 1; + unsigned char _adapterRespondsTo_selectDataNode : 1; + unsigned char _adapterRespondsTo_textDidEndEditing : 1; + unsigned char _adapterRespondsTo_updateAndSaveRoots : 1; + unsigned char _adapterRespondsTo_askToDeleteRootNodes : 1; + unsigned char _adapterRespondsTo_contextMenuForSelectedNodes : 1; + unsigned char _adapterRespondsTo_pasteboardFilenamesForNodes : 1; + unsigned char _adapterRespondsTo_writeItemsToPasteboard : 1; + unsigned char _adapterRespondsTo_writeItemsToPasteboardXXXX : 1; + + unsigned int _filler : 32; + } _flags; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + unsigned char (^c)() = ^{ + return _flags._draggedNodesAreDeletable; + }; + + c(); +} + +// Test4 unnamed bitfield +void bf4() { + + struct { + unsigned short _reserved : 16; + + unsigned char _draggedNodesAreDeletable: 1; + unsigned char _draggedOutsideOutlineView : 1; + unsigned char _adapterRespondsTo_addRootPaths : 1; + unsigned char _adapterRespondsTo_moveDataNodes : 1; + unsigned char _adapterRespondsTo_removeRootDataNode : 1; + unsigned char _adapterRespondsTo_doubleClickDataNode : 1; + unsigned char _adapterRespondsTo_selectDataNode : 1; + unsigned char _adapterRespondsTo_textDidEndEditing : 1; + + unsigned long long : 64; + + unsigned char _adapterRespondsTo_updateAndSaveRoots : 1; + unsigned char _adapterRespondsTo_askToDeleteRootNodes : 1; + unsigned char _adapterRespondsTo_contextMenuForSelectedNodes : 1; + unsigned char _adapterRespondsTo_pasteboardFilenamesForNodes : 1; + unsigned char _adapterRespondsTo_writeItemsToPasteboard : 1; + unsigned char _adapterRespondsTo_writeItemsToPasteboardXXXX : 1; + + unsigned int _filler : 32; + } _flags; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + unsigned char (^c)() = ^{ + return _flags._draggedNodesAreDeletable; + }; + + c(); +} + + + +// Test5 unnamed bitfield. +void bf5() { + struct { + unsigned char flag : 1; + unsigned int : 32; + unsigned char flag1 : 1; + } _flags; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + unsigned char (^c)() = ^{ + return _flags.flag; + }; + + c(); +} + + +// Test6 0 length bitfield. +void bf6() { + struct { + unsigned char flag : 1; + unsigned int : 0; + unsigned char flag1 : 1; + } _flags; + +// block variable layout: BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [1 x i8] zeroinitializer + unsigned char (^c)() = ^{ + return _flags.flag; + }; + + c(); +} + +// Test7 large number of captured variables. +void Test7() { + __weak id wid; + __weak id wid1, wid2, wid3, wid4; + __weak id wid5, wid6, wid7, wid8; + __weak id wid9, wid10, wid11, wid12; + __weak id wid13, wid14, wid15, wid16; + const id bar = (id) opaque_id(); +//block variable layout: BL_STRONG:1, BL_WEAK:16, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [3 x i8] c"0_\00" + void (^b)() = ^{ + x(bar); + x(wid1); + x(wid2); + x(wid3); + x(wid4); + x(wid5); + x(wid6); + x(wid7); + x(wid8); + x(wid9); + x(wid10); + x(wid11); + x(wid12); + x(wid13); + x(wid14); + x(wid15); + x(wid16); + }; +} + + +// Test 8 very large number of captured variables. +void Test8() { +__weak id wid; + __weak id wid1, wid2, wid3, wid4; + __weak id wid5, wid6, wid7, wid8; + __weak id wid9, wid10, wid11, wid12; + __weak id wid13, wid14, wid15, wid16; + __weak id w1, w2, w3, w4; + __weak id w5, w6, w7, w8; + __weak id w9, w10, w11, w12; + __weak id w13, w14, w15, w16; + const id bar = (id) opaque_id(); +// block variable layout: BL_STRONG:1, BL_WEAK:16, BL_WEAK:16, BL_WEAK:1, BL_OPERATOR:0 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [5 x i8] + void (^b)() = ^{ + x(bar); + x(wid1); + x(wid2); + x(wid3); + x(wid4); + x(wid5); + x(wid6); + x(wid7); + x(wid8); + x(wid9); + x(wid10); + x(wid11); + x(wid12); + x(wid13); + x(wid14); + x(wid15); + x(wid16); + x(w1); + x(w2); + x(w3); + x(w4); + x(w5); + x(w6); + x(w7); + x(w8); + x(w9); + x(w10); + x(w11); + x(w12); + x(w13); + x(w14); + x(w15); + x(w16); + x(wid); + }; +} -- 2.7.4