| // RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o - %s | FileCheck %s | |
| // TODO: actually test most of this instead of just emitting it | |
| int printf(const char *, ...); | |
| @interface Root | |
| -(id) alloc; | |
| -(id) init; | |
| @end | |
| @interface A : Root { | |
| int x; | |
| int y, ro, z; | |
| id ob0, ob1, ob2, ob3, ob4; | |
| } | |
| @property int x; | |
| @property int y; | |
| @property int z; | |
| @property(readonly) int ro; | |
| @property(assign) id ob0; | |
| @property(retain) id ob1; | |
| @property(copy) id ob2; | |
| @property(retain, nonatomic) id ob3; | |
| @property(copy, nonatomic) id ob4; | |
| @end | |
| @implementation A | |
| @dynamic x; | |
| @synthesize y; | |
| @synthesize z = z; | |
| @synthesize ro; | |
| @synthesize ob0; | |
| @synthesize ob1; | |
| @synthesize ob2; | |
| @synthesize ob3; | |
| @synthesize ob4; | |
| -(int) y { | |
| return x + 1; | |
| } | |
| -(void) setZ: (int) arg { | |
| x = arg - 1; | |
| } | |
| @end | |
| @interface A (Cat) | |
| @property int dyn; | |
| @end | |
| @implementation A (Cat) | |
| -(int) dyn { | |
| return 10; | |
| } | |
| @end | |
| // Test that compound operations only compute the base once. | |
| // CHECK-LABEL: define{{.*}} void @test2 | |
| A *test2_helper(void); | |
| void test2(void) { | |
| // CHECK: [[BASE:%.*]] = call ptr @test2_helper() | |
| // CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr | |
| // CHECK-NEXT: [[LD:%.*]] = call i32 @objc_msgSend(ptr noundef [[BASE]], ptr noundef [[SEL]]) | |
| // CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LD]], 1 | |
| // CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr | |
| // CHECK-NEXT: call void @objc_msgSend(ptr noundef [[BASE]], ptr noundef [[SEL]], i32 noundef [[ADD]]) | |
| test2_helper().dyn++; | |
| // CHECK: [[BASE:%.*]] = call ptr @test2_helper() | |
| // CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr | |
| // CHECK-NEXT: [[LD:%.*]] = call i32 @objc_msgSend(ptr noundef [[BASE]], ptr noundef [[SEL]]) | |
| // CHECK-NEXT: [[ADD:%.*]] = mul nsw i32 [[LD]], 10 | |
| // CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr | |
| // CHECK-NEXT: call void @objc_msgSend(ptr noundef [[BASE]], ptr noundef [[SEL]], i32 noundef [[ADD]]) | |
| test2_helper().dyn *= 10; | |
| } | |
| // Test aggregate initialization from property reads. | |
| // Not crashing is good enough for the property-specific test. | |
| struct test3_struct { int x,y,z; }; | |
| struct test3_nested { struct test3_struct t; }; | |
| @interface test3_object | |
| @property struct test3_struct s; | |
| @end | |
| void test3(test3_object *p) { | |
| struct test3_struct array[1] = { p.s }; | |
| struct test3_nested agg = { p.s }; | |
| } | |
| // PR8742 | |
| @interface Test4 {} | |
| @property float f; | |
| @end | |
| // CHECK-LABEL: define{{.*}} void @test4 | |
| void test4(Test4 *t) { | |
| extern int test4_printf(const char *, ...); | |
| // CHECK: [[TMP:%.*]] = call float @objc_msgSend | |
| // CHECK-NEXT: [[EXT:%.*]] = fpext float [[TMP]] to double | |
| // CHECK-NEXT: call i32 (ptr, ...) @test4_printf(ptr {{.*}}, double noundef [[EXT]]) | |
| // CHECK-NEXT: ret void | |
| test4_printf("%.2f", t.f); | |
| } | |
| @interface Test5 { | |
| unsigned _x : 5; | |
| } | |
| @property unsigned x; | |
| @end | |
| @implementation Test5 | |
| @synthesize x = _x; | |
| @end | |
| @interface Test6 | |
| @property void (*prop)(void); | |
| @end | |
| void test6_func(void); | |
| void test6(Test6 *a) { | |
| a.prop = test6_func; | |
| } | |
| @interface Test7 | |
| @property unsigned char x; | |
| @end | |
| void test7(Test7 *t) { | |
| t.x &= 2; | |
| t.x |= 5; | |
| t.x ^= 8; | |
| } | |
| // CHECK: define{{.*}} void @test7(ptr | |
| // CHECK: [[T:%.*]] = alloca ptr, | |
| // CHECK-NEXT: store | |
| // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[T]], align | |
| // CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES | |
| // CHECK-NEXT: [[T2:%.*]] = call zeroext i8 | |
| // CHECK-NEXT: [[T3:%.*]] = zext i8 [[T2]] to i32 | |
| // CHECK-NEXT: [[T4:%.*]] = and i32 [[T3]], 2 | |
| // CHECK-NEXT: [[T5:%.*]] = trunc i32 [[T4]] to i8 | |
| // CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES | |
| // CHECK-NEXT: call void | |
| // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[T]], align | |
| // CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES | |
| // CHECK-NEXT: [[T2:%.*]] = call zeroext i8 | |
| // CHECK-NEXT: [[T3:%.*]] = zext i8 [[T2]] to i32 | |
| // CHECK-NEXT: [[T4:%.*]] = or i32 [[T3]], 5 | |
| // CHECK-NEXT: [[T5:%.*]] = trunc i32 [[T4]] to i8 | |
| // CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES | |
| // CHECK-NEXT: call void | |
| // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[T]], align | |
| // CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES | |
| // CHECK-NEXT: [[T2:%.*]] = call zeroext i8 | |
| // CHECK-NEXT: [[T3:%.*]] = zext i8 [[T2]] to i32 | |
| // CHECK-NEXT: [[T4:%.*]] = xor i32 [[T3]], 8 | |
| // CHECK-NEXT: [[T5:%.*]] = trunc i32 [[T4]] to i8 | |
| // CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES | |
| // CHECK-NEXT: call void | |
| // CHECK-NEXT: ret void |