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