зеркало из https://github.com/microsoft/clang-1.git
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167241 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
cfa1caa926
Коммит
f22ae6512d
|
@ -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<BlockLayoutChunk, 16> 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;
|
||||
|
|
|
@ -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<const VarDecl*, Capture> Captures;
|
||||
|
|
|
@ -955,6 +955,8 @@ protected:
|
|||
ArrayRef<const FieldDecl*> RecFields,
|
||||
unsigned int BytePos, bool &HasUnion);
|
||||
|
||||
uint64_t InlineLayoutInstruction(SmallVectorImpl<unsigned char> &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<unsigned char> &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<unsigned char, 16> 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,
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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]])
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
}
|
Загрузка…
Ссылка в новой задаче