|  | // RUN: %clang_cc1 -emit-llvm -triple x86_64 -O3 -o %t.opt.ll %s \ | 
|  | // RUN:   -fdump-record-layouts > %t.dump.txt | 
|  | // RUN: FileCheck -check-prefix=CHECK-RECORD < %t.dump.txt %s | 
|  | // RUN: FileCheck -check-prefix=CHECK-OPT < %t.opt.ll %s | 
|  |  | 
|  | /****/ | 
|  |  | 
|  | // Check that we don't read off the end a packed 24-bit structure. | 
|  | // PR6176 | 
|  |  | 
|  | // CHECK-RECORD: *** Dumping IRgen Record Layout | 
|  | // CHECK-RECORD: Record: RecordDecl{{.*}}s0 | 
|  | // CHECK-RECORD: Layout: <CGRecordLayout | 
|  | // CHECK-RECORD:   LLVMType:%struct.s0 = type { [3 x i8] } | 
|  | // CHECK-RECORD:   IsZeroInitializable:1 | 
|  | // CHECK-RECORD:   BitFields:[ | 
|  | // CHECK-RECORD:     <CGBitFieldInfo Offset:0 Size:24 IsSigned:1 StorageSize:24 StorageOffset:0 | 
|  | struct __attribute((packed)) s0 { | 
|  | int f0 : 24; | 
|  | }; | 
|  |  | 
|  | struct s0 g0 = { 0xdeadbeef }; | 
|  |  | 
|  | int f0_load(struct s0 *a0) { | 
|  | int size_check[sizeof(struct s0) == 3 ? 1 : -1]; | 
|  | return a0->f0; | 
|  | } | 
|  | int f0_store(struct s0 *a0) { | 
|  | return (a0->f0 = 1); | 
|  | } | 
|  | int f0_reload(struct s0 *a0) { | 
|  | return (a0->f0 += 1); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i64 @test_0() | 
|  | // CHECK-OPT:  ret i64 1 | 
|  | // CHECK-OPT: } | 
|  | unsigned long long test_0(void) { | 
|  | struct s0 g0 = { 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g0.f0; | 
|  | res ^= f0_load(&g0) ^ f0_store(&g0) ^ f0_reload(&g0); | 
|  | res ^= g0.f0; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /****/ | 
|  |  | 
|  | // PR5591 | 
|  |  | 
|  | // CHECK-RECORD: *** Dumping IRgen Record Layout | 
|  | // CHECK-RECORD: Record: RecordDecl{{.*}}s1 | 
|  | // CHECK-RECORD: Layout: <CGRecordLayout | 
|  | // CHECK-RECORD:   LLVMType:%struct.s1 = type { [3 x i8] } | 
|  | // CHECK-RECORD:   IsZeroInitializable:1 | 
|  | // CHECK-RECORD:   BitFields:[ | 
|  | // CHECK-RECORD:     <CGBitFieldInfo Offset:0 Size:10 IsSigned:1 StorageSize:24 StorageOffset:0 | 
|  | // CHECK-RECORD:     <CGBitFieldInfo Offset:10 Size:10 IsSigned:1 StorageSize:24 StorageOffset:0 | 
|  |  | 
|  | #pragma pack(push) | 
|  | #pragma pack(1) | 
|  | struct __attribute((packed)) s1 { | 
|  | signed f0 : 10; | 
|  | signed f1 : 10; | 
|  | }; | 
|  | #pragma pack(pop) | 
|  |  | 
|  | struct s1 g1 = { 0xdeadbeef, 0xdeadbeef }; | 
|  |  | 
|  | int f1_load(struct s1 *a0) { | 
|  | int size_check[sizeof(struct s1) == 3 ? 1 : -1]; | 
|  | return a0->f1; | 
|  | } | 
|  | int f1_store(struct s1 *a0) { | 
|  | return (a0->f1 = 1234); | 
|  | } | 
|  | int f1_reload(struct s1 *a0) { | 
|  | return (a0->f1 += 1234); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i64 @test_1() | 
|  | // CHECK-OPT:  ret i64 210 | 
|  | // CHECK-OPT: } | 
|  | unsigned long long test_1(void) { | 
|  | struct s1 g1 = { 0xdeadbeef, 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g1.f0 ^ g1.f1; | 
|  | res ^= f1_load(&g1) ^ f1_store(&g1) ^ f1_reload(&g1); | 
|  | res ^= g1.f0 ^ g1.f1; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /****/ | 
|  |  | 
|  | // Check that we don't access beyond the bounds of a union. | 
|  | // | 
|  | // PR5567 | 
|  |  | 
|  | // CHECK-RECORD: *** Dumping IRgen Record Layout | 
|  | // CHECK-RECORD: Record: RecordDecl{{.*}}u2 | 
|  | // CHECK-RECORD: Layout: <CGRecordLayout | 
|  | // CHECK-RECORD:   LLVMType:%union.u2 = type { i8 } | 
|  | // CHECK-RECORD:   IsZeroInitializable:1 | 
|  | // CHECK-RECORD:   BitFields:[ | 
|  | // CHECK-RECORD:     <CGBitFieldInfo Offset:0 Size:3 IsSigned:0 StorageSize:8 StorageOffset:0 | 
|  |  | 
|  | union __attribute__((packed)) u2 { | 
|  | unsigned long long f0 : 3; | 
|  | }; | 
|  |  | 
|  | union u2 g2 = { 0xdeadbeef }; | 
|  |  | 
|  | int f2_load(union u2 *a0) { | 
|  | return a0->f0; | 
|  | } | 
|  | int f2_store(union u2 *a0) { | 
|  | return (a0->f0 = 1234); | 
|  | } | 
|  | int f2_reload(union u2 *a0) { | 
|  | return (a0->f0 += 1234); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i64 @test_2() | 
|  | // CHECK-OPT:  ret i64 2 | 
|  | // CHECK-OPT: } | 
|  | unsigned long long test_2(void) { | 
|  | union u2 g2 = { 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g2.f0; | 
|  | res ^= f2_load(&g2) ^ f2_store(&g2) ^ f2_reload(&g2); | 
|  | res ^= g2.f0; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | // PR5039 | 
|  |  | 
|  | struct s3 { | 
|  | long long f0 : 32; | 
|  | long long f1 : 32; | 
|  | }; | 
|  |  | 
|  | struct s3 g3 = { 0xdeadbeef, 0xdeadbeef }; | 
|  |  | 
|  | int f3_load(struct s3 *a0) { | 
|  | a0->f0 = 1; | 
|  | return a0->f0; | 
|  | } | 
|  | int f3_store(struct s3 *a0) { | 
|  | a0->f0 = 1; | 
|  | return (a0->f0 = 1234); | 
|  | } | 
|  | int f3_reload(struct s3 *a0) { | 
|  | a0->f0 = 1; | 
|  | return (a0->f0 += 1234); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i64 @test_3() | 
|  | // CHECK-OPT:  ret i64 -559039940 | 
|  | // CHECK-OPT: } | 
|  | unsigned long long test_3(void) { | 
|  | struct s3 g3 = { 0xdeadbeef, 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g3.f0 ^ g3.f1; | 
|  | res ^= f3_load(&g3) ^ f3_store(&g3) ^ f3_reload(&g3); | 
|  | res ^= g3.f0 ^ g3.f1; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | // This is a case where the bitfield access will straddle an alignment boundary | 
|  | // of its underlying type. | 
|  |  | 
|  | struct s4 { | 
|  | unsigned f0 : 16; | 
|  | unsigned f1 : 28 __attribute__ ((packed)); | 
|  | }; | 
|  |  | 
|  | struct s4 g4 = { 0xdeadbeef, 0xdeadbeef }; | 
|  |  | 
|  | int f4_load(struct s4 *a0) { | 
|  | return a0->f0 ^ a0->f1; | 
|  | } | 
|  | int f4_store(struct s4 *a0) { | 
|  | return (a0->f0 = 1234) ^ (a0->f1 = 5678); | 
|  | } | 
|  | int f4_reload(struct s4 *a0) { | 
|  | return (a0->f0 += 1234) ^ (a0->f1 += 5678); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i64 @test_4() | 
|  | // CHECK-OPT:  ret i64 4860 | 
|  | // CHECK-OPT: } | 
|  | unsigned long long test_4(void) { | 
|  | struct s4 g4 = { 0xdeadbeef, 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g4.f0 ^ g4.f1; | 
|  | res ^= f4_load(&g4) ^ f4_store(&g4) ^ f4_reload(&g4); | 
|  | res ^= g4.f0 ^ g4.f1; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | struct s5 { | 
|  | unsigned f0 : 2; | 
|  | _Bool f1 : 1; | 
|  | _Bool f2 : 1; | 
|  | }; | 
|  |  | 
|  | struct s5 g5 = { 0xdeadbeef, 0xdeadbeef }; | 
|  |  | 
|  | int f5_load(struct s5 *a0) { | 
|  | return a0->f0 ^ a0->f1; | 
|  | } | 
|  | int f5_store(struct s5 *a0) { | 
|  | return (a0->f0 = 0xF) ^ (a0->f1 = 0xF) ^ (a0->f2 = 0xF); | 
|  | } | 
|  | int f5_reload(struct s5 *a0) { | 
|  | return (a0->f0 += 0xF) ^ (a0->f1 += 0xF) ^ (a0->f2 += 0xF); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i64 @test_5() | 
|  | // CHECK-OPT:  ret i64 2 | 
|  | // CHECK-OPT: } | 
|  | unsigned long long test_5(void) { | 
|  | struct s5 g5 = { 0xdeadbeef, 0xdeadbeef, 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g5.f0 ^ g5.f1 ^ g5.f2; | 
|  | res ^= f5_load(&g5) ^ f5_store(&g5) ^ f5_reload(&g5); | 
|  | res ^= g5.f0 ^ g5.f1 ^ g5.f2; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | struct s6 { | 
|  | unsigned f0 : 2; | 
|  | }; | 
|  |  | 
|  | struct s6 g6 = { 0xF }; | 
|  |  | 
|  | int f6_load(struct s6 *a0) { | 
|  | return a0->f0; | 
|  | } | 
|  | int f6_store(struct s6 *a0) { | 
|  | return a0->f0 = 0x0; | 
|  | } | 
|  | int f6_reload(struct s6 *a0) { | 
|  | return (a0->f0 += 0xF); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} zeroext i1 @test_6() | 
|  | // CHECK-OPT:  ret i1 true | 
|  | // CHECK-OPT: } | 
|  | _Bool test_6(void) { | 
|  | struct s6 g6 = { 0xF }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g6.f0; | 
|  | res ^= f6_load(&g6); | 
|  | res ^= g6.f0; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | // Check that we compute the best alignment possible for each access. | 
|  | // | 
|  | // CHECK-RECORD: *** Dumping IRgen Record Layout | 
|  | // CHECK-RECORD: Record: RecordDecl{{.*}}s7 | 
|  | // CHECK-RECORD: Layout: <CGRecordLayout | 
|  | // CHECK-RECORD:   LLVMType:%struct.s7 = type <{ i32, i32, i32, i64, [12 x i8] }> | 
|  | // CHECK-RECORD:   IsZeroInitializable:1 | 
|  | // CHECK-RECORD:   BitFields:[ | 
|  | // CHECK-RECORD:     <CGBitFieldInfo Offset:0 Size:5 IsSigned:1 StorageSize:64 StorageOffset:12 | 
|  | // CHECK-RECORD:     <CGBitFieldInfo Offset:32 Size:29 IsSigned:1 StorageSize:64 StorageOffset:12 | 
|  |  | 
|  | struct __attribute__((aligned(16))) s7 { | 
|  | int a, b, c; | 
|  | int f0 : 5; | 
|  | int f1 : 29; | 
|  | }; | 
|  |  | 
|  | int f7_load(struct s7 *a0) { | 
|  | return a0->f0; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | // This is a case where we narrow the access width immediately. | 
|  |  | 
|  | struct __attribute__((packed)) s8 { | 
|  | char f0 : 4; | 
|  | char f1; | 
|  | int  f2 : 4; | 
|  | char f3 : 4; | 
|  | }; | 
|  |  | 
|  | struct s8 g8 = { 0xF }; | 
|  |  | 
|  | int f8_load(struct s8 *a0) { | 
|  | return a0->f0 ^ a0 ->f2 ^ a0->f3; | 
|  | } | 
|  | int f8_store(struct s8 *a0) { | 
|  | return (a0->f0 = 0xFD) ^ (a0->f2 = 0xFD) ^ (a0->f3 = 0xFD); | 
|  | } | 
|  | int f8_reload(struct s8 *a0) { | 
|  | return (a0->f0 += 0xFD) ^ (a0->f2 += 0xFD) ^ (a0->f3 += 0xFD); | 
|  | } | 
|  |  | 
|  | // CHECK-OPT-LABEL: define{{.*}} i32 @test_8() | 
|  | // CHECK-OPT:  ret i32 -3 | 
|  | // CHECK-OPT: } | 
|  | unsigned test_8(void) { | 
|  | struct s8 g8 = { 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef }; | 
|  | unsigned long long res = 0; | 
|  | res ^= g8.f0 ^ g8.f2 ^ g8.f3; | 
|  | res ^= f8_load(&g8) ^ f8_store(&g8) ^ f8_reload(&g8); | 
|  | res ^= g8.f0 ^ g8.f2 ^ g8.f3; | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /***/ | 
|  |  | 
|  | // This is another case where we narrow the access width immediately. | 
|  | struct __attribute__((packed)) s9 { | 
|  | unsigned f0 : 7; | 
|  | unsigned f1 : 7; | 
|  | unsigned f2 : 7; | 
|  | unsigned f3 : 7; | 
|  | unsigned f4 : 7; | 
|  | unsigned f5 : 7; | 
|  | unsigned f6 : 7; | 
|  | unsigned f7 : 7; | 
|  | }; | 
|  |  | 
|  | int f9_load(struct s9 *a0) { | 
|  | return a0->f7; | 
|  | } |