| // 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: |