1 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify -emit-llvm -o - %s \
2 // RUN: | FileCheck -check-prefix=CHECK-X86-64 %s
3 // RUN: %clang_cc1 -triple powerpc64-unknown-unknown -verify -emit-llvm -o - %s \
4 // RUN: | FileCheck -check-prefix=CHECK-PPC64 %s
6 // Tests for bitfield access patterns in C++ with special attention to
7 // conformance to C++11 memory model requirements.
10 // Test basic bitfield layout access across interesting byte and word
11 // boundaries on both little endian and big endian platforms.
12 struct __attribute__((packed)) S {
22 unsigned read00(S* s) {
23 // CHECK-X86-64: define i32 @_ZN2N06read00
24 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
25 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
26 // CHECK-X86-64: %[[and:.*]] = and i64 %[[val]], 16383
27 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
28 // CHECK-X86-64: ret i32 %[[trunc]]
29 // CHECK-PPC64: define zeroext i32 @_ZN2N06read00
30 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
31 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
32 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 50
33 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[shr]] to i32
34 // CHECK-PPC64: ret i32 %[[trunc]]
37 unsigned read01(S* s) {
38 // CHECK-X86-64: define i32 @_ZN2N06read01
39 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
40 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
41 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 14
42 // CHECK-X86-64: %[[and:.*]] = and i64 %[[shr]], 3
43 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
44 // CHECK-X86-64: ret i32 %[[trunc]]
45 // CHECK-PPC64: define zeroext i32 @_ZN2N06read01
46 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
47 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
48 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 48
49 // CHECK-PPC64: %[[and:.*]] = and i64 %[[shr]], 3
50 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
51 // CHECK-PPC64: ret i32 %[[trunc]]
54 unsigned read20(S* s) {
55 // CHECK-X86-64: define i32 @_ZN2N06read20
56 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
57 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
58 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 16
59 // CHECK-X86-64: %[[and:.*]] = and i64 %[[shr]], 63
60 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
61 // CHECK-X86-64: ret i32 %[[trunc]]
62 // CHECK-PPC64: define zeroext i32 @_ZN2N06read20
63 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
64 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
65 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 42
66 // CHECK-PPC64: %[[and:.*]] = and i64 %[[shr]], 63
67 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
68 // CHECK-PPC64: ret i32 %[[trunc]]
71 unsigned read21(S* s) {
72 // CHECK-X86-64: define i32 @_ZN2N06read21
73 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
74 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
75 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 22
76 // CHECK-X86-64: %[[and:.*]] = and i64 %[[shr]], 3
77 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
78 // CHECK-X86-64: ret i32 %[[trunc]]
79 // CHECK-PPC64: define zeroext i32 @_ZN2N06read21
80 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
81 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
82 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 40
83 // CHECK-PPC64: %[[and:.*]] = and i64 %[[shr]], 3
84 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
85 // CHECK-PPC64: ret i32 %[[trunc]]
88 unsigned read30(S* s) {
89 // CHECK-X86-64: define i32 @_ZN2N06read30
90 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
91 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
92 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 24
93 // CHECK-X86-64: %[[and:.*]] = and i64 %[[shr]], 1073741823
94 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
95 // CHECK-X86-64: ret i32 %[[trunc]]
96 // CHECK-PPC64: define zeroext i32 @_ZN2N06read30
97 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
98 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
99 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 10
100 // CHECK-PPC64: %[[and:.*]] = and i64 %[[shr]], 1073741823
101 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
102 // CHECK-PPC64: ret i32 %[[trunc]]
105 unsigned read31(S* s) {
106 // CHECK-X86-64: define i32 @_ZN2N06read31
107 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
108 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
109 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 54
110 // CHECK-X86-64: %[[and:.*]] = and i64 %[[shr]], 3
111 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
112 // CHECK-X86-64: ret i32 %[[trunc]]
113 // CHECK-PPC64: define zeroext i32 @_ZN2N06read31
114 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
115 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
116 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 8
117 // CHECK-PPC64: %[[and:.*]] = and i64 %[[shr]], 3
118 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
119 // CHECK-PPC64: ret i32 %[[trunc]]
122 unsigned read70(S* s) {
123 // CHECK-X86-64: define i32 @_ZN2N06read70
124 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
125 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
126 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 56
127 // CHECK-X86-64: %[[and:.*]] = and i64 %[[shr]], 63
128 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
129 // CHECK-X86-64: ret i32 %[[trunc]]
130 // CHECK-PPC64: define zeroext i32 @_ZN2N06read70
131 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
132 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
133 // CHECK-PPC64: %[[shr:.*]] = lshr i64 %[[val]], 2
134 // CHECK-PPC64: %[[and:.*]] = and i64 %[[shr]], 63
135 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
136 // CHECK-PPC64: ret i32 %[[trunc]]
139 unsigned read71(S* s) {
140 // CHECK-X86-64: define i32 @_ZN2N06read71
141 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
142 // CHECK-X86-64: %[[val:.*]] = load i64* %[[ptr]]
143 // CHECK-X86-64: %[[shr:.*]] = lshr i64 %[[val]], 62
144 // CHECK-X86-64: %[[trunc:.*]] = trunc i64 %[[shr]] to i32
145 // CHECK-X86-64: ret i32 %[[trunc]]
146 // CHECK-PPC64: define zeroext i32 @_ZN2N06read71
147 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64*
148 // CHECK-PPC64: %[[val:.*]] = load i64* %[[ptr]]
149 // CHECK-PPC64: %[[and:.*]] = and i64 %[[val]], 3
150 // CHECK-PPC64: %[[trunc:.*]] = trunc i64 %[[and]] to i32
151 // CHECK-PPC64: ret i32 %[[trunc]]
157 // Ensure that neither loads nor stores to bitfields are not widened into
158 // other memory locations. (PR13691)
160 // NOTE: We could potentially widen loads based on their alignment if we are
161 // comfortable requiring that subsequent memory locations within the
162 // alignment-widened load are not volatile.
168 unsigned read(S* s) {
169 // CHECK-X86-64: define i32 @_ZN2N14read
170 // CHECK-X86-64: %[[ptr:.*]] = getelementptr inbounds %{{.*}}* %{{.*}}, i32 0, i32 1
171 // CHECK-X86-64: %[[val:.*]] = load i8* %[[ptr]]
172 // CHECK-X86-64: %[[and:.*]] = and i8 %[[val]], 1
173 // CHECK-X86-64: %[[ext:.*]] = zext i8 %[[and]] to i32
174 // CHECK-X86-64: ret i32 %[[ext]]
175 // CHECK-PPC64: define zeroext i32 @_ZN2N14read
176 // CHECK-PPC64: %[[ptr:.*]] = getelementptr inbounds %{{.*}}* %{{.*}}, i32 0, i32 1
177 // CHECK-PPC64: %[[val:.*]] = load i8* %[[ptr]]
178 // CHECK-PPC64: %[[shr:.*]] = lshr i8 %[[val]], 7
179 // CHECK-PPC64: %[[ext:.*]] = zext i8 %[[shr]] to i32
180 // CHECK-PPC64: ret i32 %[[ext]]
183 void write(S* s, unsigned x) {
184 // CHECK-X86-64: define void @_ZN2N15write
185 // CHECK-X86-64: %[[ptr:.*]] = getelementptr inbounds %{{.*}}* %{{.*}}, i32 0, i32 1
186 // CHECK-X86-64: %[[x_trunc:.*]] = trunc i32 %{{.*}} to i8
187 // CHECK-X86-64: %[[old:.*]] = load i8* %[[ptr]]
188 // CHECK-X86-64: %[[x_and:.*]] = and i8 %[[x_trunc]], 1
189 // CHECK-X86-64: %[[old_and:.*]] = and i8 %[[old]], -2
190 // CHECK-X86-64: %[[new:.*]] = or i8 %[[old_and]], %[[x_and]]
191 // CHECK-X86-64: store i8 %[[new]], i8* %[[ptr]]
192 // CHECK-PPC64: define void @_ZN2N15write
193 // CHECK-PPC64: %[[ptr:.*]] = getelementptr inbounds %{{.*}}* %{{.*}}, i32 0, i32 1
194 // CHECK-PPC64: %[[x_trunc:.*]] = trunc i32 %{{.*}} to i8
195 // CHECK-PPC64: %[[old:.*]] = load i8* %[[ptr]]
196 // CHECK-PPC64: %[[x_and:.*]] = and i8 %[[x_trunc]], 1
197 // CHECK-PPC64: %[[x_shl:.*]] = shl i8 %[[x_and]], 7
198 // CHECK-PPC64: %[[old_and:.*]] = and i8 %[[old]], 127
199 // CHECK-PPC64: %[[new:.*]] = or i8 %[[old_and]], %[[x_shl]]
200 // CHECK-PPC64: store i8 %[[new]], i8* %[[ptr]]
206 // Do widen loads and stores to bitfields when those bitfields have padding
207 // within the struct following them.
212 unsigned read(S* s) {
213 // CHECK-X86-64: define i32 @_ZN2N24read
214 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
215 // CHECK-X86-64: %[[val:.*]] = load i32* %[[ptr]]
216 // CHECK-X86-64: %[[and:.*]] = and i32 %[[val]], 16777215
217 // CHECK-X86-64: ret i32 %[[and]]
218 // CHECK-PPC64: define zeroext i32 @_ZN2N24read
219 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
220 // CHECK-PPC64: %[[val:.*]] = load i32* %[[ptr]]
221 // CHECK-PPC64: %[[shr:.*]] = lshr i32 %[[val]], 8
222 // CHECK-PPC64: ret i32 %[[shr]]
225 void write(S* s, unsigned x) {
226 // CHECK-X86-64: define void @_ZN2N25write
227 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
228 // CHECK-X86-64: %[[old:.*]] = load i32* %[[ptr]]
229 // CHECK-X86-64: %[[x_and:.*]] = and i32 %{{.*}}, 16777215
230 // CHECK-X86-64: %[[old_and:.*]] = and i32 %[[old]], -16777216
231 // CHECK-X86-64: %[[new:.*]] = or i32 %[[old_and]], %[[x_and]]
232 // CHECK-X86-64: store i32 %[[new]], i32* %[[ptr]]
233 // CHECK-PPC64: define void @_ZN2N25write
234 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
235 // CHECK-PPC64: %[[old:.*]] = load i32* %[[ptr]]
236 // CHECK-PPC64: %[[x_and:.*]] = and i32 %{{.*}}, 16777215
237 // CHECK-PPC64: %[[x_shl:.*]] = shl i32 %[[x_and]], 8
238 // CHECK-PPC64: %[[old_and:.*]] = and i32 %[[old]], 255
239 // CHECK-PPC64: %[[new:.*]] = or i32 %[[old_and]], %[[x_shl]]
240 // CHECK-PPC64: store i32 %[[new]], i32* %[[ptr]]
246 // Do widen loads and stores to bitfields through the trailing padding at the
251 unsigned read(S* s) {
252 // CHECK-X86-64: define i32 @_ZN2N34read
253 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
254 // CHECK-X86-64: %[[val:.*]] = load i32* %[[ptr]]
255 // CHECK-X86-64: %[[and:.*]] = and i32 %[[val]], 16777215
256 // CHECK-X86-64: ret i32 %[[and]]
257 // CHECK-PPC64: define zeroext i32 @_ZN2N34read
258 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
259 // CHECK-PPC64: %[[val:.*]] = load i32* %[[ptr]]
260 // CHECK-PPC64: %[[shr:.*]] = lshr i32 %[[val]], 8
261 // CHECK-PPC64: ret i32 %[[shr]]
264 void write(S* s, unsigned x) {
265 // CHECK-X86-64: define void @_ZN2N35write
266 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
267 // CHECK-X86-64: %[[old:.*]] = load i32* %[[ptr]]
268 // CHECK-X86-64: %[[x_and:.*]] = and i32 %{{.*}}, 16777215
269 // CHECK-X86-64: %[[old_and:.*]] = and i32 %[[old]], -16777216
270 // CHECK-X86-64: %[[new:.*]] = or i32 %[[old_and]], %[[x_and]]
271 // CHECK-X86-64: store i32 %[[new]], i32* %[[ptr]]
272 // CHECK-PPC64: define void @_ZN2N35write
273 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
274 // CHECK-PPC64: %[[old:.*]] = load i32* %[[ptr]]
275 // CHECK-PPC64: %[[x_and:.*]] = and i32 %{{.*}}, 16777215
276 // CHECK-PPC64: %[[x_shl:.*]] = shl i32 %[[x_and]], 8
277 // CHECK-PPC64: %[[old_and:.*]] = and i32 %[[old]], 255
278 // CHECK-PPC64: %[[new:.*]] = or i32 %[[old_and]], %[[x_shl]]
279 // CHECK-PPC64: store i32 %[[new]], i32* %[[ptr]]
285 // Do NOT widen loads and stores to bitfields into padding at the end of
286 // a class which might end up with members inside of it when inside a derived
293 // Imagine some other translation unit introduces:
295 struct Derived : public Base {
299 unsigned read(Base* s) {
300 // FIXME: We should widen this load as long as the function isn't being
301 // instrumented by thread-sanitizer.
303 // CHECK-X86-64: define i32 @_ZN2N44read
304 // CHECK-X86-64: %[[gep:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
305 // CHECK-X86-64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24*
306 // CHECK-X86-64: %[[val:.*]] = load i24* %[[ptr]]
307 // CHECK-X86-64: %[[ext:.*]] = zext i24 %[[val]] to i32
308 // CHECK-X86-64: ret i32 %[[ext]]
309 // CHECK-PPC64: define zeroext i32 @_ZN2N44read
310 // CHECK-PPC64: %[[gep:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
311 // CHECK-PPC64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24*
312 // CHECK-PPC64: %[[val:.*]] = load i24* %[[ptr]]
313 // CHECK-PPC64: %[[ext:.*]] = zext i24 %[[val]] to i32
314 // CHECK-PPC64: ret i32 %[[ext]]
317 void write(Base* s, unsigned x) {
318 // CHECK-X86-64: define void @_ZN2N45write
319 // CHECK-X86-64: %[[gep:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
320 // CHECK-X86-64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24*
321 // CHECK-X86-64: %[[new:.*]] = trunc i32 %{{.*}} to i24
322 // CHECK-X86-64: store i24 %[[new]], i24* %[[ptr]]
323 // CHECK-PPC64: define void @_ZN2N45write
324 // CHECK-PPC64: %[[gep:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
325 // CHECK-PPC64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24*
326 // CHECK-PPC64: %[[new:.*]] = trunc i32 %{{.*}} to i24
327 // CHECK-PPC64: store i24 %[[new]], i24* %[[ptr]]
333 // Widen through padding at the end of a struct even if that struct
334 // participates in a union with another struct which has a separate field in
335 // that location. The reasoning is that if the operation is storing to that
336 // member of the union, it must be the active member, and thus we can write
337 // through the padding. If it is a load, it might be a load of a common
338 // prefix through a non-active member, but in such a case the extra bits
339 // loaded are masked off anyways.
341 struct X { unsigned b : 24; char c; } x;
342 struct Y { unsigned b : 24; } y;
344 unsigned read(U* u) {
345 // CHECK-X86-64: define i32 @_ZN2N54read
346 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
347 // CHECK-X86-64: %[[val:.*]] = load i32* %[[ptr]]
348 // CHECK-X86-64: %[[and:.*]] = and i32 %[[val]], 16777215
349 // CHECK-X86-64: ret i32 %[[and]]
350 // CHECK-PPC64: define zeroext i32 @_ZN2N54read
351 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
352 // CHECK-PPC64: %[[val:.*]] = load i32* %[[ptr]]
353 // CHECK-PPC64: %[[shr:.*]] = lshr i32 %[[val]], 8
354 // CHECK-PPC64: ret i32 %[[shr]]
357 void write(U* u, unsigned x) {
358 // CHECK-X86-64: define void @_ZN2N55write
359 // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
360 // CHECK-X86-64: %[[old:.*]] = load i32* %[[ptr]]
361 // CHECK-X86-64: %[[x_and:.*]] = and i32 %{{.*}}, 16777215
362 // CHECK-X86-64: %[[old_and:.*]] = and i32 %[[old]], -16777216
363 // CHECK-X86-64: %[[new:.*]] = or i32 %[[old_and]], %[[x_and]]
364 // CHECK-X86-64: store i32 %[[new]], i32* %[[ptr]]
365 // CHECK-PPC64: define void @_ZN2N55write
366 // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32*
367 // CHECK-PPC64: %[[old:.*]] = load i32* %[[ptr]]
368 // CHECK-PPC64: %[[x_and:.*]] = and i32 %{{.*}}, 16777215
369 // CHECK-PPC64: %[[x_shl:.*]] = shl i32 %[[x_and]], 8
370 // CHECK-PPC64: %[[old_and:.*]] = and i32 %[[old]], 255
371 // CHECK-PPC64: %[[new:.*]] = or i32 %[[old_and]], %[[x_shl]]
372 // CHECK-PPC64: store i32 %[[new]], i32* %[[ptr]]
378 // Zero-length bitfields partition the memory locations of bitfields for the
379 // purposes of the memory model. That means stores must not span zero-length
380 // bitfields and loads may only span them when we are not instrumenting with
382 // FIXME: We currently don't widen loads even without thread sanitizer, even
387 unsigned char b2 : 8;
389 unsigned read(S* s) {
390 // CHECK-X86-64: define i32 @_ZN2N64read
391 // CHECK-X86-64: %[[ptr1:.*]] = bitcast {{.*}}* %{{.*}} to i24*
392 // CHECK-X86-64: %[[val1:.*]] = load i24* %[[ptr1]]
393 // CHECK-X86-64: %[[ext1:.*]] = zext i24 %[[val1]] to i32
394 // CHECK-X86-64: %[[ptr2:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
395 // CHECK-X86-64: %[[val2:.*]] = load i8* %[[ptr2]]
396 // CHECK-X86-64: %[[ext2:.*]] = zext i8 %[[val2]] to i32
397 // CHECK-X86-64: %[[add:.*]] = add nsw i32 %[[ext1]], %[[ext2]]
398 // CHECK-X86-64: ret i32 %[[add]]
399 // CHECK-PPC64: define zeroext i32 @_ZN2N64read
400 // CHECK-PPC64: %[[ptr1:.*]] = bitcast {{.*}}* %{{.*}} to i24*
401 // CHECK-PPC64: %[[val1:.*]] = load i24* %[[ptr1]]
402 // CHECK-PPC64: %[[ext1:.*]] = zext i24 %[[val1]] to i32
403 // CHECK-PPC64: %[[ptr2:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
404 // CHECK-PPC64: %[[val2:.*]] = load i8* %[[ptr2]]
405 // CHECK-PPC64: %[[ext2:.*]] = zext i8 %[[val2]] to i32
406 // CHECK-PPC64: %[[add:.*]] = add nsw i32 %[[ext1]], %[[ext2]]
407 // CHECK-PPC64: ret i32 %[[add]]
408 return s->b1 + s->b2;
410 void write(S* s, unsigned x) {
411 // CHECK-X86-64: define void @_ZN2N65write
412 // CHECK-X86-64: %[[ptr1:.*]] = bitcast {{.*}}* %{{.*}} to i24*
413 // CHECK-X86-64: %[[new1:.*]] = trunc i32 %{{.*}} to i24
414 // CHECK-X86-64: store i24 %[[new1]], i24* %[[ptr1]]
415 // CHECK-X86-64: %[[new2:.*]] = trunc i32 %{{.*}} to i8
416 // CHECK-X86-64: %[[ptr2:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
417 // CHECK-X86-64: store i8 %[[new2]], i8* %[[ptr2]]
418 // CHECK-PPC64: define void @_ZN2N65write
419 // CHECK-PPC64: %[[ptr1:.*]] = bitcast {{.*}}* %{{.*}} to i24*
420 // CHECK-PPC64: %[[new1:.*]] = trunc i32 %{{.*}} to i24
421 // CHECK-PPC64: store i24 %[[new1]], i24* %[[ptr1]]
422 // CHECK-PPC64: %[[new2:.*]] = trunc i32 %{{.*}} to i8
423 // CHECK-PPC64: %[[ptr2:.*]] = getelementptr inbounds {{.*}}* %{{.*}}, i32 0, i32 1
424 // CHECK-PPC64: store i8 %[[new2]], i8* %[[ptr2]]