xref: /llvm-project/llvm/test/CodeGen/WebAssembly/offset.ll (revision 7d373cef4941e9be1c2c86375ba9a8943c55e9cd)
1; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -disable-wasm-fallthrough-return-opt -mattr=+fp16 | FileCheck %s
2
3; Test constant load and store address offsets.
4
5target triple = "wasm32-unknown-unknown"
6
7;===----------------------------------------------------------------------------
8; Loads: 32-bit
9;===----------------------------------------------------------------------------
10
11; Basic load.
12
13; CHECK-LABEL: load_i32_no_offset:
14; CHECK: i32.load $push0=, 0($0){{$}}
15; CHECK-NEXT: return $pop0{{$}}
16define i32 @load_i32_no_offset(ptr %p) {
17  %v = load i32, ptr %p
18  ret i32 %v
19}
20
21; With an nuw add, we can fold an offset.
22
23; CHECK-LABEL: load_i32_with_folded_offset:
24; CHECK: i32.load  $push0=, 24($0){{$}}
25define i32 @load_i32_with_folded_offset(ptr %p) {
26  %q = ptrtoint ptr %p to i32
27  %r = add nuw i32 %q, 24
28  %s = inttoptr i32 %r to ptr
29  %t = load i32, ptr %s
30  ret i32 %t
31}
32
33; With an inbounds gep, we can fold an offset.
34
35; CHECK-LABEL: load_i32_with_folded_gep_offset:
36; CHECK: i32.load  $push0=, 24($0){{$}}
37define i32 @load_i32_with_folded_gep_offset(ptr %p) {
38  %s = getelementptr inbounds i32, ptr %p, i32 6
39  %t = load i32, ptr %s
40  ret i32 %t
41}
42
43; Same for nusw.
44
45; CHECK-LABEL: load_i32_with_folded_gep_offset_nusw:
46; CHECK: i32.load  $push0=, 24($0){{$}}
47define i32 @load_i32_with_folded_gep_offset_nusw(ptr %p) {
48  %s = getelementptr nusw i32, ptr %p, i32 6
49  %t = load i32, ptr %s
50  ret i32 %t
51}
52
53; For nuw we don't need the offset to be positive.
54
55; CHECK-LABEL: load_i32_with_folded_gep_offset_nuw:
56; CHECK: i32.load  $push0=, -24($0){{$}}
57define i32 @load_i32_with_folded_gep_offset_nuw(ptr %p) {
58  %s = getelementptr nuw i32, ptr %p, i32 -6
59  %t = load i32, ptr %s
60  ret i32 %t
61}
62
63; We can't fold a negative offset though, even with an inbounds gep.
64
65; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset:
66; CHECK: i32.const $push0=, -24{{$}}
67; CHECK: i32.add   $push1=, $0, $pop0{{$}}
68; CHECK: i32.load  $push2=, 0($pop1){{$}}
69define i32 @load_i32_with_unfolded_gep_negative_offset(ptr %p) {
70  %s = getelementptr inbounds i32, ptr %p, i32 -6
71  %t = load i32, ptr %s
72  ret i32 %t
73}
74
75; Without nuw, and even with nsw, we can't fold an offset.
76
77; CHECK-LABEL: load_i32_with_unfolded_offset:
78; CHECK: i32.const $push0=, 24{{$}}
79; CHECK: i32.add   $push1=, $0, $pop0{{$}}
80; CHECK: i32.load  $push2=, 0($pop1){{$}}
81define i32 @load_i32_with_unfolded_offset(ptr %p) {
82  %q = ptrtoint ptr %p to i32
83  %r = add nsw i32 %q, 24
84  %s = inttoptr i32 %r to ptr
85  %t = load i32, ptr %s
86  ret i32 %t
87}
88
89; Without inbounds, we can't fold a gep offset.
90
91; CHECK-LABEL: load_i32_with_unfolded_gep_offset:
92; CHECK: i32.const $push0=, 24{{$}}
93; CHECK: i32.add   $push1=, $0, $pop0{{$}}
94; CHECK: i32.load  $push2=, 0($pop1){{$}}
95define i32 @load_i32_with_unfolded_gep_offset(ptr %p) {
96  %s = getelementptr i32, ptr %p, i32 6
97  %t = load i32, ptr %s
98  ret i32 %t
99}
100
101; When loading from a fixed address, materialize a zero.
102
103; CHECK-LABEL: load_i32_from_numeric_address
104; CHECK: i32.const $push0=, 0{{$}}
105; CHECK: i32.load  $push1=, 42($pop0){{$}}
106define i32 @load_i32_from_numeric_address() {
107  %s = inttoptr i32 42 to ptr
108  %t = load i32, ptr %s
109  ret i32 %t
110}
111
112; CHECK-LABEL: load_i32_from_global_address
113; CHECK: i32.const $push0=, 0{{$}}
114; CHECK: i32.load  $push1=, gv($pop0){{$}}
115@gv = global i32 0
116define i32 @load_i32_from_global_address() {
117  %t = load i32, ptr @gv
118  ret i32 %t
119}
120
121;===----------------------------------------------------------------------------
122; Loads: 64-bit
123;===----------------------------------------------------------------------------
124
125; Basic load.
126
127; CHECK-LABEL: load_i64_no_offset:
128; CHECK: i64.load $push0=, 0($0){{$}}
129; CHECK-NEXT: return $pop0{{$}}
130define i64 @load_i64_no_offset(ptr %p) {
131  %v = load i64, ptr %p
132  ret i64 %v
133}
134
135; With an nuw add, we can fold an offset.
136
137; CHECK-LABEL: load_i64_with_folded_offset:
138; CHECK: i64.load  $push0=, 24($0){{$}}
139define i64 @load_i64_with_folded_offset(ptr %p) {
140  %q = ptrtoint ptr %p to i32
141  %r = add nuw i32 %q, 24
142  %s = inttoptr i32 %r to ptr
143  %t = load i64, ptr %s
144  ret i64 %t
145}
146
147; With an inbounds gep, we can fold an offset.
148
149; CHECK-LABEL: load_i64_with_folded_gep_offset:
150; CHECK: i64.load  $push0=, 24($0){{$}}
151define i64 @load_i64_with_folded_gep_offset(ptr %p) {
152  %s = getelementptr inbounds i64, ptr %p, i32 3
153  %t = load i64, ptr %s
154  ret i64 %t
155}
156
157; We can't fold a negative offset though, even with an inbounds gep.
158
159; CHECK-LABEL: load_i64_with_unfolded_gep_negative_offset:
160; CHECK: i32.const $push0=, -24{{$}}
161; CHECK: i32.add   $push1=, $0, $pop0{{$}}
162; CHECK: i64.load  $push2=, 0($pop1){{$}}
163define i64 @load_i64_with_unfolded_gep_negative_offset(ptr %p) {
164  %s = getelementptr inbounds i64, ptr %p, i32 -3
165  %t = load i64, ptr %s
166  ret i64 %t
167}
168
169; Without nuw, and even with nsw, we can't fold an offset.
170
171; CHECK-LABEL: load_i64_with_unfolded_offset:
172; CHECK: i32.const $push0=, 24{{$}}
173; CHECK: i32.add   $push1=, $0, $pop0{{$}}
174; CHECK: i64.load  $push2=, 0($pop1){{$}}
175define i64 @load_i64_with_unfolded_offset(ptr %p) {
176  %q = ptrtoint ptr %p to i32
177  %r = add nsw i32 %q, 24
178  %s = inttoptr i32 %r to ptr
179  %t = load i64, ptr %s
180  ret i64 %t
181}
182
183; Without inbounds, we can't fold a gep offset.
184
185; CHECK-LABEL: load_i64_with_unfolded_gep_offset:
186; CHECK: i32.const $push0=, 24{{$}}
187; CHECK: i32.add   $push1=, $0, $pop0{{$}}
188; CHECK: i64.load  $push2=, 0($pop1){{$}}
189define i64 @load_i64_with_unfolded_gep_offset(ptr %p) {
190  %s = getelementptr i64, ptr %p, i32 3
191  %t = load i64, ptr %s
192  ret i64 %t
193}
194
195;===----------------------------------------------------------------------------
196; Stores: 32-bit
197;===----------------------------------------------------------------------------
198
199; Basic store.
200
201; CHECK-LABEL: store_i32_no_offset:
202; CHECK-NEXT: .functype store_i32_no_offset (i32, i32) -> (){{$}}
203; CHECK-NEXT: i32.store 0($0), $1{{$}}
204; CHECK-NEXT: return{{$}}
205define void @store_i32_no_offset(ptr %p, i32 %v) {
206  store i32 %v, ptr %p
207  ret void
208}
209
210; With an nuw add, we can fold an offset.
211
212; CHECK-LABEL: store_i32_with_folded_offset:
213; CHECK: i32.store 24($0), $pop0{{$}}
214define void @store_i32_with_folded_offset(ptr %p) {
215  %q = ptrtoint ptr %p to i32
216  %r = add nuw i32 %q, 24
217  %s = inttoptr i32 %r to ptr
218  store i32 0, ptr %s
219  ret void
220}
221
222; With an inbounds gep, we can fold an offset.
223
224; CHECK-LABEL: store_i32_with_folded_gep_offset:
225; CHECK: i32.store 24($0), $pop0{{$}}
226define void @store_i32_with_folded_gep_offset(ptr %p) {
227  %s = getelementptr inbounds i32, ptr %p, i32 6
228  store i32 0, ptr %s
229  ret void
230}
231
232; We can't fold a negative offset though, even with an inbounds gep.
233
234; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset:
235; CHECK: i32.const $push0=, -24{{$}}
236; CHECK: i32.add   $push1=, $0, $pop0{{$}}
237; CHECK: i32.store 0($pop1), $pop2{{$}}
238define void @store_i32_with_unfolded_gep_negative_offset(ptr %p) {
239  %s = getelementptr inbounds i32, ptr %p, i32 -6
240  store i32 0, ptr %s
241  ret void
242}
243
244; Without nuw, and even with nsw, we can't fold an offset.
245
246; CHECK-LABEL: store_i32_with_unfolded_offset:
247; CHECK: i32.const $push0=, 24{{$}}
248; CHECK: i32.add   $push1=, $0, $pop0{{$}}
249; CHECK: i32.store 0($pop1), $pop2{{$}}
250define void @store_i32_with_unfolded_offset(ptr %p) {
251  %q = ptrtoint ptr %p to i32
252  %r = add nsw i32 %q, 24
253  %s = inttoptr i32 %r to ptr
254  store i32 0, ptr %s
255  ret void
256}
257
258; Without inbounds, we can't fold a gep offset.
259
260; CHECK-LABEL: store_i32_with_unfolded_gep_offset:
261; CHECK: i32.const $push0=, 24{{$}}
262; CHECK: i32.add   $push1=, $0, $pop0{{$}}
263; CHECK: i32.store 0($pop1), $pop2{{$}}
264define void @store_i32_with_unfolded_gep_offset(ptr %p) {
265  %s = getelementptr i32, ptr %p, i32 6
266  store i32 0, ptr %s
267  ret void
268}
269
270; When storing from a fixed address, materialize a zero.
271
272; CHECK-LABEL: store_i32_to_numeric_address:
273; CHECK:      i32.const $push0=, 0{{$}}
274; CHECK-NEXT: i32.const $push1=, 0{{$}}
275; CHECK-NEXT: i32.store 42($pop0), $pop1{{$}}
276define void @store_i32_to_numeric_address() {
277  %s = inttoptr i32 42 to ptr
278  store i32 0, ptr %s
279  ret void
280}
281
282; CHECK-LABEL: store_i32_to_global_address:
283; CHECK: i32.const $push0=, 0{{$}}
284; CHECK: i32.const $push1=, 0{{$}}
285; CHECK: i32.store gv($pop0), $pop1{{$}}
286define void @store_i32_to_global_address() {
287  store i32 0, ptr @gv
288  ret void
289}
290
291;===----------------------------------------------------------------------------
292; Stores: 64-bit
293;===----------------------------------------------------------------------------
294
295; Basic store.
296
297; CHECK-LABEL: store_i64_with_folded_offset:
298; CHECK: i64.store 24($0), $pop0{{$}}
299define void @store_i64_with_folded_offset(ptr %p) {
300  %q = ptrtoint ptr %p to i32
301  %r = add nuw i32 %q, 24
302  %s = inttoptr i32 %r to ptr
303  store i64 0, ptr %s
304  ret void
305}
306
307; With an nuw add, we can fold an offset.
308
309; CHECK-LABEL: store_i64_with_folded_gep_offset:
310; CHECK: i64.store 24($0), $pop0{{$}}
311define void @store_i64_with_folded_gep_offset(ptr %p) {
312  %s = getelementptr inbounds i64, ptr %p, i32 3
313  store i64 0, ptr %s
314  ret void
315}
316
317; With an inbounds gep, we can fold an offset.
318
319; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset:
320; CHECK: i32.const $push0=, -24{{$}}
321; CHECK: i32.add   $push1=, $0, $pop0{{$}}
322; CHECK: i64.store 0($pop1), $pop2{{$}}
323define void @store_i64_with_unfolded_gep_negative_offset(ptr %p) {
324  %s = getelementptr inbounds i64, ptr %p, i32 -3
325  store i64 0, ptr %s
326  ret void
327}
328
329; We can't fold a negative offset though, even with an inbounds gep.
330
331; CHECK-LABEL: store_i64_with_unfolded_offset:
332; CHECK: i32.const $push0=, 24{{$}}
333; CHECK: i32.add   $push1=, $0, $pop0{{$}}
334; CHECK: i64.store 0($pop1), $pop2{{$}}
335define void @store_i64_with_unfolded_offset(ptr %p) {
336  %q = ptrtoint ptr %p to i32
337  %r = add nsw i32 %q, 24
338  %s = inttoptr i32 %r to ptr
339  store i64 0, ptr %s
340  ret void
341}
342
343; Without nuw, and even with nsw, we can't fold an offset.
344
345; CHECK-LABEL: store_i64_with_unfolded_gep_offset:
346; CHECK: i32.const $push0=, 24{{$}}
347; CHECK: i32.add   $push1=, $0, $pop0{{$}}
348; CHECK: i64.store 0($pop1), $pop2{{$}}
349define void @store_i64_with_unfolded_gep_offset(ptr %p) {
350  %s = getelementptr i64, ptr %p, i32 3
351  store i64 0, ptr %s
352  ret void
353}
354
355; Without inbounds, we can't fold a gep offset.
356
357; CHECK-LABEL: store_i32_with_folded_or_offset:
358; CHECK: i32.store8 2($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}}
359define void @store_i32_with_folded_or_offset(i32 %x) {
360  %and = and i32 %x, -4
361  %t0 = inttoptr i32 %and to ptr
362  %arrayidx = getelementptr inbounds i8, ptr %t0, i32 2
363  store i8 0, ptr %arrayidx, align 1
364  ret void
365}
366
367;===----------------------------------------------------------------------------
368; Sign-extending loads
369;===----------------------------------------------------------------------------
370
371; Fold an offset into a sign-extending load.
372
373; CHECK-LABEL: load_i8_i32_s_with_folded_offset:
374; CHECK: i32.load8_s $push0=, 24($0){{$}}
375define i32 @load_i8_i32_s_with_folded_offset(ptr %p) {
376  %q = ptrtoint ptr %p to i32
377  %r = add nuw i32 %q, 24
378  %s = inttoptr i32 %r to ptr
379  %t = load i8, ptr %s
380  %u = sext i8 %t to i32
381  ret i32 %u
382}
383
384; CHECK-LABEL: load_i32_i64_s_with_folded_offset:
385; CHECK: i64.load32_s $push0=, 24($0){{$}}
386define i64 @load_i32_i64_s_with_folded_offset(ptr %p) {
387  %q = ptrtoint ptr %p to i32
388  %r = add nuw i32 %q, 24
389  %s = inttoptr i32 %r to ptr
390  %t = load i32, ptr %s
391  %u = sext i32 %t to i64
392  ret i64 %u
393}
394
395; Fold a gep offset into a sign-extending load.
396
397; CHECK-LABEL: load_i8_i32_s_with_folded_gep_offset:
398; CHECK: i32.load8_s $push0=, 24($0){{$}}
399define i32 @load_i8_i32_s_with_folded_gep_offset(ptr %p) {
400  %s = getelementptr inbounds i8, ptr %p, i32 24
401  %t = load i8, ptr %s
402  %u = sext i8 %t to i32
403  ret i32 %u
404}
405
406; CHECK-LABEL: load_i16_i32_s_with_folded_gep_offset:
407; CHECK: i32.load16_s $push0=, 48($0){{$}}
408define i32 @load_i16_i32_s_with_folded_gep_offset(ptr %p) {
409  %s = getelementptr inbounds i16, ptr %p, i32 24
410  %t = load i16, ptr %s
411  %u = sext i16 %t to i32
412  ret i32 %u
413}
414
415; CHECK-LABEL: load_i16_i64_s_with_folded_gep_offset:
416; CHECK: i64.load16_s $push0=, 48($0){{$}}
417define i64 @load_i16_i64_s_with_folded_gep_offset(ptr %p) {
418  %s = getelementptr inbounds i16, ptr %p, i32 24
419  %t = load i16, ptr %s
420  %u = sext i16 %t to i64
421  ret i64 %u
422}
423
424; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as
425; an 'add' if the or'ed bits are known to be zero.
426
427; CHECK-LABEL: load_i8_i32_s_with_folded_or_offset:
428; CHECK: i32.load8_s $push{{[0-9]+}}=, 2($pop{{[0-9]+}}){{$}}
429define i32 @load_i8_i32_s_with_folded_or_offset(i32 %x) {
430  %and = and i32 %x, -4
431  %t0 = inttoptr i32 %and to ptr
432  %arrayidx = getelementptr inbounds i8, ptr %t0, i32 2
433  %t1 = load i8, ptr %arrayidx
434  %conv = sext i8 %t1 to i32
435  ret i32 %conv
436}
437
438; CHECK-LABEL: load_i8_i64_s_with_folded_or_offset:
439; CHECK: i64.load8_s $push{{[0-9]+}}=, 2($pop{{[0-9]+}}){{$}}
440define i64 @load_i8_i64_s_with_folded_or_offset(i32 %x) {
441  %and = and i32 %x, -4
442  %t0 = inttoptr i32 %and to ptr
443  %arrayidx = getelementptr inbounds i8, ptr %t0, i32 2
444  %t1 = load i8, ptr %arrayidx
445  %conv = sext i8 %t1 to i64
446  ret i64 %conv
447}
448
449; When loading from a fixed address, materialize a zero.
450
451; CHECK-LABEL: load_i16_i32_s_from_numeric_address
452; CHECK: i32.const $push0=, 0{{$}}
453; CHECK: i32.load16_s  $push1=, 42($pop0){{$}}
454define i32 @load_i16_i32_s_from_numeric_address() {
455  %s = inttoptr i32 42 to ptr
456  %t = load i16, ptr %s
457  %u = sext i16 %t to i32
458  ret i32 %u
459}
460
461; CHECK-LABEL: load_i8_i32_s_from_global_address
462; CHECK: i32.const $push0=, 0{{$}}
463; CHECK: i32.load8_s  $push1=, gv8($pop0){{$}}
464@gv8 = global i8 0
465define i32 @load_i8_i32_s_from_global_address() {
466  %t = load i8, ptr @gv8
467  %u = sext i8 %t to i32
468  ret i32 %u
469}
470
471;===----------------------------------------------------------------------------
472; Zero-extending loads
473;===----------------------------------------------------------------------------
474
475; Fold an offset into a zero-extending load.
476
477; CHECK-LABEL: load_i8_i32_z_with_folded_offset:
478; CHECK: i32.load8_u $push0=, 24($0){{$}}
479define i32 @load_i8_i32_z_with_folded_offset(ptr %p) {
480  %q = ptrtoint ptr %p to i32
481  %r = add nuw i32 %q, 24
482  %s = inttoptr i32 %r to ptr
483  %t = load i8, ptr %s
484  %u = zext i8 %t to i32
485  ret i32 %u
486}
487
488; CHECK-LABEL: load_i32_i64_z_with_folded_offset:
489; CHECK: i64.load32_u $push0=, 24($0){{$}}
490define i64 @load_i32_i64_z_with_folded_offset(ptr %p) {
491  %q = ptrtoint ptr %p to i32
492  %r = add nuw i32 %q, 24
493  %s = inttoptr i32 %r to ptr
494  %t = load i32, ptr %s
495  %u = zext i32 %t to i64
496  ret i64 %u
497}
498
499; Fold a gep offset into a zero-extending load.
500
501; CHECK-LABEL: load_i8_i32_z_with_folded_gep_offset:
502; CHECK: i32.load8_u $push0=, 24($0){{$}}
503define i32 @load_i8_i32_z_with_folded_gep_offset(ptr %p) {
504  %s = getelementptr inbounds i8, ptr %p, i32 24
505  %t = load i8, ptr %s
506  %u = zext i8 %t to i32
507  ret i32 %u
508}
509
510; CHECK-LABEL: load_i16_i32_z_with_folded_gep_offset:
511; CHECK: i32.load16_u $push0=, 48($0){{$}}
512define i32 @load_i16_i32_z_with_folded_gep_offset(ptr %p) {
513  %s = getelementptr inbounds i16, ptr %p, i32 24
514  %t = load i16, ptr %s
515  %u = zext i16 %t to i32
516  ret i32 %u
517}
518
519; CHECK-LABEL: load_i16_i64_z_with_folded_gep_offset:
520; CHECK: i64.load16_u $push0=, 48($0){{$}}
521define i64 @load_i16_i64_z_with_folded_gep_offset(ptr %p) {
522  %s = getelementptr inbounds i16, ptr %p, i64 24
523  %t = load i16, ptr %s
524  %u = zext i16 %t to i64
525  ret i64 %u
526}
527
528; When loading from a fixed address, materialize a zero.
529
530; CHECK-LABEL: load_i16_i32_z_from_numeric_address
531; CHECK: i32.const $push0=, 0{{$}}
532; CHECK: i32.load16_u  $push1=, 42($pop0){{$}}
533define i32 @load_i16_i32_z_from_numeric_address() {
534  %s = inttoptr i32 42 to ptr
535  %t = load i16, ptr %s
536  %u = zext i16 %t to i32
537  ret i32 %u
538}
539
540; CHECK-LABEL: load_i8_i32_z_from_global_address
541; CHECK: i32.const $push0=, 0{{$}}
542; CHECK: i32.load8_u  $push1=, gv8($pop0){{$}}
543define i32 @load_i8_i32_z_from_global_address() {
544  %t = load i8, ptr @gv8
545  %u = zext i8 %t to i32
546  ret i32 %u
547}
548
549; i8 return value should test anyext loads
550; CHECK-LABEL: load_i8_i32_retvalue:
551; CHECK: i32.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
552; CHECK-NEXT: return $pop[[NUM]]{{$}}
553define i8 @load_i8_i32_retvalue(ptr %p) {
554  %v = load i8, ptr %p
555  ret i8 %v
556}
557
558;===----------------------------------------------------------------------------
559; Truncating stores
560;===----------------------------------------------------------------------------
561
562; Fold an offset into a truncating store.
563
564; CHECK-LABEL: store_i8_i32_with_folded_offset:
565; CHECK: i32.store8 24($0), $1{{$}}
566define void @store_i8_i32_with_folded_offset(ptr %p, i32 %v) {
567  %q = ptrtoint ptr %p to i32
568  %r = add nuw i32 %q, 24
569  %s = inttoptr i32 %r to ptr
570  %t = trunc i32 %v to i8
571  store i8 %t, ptr %s
572  ret void
573}
574
575; CHECK-LABEL: store_i32_i64_with_folded_offset:
576; CHECK: i64.store32 24($0), $1{{$}}
577define void @store_i32_i64_with_folded_offset(ptr %p, i64 %v) {
578  %q = ptrtoint ptr %p to i32
579  %r = add nuw i32 %q, 24
580  %s = inttoptr i32 %r to ptr
581  %t = trunc i64 %v to i32
582  store i32 %t, ptr %s
583  ret void
584}
585
586; Fold a gep offset into a truncating store.
587
588; CHECK-LABEL: store_i8_i32_with_folded_gep_offset:
589; CHECK: i32.store8 24($0), $1{{$}}
590define void @store_i8_i32_with_folded_gep_offset(ptr %p, i32 %v) {
591  %s = getelementptr inbounds i8, ptr %p, i32 24
592  %t = trunc i32 %v to i8
593  store i8 %t, ptr %s
594  ret void
595}
596
597; CHECK-LABEL: store_i16_i32_with_folded_gep_offset:
598; CHECK: i32.store16 48($0), $1{{$}}
599define void @store_i16_i32_with_folded_gep_offset(ptr %p, i32 %v) {
600  %s = getelementptr inbounds i16, ptr %p, i32 24
601  %t = trunc i32 %v to i16
602  store i16 %t, ptr %s
603  ret void
604}
605
606; CHECK-LABEL: store_i16_i64_with_folded_gep_offset:
607; CHECK: i64.store16 48($0), $1{{$}}
608define void @store_i16_i64_with_folded_gep_offset(ptr %p, i64 %v) {
609  %s = getelementptr inbounds i16, ptr %p, i64 24
610  %t = trunc i64 %v to i16
611  store i16 %t, ptr %s
612  ret void
613}
614
615; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as
616; an 'add' if the or'ed bits are known to be zero.
617
618; CHECK-LABEL: store_i8_i32_with_folded_or_offset:
619; CHECK: i32.store8 2($pop{{[0-9]+}}), $1{{$}}
620define void @store_i8_i32_with_folded_or_offset(i32 %x, i32 %v) {
621  %and = and i32 %x, -4
622  %p = inttoptr i32 %and to ptr
623  %arrayidx = getelementptr inbounds i8, ptr %p, i32 2
624  %t = trunc i32 %v to i8
625  store i8 %t, ptr %arrayidx
626  ret void
627}
628
629; CHECK-LABEL: store_i8_i64_with_folded_or_offset:
630; CHECK: i64.store8 2($pop{{[0-9]+}}), $1{{$}}
631define void @store_i8_i64_with_folded_or_offset(i32 %x, i64 %v) {
632  %and = and i32 %x, -4
633  %p = inttoptr i32 %and to ptr
634  %arrayidx = getelementptr inbounds i8, ptr %p, i32 2
635  %t = trunc i64 %v to i8
636  store i8 %t, ptr %arrayidx
637  ret void
638}
639
640;===----------------------------------------------------------------------------
641; Aggregate values
642;===----------------------------------------------------------------------------
643
644; Fold the offsets when lowering aggregate loads and stores.
645
646; CHECK-LABEL: aggregate_load_store:
647; CHECK: i32.load  $2=, 0($0){{$}}
648; CHECK: i32.load  $3=, 4($0){{$}}
649; CHECK: i32.load  $4=, 8($0){{$}}
650; CHECK: i32.load  $push0=, 12($0){{$}}
651; CHECK: i32.store 12($1), $pop0{{$}}
652; CHECK: i32.store 8($1), $4{{$}}
653; CHECK: i32.store 4($1), $3{{$}}
654; CHECK: i32.store 0($1), $2{{$}}
655define void @aggregate_load_store(ptr %p, ptr %q) {
656  ; volatile so that things stay in order for the tests above
657  %t = load volatile {i32,i32,i32,i32}, ptr %p
658  store volatile {i32,i32,i32,i32} %t, ptr %q
659  ret void
660}
661
662; Fold the offsets when lowering aggregate return values. The stores get
663; merged into i64 stores.
664
665; CHECK-LABEL: aggregate_return:
666; CHECK: i64.const   $push[[L0:[0-9]+]]=, 0{{$}}
667; CHECK: i64.store   8($0), $pop[[L0]]{{$}}
668; CHECK: i64.const   $push[[L1:[0-9]+]]=, 0{{$}}
669; CHECK: i64.store   0($0), $pop[[L1]]{{$}}
670define {i32,i32,i32,i32} @aggregate_return() {
671  ret {i32,i32,i32,i32} zeroinitializer
672}
673
674; Fold the offsets when lowering aggregate return values. The stores are not
675; merged.
676
677; CHECK-LABEL: aggregate_return_without_merge:
678; CHECK: i32.const   $push[[L0:[0-9]+]]=, 0{{$}}
679; CHECK: i32.store8  14($0), $pop[[L0]]{{$}}
680; CHECK: i32.const   $push[[L1:[0-9]+]]=, 0{{$}}
681; CHECK: i32.store16 12($0), $pop[[L1]]{{$}}
682; CHECK: i32.const   $push[[L2:[0-9]+]]=, 0{{$}}
683; CHECK: i32.store   8($0), $pop[[L2]]{{$}}
684; CHECK: i64.const   $push[[L3:[0-9]+]]=, 0{{$}}
685; CHECK: i64.store   0($0), $pop[[L3]]{{$}}
686define {i64,i32,i16,i8} @aggregate_return_without_merge() {
687  ret {i64,i32,i16,i8} zeroinitializer
688}
689
690;===----------------------------------------------------------------------------
691; Loads: Half Precision
692;===----------------------------------------------------------------------------
693
694; Fold an offset into a zero-extending load.
695
696; CHECK-LABEL: load_f16_f32_with_folded_offset:
697; CHECK: f32.load_f16 $push0=, 24($0){{$}}
698define float @load_f16_f32_with_folded_offset(ptr %p) {
699  %q = ptrtoint ptr %p to i32
700  %r = add nuw i32 %q, 24
701  %s = inttoptr i32 %r to ptr
702  %t = call float @llvm.wasm.loadf16.f32(ptr %s)
703  ret float %t
704}
705
706; Fold a gep offset into a zero-extending load.
707
708; CHECK-LABEL: load_f16_f32_with_folded_gep_offset:
709; CHECK: f32.load_f16 $push0=, 24($0){{$}}
710define float @load_f16_f32_with_folded_gep_offset(ptr %p) {
711  %s = getelementptr inbounds i8, ptr %p, i32 24
712  %t = call float @llvm.wasm.loadf16.f32(ptr %s)
713  ret float %t
714}
715
716;===----------------------------------------------------------------------------
717; Stores: Half Precision
718;===----------------------------------------------------------------------------
719
720; Basic store.
721
722; CHECK-LABEL: store_f16_f32_no_offset:
723; CHECK-NEXT: .functype store_f16_f32_no_offset (i32, f32) -> (){{$}}
724; CHECK-NEXT: f32.store_f16 0($0), $1{{$}}
725; CHECK-NEXT: return{{$}}
726define void @store_f16_f32_no_offset(ptr %p, float %v) {
727  call void @llvm.wasm.storef16.f32(float %v, ptr %p)
728  ret void
729}
730
731; Storing to a fixed address.
732
733; CHECK-LABEL: store_f16_f32_to_numeric_address:
734; CHECK:      i32.const $push1=, 0{{$}}
735; CHECK-NEXT: f32.const $push0=, 0x0p0{{$}}
736; CHECK-NEXT: f32.store_f16 42($pop1), $pop0{{$}}
737define void @store_f16_f32_to_numeric_address() {
738  %s = inttoptr i32 42 to ptr
739  call void @llvm.wasm.storef16.f32(float 0.0, ptr %s)
740  ret void
741}
742