llvm.org GIT mirror llvm / 365cb32
[WebAssembly] Add the rest of the atomic loads Add extending loads and constant offset patterns A bit more refactoring of the tablegen to make the patterns fairly nice and uniform between the regular and atomic loads. Differential Revision: https://reviews.llvm.org/D38523 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@315022 91177308-0d34-0410-b5e6-96231b3b80d8 Derek Schuff 2 years ago
9 changed file(s) with 659 addition(s) and 75 deletion(s). Raw diff Collapse all Expand all
110110 case WebAssembly::LOAD8_U_I32:
111111 case WebAssembly::LOAD8_S_I64:
112112 case WebAssembly::LOAD8_U_I64:
113 case WebAssembly::ATOMIC_LOAD8_U_I32:
114 case WebAssembly::ATOMIC_LOAD8_U_I64:
113115 case WebAssembly::STORE8_I32:
114116 case WebAssembly::STORE8_I64:
115117 return 0;
117119 case WebAssembly::LOAD16_U_I32:
118120 case WebAssembly::LOAD16_S_I64:
119121 case WebAssembly::LOAD16_U_I64:
122 case WebAssembly::ATOMIC_LOAD16_U_I32:
123 case WebAssembly::ATOMIC_LOAD16_U_I64:
120124 case WebAssembly::STORE16_I32:
121125 case WebAssembly::STORE16_I64:
122126 return 1;
128132 case WebAssembly::LOAD32_U_I64:
129133 case WebAssembly::STORE32_I64:
130134 case WebAssembly::ATOMIC_LOAD_I32:
135 case WebAssembly::ATOMIC_LOAD32_U_I64:
131136 return 2;
132137 case WebAssembly::LOAD_I64:
133138 case WebAssembly::LOAD_F64:
134139 case WebAssembly::STORE_I64:
135140 case WebAssembly::STORE_F64:
141 case WebAssembly::ATOMIC_LOAD_I64:
136142 return 3;
137143 default:
138144 llvm_unreachable("Only loads and stores have p2align values");
1616 //===----------------------------------------------------------------------===//
1717
1818 let Defs = [ARGUMENTS] in {
19 // TODO: add the rest of the atomic loads
20 def ATOMIC_LOAD_I32 : CLoadI32<"i32.atomic.load", 0xfe10>;
21 def ATOMIC_LOAD_I64 : CLoadI64<"i64.atomic.load", 0xfe11>;
19 def ATOMIC_LOAD_I32 : WebAssemblyLoad>;
20 def ATOMIC_LOAD_I64 : WebAssemblyLoad;
2221 } // Defs = [ARGUMENTS]
2322
2423 // Select loads with no constant offset.
2524 let Predicates = [HasAtomics] in {
26 class ALoadPatNoOffset :
27 Pat<(ty (node I32:$addr)), (inst 0, 0, $addr)>;
28 def : ALoadPatNoOffset;
29 def : ALoadPatNoOffset;
30
31 }
25 def : LoadPatNoOffset;
26 def : LoadPatNoOffset;
27
28 // Select loads with a constant offset.
29
30 // Pattern with address + immediate offset
31 def : LoadPatImmOff;
32 def : LoadPatImmOff;
33 def : LoadPatImmOff;
34 def : LoadPatImmOff;
35
36 def : LoadPatGlobalAddr;
37 def : LoadPatGlobalAddr;
38
39 def : LoadPatExternalSym;
40 def : LoadPatExternalSym;
41
42
43 // Select loads with just a constant offset.
44 def : LoadPatOffsetOnly;
45 def : LoadPatOffsetOnly;
46
47 def : LoadPatGlobalAddrOffOnly;
48 def : LoadPatGlobalAddrOffOnly;
49
50 def : LoadPatExternSymOffOnly;
51 def : LoadPatExternSymOffOnly;
52
53 } // Predicates = [HasAtomics]
54
55 // Extending loads. Note that there are only zero-extending atomic loads, no
56 // sign-extending loads.
57 let Defs = [ARGUMENTS] in {
58 def ATOMIC_LOAD8_U_I32 : WebAssemblyLoad;
59 def ATOMIC_LOAD16_U_I32 : WebAssemblyLoad;
60 def ATOMIC_LOAD8_U_I64 : WebAssemblyLoad;
61 def ATOMIC_LOAD16_U_I64 : WebAssemblyLoad;
62 def ATOMIC_LOAD32_U_I64 : WebAssemblyLoad;
63 } // Defs = [ARGUMENTS]
64
65 // Fragments for exending loads. These are different from regular loads because
66 // the SDNodes are derived from AtomicSDNode rather than LoadSDNode and
67 // therefore don't have the extension type field. So instead of matching that,
68 // we match the patterns that the type legalizer expands them to.
69
70 // We directly match zext patterns and select the zext atomic loads.
71 // i32 (zext (i8 (atomic_load_8))) gets legalized to
72 // i32 (and (i32 (atomic_load_8)), 255)
73 // These can be selected to a single zero-extending atomic load instruction.
74 def zext_aload_8 : PatFrag<(ops node:$addr),
75 (and (i32 (atomic_load_8 node:$addr)), 255)>;
76 def zext_aload_16 : PatFrag<(ops node:$addr),
77 (and (i32 (atomic_load_16 node:$addr)), 65535)>;
78 // Unlike regular loads, extension to i64 is handled differently than i32.
79 // i64 (zext (i8 (atomic_load_8))) gets legalized to
80 // i64 (and (i64 (anyext (i32 (atomic_load_8)))), 255)
81 def zext_aload_8_64 :
82 PatFrag<(ops node:$addr),
83 (and (i64 (anyext (i32 (atomic_load_8 node:$addr)))), 255)>;
84 def zext_aload_16_64 :
85 PatFrag<(ops node:$addr),
86 (and (i64 (anyext (i32 (atomic_load_16 node:$addr)))), 65535)>;
87 def zext_aload_32_64 :
88 PatFrag<(ops node:$addr),
89 (zext (i32 (atomic_load node:$addr)))>;
90
91 // We don't have single sext atomic load instructions. So for sext loads, we
92 // match bare subword loads (for 32-bit results) and anyext loads (for 64-bit
93 // results) and select a zext load; the next instruction will be sext_inreg
94 // which is selected by itself.
95 def anyext_aload_8_64 :
96 PatFrag<(ops node:$addr), (anyext (i32 (atomic_load_8 node:$addr)))>;
97 def anyext_aload_16_64 :
98 PatFrag<(ops node:$addr), (anyext (i32 (atomic_load_16 node:$addr)))>;
99
100 let Predicates = [HasAtomics] in {
101 // Select zero-extending loads with no constant offset.
102 def : LoadPatNoOffset;
103 def : LoadPatNoOffset;
104 def : LoadPatNoOffset;
105 def : LoadPatNoOffset;
106 def : LoadPatNoOffset;
107
108 // Select sign-extending loads with no constant offset
109 def : LoadPatNoOffset;
110 def : LoadPatNoOffset;
111 def : LoadPatNoOffset;
112 def : LoadPatNoOffset;
113 // 32->64 sext load gets selected as i32.atomic.load, i64.extend_s/i64
114
115
116 // Zero-extending loads with constant offset
117 def : LoadPatImmOff;
118 def : LoadPatImmOff;
119 def : LoadPatImmOff;
120 def : LoadPatImmOff;
121 def : LoadPatImmOff;
122 def : LoadPatImmOff;
123 def : LoadPatImmOff;
124 def : LoadPatImmOff;
125 def : LoadPatImmOff;
126 def : LoadPatImmOff;
127
128 // Sign-extending loads with constant offset
129 def : LoadPatImmOff;
130 def : LoadPatImmOff;
131 def : LoadPatImmOff;
132 def : LoadPatImmOff;
133 def : LoadPatImmOff;
134 def : LoadPatImmOff;
135 def : LoadPatImmOff;
136 def : LoadPatImmOff;
137 // No 32->64 patterns, just use i32.atomic.load and i64.extend_s/i64
138
139 def : LoadPatGlobalAddr;
140 def : LoadPatGlobalAddr;
141 def : LoadPatGlobalAddr;
142 def : LoadPatGlobalAddr;
143 def : LoadPatGlobalAddr;
144 def : LoadPatGlobalAddr;
145 def : LoadPatGlobalAddr;
146 def : LoadPatGlobalAddr;
147 def : LoadPatGlobalAddr;
148
149 def : LoadPatExternalSym;
150 def : LoadPatExternalSym;
151 def : LoadPatExternalSym;
152 def : LoadPatExternalSym;
153 def : LoadPatExternalSym;
154 def : LoadPatExternalSym;
155 def : LoadPatExternalSym;
156 def : LoadPatExternalSym;
157 def : LoadPatExternalSym;
158
159
160 // Extending loads with just a constant offset
161 def : LoadPatOffsetOnly;
162 def : LoadPatOffsetOnly;
163 def : LoadPatOffsetOnly;
164 def : LoadPatOffsetOnly;
165 def : LoadPatOffsetOnly;
166 def : LoadPatOffsetOnly;
167 def : LoadPatOffsetOnly;
168 def : LoadPatOffsetOnly;
169 def : LoadPatOffsetOnly;
170
171 def : LoadPatGlobalAddrOffOnly;
172 def : LoadPatGlobalAddrOffOnly;
173 def : LoadPatGlobalAddrOffOnly;
174 def : LoadPatGlobalAddrOffOnly;
175 def : LoadPatGlobalAddrOffOnly;
176 def : LoadPatGlobalAddrOffOnly;
177 def : LoadPatGlobalAddrOffOnly;
178 def : LoadPatGlobalAddrOffOnly;
179 def : LoadPatGlobalAddrOffOnly;
180
181 def : LoadPatExternSymOffOnly;
182 def : LoadPatExternSymOffOnly;
183 def : LoadPatExternSymOffOnly;
184 def : LoadPatExternSymOffOnly;
185 def : LoadPatExternSymOffOnly;
186 def : LoadPatExternSymOffOnly;
187 def : LoadPatExternSymOffOnly;
188 def : LoadPatExternSymOffOnly;
189 def : LoadPatExternSymOffOnly;
190
191
192 } // Predicates = [HasAtomics]
32193
33194 //===----------------------------------------------------------------------===//
34195 // Atomic stores
5454
5555 let Defs = [ARGUMENTS] in {
5656
57 // Classes to define both atomic and non-atomic integer loads
58 class CLoadI32 :
59 I<(outs I32:$dst),
60 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
61 [], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"), Opcode>;
62
63 class CLoadI64 :
64 I<(outs I64:$dst),
57 // Defines atomic and non-atomic loads, regular and extending.
58 class WebAssemblyLoad :
59 I<(outs rc:$dst),
6560 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
6661 [], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"), Opcode>;
6762
6863 // Basic load.
6964 // FIXME: When we can break syntax compatibility, reorder the fields in the
7065 // asmstrings to match the binary encoding.
71 def LOAD_I32 : CLoadI32<"i32.load", 0x28>;
72 def LOAD_I64 : CLoadI64<"i64.load", 0x29>;
73 def LOAD_F32 : I<(outs F32:$dst),
74 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
75 [], "f32.load\t$dst, ${off}(${addr})${p2align}", 0x2a>;
76 def LOAD_F64 : I<(outs F64:$dst),
77 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
78 [], "f64.load\t$dst, ${off}(${addr})${p2align}", 0x2b>;
66 def LOAD_I32 : WebAssemblyLoad>;
67 def LOAD_I64 : WebAssemblyLoad;
68 def LOAD_F32 : WebAssemblyLoad;
69 def LOAD_F64 : WebAssemblyLoad;
7970
8071 } // Defs = [ARGUMENTS]
8172
152143 let Defs = [ARGUMENTS] in {
153144
154145 // Extending load.
155 def LOAD8_S_I32 : I<(outs I32:$dst),
156 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
157 [], "i32.load8_s\t$dst, ${off}(${addr})${p2align}", 0x2c>;
158 def LOAD8_U_I32 : I<(outs I32:$dst),
159 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
160 [], "i32.load8_u\t$dst, ${off}(${addr})${p2align}", 0x2d>;
161 def LOAD16_S_I32 : I<(outs I32:$dst),
162 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
163 [], "i32.load16_s\t$dst, ${off}(${addr})${p2align}", 0x2e>;
164 def LOAD16_U_I32 : I<(outs I32:$dst),
165 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
166 [], "i32.load16_u\t$dst, ${off}(${addr})${p2align}", 0x2f>;
167 def LOAD8_S_I64 : I<(outs I64:$dst),
168 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
169 [], "i64.load8_s\t$dst, ${off}(${addr})${p2align}", 0x30>;
170 def LOAD8_U_I64 : I<(outs I64:$dst),
171 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
172 [], "i64.load8_u\t$dst, ${off}(${addr})${p2align}", 0x31>;
173 def LOAD16_S_I64 : I<(outs I64:$dst),
174 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
175 [], "i64.load16_s\t$dst, ${off}(${addr})${p2align}", 0x32>;
176 def LOAD16_U_I64 : I<(outs I64:$dst),
177 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
178 [], "i64.load16_u\t$dst, ${off}(${addr})${p2align}", 0x33>;
179 def LOAD32_S_I64 : I<(outs I64:$dst),
180 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
181 [], "i64.load32_s\t$dst, ${off}(${addr})${p2align}", 0x34>;
182 def LOAD32_U_I64 : I<(outs I64:$dst),
183 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
184 [], "i64.load32_u\t$dst, ${off}(${addr})${p2align}", 0x35>;
146 def LOAD8_S_I32 : WebAssemblyLoad>;
147 def LOAD8_U_I32 : WebAssemblyLoad;
148 def LOAD16_S_I32 : WebAssemblyLoad;
149 def LOAD16_U_I32 : WebAssemblyLoad;
150 def LOAD8_S_I64 : WebAssemblyLoad;
151 def LOAD8_U_I64 : WebAssemblyLoad;
152 def LOAD16_S_I64 : WebAssemblyLoad;
153 def LOAD16_U_I64 : WebAssemblyLoad;
154 def LOAD32_S_I64 : WebAssemblyLoad;
155 def LOAD32_U_I64 : WebAssemblyLoad;
185156
186157 } // Defs = [ARGUMENTS]
187158
288259 def : LoadPatNoOffset;
289260 def : LoadPatNoOffset;
290261 def : LoadPatNoOffset;
291
292262
293263 // Select "don't care" extending loads with a constant offset.
294264 def : LoadPatImmOff;
311281 def : LoadPatExternalSym;
312282 def : LoadPatExternalSym;
313283 def : LoadPatExternalSym;
314
315284
316285 // Select "don't care" extending loads with just a constant offset.
317286 def : LoadPatOffsetOnly;
9696 case WebAssembly::LOAD32_S_I64:
9797 case WebAssembly::LOAD32_U_I64:
9898 case WebAssembly::ATOMIC_LOAD_I32:
99 case WebAssembly::ATOMIC_LOAD8_U_I32:
100 case WebAssembly::ATOMIC_LOAD16_U_I32:
101 case WebAssembly::ATOMIC_LOAD_I64:
102 case WebAssembly::ATOMIC_LOAD8_U_I64:
103 case WebAssembly::ATOMIC_LOAD16_U_I64:
104 case WebAssembly::ATOMIC_LOAD32_U_I64:
99105 RewriteP2Align(MI, WebAssembly::LoadP2AlignOperandNo);
100106 break;
101107 case WebAssembly::STORE_I32:
+0
-16
test/CodeGen/WebAssembly/atomics.ll less more
None ; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt
1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+atomics | FileCheck %s
2
3 ; Test that atomic loads are assembled properly.
4
5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6 target triple = "wasm32-unknown-unknown-wasm"
7
8 ; CHECK-LABEL: load_i32_atomic:
9 ; CHECK: i32.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
10 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
11
12 define i32 @load_i32_atomic(i32 *%p) {
13 %v = load atomic i32, i32* %p seq_cst, align 4
14 ret i32 %v
15 }
None ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
0 ; RUN: llc < %s -mattr=+atomics -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
11
22 ; Test loads and stores with custom alignment values.
33
209209 store i16 %v, i16* %p, align 4
210210 ret void
211211 }
212
213 ; Atomics.
214 ; Wasm atomics have the alignment field, but it must always have the
215 ; type's natural alignment.
216
217 ; CHECK-LABEL: ldi32_atomic_a4:
218 ; CHECK-NEXT: .param i32{{$}}
219 ; CHECK-NEXT: .result i32{{$}}
220 ; CHECK-NEXT: i32.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
221 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
222 define i32 @ldi32_atomic_a4(i32 *%p) {
223 %v = load atomic i32, i32* %p seq_cst, align 4
224 ret i32 %v
225 }
226
227 ; 8 is greater than the default alignment so it is rounded down to 4
228
229 ; CHECK-LABEL: ldi32_atomic_a8:
230 ; CHECK-NEXT: .param i32{{$}}
231 ; CHECK-NEXT: .result i32{{$}}
232 ; CHECK-NEXT: i32.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
233 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
234 define i32 @ldi32_atomic_a8(i32 *%p) {
235 %v = load atomic i32, i32* %p seq_cst, align 8
236 ret i32 %v
237 }
None ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
0 ; RUN: llc < %s -mattr=+atomics -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
11
22 ; Test loads and stores with custom alignment values.
33
322322 store i32 %v, i32* %p, align 8
323323 ret void
324324 }
325
326 ; Atomics.
327 ; CHECK-LABEL: ldi64_atomic_a8:
328 ; CHECK-NEXT: .param i32{{$}}
329 ; CHECK-NEXT: .result i64{{$}}
330 ; CHECK-NEXT: i64.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
331 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
332 define i64 @ldi64_atomic_a8(i64 *%p) {
333 %v = load atomic i64, i64* %p seq_cst, align 8
334 ret i64 %v
335 }
336
337 ; 16 is greater than the default alignment so it is ignored.
338
339 ; CHECK-LABEL: ldi64_atomic_a16:
340 ; CHECK-NEXT: .param i32{{$}}
341 ; CHECK-NEXT: .result i64{{$}}
342 ; CHECK-NEXT: i64.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
343 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
344 define i64 @ldi64_atomic_a16(i64 *%p) {
345 %v = load atomic i64, i64* %p seq_cst, align 16
346 ret i64 %v
347 }
0 ; RUN: llc < %s -mattr=+atomics -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
1
2 ; Test that extending loads are assembled properly.
3
4 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
5 target triple = "wasm32-unknown-unknown-wasm"
6
7 ; CHECK-LABEL: sext_i8_i32:
8 ; CHECK: i32.atomic.load8_u $push0=, 0($0){{$}}
9 ; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
10 ; CHECK-NEXT: return $pop1{{$}}
11 define i32 @sext_i8_i32(i8 *%p) {
12 %v = load atomic i8, i8* %p seq_cst, align 1
13 %e = sext i8 %v to i32
14 ret i32 %e
15 }
16
17 ; CHECK-LABEL: zext_i8_i32:
18 ; CHECK: i32.atomic.load8_u $push0=, 0($0){{$}}
19 ; CHECK-NEXT: return $pop0{{$}}
20 define i32 @zext_i8_i32(i8 *%p) {
21 e1:
22 %v = load atomic i8, i8* %p seq_cst, align 1
23 %e = zext i8 %v to i32
24 ret i32 %e
25 }
26
27 ; CHECK-LABEL: sext_i16_i32:
28 ; CHECK: i32.atomic.load16_u $push0=, 0($0){{$}}
29 ; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
30 ; CHECK-NEXT: return $pop1{{$}}
31 define i32 @sext_i16_i32(i16 *%p) {
32 %v = load atomic i16, i16* %p seq_cst, align 2
33 %e = sext i16 %v to i32
34 ret i32 %e
35 }
36
37 ; CHECK-LABEL: zext_i16_i32:
38 ; CHECK: i32.atomic.load16_u $push0=, 0($0){{$}}
39 ; CHECK-NEXT: return $pop0{{$}}
40 define i32 @zext_i16_i32(i16 *%p) {
41 %v = load atomic i16, i16* %p seq_cst, align 2
42 %e = zext i16 %v to i32
43 ret i32 %e
44 }
45
46 ; CHECK-LABEL: sext_i8_i64:
47 ; CHECK: i64.atomic.load8_u $push0=, 0($0){{$}}
48 ; CHECK: i64.extend8_s $push1=, $pop0{{$}}
49 ; CHECK-NEXT: return $pop1{{$}}
50 define i64 @sext_i8_i64(i8 *%p) {
51 %v = load atomic i8, i8* %p seq_cst, align 1
52 %e = sext i8 %v to i64
53 ret i64 %e
54 }
55
56 ; CHECK-LABEL: zext_i8_i64:
57 ; CHECK: i64.atomic.load8_u $push0=, 0($0){{$}}
58 ; CHECK-NEXT: return $pop0{{$}}
59 define i64 @zext_i8_i64(i8 *%p) {
60 %v = load atomic i8, i8* %p seq_cst, align 1
61 %e = zext i8 %v to i64
62 ret i64 %e
63 }
64
65 ; CHECK-LABEL: sext_i16_i64:
66 ; CHECK: i64.atomic.load16_u $push0=, 0($0){{$}}
67 ; CHECK: i64.extend16_s $push1=, $pop0{{$}}
68 ; CHECK-NEXT: return $pop1{{$}}
69 define i64 @sext_i16_i64(i16 *%p) {
70 %v = load atomic i16, i16* %p seq_cst, align 2
71 %e = sext i16 %v to i64
72 ret i64 %e
73 }
74
75 ; CHECK-LABEL: zext_i16_i64:
76 ; CHECK: i64.atomic.load16_u $push0=, 0($0){{$}}
77 ; CHECK-NEXT: return $pop0{{$}}
78 define i64 @zext_i16_i64(i16 *%p) {
79 %v = load atomic i16, i16* %p seq_cst, align 2
80 %e = zext i16 %v to i64
81 ret i64 %e
82 }
83
84 ; CHECK-LABEL: sext_i32_i64:
85 ; CHECK: i32.atomic.load $push0=, 0($0){{$}}
86 ; CHECK: i64.extend_s/i32 $push1=, $pop0{{$}}
87 ; CHECK-NEXT: return $pop1{{$}}
88 define i64 @sext_i32_i64(i32 *%p) {
89 %v = load atomic i32, i32* %p seq_cst, align 4
90 %e = sext i32 %v to i64
91 ret i64 %e
92 }
93
94 ; CHECK-LABEL: zext_i32_i64:
95 ; CHECK: i64.atomic.load32_u $push0=, 0($0){{$}}
96 ; CHECK: return $pop0{{$}}
97 define i64 @zext_i32_i64(i32 *%p) {
98 %v = load atomic i32, i32* %p seq_cst, align 4
99 %e = zext i32 %v to i64
100 ret i64 %e
101 }
0 ; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt
1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+atomics | FileCheck %s
2
3 ; Test that atomic loads are assembled properly.
4
5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6 target triple = "wasm32-unknown-unknown-wasm"
7
8 ; CHECK-LABEL: load_i32_no_offset:
9 ; CHECK: i32.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
10 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
11 define i32 @load_i32_no_offset(i32 *%p) {
12 %v = load atomic i32, i32* %p seq_cst, align 4
13 ret i32 %v
14 }
15
16 ; With an nuw add, we can fold an offset.
17
18 ; CHECK-LABEL: load_i32_with_folded_offset:
19 ; CHECK: i32.atomic.load $push0=, 24($0){{$}}
20 define i32 @load_i32_with_folded_offset(i32* %p) {
21 %q = ptrtoint i32* %p to i32
22 %r = add nuw i32 %q, 24
23 %s = inttoptr i32 %r to i32*
24 %t = load atomic i32, i32* %s seq_cst, align 4
25 ret i32 %t
26 }
27
28 ; With an inbounds gep, we can fold an offset.
29
30 ; CHECK-LABEL: load_i32_with_folded_gep_offset:
31 ; CHECK: i32.atomic.load $push0=, 24($0){{$}}
32 define i32 @load_i32_with_folded_gep_offset(i32* %p) {
33 %s = getelementptr inbounds i32, i32* %p, i32 6
34 %t = load atomic i32, i32* %s seq_cst, align 4
35 ret i32 %t
36 }
37
38 ; We can't fold a negative offset though, even with an inbounds gep.
39
40 ; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset:
41 ; CHECK: i32.const $push0=, -24{{$}}
42 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
43 ; CHECK: i32.atomic.load $push2=, 0($pop1){{$}}
44 define i32 @load_i32_with_unfolded_gep_negative_offset(i32* %p) {
45 %s = getelementptr inbounds i32, i32* %p, i32 -6
46 %t = load atomic i32, i32* %s seq_cst, align 4
47 ret i32 %t
48 }
49
50 ; Without nuw, and even with nsw, we can't fold an offset.
51
52 ; CHECK-LABEL: load_i32_with_unfolded_offset:
53 ; CHECK: i32.const $push0=, 24{{$}}
54 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
55 ; CHECK: i32.atomic.load $push2=, 0($pop1){{$}}
56 define i32 @load_i32_with_unfolded_offset(i32* %p) {
57 %q = ptrtoint i32* %p to i32
58 %r = add nsw i32 %q, 24
59 %s = inttoptr i32 %r to i32*
60 %t = load atomic i32, i32* %s seq_cst, align 4
61 ret i32 %t
62 }
63
64 ; Without inbounds, we can't fold a gep offset.
65
66 ; CHECK-LABEL: load_i32_with_unfolded_gep_offset:
67 ; CHECK: i32.const $push0=, 24{{$}}
68 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
69 ; CHECK: i32.atomic.load $push2=, 0($pop1){{$}}
70 define i32 @load_i32_with_unfolded_gep_offset(i32* %p) {
71 %s = getelementptr i32, i32* %p, i32 6
72 %t = load atomic i32, i32* %s seq_cst, align 4
73 ret i32 %t
74 }
75
76 ; CHECK-LABEL: load_i64_no_offset:
77 ; CHECK: i64.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
78 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
79 define i64 @load_i64_no_offset(i64 *%p) {
80 %v = load atomic i64, i64* %p seq_cst, align 8
81 ret i64 %v
82 }
83
84 ; Same as above but with i64.
85
86 ; CHECK-LABEL: load_i64_with_folded_offset:
87 ; CHECK: i64.atomic.load $push0=, 24($0){{$}}
88 define i64 @load_i64_with_folded_offset(i64* %p) {
89 %q = ptrtoint i64* %p to i32
90 %r = add nuw i32 %q, 24
91 %s = inttoptr i32 %r to i64*
92 %t = load atomic i64, i64* %s seq_cst, align 8
93 ret i64 %t
94 }
95
96 ; Same as above but with i64.
97
98 ; CHECK-LABEL: load_i64_with_folded_gep_offset:
99 ; CHECK: i64.atomic.load $push0=, 24($0){{$}}
100 define i64 @load_i64_with_folded_gep_offset(i64* %p) {
101 %s = getelementptr inbounds i64, i64* %p, i32 3
102 %t = load atomic i64, i64* %s seq_cst, align 8
103 ret i64 %t
104 }
105
106 ; Same as above but with i64.
107
108 ; CHECK-LABEL: load_i64_with_unfolded_gep_negative_offset:
109 ; CHECK: i32.const $push0=, -24{{$}}
110 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
111 ; CHECK: i64.atomic.load $push2=, 0($pop1){{$}}
112 define i64 @load_i64_with_unfolded_gep_negative_offset(i64* %p) {
113 %s = getelementptr inbounds i64, i64* %p, i32 -3
114 %t = load atomic i64, i64* %s seq_cst, align 8
115 ret i64 %t
116 }
117
118 ; Same as above but with i64.
119
120 ; CHECK-LABEL: load_i64_with_unfolded_offset:
121 ; CHECK: i32.const $push0=, 24{{$}}
122 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
123 ; CHECK: i64.atomic.load $push2=, 0($pop1){{$}}
124 define i64 @load_i64_with_unfolded_offset(i64* %p) {
125 %q = ptrtoint i64* %p to i32
126 %r = add nsw i32 %q, 24
127 %s = inttoptr i32 %r to i64*
128 %t = load atomic i64, i64* %s seq_cst, align 8
129 ret i64 %t
130 }
131
132 ; Same as above but with i64.
133
134 ; CHECK-LABEL: load_i64_with_unfolded_gep_offset:
135 ; CHECK: i32.const $push0=, 24{{$}}
136 ; CHECK: i32.add $push1=, $0, $pop0{{$}}
137 ; CHECK: i64.atomic.load $push2=, 0($pop1){{$}}
138 define i64 @load_i64_with_unfolded_gep_offset(i64* %p) {
139 %s = getelementptr i64, i64* %p, i32 3
140 %t = load atomic i64, i64* %s seq_cst, align 8
141 ret i64 %t
142 }
143
144 ; CHECK-LABEL: load_i32_with_folded_or_offset:
145 ; CHECK: i32.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}}
146 ; CHECK-NEXT: i32.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
147 define i32 @load_i32_with_folded_or_offset(i32 %x) {
148 %and = and i32 %x, -4
149 %t0 = inttoptr i32 %and to i8*
150 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
151 %t1 = load atomic i8, i8* %arrayidx seq_cst, align 8
152 %conv = sext i8 %t1 to i32
153 ret i32 %conv
154 }
155
156 ; When loading from a fixed address, materialize a zero.
157
158 ; CHECK-LABEL: load_i32_from_numeric_address
159 ; CHECK: i32.const $push0=, 0{{$}}
160 ; CHECK: i32.atomic.load $push1=, 42($pop0){{$}}
161 define i32 @load_i32_from_numeric_address() {
162 %s = inttoptr i32 42 to i32*
163 %t = load atomic i32, i32* %s seq_cst, align 4
164 ret i32 %t
165 }
166
167
168 ; CHECK-LABEL: load_i32_from_global_address
169 ; CHECK: i32.const $push0=, 0{{$}}
170 ; CHECK: i32.atomic.load $push1=, gv($pop0){{$}}
171 @gv = global i32 0
172 define i32 @load_i32_from_global_address() {
173 %t = load atomic i32, i32* @gv seq_cst, align 4
174 ret i32 %t
175 }
176
177 ; Fold an offset into a sign-extending load.
178
179 ; CHECK-LABEL: load_i8_s_with_folded_offset:
180 ; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}}
181 ; CHECK-NEXT: i32.extend8_s $push1=, $pop0
182 define i32 @load_i8_s_with_folded_offset(i8* %p) {
183 %q = ptrtoint i8* %p to i32
184 %r = add nuw i32 %q, 24
185 %s = inttoptr i32 %r to i8*
186 %t = load atomic i8, i8* %s seq_cst, align 1
187 %u = sext i8 %t to i32
188 ret i32 %u
189 }
190
191 ; Fold a gep offset into a sign-extending load.
192
193 ; CHECK-LABEL: load_i8_s_with_folded_gep_offset:
194 ; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}}
195 ; CHECK-NEXT: i32.extend8_s $push1=, $pop0
196 define i32 @load_i8_s_with_folded_gep_offset(i8* %p) {
197 %s = getelementptr inbounds i8, i8* %p, i32 24
198 %t = load atomic i8, i8* %s seq_cst, align 1
199 %u = sext i8 %t to i32
200 ret i32 %u
201 }
202
203 ; CHECK-LABEL: load_i16_s_i64_with_folded_gep_offset:
204 ; CHECK: i64.atomic.load16_u $push0=, 6($0){{$}}
205 define i64 @load_i16_s_i64_with_folded_gep_offset(i16* %p) {
206 %s = getelementptr inbounds i16, i16* %p, i32 3
207 %t = load atomic i16, i16* %s seq_cst, align 2
208 %u = zext i16 %t to i64
209 ret i64 %u
210 }
211
212 ; CHECK-LABEL: load_i64_with_folded_or_offset:
213 ; CHECK: i64.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}}
214 ; CHECK-NEXT: i64.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
215 define i64 @load_i64_with_folded_or_offset(i32 %x) {
216 %and = and i32 %x, -4
217 %t0 = inttoptr i32 %and to i8*
218 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
219 %t1 = load atomic i8, i8* %arrayidx seq_cst, align 8
220 %conv = sext i8 %t1 to i64
221 ret i64 %conv
222 }
223
224
225 ; Fold an offset into a zero-extending load.
226
227 ; CHECK-LABEL: load_i16_u_with_folded_offset:
228 ; CHECK: i32.atomic.load16_u $push0=, 24($0){{$}}
229 define i32 @load_i16_u_with_folded_offset(i8* %p) {
230 %q = ptrtoint i8* %p to i32
231 %r = add nuw i32 %q, 24
232 %s = inttoptr i32 %r to i16*
233 %t = load atomic i16, i16* %s seq_cst, align 2
234 %u = zext i16 %t to i32
235 ret i32 %u
236 }
237
238 ; Fold a gep offset into a zero-extending load.
239
240 ; CHECK-LABEL: load_i8_u_with_folded_gep_offset:
241 ; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}}
242 define i32 @load_i8_u_with_folded_gep_offset(i8* %p) {
243 %s = getelementptr inbounds i8, i8* %p, i32 24
244 %t = load atomic i8, i8* %s seq_cst, align 1
245 %u = zext i8 %t to i32
246 ret i32 %u
247 }
248
249
250 ; When loading from a fixed address, materialize a zero.
251 ; As above but with extending load.
252
253 ; CHECK-LABEL: load_zext_i32_from_numeric_address
254 ; CHECK: i32.const $push0=, 0{{$}}
255 ; CHECK: i32.atomic.load16_u $push1=, 42($pop0){{$}}
256 define i32 @load_zext_i32_from_numeric_address() {
257 %s = inttoptr i32 42 to i16*
258 %t = load atomic i16, i16* %s seq_cst, align 2
259 %u = zext i16 %t to i32
260 ret i32 %u
261 }
262
263 ; CHECK-LABEL: load_sext_i32_from_global_address
264 ; CHECK: i32.const $push0=, 0{{$}}
265 ; CHECK: i32.atomic.load8_u $push1=, gv8($pop0){{$}}
266 ; CHECK-NEXT: i32.extend8_s $push2=, $pop1{{$}}
267 @gv8 = global i8 0
268 define i32 @load_sext_i32_from_global_address() {
269 %t = load atomic i8, i8* @gv8 seq_cst, align 1
270 %u = sext i8 %t to i32
271 ret i32 %u
272 }
273
274 ; Fold an offset into a sign-extending load.
275 ; As above but 32 extended to 64 bit.
276 ; CHECK-LABEL: load_i32_i64_s_with_folded_offset:
277 ; CHECK: i32.atomic.load $push0=, 24($0){{$}}
278 ; CHECK-NEXT: i64.extend_s/i32 $push1=, $pop0{{$}}
279 define i64 @load_i32_i64_s_with_folded_offset(i32* %p) {
280 %q = ptrtoint i32* %p to i32
281 %r = add nuw i32 %q, 24
282 %s = inttoptr i32 %r to i32*
283 %t = load atomic i32, i32* %s seq_cst, align 4
284 %u = sext i32 %t to i64
285 ret i64 %u
286 }
287
288 ; Fold a gep offset into a zero-extending load.
289 ; As above but 32 extended to 64 bit.
290 ; CHECK-LABEL: load_i32_i64_u_with_folded_gep_offset:
291 ; CHECK: i64.atomic.load32_u $push0=, 96($0){{$}}
292 define i64 @load_i32_i64_u_with_folded_gep_offset(i32* %p) {
293 %s = getelementptr inbounds i32, i32* %p, i32 24
294 %t = load atomic i32, i32* %s seq_cst, align 4
295 %u = zext i32 %t to i64
296 ret i64 %u
297 }
298
299 ; i8 return value should test anyext loads
300 ; CHECK-LABEL: ldi8_a1:
301 ; CHECK: i32.atomic.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
302 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
303 define i8 @ldi8_a1(i8 *%p) {
304 %v = load atomic i8, i8* %p seq_cst, align 1
305 ret i8 %v
306 }