blob: bc7a94db112cafafa5fec1cd420995555e3a8a62 [file] [edit]
// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple arm64-apple-darwin10 \
// RUN: -fobjc-direct-precondition-thunk %s -o - | FileCheck %s
struct my_complex_struct {
int a, b;
};
struct my_aggregate_struct {
int a, b;
char buf[128];
};
__attribute__((objc_root_class))
@interface Root
- (int)getInt __attribute__((objc_direct));
@property(direct, readonly) int intProperty;
@property(direct, readonly) int intProperty2;
@property(direct, readonly) id objectProperty;
@end
@implementation Root
// CHECK-LABEL: define hidden i32 @"-[Root intProperty2]D"(ptr noundef %self)
- (int)intProperty2 {
return 42;
}
// CHECK-LABEL: define hidden i32 @"-[Root getInt]D"(ptr noundef %self)
- (int)getInt __attribute__((objc_direct)) {
return 42;
}
// CHECK-LABEL: define hidden i32 @"+[Root classGetInt]D"(ptr noundef %self)
+ (int)classGetInt __attribute__((objc_direct)) {
return 42;
}
// CHECK-LABEL: define hidden i64 @"-[Root getComplex]D"(ptr noundef %self)
- (struct my_complex_struct)getComplex __attribute__((objc_direct)) {
struct my_complex_struct st = {.a = 42};
return st;
}
// CHECK-LABEL: define hidden i64 @"+[Root classGetComplex]D"(ptr noundef %self)
+ (struct my_complex_struct)classGetComplex __attribute__((objc_direct)) {
struct my_complex_struct st = {.a = 42};
return st;
}
// CHECK-LABEL: define hidden void @"-[Root getAggregate]D"(ptr {{.*}} sret(%struct.my_aggregate_struct) {{.*}} %agg.result, ptr noundef %self)
- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) {
struct my_aggregate_struct st = {.a = 42};
return st;
}
// CHECK-LABEL: define hidden void @"+[Root classGetAggregate]D"(ptr {{.*}} sret(%struct.my_aggregate_struct) {{.*}} %agg.result, ptr noundef %self)
+ (struct my_aggregate_struct)classGetAggregate __attribute__((objc_direct)) {
struct my_aggregate_struct st = {.a = 42};
return st;
}
// CHECK-LABEL: define hidden void @"-[Root accessCmd]D"(ptr noundef %self)
- (void)accessCmd __attribute__((objc_direct)) {
// loading the _cmd selector
SEL sel = _cmd;
}
@end
// CHECK-LABEL: define hidden i32 @"-[Root intProperty]D"(ptr noundef %self)
// CHECK-LABEL: define hidden ptr @"-[Root objectProperty]D"(ptr noundef %self)
@interface Foo : Root {
id __strong _cause_cxx_destruct;
}
@property(nonatomic, readonly, direct) int getDirect_setDynamic;
@property(nonatomic, readonly) int getDynamic_setDirect;
@end
@interface Foo ()
@property(nonatomic, readwrite) int getDirect_setDynamic;
@property(nonatomic, readwrite, direct) int getDynamic_setDirect;
- (int)directMethodInExtension __attribute__((objc_direct));
@end
@interface Foo (Cat)
- (int)directMethodInCategory __attribute__((objc_direct));
@end
__attribute__((objc_direct_members))
@implementation Foo
// CHECK-LABEL: define hidden i32 @"-[Foo directMethodInExtension]D"(ptr noundef %self)
- (int)directMethodInExtension {
return 42;
}
// CHECK-LABEL: define hidden i32 @"-[Foo getDirect_setDynamic]D"(ptr noundef %self)
// CHECK-LABEL: define internal void @"\01-[Foo setGetDirect_setDynamic:]"(ptr noundef %self, ptr noundef %_cmd, i32 noundef %getDirect_setDynamic)
// CHECK-LABEL: define internal i32 @"\01-[Foo getDynamic_setDirect]"(ptr noundef %self, ptr noundef %_cmd)
// CHECK-LABEL: define hidden void @"-[Foo setGetDynamic_setDirect:]D"(ptr noundef %self, i32 noundef %getDynamic_setDirect)
@end
@implementation Foo (Cat)
// CHECK-LABEL: define hidden i32 @"-[Foo directMethodInCategory]D"(ptr noundef %self)
- (int)directMethodInCategory {
return 42;
}
// CHECK-LABEL: define hidden i32 @"-[Foo directMethodInCategoryNoDecl]D"(ptr noundef %self)
- (int)directMethodInCategoryNoDecl __attribute__((objc_direct)) {
return 42;
}
@end
// CHECK-LABEL: define{{.*}} i32 @useRoot(ptr noundef %r)
int useRoot(Root *r) {
// CHECK: call i32 @"-[Root getInt]D_thunk"(ptr noundef %{{[0-9]+}})
// CHECK: call i32 @"-[Root intProperty]D_thunk"(ptr noundef %{{[0-9]+}})
// CHECK: call i32 @"-[Root intProperty2]D_thunk"(ptr noundef %{{[0-9]+}})
return [r getInt] + [r intProperty] + [r intProperty2];
}
// Thunks are emitted after the first function that uses them
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Root getInt]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Root getInt]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Root intProperty]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Root intProperty]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Root intProperty2]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Root intProperty2]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
// CHECK-LABEL: define{{.*}} i32 @useFoo(ptr noundef %f)
int useFoo(Foo *f) {
// CHECK: call void @"-[Foo setGetDynamic_setDirect:]D_thunk"(ptr noundef %{{[0-9]+}}, i32 noundef 1)
// CHECK: call i32 @"-[Foo getDirect_setDynamic]D_thunk"(ptr noundef %{{[0-9]+}})
// CHECK: call i32 @"-[Foo directMethodInExtension]D_thunk"(ptr noundef %{{[0-9]+}})
// CHECK: call i32 @"-[Foo directMethodInCategory]D_thunk"(ptr noundef %{{[0-9]+}})
// CHECK: call i32 @"-[Foo directMethodInCategoryNoDecl]D_thunk"(ptr noundef %{{[0-9]+}})
[f setGetDynamic_setDirect:1];
return [f getDirect_setDynamic] +
[f directMethodInExtension] +
[f directMethodInCategory] +
[f directMethodInCategoryNoDecl];
}
// CHECK-LABEL: define linkonce_odr hidden void @"-[Foo setGetDynamic_setDirect:]D_thunk"(ptr noundef %self, i32 noundef
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: musttail call void @"-[Foo setGetDynamic_setDirect:]D"(ptr noundef %self, i32 noundef
// CHECK: ret void
// CHECK: dummy_ret_block:
// CHECK: ret void
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Foo getDirect_setDynamic]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Foo getDirect_setDynamic]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Foo directMethodInExtension]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Foo directMethodInExtension]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Foo directMethodInCategory]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Foo directMethodInCategory]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[Foo directMethodInCategoryNoDecl]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[Foo directMethodInCategoryNoDecl]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
__attribute__((objc_root_class))
@interface RootDeclOnly
@property(direct, readonly) int intProperty;
@end
__attribute__((objc_root_class, weak_import))
@interface WeakRoot
+ (int)classGetInt __attribute__((objc_direct, weak_import));
@end
// CHECK-LABEL: define{{.*}} i32 @useRootDeclOnly(ptr noundef %r)
int useRootDeclOnly(RootDeclOnly *r) {
// CHECK: call i32 @"-[RootDeclOnly intProperty]D_thunk"(ptr noundef %{{[0-9]+}})
return [r intProperty];
}
// Verify thunk is generated for external direct method
// CHECK: declare{{.*}} i32 @"-[RootDeclOnly intProperty]D"(ptr)
// CHECK-LABEL: define linkonce_odr hidden i32 @"-[RootDeclOnly intProperty]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"-[RootDeclOnly intProperty]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block:
int useSRet(Root *r) {
return (
// First call is to instance method - uses thunk
// CHECK: call i64 @"-[Root getComplex]D_thunk"
[r getComplex].a +
// TODO: we should know that this instance is non nil.
// CHECK: call void @"-[Root getAggregate]D_thunk"
[r getAggregate].a +
// TODO: The compiler is not smart enough to know the class object must be realized yet.
// CHECK: call i64 @"+[Root classGetComplex]D_thunk"(ptr noundef
[Root classGetComplex].a +
// CHECK: call void @"+[Root classGetAggregate]D_thunk"(ptr {{.*}}sret
[Root classGetAggregate].a
);
}
// CHECK-LABEL: define linkonce_odr hidden i64 @"-[Root getComplex]D_thunk"
// CHECK-LABEL: define linkonce_odr hidden void @"-[Root getAggregate]D_thunk"(ptr dead_on_unwind writable sret(%struct.my_aggregate_struct) {{.*}} %agg.result, ptr noundef %self)
// CHECK: entry:
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: musttail call void @"-[Root getAggregate]D"(ptr dead_on_unwind writable sret(%struct.my_aggregate_struct) {{.*}} %agg.result, ptr noundef %self)
// CHECK: ret void
// CHECK: dummy_ret_block:
// CHECK: ret void
// Class method thunks should have class realization (objc_msgSend)
// instead of a nil check.
// CHECK-LABEL: define linkonce_odr hidden i64 @"+[Root classGetComplex]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK-NOT: icmp eq ptr
// CHECK: call ptr @objc_msgSend
// CHECK: musttail call i64 @"+[Root classGetComplex]D"(ptr noundef %self)
// CHECK: ret i64
// CHECK-LABEL: define linkonce_odr hidden void @"+[Root classGetAggregate]D_thunk"(ptr {{.*}} sret(%struct.my_aggregate_struct) {{.*}} %agg.result, ptr noundef %self)
// CHECK: entry:
// CHECK-NOT: icmp eq ptr
// CHECK: call ptr @objc_msgSend
// CHECK: musttail call void @"+[Root classGetAggregate]D"(ptr {{.*}} sret(%struct.my_aggregate_struct) {{.*}} %agg.result, ptr noundef %self)
// CHECK: ret void
// CHECK-LABEL: define{{.*}} i32 @useWeakRoot()
int useWeakRoot() {
// CHECK: call i32 @"+[WeakRoot classGetInt]D_thunk"
return [WeakRoot classGetInt];
}
// Weakly linked class method thunks should have class realization
// AND a nil check (the weak class may not exist at runtime).
// CHECK-LABEL: define linkonce_odr hidden i32 @"+[WeakRoot classGetInt]D_thunk"(ptr noundef %self)
// CHECK: entry:
// CHECK: call ptr @objc_msgSend
// CHECK: %[[IS_NIL:.*]] = icmp eq ptr {{.*}}, null
// CHECK: br i1 %[[IS_NIL]], label %objc_direct_method.self_is_nil, label %objc_direct_method.cont
// CHECK: objc_direct_method.self_is_nil:
// CHECK: call void @llvm.memset
// CHECK: br label %dummy_ret_block
// CHECK: objc_direct_method.cont:
// CHECK: %[[RET:.*]] = musttail call i32 @"+[WeakRoot classGetInt]D"(ptr noundef %self)
// CHECK: ret i32 %[[RET]]
// CHECK: dummy_ret_block: