[WebAssembly] Add new target feature in support of 'extended-const' proposal
[llvm-project.git] / llvm / lib / Target / WebAssembly / WebAssemblyInstrAtomics.td
blob42183d1645e1a3d1051ccdd4140d32692446b75c
1 // WebAssemblyInstrAtomics.td-WebAssembly Atomic codegen support-*- tablegen -*-
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// WebAssembly Atomic operand code-gen constructs.
11 ///
12 //===----------------------------------------------------------------------===//
14 let UseNamedOperandTable = 1 in
15 multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
16                     list<dag> pattern_r, string asmstr_r,
17                     string asmstr_s, bits<32> atomic_op,
18                     string is64 = "false"> {
19   defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
20               !or(0xfe00, !and(0xff, atomic_op)), is64>,
21             Requires<[HasAtomics]>;
24 multiclass ATOMIC_NRI<dag oops, dag iops, list<dag> pattern, string asmstr = "",
25                       bits<32> atomic_op = -1> {
26   defm "" : NRI<oops, iops, pattern, asmstr,
27                 !or(0xfe00, !and(0xff, atomic_op))>,
28             Requires<[HasAtomics]>;
31 //===----------------------------------------------------------------------===//
32 // Atomic wait / notify
33 //===----------------------------------------------------------------------===//
35 let hasSideEffects = 1 in {
36 defm MEMORY_ATOMIC_NOTIFY_A32 :
37   ATOMIC_I<(outs I32:$dst),
38            (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$count),
39            (outs), (ins P2Align:$p2align, offset32_op:$off), [],
40            "memory.atomic.notify \t$dst, ${off}(${addr})${p2align}, $count",
41            "memory.atomic.notify \t${off}${p2align}", 0x00, "false">;
42 defm MEMORY_ATOMIC_NOTIFY_A64 :
43   ATOMIC_I<(outs I32:$dst),
44            (ins P2Align:$p2align, offset64_op:$off, I64:$addr, I32:$count),
45            (outs), (ins P2Align:$p2align, offset64_op:$off), [],
46            "memory.atomic.notify \t$dst, ${off}(${addr})${p2align}, $count",
47            "memory.atomic.notify \t${off}${p2align}", 0x00, "true">;
48 let mayLoad = 1 in {
49 defm MEMORY_ATOMIC_WAIT32_A32 :
50   ATOMIC_I<(outs I32:$dst),
51            (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$exp,
52                 I64:$timeout),
53            (outs), (ins P2Align:$p2align, offset32_op:$off), [],
54            "memory.atomic.wait32 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
55            "memory.atomic.wait32 \t${off}${p2align}", 0x01, "false">;
56 defm MEMORY_ATOMIC_WAIT32_A64 :
57   ATOMIC_I<(outs I32:$dst),
58            (ins P2Align:$p2align, offset64_op:$off, I64:$addr, I32:$exp,
59                 I64:$timeout),
60            (outs), (ins P2Align:$p2align, offset64_op:$off), [],
61            "memory.atomic.wait32 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
62            "memory.atomic.wait32 \t${off}${p2align}", 0x01, "true">;
63 defm MEMORY_ATOMIC_WAIT64_A32 :
64   ATOMIC_I<(outs I32:$dst),
65            (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I64:$exp,
66                 I64:$timeout),
67            (outs), (ins P2Align:$p2align, offset32_op:$off), [],
68            "memory.atomic.wait64 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
69            "memory.atomic.wait64 \t${off}${p2align}", 0x02, "false">;
70 defm MEMORY_ATOMIC_WAIT64_A64 :
71   ATOMIC_I<(outs I32:$dst),
72            (ins P2Align:$p2align, offset64_op:$off, I64:$addr, I64:$exp,
73                 I64:$timeout),
74            (outs), (ins P2Align:$p2align, offset64_op:$off), [],
75            "memory.atomic.wait64 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
76            "memory.atomic.wait64 \t${off}${p2align}", 0x02, "true">;
77 } // mayLoad = 1
78 } // hasSideEffects = 1
80 // Select notifys with no constant offset.
81 def NotifyPatNoOffset_A32 :
82   Pat<(i32 (int_wasm_memory_atomic_notify I32:$addr, I32:$count)),
83       (MEMORY_ATOMIC_NOTIFY_A32 0, 0, I32:$addr, I32:$count)>,
84   Requires<[HasAddr32, HasAtomics]>;
85 def NotifyPatNoOffset_A64 :
86   Pat<(i32 (int_wasm_memory_atomic_notify I64:$addr, I32:$count)),
87       (MEMORY_ATOMIC_NOTIFY_A64 0, 0, I64:$addr, I32:$count)>,
88   Requires<[HasAddr64, HasAtomics]>;
90 // Select notifys with a constant offset.
92 // Pattern with address + immediate offset
93 multiclass NotifyPatImmOff<PatFrag operand, string inst> {
94   def : Pat<(i32 (int_wasm_memory_atomic_notify (operand I32:$addr, imm:$off),
95                   I32:$count)),
96             (!cast<NI>(inst#_A32) 0, imm:$off, I32:$addr, I32:$count)>,
97         Requires<[HasAddr32, HasAtomics]>;
98   def : Pat<(i32 (int_wasm_memory_atomic_notify (operand I64:$addr, imm:$off),
99                   I32:$count)),
100             (!cast<NI>(inst#_A64) 0, imm:$off, I64:$addr, I32:$count)>,
101         Requires<[HasAddr64, HasAtomics]>;
103 defm : NotifyPatImmOff<regPlusImm, "MEMORY_ATOMIC_NOTIFY">;
104 defm : NotifyPatImmOff<or_is_add, "MEMORY_ATOMIC_NOTIFY">;
106 // Select notifys with just a constant offset.
107 def NotifyPatOffsetOnly_A32 :
108   Pat<(i32 (int_wasm_memory_atomic_notify imm:$off, I32:$count)),
109       (MEMORY_ATOMIC_NOTIFY_A32 0, imm:$off, (CONST_I32 0), I32:$count)>,
110   Requires<[HasAddr32, HasAtomics]>;
111 def NotifyPatOffsetOnly_A64 :
112   Pat<(i32 (int_wasm_memory_atomic_notify imm:$off, I32:$count)),
113       (MEMORY_ATOMIC_NOTIFY_A64 0, imm:$off, (CONST_I64 0), I32:$count)>,
114   Requires<[HasAddr64, HasAtomics]>;
116 def NotifyPatGlobalAddrOffOnly_A32 :
117   Pat<(i32 (int_wasm_memory_atomic_notify (WebAssemblyWrapper tglobaladdr:$off),
118                                           I32:$count)),
119       (MEMORY_ATOMIC_NOTIFY_A32 0, tglobaladdr:$off, (CONST_I32 0), I32:$count)
120      >,
121   Requires<[HasAddr32, HasAtomics, IsNotPIC]>;
122 def NotifyPatGlobalAddrOffOnly_A64 :
123   Pat<(i32 (int_wasm_memory_atomic_notify (WebAssemblyWrapper tglobaladdr:$off),
124                                           I32:$count)),
125       (MEMORY_ATOMIC_NOTIFY_A64 0, tglobaladdr:$off, (CONST_I64 0), I32:$count)
126      >,
127   Requires<[HasAddr64, HasAtomics, IsNotPIC]>;
129 // Select waits with no constant offset.
130 multiclass WaitPatNoOffset<ValueType ty, Intrinsic kind,
131                       string inst> {
132   def : Pat<(i32 (kind I32:$addr, ty:$exp, I64:$timeout)),
133             (!cast<NI>(inst#_A32) 0, 0, I32:$addr, ty:$exp, I64:$timeout)>,
134         Requires<[HasAddr32, HasAtomics]>;
135   def : Pat<(i32 (kind I64:$addr, ty:$exp, I64:$timeout)),
136             (!cast<NI>(inst#_A64) 0, 0, I64:$addr, ty:$exp, I64:$timeout)>,
137         Requires<[HasAddr64, HasAtomics]>;
139 defm : WaitPatNoOffset<i32, int_wasm_memory_atomic_wait32,
140                        "MEMORY_ATOMIC_WAIT32">;
141 defm : WaitPatNoOffset<i64, int_wasm_memory_atomic_wait64,
142                        "MEMORY_ATOMIC_WAIT64">;
143 defm : WaitPatNoOffset<i32, int_wasm_memory_atomic_wait32,
144                        "MEMORY_ATOMIC_WAIT32">;
145 defm : WaitPatNoOffset<i64, int_wasm_memory_atomic_wait64,
146                        "MEMORY_ATOMIC_WAIT64">;
148 // Select waits with a constant offset.
150 // Pattern with address + immediate offset
151 multiclass WaitPatImmOff<ValueType ty, Intrinsic kind, PatFrag operand,
152                          string inst> {
153   def : Pat<(i32 (kind (operand I32:$addr, imm:$off), ty:$exp, I64:$timeout)),
154             (!cast<NI>(inst#_A32) 0, imm:$off, I32:$addr, ty:$exp,
155               I64:$timeout)>,
156         Requires<[HasAddr32, HasAtomics]>;
157   def : Pat<(i32 (kind (operand I64:$addr, imm:$off), ty:$exp, I64:$timeout)),
158             (!cast<NI>(inst#_A64) 0, imm:$off, I64:$addr, ty:$exp,
159               I64:$timeout)>,
160         Requires<[HasAddr64, HasAtomics]>;
162 defm : WaitPatImmOff<i32, int_wasm_memory_atomic_wait32, regPlusImm,
163                      "MEMORY_ATOMIC_WAIT32">;
164 defm : WaitPatImmOff<i32, int_wasm_memory_atomic_wait32, or_is_add,
165                      "MEMORY_ATOMIC_WAIT32">;
166 defm : WaitPatImmOff<i64, int_wasm_memory_atomic_wait64, regPlusImm,
167                      "MEMORY_ATOMIC_WAIT64">;
168 defm : WaitPatImmOff<i64, int_wasm_memory_atomic_wait64, or_is_add,
169                      "MEMORY_ATOMIC_WAIT64">;
171 // Select waits with just a constant offset.
172 multiclass WaitPatOffsetOnly<ValueType ty, Intrinsic kind, string inst> {
173   def : Pat<(i32 (kind imm:$off, ty:$exp, I64:$timeout)),
174             (!cast<NI>(inst#_A32) 0, imm:$off, (CONST_I32 0), ty:$exp,
175                I64:$timeout)>,
176         Requires<[HasAddr32, HasAtomics]>;
177   def : Pat<(i32 (kind imm:$off, ty:$exp, I64:$timeout)),
178             (!cast<NI>(inst#_A64) 0, imm:$off, (CONST_I64 0), ty:$exp,
179                I64:$timeout)>,
180         Requires<[HasAddr64, HasAtomics]>;
182 defm : WaitPatOffsetOnly<i32, int_wasm_memory_atomic_wait32,
183                          "MEMORY_ATOMIC_WAIT32">;
184 defm : WaitPatOffsetOnly<i64, int_wasm_memory_atomic_wait64,
185                          "MEMORY_ATOMIC_WAIT64">;
187 multiclass WaitPatGlobalAddrOffOnly<ValueType ty, Intrinsic kind, string inst> {
188   def : Pat<(i32 (kind (WebAssemblyWrapper tglobaladdr:$off), ty:$exp,
189                   I64:$timeout)),
190             (!cast<NI>(inst#_A32) 0, tglobaladdr:$off, (CONST_I32 0), ty:$exp,
191                I64:$timeout)>,
192         Requires<[HasAddr32, HasAtomics, IsNotPIC]>;
193   def : Pat<(i32 (kind (WebAssemblyWrapper tglobaladdr:$off), ty:$exp,
194                   I64:$timeout)),
195             (!cast<NI>(inst#_A64) 0, tglobaladdr:$off, (CONST_I64 0), ty:$exp,
196                I64:$timeout)>,
197         Requires<[HasAddr64, HasAtomics, IsNotPIC]>;
199 defm : WaitPatGlobalAddrOffOnly<i32, int_wasm_memory_atomic_wait32,
200                                 "MEMORY_ATOMIC_WAIT32">;
201 defm : WaitPatGlobalAddrOffOnly<i64, int_wasm_memory_atomic_wait64,
202                                 "MEMORY_ATOMIC_WAIT64">;
204 //===----------------------------------------------------------------------===//
205 // Atomic fences
206 //===----------------------------------------------------------------------===//
208 // A compiler fence instruction that prevents reordering of instructions.
209 let Defs = [ARGUMENTS] in {
210 let isPseudo = 1, hasSideEffects = 1 in
211 defm COMPILER_FENCE : ATOMIC_NRI<(outs), (ins), [], "compiler_fence">;
212 let hasSideEffects = 1 in
213 defm ATOMIC_FENCE : ATOMIC_NRI<(outs), (ins i8imm:$flags), [], "atomic.fence",
214                                0x03>;
215 } // Defs = [ARGUMENTS]
217 //===----------------------------------------------------------------------===//
218 // Atomic loads
219 //===----------------------------------------------------------------------===//
221 multiclass AtomicLoad<WebAssemblyRegClass rc, string name, int atomic_op> {
222   defm "" : WebAssemblyLoad<rc, name, !or(0xfe00, !and(0xff, atomic_op)),
223                             [HasAtomics]>;
226 defm ATOMIC_LOAD_I32 : AtomicLoad<I32, "i32.atomic.load", 0x10>;
227 defm ATOMIC_LOAD_I64 : AtomicLoad<I64, "i64.atomic.load", 0x11>;
229 // Select loads with no constant offset.
230 defm : LoadPatNoOffset<i32, atomic_load_32, "ATOMIC_LOAD_I32">;
231 defm : LoadPatNoOffset<i64, atomic_load_64, "ATOMIC_LOAD_I64">;
233 // Select loads with a constant offset.
235 // Pattern with address + immediate offset
236 defm : LoadPatImmOff<i32, atomic_load_32, regPlusImm, "ATOMIC_LOAD_I32">;
237 defm : LoadPatImmOff<i64, atomic_load_64, regPlusImm, "ATOMIC_LOAD_I64">;
238 defm : LoadPatImmOff<i32, atomic_load_32, or_is_add, "ATOMIC_LOAD_I32">;
239 defm : LoadPatImmOff<i64, atomic_load_64, or_is_add, "ATOMIC_LOAD_I64">;
241 // Select loads with just a constant offset.
242 defm : LoadPatOffsetOnly<i32, atomic_load_32, "ATOMIC_LOAD_I32">;
243 defm : LoadPatOffsetOnly<i64, atomic_load_64, "ATOMIC_LOAD_I64">;
245 defm : LoadPatGlobalAddrOffOnly<i32, atomic_load_32, "ATOMIC_LOAD_I32">;
246 defm : LoadPatGlobalAddrOffOnly<i64, atomic_load_64, "ATOMIC_LOAD_I64">;
249 // Extending loads. Note that there are only zero-extending atomic loads, no
250 // sign-extending loads.
251 defm ATOMIC_LOAD8_U_I32 : AtomicLoad<I32, "i32.atomic.load8_u", 0x12>;
252 defm ATOMIC_LOAD16_U_I32 : AtomicLoad<I32, "i32.atomic.load16_u", 0x13>;
253 defm ATOMIC_LOAD8_U_I64 : AtomicLoad<I64, "i64.atomic.load8_u", 0x14>;
254 defm ATOMIC_LOAD16_U_I64 : AtomicLoad<I64, "i64.atomic.load16_u", 0x15>;
255 defm ATOMIC_LOAD32_U_I64 : AtomicLoad<I64, "i64.atomic.load32_u", 0x16>;
257 // Fragments for extending loads. These are different from regular loads because
258 // the SDNodes are derived from AtomicSDNode rather than LoadSDNode and
259 // therefore don't have the extension type field. So instead of matching that,
260 // we match the patterns that the type legalizer expands them to.
262 // Unlike regular loads, extension to i64 is handled differently than i32.
263 // i64 (zext (i8 (atomic_load_8))) gets legalized to
264 // i64 (and (i64 (anyext (i32 (atomic_load_8)))), 255)
265 // Extension to i32 is elided by SelectionDAG as our atomic loads are
266 // zero-extending.
267 def zext_aload_8_64 :
268   PatFrag<(ops node:$addr),
269           (i64 (zext (i32 (atomic_load_8 node:$addr))))>;
270 def zext_aload_16_64 :
271   PatFrag<(ops node:$addr),
272           (i64 (zext (i32 (atomic_load_16 node:$addr))))>;
273 def zext_aload_32_64 :
274   PatFrag<(ops node:$addr),
275           (i64 (zext (i32 (atomic_load_32 node:$addr))))>;
277 // We don't have single sext atomic load instructions. So for sext loads, we
278 // match bare subword loads (for 32-bit results) and anyext loads (for 64-bit
279 // results) and select a zext load; the next instruction will be sext_inreg
280 // which is selected by itself.
281 def sext_aload_8_64 :
282   PatFrag<(ops node:$addr), (anyext (i32 (atomic_load_8 node:$addr)))>;
283 def sext_aload_16_64 :
284   PatFrag<(ops node:$addr), (anyext (i32 (atomic_load_16 node:$addr)))>;
286 // Select zero-extending loads with no constant offset.
287 defm : LoadPatNoOffset<i64, zext_aload_8_64, "ATOMIC_LOAD8_U_I64">;
288 defm : LoadPatNoOffset<i64, zext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
289 defm : LoadPatNoOffset<i64, zext_aload_32_64, "ATOMIC_LOAD32_U_I64">;
291 // Select sign-extending loads with no constant offset
292 defm : LoadPatNoOffset<i32, atomic_load_8, "ATOMIC_LOAD8_U_I32">;
293 defm : LoadPatNoOffset<i32, atomic_load_16, "ATOMIC_LOAD16_U_I32">;
294 defm : LoadPatNoOffset<i64, sext_aload_8_64, "ATOMIC_LOAD8_U_I64">;
295 defm : LoadPatNoOffset<i64, sext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
296 // 32->64 sext load gets selected as i32.atomic.load, i64.extend_i32_s
298 // Zero-extending loads with constant offset
299 defm : LoadPatImmOff<i64, zext_aload_8_64, regPlusImm, "ATOMIC_LOAD8_U_I64">;
300 defm : LoadPatImmOff<i64, zext_aload_16_64, regPlusImm, "ATOMIC_LOAD16_U_I64">;
301 defm : LoadPatImmOff<i64, zext_aload_32_64, regPlusImm, "ATOMIC_LOAD32_U_I64">;
302 defm : LoadPatImmOff<i64, zext_aload_8_64, or_is_add, "ATOMIC_LOAD8_U_I64">;
303 defm : LoadPatImmOff<i64, zext_aload_16_64, or_is_add, "ATOMIC_LOAD16_U_I64">;
304 defm : LoadPatImmOff<i64, zext_aload_32_64, or_is_add, "ATOMIC_LOAD32_U_I64">;
306 // Sign-extending loads with constant offset
307 defm : LoadPatImmOff<i32, atomic_load_8, regPlusImm, "ATOMIC_LOAD8_U_I32">;
308 defm : LoadPatImmOff<i32, atomic_load_16, regPlusImm, "ATOMIC_LOAD16_U_I32">;
309 defm : LoadPatImmOff<i32, atomic_load_8, or_is_add, "ATOMIC_LOAD8_U_I32">;
310 defm : LoadPatImmOff<i32, atomic_load_16, or_is_add, "ATOMIC_LOAD16_U_I32">;
311 defm : LoadPatImmOff<i64, sext_aload_8_64, regPlusImm, "ATOMIC_LOAD8_U_I64">;
312 defm : LoadPatImmOff<i64, sext_aload_16_64, regPlusImm, "ATOMIC_LOAD16_U_I64">;
313 defm : LoadPatImmOff<i64, sext_aload_8_64, or_is_add, "ATOMIC_LOAD8_U_I64">;
314 defm : LoadPatImmOff<i64, sext_aload_16_64, or_is_add, "ATOMIC_LOAD16_U_I64">;
315 // No 32->64 patterns, just use i32.atomic.load and i64.extend_s/i64
317 // Extending loads with just a constant offset
318 defm : LoadPatOffsetOnly<i64, zext_aload_8_64, "ATOMIC_LOAD8_U_I64">;
319 defm : LoadPatOffsetOnly<i64, zext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
320 defm : LoadPatOffsetOnly<i64, zext_aload_32_64, "ATOMIC_LOAD32_U_I64">;
321 defm : LoadPatOffsetOnly<i32, atomic_load_8, "ATOMIC_LOAD8_U_I32">;
322 defm : LoadPatOffsetOnly<i32, atomic_load_16, "ATOMIC_LOAD16_U_I32">;
323 defm : LoadPatOffsetOnly<i64, sext_aload_8_64, "ATOMIC_LOAD8_U_I64">;
324 defm : LoadPatOffsetOnly<i64, sext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
326 defm : LoadPatGlobalAddrOffOnly<i64, zext_aload_8_64, "ATOMIC_LOAD8_U_I64">;
327 defm : LoadPatGlobalAddrOffOnly<i64, zext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
328 defm : LoadPatGlobalAddrOffOnly<i64, zext_aload_32_64, "ATOMIC_LOAD32_U_I64">;
329 defm : LoadPatGlobalAddrOffOnly<i32, atomic_load_8, "ATOMIC_LOAD8_U_I32">;
330 defm : LoadPatGlobalAddrOffOnly<i32, atomic_load_16, "ATOMIC_LOAD16_U_I32">;
331 defm : LoadPatGlobalAddrOffOnly<i64, sext_aload_8_64, "ATOMIC_LOAD8_U_I64">;
332 defm : LoadPatGlobalAddrOffOnly<i64, sext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
335 //===----------------------------------------------------------------------===//
336 // Atomic stores
337 //===----------------------------------------------------------------------===//
339 multiclass AtomicStore<WebAssemblyRegClass rc, string name, int atomic_op> {
340   defm "" : WebAssemblyStore<rc, name, !or(0xfe00, !and(0xff, atomic_op)),
341                              [HasAtomics]>;
344 defm ATOMIC_STORE_I32 : AtomicStore<I32, "i32.atomic.store", 0x17>;
345 defm ATOMIC_STORE_I64 : AtomicStore<I64, "i64.atomic.store", 0x18>;
347 // We need an 'atomic' version of store patterns because store and atomic_store
348 // nodes have different operand orders:
349 // store: (store $val, $ptr)
350 // atomic_store: (store $ptr, $val)
353 // Select stores with no constant offset.
354 multiclass AStorePatNoOffset<ValueType ty, PatFrag kind, string inst> {
355   def : Pat<(kind I32:$addr, ty:$val),
356             (!cast<NI>(inst#_A32) 0, 0, I32:$addr, ty:$val)>,
357         Requires<[HasAddr32, HasAtomics]>;
358   def : Pat<(kind I64:$addr, ty:$val),
359             (!cast<NI>(inst#_A64) 0, 0, I64:$addr, ty:$val)>,
360         Requires<[HasAddr64, HasAtomics]>;
362 defm : AStorePatNoOffset<i32, atomic_store_32, "ATOMIC_STORE_I32">;
363 defm : AStorePatNoOffset<i64, atomic_store_64, "ATOMIC_STORE_I64">;
365 // Select stores with a constant offset.
367 // Pattern with address + immediate offset
368 multiclass AStorePatImmOff<ValueType ty, PatFrag kind, PatFrag operand,
369                            string inst> {
370   def : Pat<(kind (operand I32:$addr, imm:$off), ty:$val),
371             (!cast<NI>(inst#_A32) 0, imm:$off, I32:$addr, ty:$val)>,
372         Requires<[HasAddr32, HasAtomics]>;
373   def : Pat<(kind (operand I64:$addr, imm:$off), ty:$val),
374             (!cast<NI>(inst#_A64) 0, imm:$off, I64:$addr, ty:$val)>,
375         Requires<[HasAddr64, HasAtomics]>;
377 defm : AStorePatImmOff<i32, atomic_store_32, regPlusImm, "ATOMIC_STORE_I32">;
378 defm : AStorePatImmOff<i64, atomic_store_64, regPlusImm, "ATOMIC_STORE_I64">;
380 // Select stores with just a constant offset.
381 multiclass AStorePatOffsetOnly<ValueType ty, PatFrag kind, string inst> {
382   def : Pat<(kind imm:$off, ty:$val),
383             (!cast<NI>(inst#_A32) 0, imm:$off, (CONST_I32 0), ty:$val)>,
384         Requires<[HasAddr32, HasAtomics]>;
385   def : Pat<(kind imm:$off, ty:$val),
386             (!cast<NI>(inst#_A64) 0, imm:$off, (CONST_I64 0), ty:$val)>,
387         Requires<[HasAddr64, HasAtomics]>;
389 defm : AStorePatOffsetOnly<i32, atomic_store_32, "ATOMIC_STORE_I32">;
390 defm : AStorePatOffsetOnly<i64, atomic_store_64, "ATOMIC_STORE_I64">;
392 multiclass AStorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, string inst> {
393   def : Pat<(kind (WebAssemblyWrapper tglobaladdr:$off), ty:$val),
394             (!cast<NI>(inst#_A32) 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>,
395         Requires<[HasAddr32, HasAtomics, IsNotPIC]>;
396   def : Pat<(kind (WebAssemblyWrapper tglobaladdr:$off), ty:$val),
397             (!cast<NI>(inst#_A64) 0, tglobaladdr:$off, (CONST_I64 0), ty:$val)>,
398         Requires<[HasAddr64, HasAtomics, IsNotPIC]>;
400 defm : AStorePatGlobalAddrOffOnly<i32, atomic_store_32, "ATOMIC_STORE_I32">;
401 defm : AStorePatGlobalAddrOffOnly<i64, atomic_store_64, "ATOMIC_STORE_I64">;
404 // Truncating stores.
405 defm ATOMIC_STORE8_I32 : AtomicStore<I32, "i32.atomic.store8", 0x19>;
406 defm ATOMIC_STORE16_I32 : AtomicStore<I32, "i32.atomic.store16", 0x1a>;
407 defm ATOMIC_STORE8_I64 : AtomicStore<I64, "i64.atomic.store8", 0x1b>;
408 defm ATOMIC_STORE16_I64 : AtomicStore<I64, "i64.atomic.store16", 0x1c>;
409 defm ATOMIC_STORE32_I64 : AtomicStore<I64, "i64.atomic.store32", 0x1d>;
411 // Fragments for truncating stores.
413 // We don't have single truncating atomic store instructions. For 32-bit
414 // instructions, we just need to match bare atomic stores. On the other hand,
415 // truncating stores from i64 values are once truncated to i32 first.
416 class trunc_astore_64<PatFrag kind> :
417   PatFrag<(ops node:$addr, node:$val),
418           (kind node:$addr, (i32 (trunc (i64 node:$val))))>;
419 def trunc_astore_8_64 : trunc_astore_64<atomic_store_8>;
420 def trunc_astore_16_64 : trunc_astore_64<atomic_store_16>;
421 def trunc_astore_32_64 : trunc_astore_64<atomic_store_32>;
424 // Truncating stores with no constant offset
425 defm : AStorePatNoOffset<i32, atomic_store_8, "ATOMIC_STORE8_I32">;
426 defm : AStorePatNoOffset<i32, atomic_store_16, "ATOMIC_STORE16_I32">;
427 defm : AStorePatNoOffset<i64, trunc_astore_8_64, "ATOMIC_STORE8_I64">;
428 defm : AStorePatNoOffset<i64, trunc_astore_16_64, "ATOMIC_STORE16_I64">;
429 defm : AStorePatNoOffset<i64, trunc_astore_32_64, "ATOMIC_STORE32_I64">;
431 // Truncating stores with a constant offset
432 defm : AStorePatImmOff<i32, atomic_store_8, regPlusImm, "ATOMIC_STORE8_I32">;
433 defm : AStorePatImmOff<i32, atomic_store_16, regPlusImm, "ATOMIC_STORE16_I32">;
434 defm : AStorePatImmOff<i64, trunc_astore_8_64, regPlusImm, "ATOMIC_STORE8_I64">;
435 defm : AStorePatImmOff<i64, trunc_astore_16_64, regPlusImm,
436                        "ATOMIC_STORE16_I64">;
437 defm : AStorePatImmOff<i64, trunc_astore_32_64, regPlusImm,
438                        "ATOMIC_STORE32_I64">;
439 defm : AStorePatImmOff<i32, atomic_store_8, or_is_add, "ATOMIC_STORE8_I32">;
440 defm : AStorePatImmOff<i32, atomic_store_16, or_is_add, "ATOMIC_STORE16_I32">;
441 defm : AStorePatImmOff<i64, trunc_astore_8_64, or_is_add, "ATOMIC_STORE8_I64">;
442 defm : AStorePatImmOff<i64, trunc_astore_16_64, or_is_add,
443                        "ATOMIC_STORE16_I64">;
444 defm : AStorePatImmOff<i64, trunc_astore_32_64, or_is_add,
445                        "ATOMIC_STORE32_I64">;
447 // Truncating stores with just a constant offset
448 defm : AStorePatOffsetOnly<i32, atomic_store_8, "ATOMIC_STORE8_I32">;
449 defm : AStorePatOffsetOnly<i32, atomic_store_16, "ATOMIC_STORE16_I32">;
450 defm : AStorePatOffsetOnly<i64, trunc_astore_8_64, "ATOMIC_STORE8_I64">;
451 defm : AStorePatOffsetOnly<i64, trunc_astore_16_64, "ATOMIC_STORE16_I64">;
452 defm : AStorePatOffsetOnly<i64, trunc_astore_32_64, "ATOMIC_STORE32_I64">;
454 defm : AStorePatGlobalAddrOffOnly<i32, atomic_store_8, "ATOMIC_STORE8_I32">;
455 defm : AStorePatGlobalAddrOffOnly<i32, atomic_store_16, "ATOMIC_STORE16_I32">;
456 defm : AStorePatGlobalAddrOffOnly<i64, trunc_astore_8_64, "ATOMIC_STORE8_I64">;
457 defm : AStorePatGlobalAddrOffOnly<i64, trunc_astore_16_64, "ATOMIC_STORE16_I64">;
458 defm : AStorePatGlobalAddrOffOnly<i64, trunc_astore_32_64, "ATOMIC_STORE32_I64">;
461 //===----------------------------------------------------------------------===//
462 // Atomic binary read-modify-writes
463 //===----------------------------------------------------------------------===//
465 multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string name,
466                              int atomic_op> {
467   defm "_A32" :
468     ATOMIC_I<(outs rc:$dst),
469              (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
470              (outs), (ins P2Align:$p2align, offset32_op:$off), [],
471              !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"),
472              !strconcat(name, "\t${off}${p2align}"), atomic_op, "false">;
473   defm "_A64" :
474     ATOMIC_I<(outs rc:$dst),
475              (ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$val),
476              (outs), (ins P2Align:$p2align, offset64_op:$off), [],
477              !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"),
478              !strconcat(name, "\t${off}${p2align}"), atomic_op, "true">;
481 defm ATOMIC_RMW_ADD_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.add", 0x1e>;
482 defm ATOMIC_RMW_ADD_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.add", 0x1f>;
483 defm ATOMIC_RMW8_U_ADD_I32 :
484   WebAssemblyBinRMW<I32, "i32.atomic.rmw8.add_u", 0x20>;
485 defm ATOMIC_RMW16_U_ADD_I32 :
486   WebAssemblyBinRMW<I32, "i32.atomic.rmw16.add_u", 0x21>;
487 defm ATOMIC_RMW8_U_ADD_I64 :
488   WebAssemblyBinRMW<I64, "i64.atomic.rmw8.add_u", 0x22>;
489 defm ATOMIC_RMW16_U_ADD_I64 :
490   WebAssemblyBinRMW<I64, "i64.atomic.rmw16.add_u", 0x23>;
491 defm ATOMIC_RMW32_U_ADD_I64 :
492   WebAssemblyBinRMW<I64, "i64.atomic.rmw32.add_u", 0x24>;
494 defm ATOMIC_RMW_SUB_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.sub", 0x25>;
495 defm ATOMIC_RMW_SUB_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.sub", 0x26>;
496 defm ATOMIC_RMW8_U_SUB_I32 :
497   WebAssemblyBinRMW<I32, "i32.atomic.rmw8.sub_u", 0x27>;
498 defm ATOMIC_RMW16_U_SUB_I32 :
499   WebAssemblyBinRMW<I32, "i32.atomic.rmw16.sub_u", 0x28>;
500 defm ATOMIC_RMW8_U_SUB_I64 :
501   WebAssemblyBinRMW<I64, "i64.atomic.rmw8.sub_u", 0x29>;
502 defm ATOMIC_RMW16_U_SUB_I64 :
503   WebAssemblyBinRMW<I64, "i64.atomic.rmw16.sub_u", 0x2a>;
504 defm ATOMIC_RMW32_U_SUB_I64 :
505   WebAssemblyBinRMW<I64, "i64.atomic.rmw32.sub_u", 0x2b>;
507 defm ATOMIC_RMW_AND_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.and", 0x2c>;
508 defm ATOMIC_RMW_AND_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.and", 0x2d>;
509 defm ATOMIC_RMW8_U_AND_I32 :
510   WebAssemblyBinRMW<I32, "i32.atomic.rmw8.and_u", 0x2e>;
511 defm ATOMIC_RMW16_U_AND_I32 :
512   WebAssemblyBinRMW<I32, "i32.atomic.rmw16.and_u", 0x2f>;
513 defm ATOMIC_RMW8_U_AND_I64 :
514   WebAssemblyBinRMW<I64, "i64.atomic.rmw8.and_u", 0x30>;
515 defm ATOMIC_RMW16_U_AND_I64 :
516   WebAssemblyBinRMW<I64, "i64.atomic.rmw16.and_u", 0x31>;
517 defm ATOMIC_RMW32_U_AND_I64 :
518   WebAssemblyBinRMW<I64, "i64.atomic.rmw32.and_u", 0x32>;
520 defm ATOMIC_RMW_OR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.or", 0x33>;
521 defm ATOMIC_RMW_OR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.or", 0x34>;
522 defm ATOMIC_RMW8_U_OR_I32 :
523   WebAssemblyBinRMW<I32, "i32.atomic.rmw8.or_u", 0x35>;
524 defm ATOMIC_RMW16_U_OR_I32 :
525   WebAssemblyBinRMW<I32, "i32.atomic.rmw16.or_u", 0x36>;
526 defm ATOMIC_RMW8_U_OR_I64 :
527   WebAssemblyBinRMW<I64, "i64.atomic.rmw8.or_u", 0x37>;
528 defm ATOMIC_RMW16_U_OR_I64 :
529   WebAssemblyBinRMW<I64, "i64.atomic.rmw16.or_u", 0x38>;
530 defm ATOMIC_RMW32_U_OR_I64 :
531   WebAssemblyBinRMW<I64, "i64.atomic.rmw32.or_u", 0x39>;
533 defm ATOMIC_RMW_XOR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.xor", 0x3a>;
534 defm ATOMIC_RMW_XOR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.xor", 0x3b>;
535 defm ATOMIC_RMW8_U_XOR_I32 :
536   WebAssemblyBinRMW<I32, "i32.atomic.rmw8.xor_u", 0x3c>;
537 defm ATOMIC_RMW16_U_XOR_I32 :
538   WebAssemblyBinRMW<I32, "i32.atomic.rmw16.xor_u", 0x3d>;
539 defm ATOMIC_RMW8_U_XOR_I64 :
540   WebAssemblyBinRMW<I64, "i64.atomic.rmw8.xor_u", 0x3e>;
541 defm ATOMIC_RMW16_U_XOR_I64 :
542   WebAssemblyBinRMW<I64, "i64.atomic.rmw16.xor_u", 0x3f>;
543 defm ATOMIC_RMW32_U_XOR_I64 :
544   WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xor_u", 0x40>;
546 defm ATOMIC_RMW_XCHG_I32 :
547   WebAssemblyBinRMW<I32, "i32.atomic.rmw.xchg", 0x41>;
548 defm ATOMIC_RMW_XCHG_I64 :
549   WebAssemblyBinRMW<I64, "i64.atomic.rmw.xchg", 0x42>;
550 defm ATOMIC_RMW8_U_XCHG_I32 :
551   WebAssemblyBinRMW<I32, "i32.atomic.rmw8.xchg_u", 0x43>;
552 defm ATOMIC_RMW16_U_XCHG_I32 :
553   WebAssemblyBinRMW<I32, "i32.atomic.rmw16.xchg_u", 0x44>;
554 defm ATOMIC_RMW8_U_XCHG_I64 :
555   WebAssemblyBinRMW<I64, "i64.atomic.rmw8.xchg_u", 0x45>;
556 defm ATOMIC_RMW16_U_XCHG_I64 :
557   WebAssemblyBinRMW<I64, "i64.atomic.rmw16.xchg_u", 0x46>;
558 defm ATOMIC_RMW32_U_XCHG_I64 :
559   WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xchg_u", 0x47>;
561 // Select binary RMWs with no constant offset.
562 multiclass BinRMWPatNoOffset<ValueType ty, PatFrag kind, string inst> {
563   def : Pat<(ty (kind I32:$addr, ty:$val)),
564             (!cast<NI>(inst#_A32) 0, 0, I32:$addr, ty:$val)>,
565         Requires<[HasAddr32, HasAtomics]>;
566   def : Pat<(ty (kind I64:$addr, ty:$val)),
567             (!cast<NI>(inst#_A64) 0, 0, I64:$addr, ty:$val)>,
568         Requires<[HasAddr64, HasAtomics]>;
571 // Select binary RMWs with a constant offset.
573 // Pattern with address + immediate offset
574 multiclass BinRMWPatImmOff<ValueType ty, PatFrag kind, PatFrag operand,
575                            string inst> {
576   def : Pat<(ty (kind (operand I32:$addr, imm:$off), ty:$val)),
577             (!cast<NI>(inst#_A32) 0, imm:$off, I32:$addr, ty:$val)>,
578         Requires<[HasAddr32, HasAtomics]>;
579   def : Pat<(ty (kind (operand I64:$addr, imm:$off), ty:$val)),
580             (!cast<NI>(inst#_A64) 0, imm:$off, I64:$addr, ty:$val)>,
581         Requires<[HasAddr64, HasAtomics]>;
584 // Select binary RMWs with just a constant offset.
585 multiclass BinRMWPatOffsetOnly<ValueType ty, PatFrag kind, string inst> {
586   def : Pat<(ty (kind imm:$off, ty:$val)),
587             (!cast<NI>(inst#_A32) 0, imm:$off, (CONST_I32 0), ty:$val)>,
588         Requires<[HasAddr32, HasAtomics]>;
589   def : Pat<(ty (kind imm:$off, ty:$val)),
590             (!cast<NI>(inst#_A64) 0, imm:$off, (CONST_I64 0), ty:$val)>,
591         Requires<[HasAddr64, HasAtomics]>;
594 multiclass BinRMWPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, string inst> {
595   def : Pat<(ty (kind (WebAssemblyWrapper tglobaladdr:$off), ty:$val)),
596             (!cast<NI>(inst#_A32) 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>,
597         Requires<[HasAddr32, HasAtomics, IsNotPIC]>;
598   def : Pat<(ty (kind (WebAssemblyWrapper tglobaladdr:$off), ty:$val)),
599             (!cast<NI>(inst#_A64) 0, tglobaladdr:$off, (CONST_I64 0), ty:$val)>,
600         Requires<[HasAddr64, HasAtomics, IsNotPIC]>;
603 // Patterns for various addressing modes.
604 multiclass BinRMWPattern<PatFrag rmw_32, PatFrag rmw_64, string inst_32,
605                          string inst_64> {
606   defm : BinRMWPatNoOffset<i32, rmw_32, inst_32>;
607   defm : BinRMWPatNoOffset<i64, rmw_64, inst_64>;
609   defm : BinRMWPatImmOff<i32, rmw_32, regPlusImm, inst_32>;
610   defm : BinRMWPatImmOff<i64, rmw_64, regPlusImm, inst_64>;
611   defm : BinRMWPatImmOff<i32, rmw_32, or_is_add, inst_32>;
612   defm : BinRMWPatImmOff<i64, rmw_64, or_is_add, inst_64>;
614   defm : BinRMWPatOffsetOnly<i32, rmw_32, inst_32>;
615   defm : BinRMWPatOffsetOnly<i64, rmw_64, inst_64>;
617   defm : BinRMWPatGlobalAddrOffOnly<i32, rmw_32, inst_32>;
618   defm : BinRMWPatGlobalAddrOffOnly<i64, rmw_64, inst_64>;
621 defm : BinRMWPattern<atomic_load_add_32, atomic_load_add_64,
622                      "ATOMIC_RMW_ADD_I32", "ATOMIC_RMW_ADD_I64">;
623 defm : BinRMWPattern<atomic_load_sub_32, atomic_load_sub_64,
624                      "ATOMIC_RMW_SUB_I32", "ATOMIC_RMW_SUB_I64">;
625 defm : BinRMWPattern<atomic_load_and_32, atomic_load_and_64,
626                      "ATOMIC_RMW_AND_I32", "ATOMIC_RMW_AND_I64">;
627 defm : BinRMWPattern<atomic_load_or_32, atomic_load_or_64,
628                      "ATOMIC_RMW_OR_I32", "ATOMIC_RMW_OR_I64">;
629 defm : BinRMWPattern<atomic_load_xor_32, atomic_load_xor_64,
630                      "ATOMIC_RMW_XOR_I32", "ATOMIC_RMW_XOR_I64">;
631 defm : BinRMWPattern<atomic_swap_32, atomic_swap_64,
632                      "ATOMIC_RMW_XCHG_I32", "ATOMIC_RMW_XCHG_I64">;
634 // Truncating & zero-extending binary RMW patterns.
635 // These are combined patterns of truncating store patterns and zero-extending
636 // load patterns above.
637 class zext_bin_rmw_8_32<PatFrag kind> :
638   PatFrag<(ops node:$addr, node:$val), (i32 (kind node:$addr, node:$val))>;
639 class zext_bin_rmw_16_32<PatFrag kind> : zext_bin_rmw_8_32<kind>;
640 class zext_bin_rmw_8_64<PatFrag kind> :
641   PatFrag<(ops node:$addr, node:$val),
642           (zext (i32 (kind node:$addr, (i32 (trunc (i64 node:$val))))))>;
643 class zext_bin_rmw_16_64<PatFrag kind> : zext_bin_rmw_8_64<kind>;
644 class zext_bin_rmw_32_64<PatFrag kind> : zext_bin_rmw_8_64<kind>;
646 // Truncating & sign-extending binary RMW patterns.
647 // These are combined patterns of truncating store patterns and sign-extending
648 // load patterns above. We match subword RMWs (for 32-bit) and anyext RMWs (for
649 // 64-bit) and select a zext RMW; the next instruction will be sext_inreg which
650 // is selected by itself.
651 class sext_bin_rmw_8_32<PatFrag kind> :
652   PatFrag<(ops node:$addr, node:$val), (kind node:$addr, node:$val)>;
653 class sext_bin_rmw_16_32<PatFrag kind> : sext_bin_rmw_8_32<kind>;
654 class sext_bin_rmw_8_64<PatFrag kind> :
655   PatFrag<(ops node:$addr, node:$val),
656           (anyext (i32 (kind node:$addr, (i32 (trunc (i64 node:$val))))))>;
657 class sext_bin_rmw_16_64<PatFrag kind> : sext_bin_rmw_8_64<kind>;
658 // 32->64 sext RMW gets selected as i32.atomic.rmw.***, i64.extend_i32_s
660 // Patterns for various addressing modes for truncating-extending binary RMWs.
661 multiclass BinRMWTruncExtPattern<
662   PatFrag rmw_8, PatFrag rmw_16, PatFrag rmw_32,
663   string inst8_32, string inst16_32, string inst8_64, string inst16_64, string inst32_64> {
664   // Truncating-extending binary RMWs with no constant offset
665   defm : BinRMWPatNoOffset<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
666   defm : BinRMWPatNoOffset<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
667   defm : BinRMWPatNoOffset<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
668   defm : BinRMWPatNoOffset<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
669   defm : BinRMWPatNoOffset<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
671   defm : BinRMWPatNoOffset<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
672   defm : BinRMWPatNoOffset<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
673   defm : BinRMWPatNoOffset<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
674   defm : BinRMWPatNoOffset<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
676   // Truncating-extending binary RMWs with a constant offset
677   defm : BinRMWPatImmOff<i32, zext_bin_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
678   defm : BinRMWPatImmOff<i32, zext_bin_rmw_16_32<rmw_16>, regPlusImm,
679                          inst16_32>;
680   defm : BinRMWPatImmOff<i64, zext_bin_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
681   defm : BinRMWPatImmOff<i64, zext_bin_rmw_16_64<rmw_16>, regPlusImm,
682                          inst16_64>;
683   defm : BinRMWPatImmOff<i64, zext_bin_rmw_32_64<rmw_32>, regPlusImm,
684                          inst32_64>;
685   defm : BinRMWPatImmOff<i32, zext_bin_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
686   defm : BinRMWPatImmOff<i32, zext_bin_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
687   defm : BinRMWPatImmOff<i64, zext_bin_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
688   defm : BinRMWPatImmOff<i64, zext_bin_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
689   defm : BinRMWPatImmOff<i64, zext_bin_rmw_32_64<rmw_32>, or_is_add, inst32_64>;
691   defm : BinRMWPatImmOff<i32, sext_bin_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
692   defm : BinRMWPatImmOff<i32, sext_bin_rmw_16_32<rmw_16>, regPlusImm,
693                          inst16_32>;
694   defm : BinRMWPatImmOff<i64, sext_bin_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
695   defm : BinRMWPatImmOff<i64, sext_bin_rmw_16_64<rmw_16>, regPlusImm,
696                          inst16_64>;
697   defm : BinRMWPatImmOff<i32, sext_bin_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
698   defm : BinRMWPatImmOff<i32, sext_bin_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
699   defm : BinRMWPatImmOff<i64, sext_bin_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
700   defm : BinRMWPatImmOff<i64, sext_bin_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
702   // Truncating-extending binary RMWs with just a constant offset
703   defm : BinRMWPatOffsetOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
704   defm : BinRMWPatOffsetOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
705   defm : BinRMWPatOffsetOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
706   defm : BinRMWPatOffsetOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
707   defm : BinRMWPatOffsetOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
709   defm : BinRMWPatOffsetOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
710   defm : BinRMWPatOffsetOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
711   defm : BinRMWPatOffsetOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
712   defm : BinRMWPatOffsetOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
714   defm : BinRMWPatGlobalAddrOffOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
715   defm : BinRMWPatGlobalAddrOffOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
716   defm : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
717   defm : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
718   defm : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
720   defm : BinRMWPatGlobalAddrOffOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
721   defm : BinRMWPatGlobalAddrOffOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
722   defm : BinRMWPatGlobalAddrOffOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
723   defm : BinRMWPatGlobalAddrOffOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
726 defm : BinRMWTruncExtPattern<
727   atomic_load_add_8, atomic_load_add_16, atomic_load_add_32,
728   "ATOMIC_RMW8_U_ADD_I32", "ATOMIC_RMW16_U_ADD_I32",
729   "ATOMIC_RMW8_U_ADD_I64", "ATOMIC_RMW16_U_ADD_I64", "ATOMIC_RMW32_U_ADD_I64">;
730 defm : BinRMWTruncExtPattern<
731   atomic_load_sub_8, atomic_load_sub_16, atomic_load_sub_32,
732   "ATOMIC_RMW8_U_SUB_I32", "ATOMIC_RMW16_U_SUB_I32",
733   "ATOMIC_RMW8_U_SUB_I64", "ATOMIC_RMW16_U_SUB_I64", "ATOMIC_RMW32_U_SUB_I64">;
734 defm : BinRMWTruncExtPattern<
735   atomic_load_and_8, atomic_load_and_16, atomic_load_and_32,
736   "ATOMIC_RMW8_U_AND_I32", "ATOMIC_RMW16_U_AND_I32",
737   "ATOMIC_RMW8_U_AND_I64", "ATOMIC_RMW16_U_AND_I64", "ATOMIC_RMW32_U_AND_I64">;
738 defm : BinRMWTruncExtPattern<
739   atomic_load_or_8, atomic_load_or_16, atomic_load_or_32,
740   "ATOMIC_RMW8_U_OR_I32", "ATOMIC_RMW16_U_OR_I32",
741   "ATOMIC_RMW8_U_OR_I64", "ATOMIC_RMW16_U_OR_I64", "ATOMIC_RMW32_U_OR_I64">;
742 defm : BinRMWTruncExtPattern<
743   atomic_load_xor_8, atomic_load_xor_16, atomic_load_xor_32,
744   "ATOMIC_RMW8_U_XOR_I32", "ATOMIC_RMW16_U_XOR_I32",
745   "ATOMIC_RMW8_U_XOR_I64", "ATOMIC_RMW16_U_XOR_I64", "ATOMIC_RMW32_U_XOR_I64">;
746 defm : BinRMWTruncExtPattern<
747   atomic_swap_8, atomic_swap_16, atomic_swap_32,
748   "ATOMIC_RMW8_U_XCHG_I32", "ATOMIC_RMW16_U_XCHG_I32",
749   "ATOMIC_RMW8_U_XCHG_I64", "ATOMIC_RMW16_U_XCHG_I64",
750   "ATOMIC_RMW32_U_XCHG_I64">;
752 //===----------------------------------------------------------------------===//
753 // Atomic ternary read-modify-writes
754 //===----------------------------------------------------------------------===//
756 // TODO LLVM IR's cmpxchg instruction returns a pair of {loaded value, success
757 // flag}. When we use the success flag or both values, we can't make use of i64
758 // truncate/extend versions of instructions for now, which is suboptimal.
759 // Consider adding a pass after instruction selection that optimizes this case
760 // if it is frequent.
762 multiclass WebAssemblyTerRMW<WebAssemblyRegClass rc, string name,
763                              int atomic_op> {
764   defm "_A32" :
765     ATOMIC_I<(outs rc:$dst),
766              (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$exp,
767                   rc:$new_),
768              (outs), (ins P2Align:$p2align, offset32_op:$off), [],
769              !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new_"),
770              !strconcat(name, "\t${off}${p2align}"), atomic_op, "false">;
771   defm "_A64" :
772     ATOMIC_I<(outs rc:$dst),
773              (ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$exp,
774                   rc:$new_),
775              (outs), (ins P2Align:$p2align, offset64_op:$off), [],
776              !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new_"),
777              !strconcat(name, "\t${off}${p2align}"), atomic_op, "true">;
780 defm ATOMIC_RMW_CMPXCHG_I32 :
781   WebAssemblyTerRMW<I32, "i32.atomic.rmw.cmpxchg", 0x48>;
782 defm ATOMIC_RMW_CMPXCHG_I64 :
783   WebAssemblyTerRMW<I64, "i64.atomic.rmw.cmpxchg", 0x49>;
784 defm ATOMIC_RMW8_U_CMPXCHG_I32 :
785   WebAssemblyTerRMW<I32, "i32.atomic.rmw8.cmpxchg_u", 0x4a>;
786 defm ATOMIC_RMW16_U_CMPXCHG_I32 :
787   WebAssemblyTerRMW<I32, "i32.atomic.rmw16.cmpxchg_u", 0x4b>;
788 defm ATOMIC_RMW8_U_CMPXCHG_I64 :
789   WebAssemblyTerRMW<I64, "i64.atomic.rmw8.cmpxchg_u", 0x4c>;
790 defm ATOMIC_RMW16_U_CMPXCHG_I64 :
791   WebAssemblyTerRMW<I64, "i64.atomic.rmw16.cmpxchg_u", 0x4d>;
792 defm ATOMIC_RMW32_U_CMPXCHG_I64 :
793   WebAssemblyTerRMW<I64, "i64.atomic.rmw32.cmpxchg_u", 0x4e>;
795 // Select ternary RMWs with no constant offset.
796 multiclass TerRMWPatNoOffset<ValueType ty, PatFrag kind, string inst> {
797   def : Pat<(ty (kind I32:$addr, ty:$exp, ty:$new)),
798             (!cast<NI>(inst#_A32) 0, 0, I32:$addr, ty:$exp, ty:$new)>,
799         Requires<[HasAddr32, HasAtomics]>;
800   def : Pat<(ty (kind I64:$addr, ty:$exp, ty:$new)),
801             (!cast<NI>(inst#_A64) 0, 0, I64:$addr, ty:$exp, ty:$new)>,
802         Requires<[HasAddr64, HasAtomics]>;
805 // Select ternary RMWs with a constant offset.
807 // Pattern with address + immediate offset
808 multiclass TerRMWPatImmOff<ValueType ty, PatFrag kind, PatFrag operand,
809                            string inst> {
810   def : Pat<(ty (kind (operand I32:$addr, imm:$off), ty:$exp, ty:$new)),
811             (!cast<NI>(inst#_A32) 0, imm:$off, I32:$addr, ty:$exp, ty:$new)>,
812         Requires<[HasAddr32, HasAtomics]>;
813   def : Pat<(ty (kind (operand I64:$addr, imm:$off), ty:$exp, ty:$new)),
814             (!cast<NI>(inst#_A64) 0, imm:$off, I64:$addr, ty:$exp, ty:$new)>,
815         Requires<[HasAddr64, HasAtomics]>;
818 // Select ternary RMWs with just a constant offset.
819 multiclass TerRMWPatOffsetOnly<ValueType ty, PatFrag kind, string inst> {
820   def : Pat<(ty (kind imm:$off, ty:$exp, ty:$new)),
821             (!cast<NI>(inst#_A32) 0, imm:$off, (CONST_I32 0), ty:$exp,
822               ty:$new)>;
823   def : Pat<(ty (kind imm:$off, ty:$exp, ty:$new)),
824             (!cast<NI>(inst#_A64) 0, imm:$off, (CONST_I64 0), ty:$exp,
825               ty:$new)>;
828 multiclass TerRMWPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, string inst> {
829   def : Pat<(ty (kind (WebAssemblyWrapper tglobaladdr:$off), ty:$exp, ty:$new)),
830             (!cast<NI>(inst#_A32) 0, tglobaladdr:$off, (CONST_I32 0), ty:$exp,
831               ty:$new)>,
832         Requires<[HasAddr32, HasAtomics, IsNotPIC]>;
833   def : Pat<(ty (kind (WebAssemblyWrapper tglobaladdr:$off), ty:$exp, ty:$new)),
834             (!cast<NI>(inst#_A64) 0, tglobaladdr:$off, (CONST_I64 0), ty:$exp,
835               ty:$new)>,
836         Requires<[HasAddr64, HasAtomics, IsNotPIC]>;
839 // Patterns for various addressing modes.
840 multiclass TerRMWPattern<PatFrag rmw_32, PatFrag rmw_64, string inst_32,
841                          string inst_64> {
842   defm : TerRMWPatNoOffset<i32, rmw_32, inst_32>;
843   defm : TerRMWPatNoOffset<i64, rmw_64, inst_64>;
845   defm : TerRMWPatImmOff<i32, rmw_32, regPlusImm, inst_32>;
846   defm : TerRMWPatImmOff<i64, rmw_64, regPlusImm, inst_64>;
847   defm : TerRMWPatImmOff<i32, rmw_32, or_is_add, inst_32>;
848   defm : TerRMWPatImmOff<i64, rmw_64, or_is_add, inst_64>;
850   defm : TerRMWPatOffsetOnly<i32, rmw_32, inst_32>;
851   defm : TerRMWPatOffsetOnly<i64, rmw_64, inst_64>;
853   defm : TerRMWPatGlobalAddrOffOnly<i32, rmw_32, inst_32>;
854   defm : TerRMWPatGlobalAddrOffOnly<i64, rmw_64, inst_64>;
857 defm : TerRMWPattern<atomic_cmp_swap_32, atomic_cmp_swap_64,
858                      "ATOMIC_RMW_CMPXCHG_I32", "ATOMIC_RMW_CMPXCHG_I64">;
860 // Truncating & zero-extending ternary RMW patterns.
861 // DAG legalization & optimization before instruction selection may introduce
862 // additional nodes such as anyext or assertzext depending on operand types.
863 class zext_ter_rmw_8_32<PatFrag kind> :
864   PatFrag<(ops node:$addr, node:$exp, node:$new),
865           (i32 (kind node:$addr, node:$exp, node:$new))>;
866 class zext_ter_rmw_16_32<PatFrag kind> : zext_ter_rmw_8_32<kind>;
867 class zext_ter_rmw_8_64<PatFrag kind> :
868   PatFrag<(ops node:$addr, node:$exp, node:$new),
869           (zext (i32 (assertzext (i32 (kind node:$addr,
870                                             (i32 (trunc (i64 node:$exp))),
871                                             (i32 (trunc (i64 node:$new))))))))>;
872 class zext_ter_rmw_16_64<PatFrag kind> : zext_ter_rmw_8_64<kind>;
873 class zext_ter_rmw_32_64<PatFrag kind> :
874   PatFrag<(ops node:$addr, node:$exp, node:$new),
875           (zext (i32 (kind node:$addr,
876                            (i32 (trunc (i64 node:$exp))),
877                            (i32 (trunc (i64 node:$new))))))>;
879 // Truncating & sign-extending ternary RMW patterns.
880 // We match subword RMWs (for 32-bit) and anyext RMWs (for 64-bit) and select a
881 // zext RMW; the next instruction will be sext_inreg which is selected by
882 // itself.
883 class sext_ter_rmw_8_32<PatFrag kind> :
884   PatFrag<(ops node:$addr, node:$exp, node:$new),
885           (kind node:$addr, node:$exp, node:$new)>;
886 class sext_ter_rmw_16_32<PatFrag kind> : sext_ter_rmw_8_32<kind>;
887 class sext_ter_rmw_8_64<PatFrag kind> :
888   PatFrag<(ops node:$addr, node:$exp, node:$new),
889           (anyext (i32 (assertzext (i32
890             (kind node:$addr,
891                   (i32 (trunc (i64 node:$exp))),
892                   (i32 (trunc (i64 node:$new))))))))>;
893 class sext_ter_rmw_16_64<PatFrag kind> : sext_ter_rmw_8_64<kind>;
894 // 32->64 sext RMW gets selected as i32.atomic.rmw.***, i64.extend_i32_s
896 // Patterns for various addressing modes for truncating-extending ternary RMWs.
897 multiclass TerRMWTruncExtPattern<
898   PatFrag rmw_8, PatFrag rmw_16, PatFrag rmw_32,
899   string inst8_32, string inst16_32, string inst8_64, string inst16_64,
900   string inst32_64> {
901   // Truncating-extending ternary RMWs with no constant offset
902   defm : TerRMWPatNoOffset<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
903   defm : TerRMWPatNoOffset<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
904   defm : TerRMWPatNoOffset<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
905   defm : TerRMWPatNoOffset<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
906   defm : TerRMWPatNoOffset<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
908   defm : TerRMWPatNoOffset<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
909   defm : TerRMWPatNoOffset<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
910   defm : TerRMWPatNoOffset<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
911   defm : TerRMWPatNoOffset<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
913   // Truncating-extending ternary RMWs with a constant offset
914   defm : TerRMWPatImmOff<i32, zext_ter_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
915   defm : TerRMWPatImmOff<i32, zext_ter_rmw_16_32<rmw_16>, regPlusImm,
916                          inst16_32>;
917   defm : TerRMWPatImmOff<i64, zext_ter_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
918   defm : TerRMWPatImmOff<i64, zext_ter_rmw_16_64<rmw_16>, regPlusImm,
919                          inst16_64>;
920   defm : TerRMWPatImmOff<i64, zext_ter_rmw_32_64<rmw_32>, regPlusImm,
921                          inst32_64>;
922   defm : TerRMWPatImmOff<i32, zext_ter_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
923   defm : TerRMWPatImmOff<i32, zext_ter_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
924   defm : TerRMWPatImmOff<i64, zext_ter_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
925   defm : TerRMWPatImmOff<i64, zext_ter_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
926   defm : TerRMWPatImmOff<i64, zext_ter_rmw_32_64<rmw_32>, or_is_add, inst32_64>;
928   defm : TerRMWPatImmOff<i32, sext_ter_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
929   defm : TerRMWPatImmOff<i32, sext_ter_rmw_16_32<rmw_16>, regPlusImm,
930                          inst16_32>;
931   defm : TerRMWPatImmOff<i64, sext_ter_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
932   defm : TerRMWPatImmOff<i64, sext_ter_rmw_16_64<rmw_16>, regPlusImm,
933                          inst16_64>;
934   defm : TerRMWPatImmOff<i32, sext_ter_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
935   defm : TerRMWPatImmOff<i32, sext_ter_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
936   defm : TerRMWPatImmOff<i64, sext_ter_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
937   defm : TerRMWPatImmOff<i64, sext_ter_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
939   // Truncating-extending ternary RMWs with just a constant offset
940   defm : TerRMWPatOffsetOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
941   defm : TerRMWPatOffsetOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
942   defm : TerRMWPatOffsetOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
943   defm : TerRMWPatOffsetOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
944   defm : TerRMWPatOffsetOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
946   defm : TerRMWPatOffsetOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
947   defm : TerRMWPatOffsetOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
948   defm : TerRMWPatOffsetOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
949   defm : TerRMWPatOffsetOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
951   defm : TerRMWPatGlobalAddrOffOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
952   defm : TerRMWPatGlobalAddrOffOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
953   defm : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
954   defm : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
955   defm : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
957   defm : TerRMWPatGlobalAddrOffOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
958   defm : TerRMWPatGlobalAddrOffOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
959   defm : TerRMWPatGlobalAddrOffOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
960   defm : TerRMWPatGlobalAddrOffOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
963 defm : TerRMWTruncExtPattern<
964   atomic_cmp_swap_8, atomic_cmp_swap_16, atomic_cmp_swap_32,
965   "ATOMIC_RMW8_U_CMPXCHG_I32", "ATOMIC_RMW16_U_CMPXCHG_I32",
966   "ATOMIC_RMW8_U_CMPXCHG_I64", "ATOMIC_RMW16_U_CMPXCHG_I64",
967   "ATOMIC_RMW32_U_CMPXCHG_I64">;