1 | // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s \ |
2 | // RUN: | FileCheck -check-prefix=CHECK-X86-64 %s |
3 | // RUN: %clang_cc1 -triple powerpc64-unknown-unknown -emit-llvm -o - %s \ |
4 | // RUN: | FileCheck -check-prefix=CHECK-PPC64 %s |
5 | // |
6 | // Tests for bitfield access patterns in C++ with special attention to |
7 | // conformance to C++11 memory model requirements. |
8 | |
9 | namespace N0 { |
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 { |
13 | unsigned b00 : 14; |
14 | unsigned b01 : 2; |
15 | unsigned b20 : 6; |
16 | unsigned b21 : 2; |
17 | unsigned b30 : 30; |
18 | unsigned b31 : 2; |
19 | unsigned b70 : 6; |
20 | unsigned b71 : 2; |
21 | }; |
22 | unsigned read00(S* s) { |
23 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read00 |
24 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
25 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read00 |
30 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
31 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
35 | return s->b00; |
36 | } |
37 | unsigned read01(S* s) { |
38 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read01 |
39 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
40 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read01 |
46 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
47 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
52 | return s->b01; |
53 | } |
54 | unsigned read20(S* s) { |
55 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read20 |
56 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
57 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read20 |
63 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
64 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
69 | return s->b20; |
70 | } |
71 | unsigned read21(S* s) { |
72 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read21 |
73 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
74 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read21 |
80 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
81 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
86 | return s->b21; |
87 | } |
88 | unsigned read30(S* s) { |
89 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read30 |
90 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
91 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read30 |
97 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
98 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
103 | return s->b30; |
104 | } |
105 | unsigned read31(S* s) { |
106 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read31 |
107 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
108 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read31 |
114 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
115 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
120 | return s->b31; |
121 | } |
122 | unsigned read70(S* s) { |
123 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read70 |
124 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
125 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read70 |
131 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
132 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
137 | return s->b70; |
138 | } |
139 | unsigned read71(S* s) { |
140 | // CHECK-X86-64-LABEL: define i32 @_ZN2N06read71 |
141 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
142 | // CHECK-X86-64: %[[val:.*]] = load i64, 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-LABEL: define zeroext i32 @_ZN2N06read71 |
147 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i64* |
148 | // CHECK-PPC64: %[[val:.*]] = load i64, 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]] |
152 | return s->b71; |
153 | } |
154 | } |
155 | |
156 | namespace N1 { |
157 | // Ensure that neither loads nor stores to bitfields are not widened into |
158 | // other memory locations. (PR13691) |
159 | // |
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. |
163 | struct S { |
164 | char a; |
165 | unsigned b : 1; |
166 | char c; |
167 | }; |
168 | unsigned read(S* s) { |
169 | // CHECK-X86-64-LABEL: define i32 @_ZN2N14read |
170 | // CHECK-X86-64: %[[ptr:.*]] = getelementptr inbounds %{{.*}}, %{{.*}}* %{{.*}}, i32 0, i32 1 |
171 | // CHECK-X86-64: %[[val:.*]] = load i8, 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-LABEL: define zeroext i32 @_ZN2N14read |
176 | // CHECK-PPC64: %[[ptr:.*]] = getelementptr inbounds %{{.*}}, %{{.*}}* %{{.*}}, i32 0, i32 1 |
177 | // CHECK-PPC64: %[[val:.*]] = load i8, 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]] |
181 | return s->b; |
182 | } |
183 | void write(S* s, unsigned x) { |
184 | // CHECK-X86-64-LABEL: 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, 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-LABEL: 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, 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]] |
201 | s->b = x; |
202 | } |
203 | } |
204 | |
205 | namespace N2 { |
206 | // Do widen loads and stores to bitfields when those bitfields have padding |
207 | // within the struct following them. |
208 | struct S { |
209 | unsigned b : 24; |
210 | void *p; |
211 | }; |
212 | unsigned read(S* s) { |
213 | // CHECK-X86-64-LABEL: define i32 @_ZN2N24read |
214 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
215 | // CHECK-X86-64: %[[val:.*]] = load i32, i32* %[[ptr]] |
216 | // CHECK-X86-64: %[[and:.*]] = and i32 %[[val]], 16777215 |
217 | // CHECK-X86-64: ret i32 %[[and]] |
218 | // CHECK-PPC64-LABEL: define zeroext i32 @_ZN2N24read |
219 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
220 | // CHECK-PPC64: %[[val:.*]] = load i32, i32* %[[ptr]] |
221 | // CHECK-PPC64: %[[shr:.*]] = lshr i32 %[[val]], 8 |
222 | // CHECK-PPC64: ret i32 %[[shr]] |
223 | return s->b; |
224 | } |
225 | void write(S* s, unsigned x) { |
226 | // CHECK-X86-64-LABEL: define void @_ZN2N25write |
227 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
228 | // CHECK-X86-64: %[[old:.*]] = load i32, 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-LABEL: define void @_ZN2N25write |
234 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
235 | // CHECK-PPC64: %[[old:.*]] = load i32, 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]] |
241 | s->b = x; |
242 | } |
243 | } |
244 | |
245 | namespace N3 { |
246 | // Do widen loads and stores to bitfields through the trailing padding at the |
247 | // end of a struct. |
248 | struct S { |
249 | unsigned b : 24; |
250 | }; |
251 | unsigned read(S* s) { |
252 | // CHECK-X86-64-LABEL: define i32 @_ZN2N34read |
253 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
254 | // CHECK-X86-64: %[[val:.*]] = load i32, i32* %[[ptr]] |
255 | // CHECK-X86-64: %[[and:.*]] = and i32 %[[val]], 16777215 |
256 | // CHECK-X86-64: ret i32 %[[and]] |
257 | // CHECK-PPC64-LABEL: define zeroext i32 @_ZN2N34read |
258 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
259 | // CHECK-PPC64: %[[val:.*]] = load i32, i32* %[[ptr]] |
260 | // CHECK-PPC64: %[[shr:.*]] = lshr i32 %[[val]], 8 |
261 | // CHECK-PPC64: ret i32 %[[shr]] |
262 | return s->b; |
263 | } |
264 | void write(S* s, unsigned x) { |
265 | // CHECK-X86-64-LABEL: define void @_ZN2N35write |
266 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
267 | // CHECK-X86-64: %[[old:.*]] = load i32, 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-LABEL: define void @_ZN2N35write |
273 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
274 | // CHECK-PPC64: %[[old:.*]] = load i32, 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]] |
280 | s->b = x; |
281 | } |
282 | } |
283 | |
284 | namespace N4 { |
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 |
287 | // class. |
288 | struct Base { |
289 | virtual ~Base() {} |
290 | |
291 | unsigned b : 24; |
292 | }; |
293 | // Imagine some other translation unit introduces: |
294 | #if 0 |
295 | struct Derived : public Base { |
296 | char c; |
297 | }; |
298 | #endif |
299 | unsigned read(Base* s) { |
300 | // FIXME: We should widen this load as long as the function isn't being |
301 | // instrumented by ThreadSanitizer. |
302 | // |
303 | // CHECK-X86-64-LABEL: 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, i24* %[[ptr]] |
307 | // CHECK-X86-64: %[[ext:.*]] = zext i24 %[[val]] to i32 |
308 | // CHECK-X86-64: ret i32 %[[ext]] |
309 | // CHECK-PPC64-LABEL: 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, i24* %[[ptr]] |
313 | // CHECK-PPC64: %[[ext:.*]] = zext i24 %[[val]] to i32 |
314 | // CHECK-PPC64: ret i32 %[[ext]] |
315 | return s->b; |
316 | } |
317 | void write(Base* s, unsigned x) { |
318 | // CHECK-X86-64-LABEL: 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-LABEL: 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]] |
328 | s->b = x; |
329 | } |
330 | } |
331 | |
332 | namespace N5 { |
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. |
340 | union U { |
341 | struct X { unsigned b : 24; char c; } x; |
342 | struct Y { unsigned b : 24; } y; |
343 | }; |
344 | unsigned read(U* u) { |
345 | // CHECK-X86-64-LABEL: define i32 @_ZN2N54read |
346 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
347 | // CHECK-X86-64: %[[val:.*]] = load i32, i32* %[[ptr]] |
348 | // CHECK-X86-64: %[[and:.*]] = and i32 %[[val]], 16777215 |
349 | // CHECK-X86-64: ret i32 %[[and]] |
350 | // CHECK-PPC64-LABEL: define zeroext i32 @_ZN2N54read |
351 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
352 | // CHECK-PPC64: %[[val:.*]] = load i32, i32* %[[ptr]] |
353 | // CHECK-PPC64: %[[shr:.*]] = lshr i32 %[[val]], 8 |
354 | // CHECK-PPC64: ret i32 %[[shr]] |
355 | return u->y.b; |
356 | } |
357 | void write(U* u, unsigned x) { |
358 | // CHECK-X86-64-LABEL: define void @_ZN2N55write |
359 | // CHECK-X86-64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
360 | // CHECK-X86-64: %[[old:.*]] = load i32, 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-LABEL: define void @_ZN2N55write |
366 | // CHECK-PPC64: %[[ptr:.*]] = bitcast %{{.*}}* %{{.*}} to i32* |
367 | // CHECK-PPC64: %[[old:.*]] = load i32, 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]] |
373 | u->y.b = x; |
374 | } |
375 | } |
376 | |
377 | namespace N6 { |
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 |
381 | // ThreadSanitizer. |
382 | // FIXME: We currently don't widen loads even without ThreadSanitizer, even |
383 | // though we could. |
384 | struct S { |
385 | unsigned b1 : 24; |
386 | unsigned char : 0; |
387 | unsigned char b2 : 8; |
388 | }; |
389 | unsigned read(S* s) { |
390 | // CHECK-X86-64-LABEL: define i32 @_ZN2N64read |
391 | // CHECK-X86-64: %[[ptr1:.*]] = bitcast {{.*}}* %{{.*}} to i24* |
392 | // CHECK-X86-64: %[[val1:.*]] = load i24, 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, 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-LABEL: define zeroext i32 @_ZN2N64read |
400 | // CHECK-PPC64: %[[ptr1:.*]] = bitcast {{.*}}* %{{.*}} to i24* |
401 | // CHECK-PPC64: %[[val1:.*]] = load i24, 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, 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; |
409 | } |
410 | void write(S* s, unsigned x) { |
411 | // CHECK-X86-64-LABEL: 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-LABEL: 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]] |
425 | s->b1 = x; |
426 | s->b2 = x; |
427 | } |
428 | } |
429 | |
430 | namespace N7 { |
431 | // Similar to N4 except that this adds a virtual base to the picture. (PR18430) |
432 | // Do NOT widen loads and stores to bitfields into padding at the end of |
433 | // a class which might end up with members inside of it when inside a derived |
434 | // class. |
435 | struct B1 { |
436 | virtual void f(); |
437 | unsigned b1 : 24; |
438 | }; |
439 | struct B2 : virtual B1 { |
440 | virtual ~B2(); |
441 | unsigned b : 24; |
442 | }; |
443 | // Imagine some other translation unit introduces: |
444 | #if 0 |
445 | struct Derived : public B2 { |
446 | char c; |
447 | }; |
448 | #endif |
449 | unsigned read(B2* s) { |
450 | // FIXME: We should widen this load as long as the function isn't being |
451 | // instrumented by ThreadSanitizer. |
452 | // |
453 | // CHECK-X86-64-LABEL: define i32 @_ZN2N74read |
454 | // CHECK-X86-64: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %{{.*}}, i32 0, i32 1 |
455 | // CHECK-X86-64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24* |
456 | // CHECK-X86-64: %[[val:.*]] = load i24, i24* %[[ptr]] |
457 | // CHECK-X86-64: %[[ext:.*]] = zext i24 %[[val]] to i32 |
458 | // CHECK-X86-64: ret i32 %[[ext]] |
459 | // CHECK-PPC64-LABEL: define zeroext i32 @_ZN2N74read |
460 | // CHECK-PPC64: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %{{.*}}, i32 0, i32 1 |
461 | // CHECK-PPC64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24* |
462 | // CHECK-PPC64: %[[val:.*]] = load i24, i24* %[[ptr]] |
463 | // CHECK-PPC64: %[[ext:.*]] = zext i24 %[[val]] to i32 |
464 | // CHECK-PPC64: ret i32 %[[ext]] |
465 | return s->b; |
466 | } |
467 | void write(B2* s, unsigned x) { |
468 | // CHECK-X86-64-LABEL: define void @_ZN2N75write |
469 | // CHECK-X86-64: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %{{.*}}, i32 0, i32 1 |
470 | // CHECK-X86-64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24* |
471 | // CHECK-X86-64: %[[new:.*]] = trunc i32 %{{.*}} to i24 |
472 | // CHECK-X86-64: store i24 %[[new]], i24* %[[ptr]] |
473 | // CHECK-PPC64-LABEL: define void @_ZN2N75write |
474 | // CHECK-PPC64: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %{{.*}}, i32 0, i32 1 |
475 | // CHECK-PPC64: %[[ptr:.*]] = bitcast [3 x i8]* %[[gep]] to i24* |
476 | // CHECK-PPC64: %[[new:.*]] = trunc i32 %{{.*}} to i24 |
477 | // CHECK-PPC64: store i24 %[[new]], i24* %[[ptr]] |
478 | s->b = x; |
479 | } |
480 | } |
481 | |