1; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt
2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+atomics,+sign-ext | FileCheck %s
3
4; Test that atomic loads are assembled properly.
5
6target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
7target triple = "wasm32-unknown-unknown"
8
9; CHECK-LABEL: load_i32_no_offset:
10; CHECK: i32.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
11; CHECK-NEXT: return $pop[[NUM]]{{$}}
12define i32 @load_i32_no_offset(i32 *%p) {
13  %v = load atomic i32, i32* %p seq_cst, align 4
14  ret i32 %v
15}
16
17; With an nuw add, we can fold an offset.
18
19; CHECK-LABEL: load_i32_with_folded_offset:
20; CHECK: i32.atomic.load  $push0=, 24($0){{$}}
21define i32 @load_i32_with_folded_offset(i32* %p) {
22  %q = ptrtoint i32* %p to i32
23  %r = add nuw i32 %q, 24
24  %s = inttoptr i32 %r to i32*
25  %t = load atomic i32, i32* %s seq_cst, align 4
26  ret i32 %t
27}
28
29; With an inbounds gep, we can fold an offset.
30
31; CHECK-LABEL: load_i32_with_folded_gep_offset:
32; CHECK: i32.atomic.load  $push0=, 24($0){{$}}
33define i32 @load_i32_with_folded_gep_offset(i32* %p) {
34  %s = getelementptr inbounds i32, i32* %p, i32 6
35  %t = load atomic i32, i32* %s seq_cst, align 4
36  ret i32 %t
37}
38
39; We can't fold a negative offset though, even with an inbounds gep.
40
41; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset:
42; CHECK: i32.const $push0=, -24{{$}}
43; CHECK: i32.add   $push1=, $0, $pop0{{$}}
44; CHECK: i32.atomic.load  $push2=, 0($pop1){{$}}
45define i32 @load_i32_with_unfolded_gep_negative_offset(i32* %p) {
46  %s = getelementptr inbounds i32, i32* %p, i32 -6
47  %t = load atomic i32, i32* %s seq_cst, align 4
48  ret i32 %t
49}
50
51; Without nuw, and even with nsw, we can't fold an offset.
52
53; CHECK-LABEL: load_i32_with_unfolded_offset:
54; CHECK: i32.const $push0=, 24{{$}}
55; CHECK: i32.add   $push1=, $0, $pop0{{$}}
56; CHECK: i32.atomic.load  $push2=, 0($pop1){{$}}
57define i32 @load_i32_with_unfolded_offset(i32* %p) {
58  %q = ptrtoint i32* %p to i32
59  %r = add nsw i32 %q, 24
60  %s = inttoptr i32 %r to i32*
61  %t = load atomic i32, i32* %s seq_cst, align 4
62  ret i32 %t
63}
64
65; Without inbounds, we can't fold a gep offset.
66
67; CHECK-LABEL: load_i32_with_unfolded_gep_offset:
68; CHECK: i32.const $push0=, 24{{$}}
69; CHECK: i32.add   $push1=, $0, $pop0{{$}}
70; CHECK: i32.atomic.load  $push2=, 0($pop1){{$}}
71define i32 @load_i32_with_unfolded_gep_offset(i32* %p) {
72  %s = getelementptr i32, i32* %p, i32 6
73  %t = load atomic i32, i32* %s seq_cst, align 4
74  ret i32 %t
75}
76
77; CHECK-LABEL: load_i64_no_offset:
78; CHECK: i64.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
79; CHECK-NEXT: return $pop[[NUM]]{{$}}
80define i64 @load_i64_no_offset(i64 *%p) {
81  %v = load atomic i64, i64* %p seq_cst, align 8
82  ret i64 %v
83}
84
85; Same as above but with i64.
86
87; CHECK-LABEL: load_i64_with_folded_offset:
88; CHECK: i64.atomic.load  $push0=, 24($0){{$}}
89define i64 @load_i64_with_folded_offset(i64* %p) {
90  %q = ptrtoint i64* %p to i32
91  %r = add nuw i32 %q, 24
92  %s = inttoptr i32 %r to i64*
93  %t = load atomic i64, i64* %s seq_cst, align 8
94  ret i64 %t
95}
96
97; Same as above but with i64.
98
99; CHECK-LABEL: load_i64_with_folded_gep_offset:
100; CHECK: i64.atomic.load  $push0=, 24($0){{$}}
101define i64 @load_i64_with_folded_gep_offset(i64* %p) {
102  %s = getelementptr inbounds i64, i64* %p, i32 3
103  %t = load atomic i64, i64* %s seq_cst, align 8
104  ret i64 %t
105}
106
107; Same as above but with i64.
108
109; CHECK-LABEL: load_i64_with_unfolded_gep_negative_offset:
110; CHECK: i32.const $push0=, -24{{$}}
111; CHECK: i32.add   $push1=, $0, $pop0{{$}}
112; CHECK: i64.atomic.load  $push2=, 0($pop1){{$}}
113define i64 @load_i64_with_unfolded_gep_negative_offset(i64* %p) {
114  %s = getelementptr inbounds i64, i64* %p, i32 -3
115  %t = load atomic i64, i64* %s seq_cst, align 8
116  ret i64 %t
117}
118
119; Same as above but with i64.
120
121; CHECK-LABEL: load_i64_with_unfolded_offset:
122; CHECK: i32.const $push0=, 24{{$}}
123; CHECK: i32.add   $push1=, $0, $pop0{{$}}
124; CHECK: i64.atomic.load  $push2=, 0($pop1){{$}}
125define i64 @load_i64_with_unfolded_offset(i64* %p) {
126  %q = ptrtoint i64* %p to i32
127  %r = add nsw i32 %q, 24
128  %s = inttoptr i32 %r to i64*
129  %t = load atomic i64, i64* %s seq_cst, align 8
130  ret i64 %t
131}
132
133; Same as above but with i64.
134
135; CHECK-LABEL: load_i64_with_unfolded_gep_offset:
136; CHECK: i32.const $push0=, 24{{$}}
137; CHECK: i32.add   $push1=, $0, $pop0{{$}}
138; CHECK: i64.atomic.load  $push2=, 0($pop1){{$}}
139define i64 @load_i64_with_unfolded_gep_offset(i64* %p) {
140  %s = getelementptr i64, i64* %p, i32 3
141  %t = load atomic i64, i64* %s seq_cst, align 8
142  ret i64 %t
143}
144
145; CHECK-LABEL: load_i32_with_folded_or_offset:
146; CHECK: i32.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}}
147; CHECK-NEXT: i32.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
148define i32 @load_i32_with_folded_or_offset(i32 %x) {
149  %and = and i32 %x, -4
150  %t0 = inttoptr i32 %and to i8*
151  %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
152  %t1 = load atomic i8, i8* %arrayidx seq_cst, align 8
153  %conv = sext i8 %t1 to i32
154  ret i32 %conv
155}
156
157; When loading from a fixed address, materialize a zero.
158
159; CHECK-LABEL: load_i32_from_numeric_address
160; CHECK: i32.const $push0=, 0{{$}}
161; CHECK: i32.atomic.load  $push1=, 42($pop0){{$}}
162define i32 @load_i32_from_numeric_address() {
163  %s = inttoptr i32 42 to i32*
164  %t = load atomic i32, i32* %s seq_cst, align 4
165  ret i32 %t
166}
167
168
169; CHECK-LABEL: load_i32_from_global_address
170; CHECK: i32.const $push0=, 0{{$}}
171; CHECK: i32.atomic.load  $push1=, gv($pop0){{$}}
172@gv = global i32 0
173define i32 @load_i32_from_global_address() {
174  %t = load atomic i32, i32* @gv seq_cst, align 4
175  ret i32 %t
176}
177
178; Fold an offset into a sign-extending load.
179
180; CHECK-LABEL: load_i8_s_with_folded_offset:
181; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}}
182; CHECK-NEXT: i32.extend8_s $push1=, $pop0
183define i32 @load_i8_s_with_folded_offset(i8* %p) {
184  %q = ptrtoint i8* %p to i32
185  %r = add nuw i32 %q, 24
186  %s = inttoptr i32 %r to i8*
187  %t = load atomic i8, i8* %s seq_cst, align 1
188  %u = sext i8 %t to i32
189  ret i32 %u
190}
191
192; Fold a gep offset into a sign-extending load.
193
194; CHECK-LABEL: load_i8_s_with_folded_gep_offset:
195; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}}
196; CHECK-NEXT: i32.extend8_s $push1=, $pop0
197define i32 @load_i8_s_with_folded_gep_offset(i8* %p) {
198  %s = getelementptr inbounds i8, i8* %p, i32 24
199  %t = load atomic i8, i8* %s seq_cst, align 1
200  %u = sext i8 %t to i32
201  ret i32 %u
202}
203
204; CHECK-LABEL: load_i16_s_i64_with_folded_gep_offset:
205; CHECK: i64.atomic.load16_u  $push0=, 6($0){{$}}
206define i64 @load_i16_s_i64_with_folded_gep_offset(i16* %p) {
207  %s = getelementptr inbounds i16, i16* %p, i32 3
208  %t = load atomic i16, i16* %s seq_cst, align 2
209  %u = zext i16 %t to i64
210  ret i64 %u
211}
212
213; CHECK-LABEL: load_i64_with_folded_or_offset:
214; CHECK: i64.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}}
215; CHECK-NEXT: i64.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
216define i64 @load_i64_with_folded_or_offset(i32 %x) {
217  %and = and i32 %x, -4
218  %t0 = inttoptr i32 %and to i8*
219  %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
220  %t1 = load atomic i8, i8* %arrayidx seq_cst, align 8
221  %conv = sext i8 %t1 to i64
222  ret i64 %conv
223}
224
225
226; Fold an offset into a zero-extending load.
227
228; CHECK-LABEL: load_i16_u_with_folded_offset:
229; CHECK: i32.atomic.load16_u $push0=, 24($0){{$}}
230define i32 @load_i16_u_with_folded_offset(i8* %p) {
231  %q = ptrtoint i8* %p to i32
232  %r = add nuw i32 %q, 24
233  %s = inttoptr i32 %r to i16*
234  %t = load atomic i16, i16* %s seq_cst, align 2
235  %u = zext i16 %t to i32
236  ret i32 %u
237}
238
239; Fold a gep offset into a zero-extending load.
240
241; CHECK-LABEL: load_i8_u_with_folded_gep_offset:
242; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}}
243define i32 @load_i8_u_with_folded_gep_offset(i8* %p) {
244  %s = getelementptr inbounds i8, i8* %p, i32 24
245  %t = load atomic i8, i8* %s seq_cst, align 1
246  %u = zext i8 %t to i32
247  ret i32 %u
248}
249
250
251; When loading from a fixed address, materialize a zero.
252; As above but with extending load.
253
254; CHECK-LABEL: load_zext_i32_from_numeric_address
255; CHECK: i32.const $push0=, 0{{$}}
256; CHECK: i32.atomic.load16_u  $push1=, 42($pop0){{$}}
257define i32 @load_zext_i32_from_numeric_address() {
258  %s = inttoptr i32 42 to i16*
259  %t = load atomic i16, i16* %s seq_cst, align 2
260  %u = zext i16 %t to i32
261  ret i32 %u
262}
263
264; CHECK-LABEL: load_sext_i32_from_global_address
265; CHECK: i32.const $push0=, 0{{$}}
266; CHECK: i32.atomic.load8_u  $push1=, gv8($pop0){{$}}
267; CHECK-NEXT: i32.extend8_s $push2=, $pop1{{$}}
268@gv8 = global i8 0
269define i32 @load_sext_i32_from_global_address() {
270  %t = load atomic i8, i8* @gv8 seq_cst, align 1
271  %u = sext i8 %t to i32
272  ret i32 %u
273}
274
275; Fold an offset into a sign-extending load.
276; As above but 32 extended to 64 bit.
277; CHECK-LABEL: load_i32_i64_s_with_folded_offset:
278; CHECK: i32.atomic.load $push0=, 24($0){{$}}
279; CHECK-NEXT: i64.extend_s/i32 $push1=, $pop0{{$}}
280define i64 @load_i32_i64_s_with_folded_offset(i32* %p) {
281  %q = ptrtoint i32* %p to i32
282  %r = add nuw i32 %q, 24
283  %s = inttoptr i32 %r to i32*
284  %t = load atomic i32, i32* %s seq_cst, align 4
285  %u = sext i32 %t to i64
286  ret i64 %u
287}
288
289; Fold a gep offset into a zero-extending load.
290; As above but 32 extended to 64 bit.
291; CHECK-LABEL: load_i32_i64_u_with_folded_gep_offset:
292; CHECK: i64.atomic.load32_u $push0=, 96($0){{$}}
293define i64 @load_i32_i64_u_with_folded_gep_offset(i32* %p) {
294  %s = getelementptr inbounds i32, i32* %p, i32 24
295  %t = load atomic i32, i32* %s seq_cst, align 4
296  %u = zext i32 %t to i64
297  ret i64 %u
298}
299
300; i8 return value should test anyext loads
301; CHECK-LABEL: ldi8_a1:
302; CHECK: i32.atomic.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
303; CHECK-NEXT: return $pop[[NUM]]{{$}}
304define i8 @ldi8_a1(i8 *%p) {
305  %v = load atomic i8, i8* %p seq_cst, align 1
306  ret i8 %v
307}
308