
Clang generates copy and dispose helper functions for each block literal on the stack. Often these functions are equivalent for different blocks. This commit makes changes to merge equivalent copy and dispose helper functions and reduce code size. To enable merging equivalent copy/dispose functions, the captured object infomation is encoded into the helper function name. This allows IRGen to check whether an equivalent helper function has already been emitted and reuse the function instead of generating a new helper function whenever a block is defined. In addition, the helper functions are marked as linkonce_odr to enable merging helper functions that have the same name across translation units and marked as unnamed_addr to enable the linker's deduplication pass to merge functions that have different names but the same content. rdar://problem/42640608 Differential Revision: https://reviews.llvm.org/D50152 llvm-svn: 339438
192 lines
6.4 KiB
Objective-C
192 lines
6.4 KiB
Objective-C
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-10.10 -emit-llvm -fblocks -fobjc-weak -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-MODERN
|
|
// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.10 -emit-llvm -fblocks -fobjc-weak -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-FRAGILE
|
|
|
|
@interface Object
|
|
- (instancetype) retain;
|
|
- (void) run;
|
|
@end
|
|
|
|
// The ivars in HighlyAlignedSubclass should be placed in the tail-padding
|
|
// of the superclass. Ensure that they're still covered by layouts.
|
|
@interface HighlyAligned : Object {
|
|
__attribute__((aligned(32))) void *array[2];
|
|
}
|
|
@end
|
|
// CHECK-MODERN: @"OBJC_IVAR_$_HighlyAlignedSubclass.ivar2" = global i64 24,
|
|
// CHECK-MODERN: @"OBJC_IVAR_$_HighlyAlignedSubclass.ivar" = global i64 16,
|
|
// CHECK-MODERN: @OBJC_CLASS_NAME_{{.*}} = {{.*}} c"\02\00"
|
|
// CHECK-MODERN: @"\01l_OBJC_CLASS_RO_$_HighlyAlignedSubclass" = {{.*}} {
|
|
// CHECK-FRAGILE: @OBJC_INSTANCE_VARIABLES_HighlyAlignedSubclass = {{.*}}, i32 8 }, {{.*}}, i32 12 }]
|
|
// CHECK-FRAGILE: @OBJC_CLASS_NAME_{{.*}} = {{.*}} c"\02\00"
|
|
// CHECK-FRAGILE: @OBJC_CLASS_HighlyAlignedSubclass
|
|
@interface HighlyAlignedSubclass : HighlyAligned {
|
|
__weak id ivar;
|
|
__weak id ivar2;
|
|
}
|
|
@end
|
|
@implementation HighlyAlignedSubclass @end
|
|
|
|
// CHECK-MODERN: @OBJC_CLASS_NAME_{{.*}} = {{.*}} c"\01\00"
|
|
// CHECK-MODERN: @"\01l_OBJC_CLASS_RO_$_Foo" = {{.*}} { i32 772
|
|
// 772 == 0x304
|
|
// ^ HasMRCWeakIvars
|
|
// ^ HasCXXDestructorOnly
|
|
// ^ HasCXXStructors
|
|
|
|
// CHECK-FRAGILE: @OBJC_CLASS_NAME_{{.*}} = {{.*}} c"\01\00"
|
|
// CHECK-FRAGILE: @OBJC_CLASS_Foo = {{.*}} i32 134225921,
|
|
// 134225921 == 0x08002001
|
|
// ^ HasMRCWeakIvars
|
|
// ^ HasCXXStructors
|
|
// ^ Factory
|
|
@interface Foo : Object {
|
|
__weak id ivar;
|
|
}
|
|
@end
|
|
|
|
@implementation Foo
|
|
// CHECK-LABEL: define internal void @"\01-[Foo .cxx_destruct]"
|
|
// CHECK: call void @objc_destroyWeak
|
|
@end
|
|
|
|
|
|
void test1(__weak id x) {}
|
|
// CHECK-LABEL: define void @test1
|
|
// CHECK: [[X:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: objc_initWeak
|
|
// CHECK-NEXT: objc_destroyWeak
|
|
// CHECK-NEXT: ret void
|
|
|
|
void test2(id y) {
|
|
__weak id z = y;
|
|
}
|
|
// CHECK-LABEL: define void @test2
|
|
// CHECK: [[Y:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: [[Z:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]]
|
|
// CHECK-NEXT: call i8* @objc_initWeak(i8** [[Z]], i8* [[T0]])
|
|
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[Z]])
|
|
// CHECK-NEXT: ret void
|
|
|
|
void test3(id y) {
|
|
__weak id z;
|
|
z = y;
|
|
}
|
|
// CHECK-LABEL: define void @test3
|
|
// CHECK: [[Y:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: [[Z:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: store i8* null, i8** [[Z]]
|
|
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]]
|
|
// CHECK-NEXT: call i8* @objc_storeWeak(i8** [[Z]], i8* [[T0]])
|
|
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[Z]])
|
|
// CHECK-NEXT: ret void
|
|
|
|
void test4(__weak id *p) {
|
|
id y = *p;
|
|
}
|
|
// CHECK-LABEL: define void @test4
|
|
// CHECK: [[P:%.*]] = alloca i8**,
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: [[T0:%.*]] = load i8**, i8*** [[P]]
|
|
// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_loadWeak(i8** [[T0]])
|
|
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
|
// CHECK-NEXT: ret void
|
|
|
|
void test5(__weak id *p) {
|
|
id y = [*p retain];
|
|
}
|
|
// CHECK-LABEL: define void @test5
|
|
// CHECK: [[P:%.*]] = alloca i8**,
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: [[T0:%.*]] = load i8**, i8*** [[P]]
|
|
// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T0]])
|
|
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
|
// CHECK-NEXT: ret void
|
|
|
|
void test6(__weak Foo **p) {
|
|
Foo *y = [*p retain];
|
|
}
|
|
// CHECK-LABEL: define void @test6
|
|
// CHECK: [[P:%.*]] = alloca [[FOO:%.*]]**,
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca [[FOO]]*,
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: [[T0:%.*]] = load [[FOO]]**, [[FOO]]*** [[P]]
|
|
// CHECK-NEXT: [[T1:%.*]] = bitcast [[FOO]]** [[T0]] to i8**
|
|
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T1]])
|
|
// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[FOO]]*
|
|
// CHECK-NEXT: store [[FOO]]* [[T3]], [[FOO]]** [[Y]]
|
|
// CHECK-NEXT: ret void
|
|
|
|
extern id get_object(void);
|
|
extern void use_block(void (^)(void));
|
|
|
|
void test7(void) {
|
|
__weak Foo *p = get_object();
|
|
use_block(^{ [p run ]; });
|
|
}
|
|
// CHECK-LABEL: define void @test7
|
|
// CHECK: [[P:%.*]] = alloca [[FOO]]*,
|
|
// CHECK: [[T0:%.*]] = call i8* @get_object()
|
|
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[FOO]]*
|
|
// CHECK-NEXT: [[T2:%.*]] = bitcast [[FOO]]** [[P]] to i8**
|
|
// CHECK-NEXT: [[T3:%.*]] = bitcast [[FOO]]* [[T1]] to i8*
|
|
// CHECK-NEXT: call i8* @objc_initWeak(i8** [[T2]], i8* [[T3]])
|
|
// CHECK: call void @objc_copyWeak
|
|
// CHECK: call void @use_block
|
|
// CHECK: call void @objc_destroyWeak
|
|
|
|
// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block
|
|
// CHECK: @objc_copyWeak
|
|
|
|
// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block
|
|
// CHECK: @objc_destroyWeak
|
|
|
|
void test8(void) {
|
|
__block __weak Foo *p = get_object();
|
|
use_block(^{ [p run ]; });
|
|
}
|
|
// CHECK-LABEL: define void @test8
|
|
// CHECK: call i8* @objc_initWeak
|
|
// CHECK-NOT: call void @objc_copyWeak
|
|
// CHECK: call void @use_block
|
|
// CHECK: call void @objc_destroyWeak
|
|
|
|
// CHECK-LABEL: define internal void @__Block_byref_object_copy
|
|
// CHECK: call void @objc_moveWeak
|
|
|
|
// CHECK-LABEL: define internal void @__Block_byref_object_dispose
|
|
// CHECK: call void @objc_destroyWeak
|
|
|
|
// CHECK-LABEL: define void @test9_baseline()
|
|
// CHECK: define linkonce_odr hidden void @__copy_helper
|
|
// CHECK: define linkonce_odr hidden void @__destroy_helper
|
|
void test9_baseline(void) {
|
|
Foo *p = get_object();
|
|
use_block(^{ [p run]; });
|
|
}
|
|
|
|
// CHECK-LABEL: define void @test9()
|
|
// CHECK-NOT: define linkonce_odr hidden void @__copy_helper
|
|
// CHECK-NOT: define linkonce_odr hidden void @__destroy_helper
|
|
// CHECK: define void @test9_fin()
|
|
void test9(void) {
|
|
__unsafe_unretained Foo *p = get_object();
|
|
use_block(^{ [p run]; });
|
|
}
|
|
void test9_fin() {}
|
|
|
|
// CHECK-LABEL: define void @test10()
|
|
// CHECK-NOT: define linkonce_odr hidden void @__copy_helper
|
|
// CHECK-NOT: define linkonce_odr hidden void @__destroy_helper
|
|
// CHECK: define void @test10_fin()
|
|
void test10(void) {
|
|
typedef __unsafe_unretained Foo *UnsafeFooPtr;
|
|
UnsafeFooPtr p = get_object();
|
|
use_block(^{ [p run]; });
|
|
}
|
|
void test10_fin() {}
|