1// WebAssemblyInstrMemory.td-WebAssembly Memory 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 Memory operand code-gen constructs.
11///
12//===----------------------------------------------------------------------===//
13
14// TODO:
15//  - WebAssemblyTargetLowering having to do with atomics
16//  - Each has optional alignment.
17
18// WebAssembly has i8/i16/i32/i64/f32/f64 memory types, but doesn't have i8/i16
19// local types. These memory-only types instead zero- or sign-extend into local
20// types when loading, and truncate when storing.
21
22// WebAssembly constant offsets are performed as unsigned with infinite
23// precision, so we need to check for NoUnsignedWrap so that we don't fold an
24// offset for an add that needs wrapping.
25def regPlusImm : PatFrag<(ops node:$addr, node:$off),
26                         (add node:$addr, node:$off),
27                         [{ return N->getFlags().hasNoUnsignedWrap(); }]>;
28
29// Treat an 'or' node as an 'add' if the or'ed bits are known to be zero.
30def or_is_add : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{
31  if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N->getOperand(1)))
32    return CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
33
34  KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
35  KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
36  return (~Known0.Zero & ~Known1.Zero) == 0;
37}]>;
38
39// We don't need a regPlusES because external symbols never have constant
40// offsets folded into them, so we can just use add.
41
42// Defines atomic and non-atomic loads, regular and extending.
43multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode,
44                           list<Predicate> reqs = []> {
45  let mayLoad = 1, UseNamedOperandTable = 1 in {
46  defm "_A32": I<(outs rc:$dst),
47                 (ins P2Align:$p2align, offset32_op:$off, I32:$addr),
48                 (outs), (ins P2Align:$p2align, offset32_op:$off),
49                 [], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"),
50                 !strconcat(Name, "\t${off}${p2align}"), Opcode, false>,
51               Requires<reqs>;
52  defm "_A64": I<(outs rc:$dst),
53                 (ins P2Align:$p2align, offset64_op:$off, I64:$addr),
54                 (outs), (ins P2Align:$p2align, offset64_op:$off),
55                 [], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"),
56                 !strconcat(Name, "\t${off}${p2align}"), Opcode, true>,
57               Requires<reqs>;
58  }
59}
60
61// Basic load.
62// FIXME: When we can break syntax compatibility, reorder the fields in the
63// asmstrings to match the binary encoding.
64defm LOAD_I32 : WebAssemblyLoad<I32, "i32.load", 0x28, []>;
65defm LOAD_I64 : WebAssemblyLoad<I64, "i64.load", 0x29, []>;
66defm LOAD_F32 : WebAssemblyLoad<F32, "f32.load", 0x2a, []>;
67defm LOAD_F64 : WebAssemblyLoad<F64, "f64.load", 0x2b, []>;
68
69// Select loads with no constant offset.
70multiclass LoadPatNoOffset<ValueType ty, SDPatternOperator kind, string inst> {
71  def : Pat<(ty (kind I32:$addr)), (!cast<NI>(inst # "_A32") 0, 0, I32:$addr)>,
72        Requires<[HasAddr32]>;
73  def : Pat<(ty (kind (i64 I64:$addr))), (!cast<NI>(inst # "_A64") 0, 0, I64:$addr)>,
74        Requires<[HasAddr64]>;
75}
76
77defm : LoadPatNoOffset<i32, load, "LOAD_I32">;
78defm : LoadPatNoOffset<i64, load, "LOAD_I64">;
79defm : LoadPatNoOffset<f32, load, "LOAD_F32">;
80defm : LoadPatNoOffset<f64, load, "LOAD_F64">;
81
82// Select loads with a constant offset.
83
84// Pattern with address + immediate offset
85multiclass LoadPatImmOff<ValueType ty, SDPatternOperator kind, PatFrag operand,
86                         string inst> {
87  def : Pat<(ty (kind (operand I32:$addr, imm:$off))),
88            (!cast<NI>(inst # "_A32") 0, imm:$off, I32:$addr)>,
89        Requires<[HasAddr32]>;
90  def : Pat<(ty (kind (operand I64:$addr, imm:$off))),
91            (!cast<NI>(inst # "_A64") 0, imm:$off, I64:$addr)>,
92        Requires<[HasAddr64]>;
93}
94
95defm : LoadPatImmOff<i32, load, regPlusImm, "LOAD_I32">;
96defm : LoadPatImmOff<i64, load, regPlusImm, "LOAD_I64">;
97defm : LoadPatImmOff<f32, load, regPlusImm, "LOAD_F32">;
98defm : LoadPatImmOff<f64, load, regPlusImm, "LOAD_F64">;
99defm : LoadPatImmOff<i32, load, or_is_add, "LOAD_I32">;
100defm : LoadPatImmOff<i64, load, or_is_add, "LOAD_I64">;
101defm : LoadPatImmOff<f32, load, or_is_add, "LOAD_F32">;
102defm : LoadPatImmOff<f64, load, or_is_add, "LOAD_F64">;
103
104// Select loads with just a constant offset.
105multiclass LoadPatOffsetOnly<ValueType ty, SDPatternOperator kind, string inst> {
106  def : Pat<(ty (kind imm:$off)),
107            (!cast<NI>(inst # "_A32") 0, imm:$off, (CONST_I32 0))>,
108        Requires<[HasAddr32]>;
109  def : Pat<(ty (kind imm:$off)),
110            (!cast<NI>(inst # "_A64") 0, imm:$off, (CONST_I64 0))>,
111        Requires<[HasAddr64]>;
112}
113
114defm : LoadPatOffsetOnly<i32, load, "LOAD_I32">;
115defm : LoadPatOffsetOnly<i64, load, "LOAD_I64">;
116defm : LoadPatOffsetOnly<f32, load, "LOAD_F32">;
117defm : LoadPatOffsetOnly<f64, load, "LOAD_F64">;
118
119multiclass LoadPatGlobalAddrOffOnly<ValueType ty, SDPatternOperator kind, string inst> {
120  def : Pat<(ty (kind (WebAssemblyWrapper tglobaladdr:$off))),
121            (!cast<NI>(inst # "_A32") 0, tglobaladdr:$off, (CONST_I32 0))>,
122        Requires<[IsNotPIC, HasAddr32]>;
123  def : Pat<(ty (kind (WebAssemblyWrapper tglobaladdr:$off))),
124            (!cast<NI>(inst # "_A64") 0, tglobaladdr:$off, (CONST_I64 0))>,
125        Requires<[IsNotPIC, HasAddr64]>;
126}
127
128defm : LoadPatGlobalAddrOffOnly<i32, load, "LOAD_I32">;
129defm : LoadPatGlobalAddrOffOnly<i64, load, "LOAD_I64">;
130defm : LoadPatGlobalAddrOffOnly<f32, load, "LOAD_F32">;
131defm : LoadPatGlobalAddrOffOnly<f64, load, "LOAD_F64">;
132
133// Extending load.
134defm LOAD8_S_I32 : WebAssemblyLoad<I32, "i32.load8_s", 0x2c, []>;
135defm LOAD8_U_I32 : WebAssemblyLoad<I32, "i32.load8_u", 0x2d, []>;
136defm LOAD16_S_I32 : WebAssemblyLoad<I32, "i32.load16_s", 0x2e, []>;
137defm LOAD16_U_I32 : WebAssemblyLoad<I32, "i32.load16_u", 0x2f, []>;
138defm LOAD8_S_I64 : WebAssemblyLoad<I64, "i64.load8_s", 0x30, []>;
139defm LOAD8_U_I64 : WebAssemblyLoad<I64, "i64.load8_u", 0x31, []>;
140defm LOAD16_S_I64 : WebAssemblyLoad<I64, "i64.load16_s", 0x32, []>;
141defm LOAD16_U_I64 : WebAssemblyLoad<I64, "i64.load16_u", 0x33, []>;
142defm LOAD32_S_I64 : WebAssemblyLoad<I64, "i64.load32_s", 0x34, []>;
143defm LOAD32_U_I64 : WebAssemblyLoad<I64, "i64.load32_u", 0x35, []>;
144
145// Select extending loads with no constant offset.
146defm : LoadPatNoOffset<i32, sextloadi8, "LOAD8_S_I32">;
147defm : LoadPatNoOffset<i32, zextloadi8, "LOAD8_U_I32">;
148defm : LoadPatNoOffset<i32, sextloadi16, "LOAD16_S_I32">;
149defm : LoadPatNoOffset<i32, zextloadi16, "LOAD16_U_I32">;
150defm : LoadPatNoOffset<i64, sextloadi8, "LOAD8_S_I64">;
151defm : LoadPatNoOffset<i64, zextloadi8, "LOAD8_U_I64">;
152defm : LoadPatNoOffset<i64, sextloadi16, "LOAD16_S_I64">;
153defm : LoadPatNoOffset<i64, zextloadi16, "LOAD16_U_I64">;
154defm : LoadPatNoOffset<i64, sextloadi32, "LOAD32_S_I64">;
155defm : LoadPatNoOffset<i64, zextloadi32, "LOAD32_U_I64">;
156
157// Select extending loads with a constant offset.
158defm : LoadPatImmOff<i32, sextloadi8, regPlusImm, "LOAD8_S_I32">;
159defm : LoadPatImmOff<i32, zextloadi8, regPlusImm, "LOAD8_U_I32">;
160defm : LoadPatImmOff<i32, sextloadi16, regPlusImm, "LOAD16_S_I32">;
161defm : LoadPatImmOff<i32, zextloadi16, regPlusImm, "LOAD16_U_I32">;
162defm : LoadPatImmOff<i64, sextloadi8, regPlusImm, "LOAD8_S_I64">;
163defm : LoadPatImmOff<i64, zextloadi8, regPlusImm, "LOAD8_U_I64">;
164defm : LoadPatImmOff<i64, sextloadi16, regPlusImm, "LOAD16_S_I64">;
165defm : LoadPatImmOff<i64, zextloadi16, regPlusImm, "LOAD16_U_I64">;
166defm : LoadPatImmOff<i64, sextloadi32, regPlusImm, "LOAD32_S_I64">;
167defm : LoadPatImmOff<i64, zextloadi32, regPlusImm, "LOAD32_U_I64">;
168
169defm : LoadPatImmOff<i32, sextloadi8, or_is_add, "LOAD8_S_I32">;
170defm : LoadPatImmOff<i32, zextloadi8, or_is_add, "LOAD8_U_I32">;
171defm : LoadPatImmOff<i32, sextloadi16, or_is_add, "LOAD16_S_I32">;
172defm : LoadPatImmOff<i32, zextloadi16, or_is_add, "LOAD16_U_I32">;
173defm : LoadPatImmOff<i64, sextloadi8, or_is_add, "LOAD8_S_I64">;
174defm : LoadPatImmOff<i64, zextloadi8, or_is_add, "LOAD8_U_I64">;
175defm : LoadPatImmOff<i64, sextloadi16, or_is_add, "LOAD16_S_I64">;
176defm : LoadPatImmOff<i64, zextloadi16, or_is_add, "LOAD16_U_I64">;
177defm : LoadPatImmOff<i64, sextloadi32, or_is_add, "LOAD32_S_I64">;
178defm : LoadPatImmOff<i64, zextloadi32, or_is_add, "LOAD32_U_I64">;
179
180// Select extending loads with just a constant offset.
181defm : LoadPatOffsetOnly<i32, sextloadi8, "LOAD8_S_I32">;
182defm : LoadPatOffsetOnly<i32, zextloadi8, "LOAD8_U_I32">;
183defm : LoadPatOffsetOnly<i32, sextloadi16, "LOAD16_S_I32">;
184defm : LoadPatOffsetOnly<i32, zextloadi16, "LOAD16_U_I32">;
185
186defm : LoadPatOffsetOnly<i64, sextloadi8, "LOAD8_S_I64">;
187defm : LoadPatOffsetOnly<i64, zextloadi8, "LOAD8_U_I64">;
188defm : LoadPatOffsetOnly<i64, sextloadi16, "LOAD16_S_I64">;
189defm : LoadPatOffsetOnly<i64, zextloadi16, "LOAD16_U_I64">;
190defm : LoadPatOffsetOnly<i64, sextloadi32, "LOAD32_S_I64">;
191defm : LoadPatOffsetOnly<i64, zextloadi32, "LOAD32_U_I64">;
192
193defm : LoadPatGlobalAddrOffOnly<i32, sextloadi8, "LOAD8_S_I32">;
194defm : LoadPatGlobalAddrOffOnly<i32, zextloadi8, "LOAD8_U_I32">;
195defm : LoadPatGlobalAddrOffOnly<i32, sextloadi16, "LOAD16_S_I32">;
196defm : LoadPatGlobalAddrOffOnly<i32, zextloadi16, "LOAD16_U_I32">;
197defm : LoadPatGlobalAddrOffOnly<i64, sextloadi8, "LOAD8_S_I64">;
198defm : LoadPatGlobalAddrOffOnly<i64, zextloadi8, "LOAD8_U_I64">;
199defm : LoadPatGlobalAddrOffOnly<i64, sextloadi16, "LOAD16_S_I64">;
200defm : LoadPatGlobalAddrOffOnly<i64, zextloadi16, "LOAD16_U_I64">;
201defm : LoadPatGlobalAddrOffOnly<i64, sextloadi32, "LOAD32_S_I64">;
202defm : LoadPatGlobalAddrOffOnly<i64, zextloadi32, "LOAD32_U_I64">;
203
204// Resolve "don't care" extending loads to zero-extending loads. This is
205// somewhat arbitrary, but zero-extending is conceptually simpler.
206
207// Select "don't care" extending loads with no constant offset.
208defm : LoadPatNoOffset<i32, extloadi8, "LOAD8_U_I32">;
209defm : LoadPatNoOffset<i32, extloadi16, "LOAD16_U_I32">;
210defm : LoadPatNoOffset<i64, extloadi8, "LOAD8_U_I64">;
211defm : LoadPatNoOffset<i64, extloadi16, "LOAD16_U_I64">;
212defm : LoadPatNoOffset<i64, extloadi32, "LOAD32_U_I64">;
213
214// Select "don't care" extending loads with a constant offset.
215defm : LoadPatImmOff<i32, extloadi8, regPlusImm, "LOAD8_U_I32">;
216defm : LoadPatImmOff<i32, extloadi16, regPlusImm, "LOAD16_U_I32">;
217defm : LoadPatImmOff<i64, extloadi8, regPlusImm, "LOAD8_U_I64">;
218defm : LoadPatImmOff<i64, extloadi16, regPlusImm, "LOAD16_U_I64">;
219defm : LoadPatImmOff<i64, extloadi32, regPlusImm, "LOAD32_U_I64">;
220defm : LoadPatImmOff<i32, extloadi8, or_is_add, "LOAD8_U_I32">;
221defm : LoadPatImmOff<i32, extloadi16, or_is_add, "LOAD16_U_I32">;
222defm : LoadPatImmOff<i64, extloadi8, or_is_add, "LOAD8_U_I64">;
223defm : LoadPatImmOff<i64, extloadi16, or_is_add, "LOAD16_U_I64">;
224defm : LoadPatImmOff<i64, extloadi32, or_is_add, "LOAD32_U_I64">;
225
226// Select "don't care" extending loads with just a constant offset.
227defm : LoadPatOffsetOnly<i32, extloadi8, "LOAD8_U_I32">;
228defm : LoadPatOffsetOnly<i32, extloadi16, "LOAD16_U_I32">;
229defm : LoadPatOffsetOnly<i64, extloadi8, "LOAD8_U_I64">;
230defm : LoadPatOffsetOnly<i64, extloadi16, "LOAD16_U_I64">;
231defm : LoadPatOffsetOnly<i64, extloadi32, "LOAD32_U_I64">;
232defm : LoadPatGlobalAddrOffOnly<i32, extloadi8, "LOAD8_U_I32">;
233defm : LoadPatGlobalAddrOffOnly<i32, extloadi16, "LOAD16_U_I32">;
234defm : LoadPatGlobalAddrOffOnly<i64, extloadi8, "LOAD8_U_I64">;
235defm : LoadPatGlobalAddrOffOnly<i64, extloadi16, "LOAD16_U_I64">;
236defm : LoadPatGlobalAddrOffOnly<i64, extloadi32, "LOAD32_U_I64">;
237
238// Defines atomic and non-atomic stores, regular and truncating
239multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode,
240                            list<Predicate> reqs = []> {
241  let mayStore = 1, UseNamedOperandTable = 1 in
242  defm "_A32" : I<(outs),
243                  (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
244                  (outs),
245                  (ins P2Align:$p2align, offset32_op:$off), [],
246                  !strconcat(Name, "\t${off}(${addr})${p2align}, $val"),
247                  !strconcat(Name, "\t${off}${p2align}"), Opcode, false>,
248                Requires<reqs>;
249  let mayStore = 1, UseNamedOperandTable = 1 in
250  defm "_A64" : I<(outs),
251                  (ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$val),
252                  (outs),
253                  (ins P2Align:$p2align, offset64_op:$off), [],
254                  !strconcat(Name, "\t${off}(${addr})${p2align}, $val"),
255                  !strconcat(Name, "\t${off}${p2align}"), Opcode, true>,
256                Requires<reqs>;
257}
258
259// Basic store.
260// Note: WebAssembly inverts SelectionDAG's usual operand order.
261defm STORE_I32  : WebAssemblyStore<I32, "i32.store", 0x36>;
262defm STORE_I64  : WebAssemblyStore<I64, "i64.store", 0x37>;
263defm STORE_F32  : WebAssemblyStore<F32, "f32.store", 0x38>;
264defm STORE_F64  : WebAssemblyStore<F64, "f64.store", 0x39>;
265
266// Select stores with no constant offset.
267multiclass StorePatNoOffset<ValueType ty, PatFrag node, string inst> {
268  def : Pat<(node ty:$val, I32:$addr),
269            (!cast<NI>(inst # "_A32") 0, 0, I32:$addr, ty:$val)>,
270        Requires<[HasAddr32]>;
271  def : Pat<(node ty:$val, I64:$addr),
272            (!cast<NI>(inst # "_A64") 0, 0, I64:$addr, ty:$val)>,
273        Requires<[HasAddr64]>;
274}
275
276defm : StorePatNoOffset<i32, store, "STORE_I32">;
277defm : StorePatNoOffset<i64, store, "STORE_I64">;
278defm : StorePatNoOffset<f32, store, "STORE_F32">;
279defm : StorePatNoOffset<f64, store, "STORE_F64">;
280
281// Select stores with a constant offset.
282multiclass StorePatImmOff<ValueType ty, PatFrag kind, PatFrag operand,
283                          string inst> {
284  def : Pat<(kind ty:$val, (operand I32:$addr, imm:$off)),
285            (!cast<NI>(inst # "_A32") 0, imm:$off, I32:$addr, ty:$val)>,
286        Requires<[HasAddr32]>;
287  def : Pat<(kind ty:$val, (operand I64:$addr, imm:$off)),
288            (!cast<NI>(inst # "_A64") 0, imm:$off, I64:$addr, ty:$val)>,
289        Requires<[HasAddr64]>;
290}
291
292defm : StorePatImmOff<i32, store, regPlusImm, "STORE_I32">;
293defm : StorePatImmOff<i64, store, regPlusImm, "STORE_I64">;
294defm : StorePatImmOff<f32, store, regPlusImm, "STORE_F32">;
295defm : StorePatImmOff<f64, store, regPlusImm, "STORE_F64">;
296defm : StorePatImmOff<i32, store, or_is_add, "STORE_I32">;
297defm : StorePatImmOff<i64, store, or_is_add, "STORE_I64">;
298defm : StorePatImmOff<f32, store, or_is_add, "STORE_F32">;
299defm : StorePatImmOff<f64, store, or_is_add, "STORE_F64">;
300
301// Select stores with just a constant offset.
302multiclass StorePatOffsetOnly<ValueType ty, PatFrag kind, string inst> {
303  def : Pat<(kind ty:$val, imm:$off),
304            (!cast<NI>(inst # "_A32") 0, imm:$off, (CONST_I32 0), ty:$val)>,
305        Requires<[HasAddr32]>;
306  def : Pat<(kind ty:$val, imm:$off),
307            (!cast<NI>(inst # "_A64") 0, imm:$off, (CONST_I64 0), ty:$val)>,
308        Requires<[HasAddr64]>;
309}
310defm : StorePatOffsetOnly<i32, store, "STORE_I32">;
311defm : StorePatOffsetOnly<i64, store, "STORE_I64">;
312defm : StorePatOffsetOnly<f32, store, "STORE_F32">;
313defm : StorePatOffsetOnly<f64, store, "STORE_F64">;
314
315multiclass StorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, string inst> {
316  def : Pat<(kind ty:$val, (WebAssemblyWrapper tglobaladdr:$off)),
317            (!cast<NI>(inst # "_A32") 0, tglobaladdr:$off, (CONST_I32 0),
318             ty:$val)>,
319        Requires<[IsNotPIC, HasAddr32]>;
320  def : Pat<(kind ty:$val, (WebAssemblyWrapper tglobaladdr:$off)),
321            (!cast<NI>(inst # "_A64") 0, tglobaladdr:$off, (CONST_I64 0),
322             ty:$val)>,
323        Requires<[IsNotPIC, HasAddr64]>;
324}
325defm : StorePatGlobalAddrOffOnly<i32, store, "STORE_I32">;
326defm : StorePatGlobalAddrOffOnly<i64, store, "STORE_I64">;
327defm : StorePatGlobalAddrOffOnly<f32, store, "STORE_F32">;
328defm : StorePatGlobalAddrOffOnly<f64, store, "STORE_F64">;
329
330// Truncating store.
331defm STORE8_I32 : WebAssemblyStore<I32, "i32.store8", 0x3a>;
332defm STORE16_I32 : WebAssemblyStore<I32, "i32.store16", 0x3b>;
333defm STORE8_I64 : WebAssemblyStore<I64, "i64.store8", 0x3c>;
334defm STORE16_I64 : WebAssemblyStore<I64, "i64.store16", 0x3d>;
335defm STORE32_I64 : WebAssemblyStore<I64, "i64.store32", 0x3e>;
336
337// Select truncating stores with no constant offset.
338defm : StorePatNoOffset<i32, truncstorei8, "STORE8_I32">;
339defm : StorePatNoOffset<i32, truncstorei16, "STORE16_I32">;
340defm : StorePatNoOffset<i64, truncstorei8, "STORE8_I64">;
341defm : StorePatNoOffset<i64, truncstorei16, "STORE16_I64">;
342defm : StorePatNoOffset<i64, truncstorei32, "STORE32_I64">;
343
344// Select truncating stores with a constant offset.
345defm : StorePatImmOff<i32, truncstorei8, regPlusImm, "STORE8_I32">;
346defm : StorePatImmOff<i32, truncstorei16, regPlusImm, "STORE16_I32">;
347defm : StorePatImmOff<i64, truncstorei8, regPlusImm, "STORE8_I64">;
348defm : StorePatImmOff<i64, truncstorei16, regPlusImm, "STORE16_I64">;
349defm : StorePatImmOff<i64, truncstorei32, regPlusImm, "STORE32_I64">;
350defm : StorePatImmOff<i32, truncstorei8, or_is_add, "STORE8_I32">;
351defm : StorePatImmOff<i32, truncstorei16, or_is_add, "STORE16_I32">;
352defm : StorePatImmOff<i64, truncstorei8, or_is_add, "STORE8_I64">;
353defm : StorePatImmOff<i64, truncstorei16, or_is_add, "STORE16_I64">;
354defm : StorePatImmOff<i64, truncstorei32, or_is_add, "STORE32_I64">;
355
356// Select truncating stores with just a constant offset.
357defm : StorePatOffsetOnly<i32, truncstorei8, "STORE8_I32">;
358defm : StorePatOffsetOnly<i32, truncstorei16, "STORE16_I32">;
359defm : StorePatOffsetOnly<i64, truncstorei8, "STORE8_I64">;
360defm : StorePatOffsetOnly<i64, truncstorei16, "STORE16_I64">;
361defm : StorePatOffsetOnly<i64, truncstorei32, "STORE32_I64">;
362defm : StorePatGlobalAddrOffOnly<i32, truncstorei8, "STORE8_I32">;
363defm : StorePatGlobalAddrOffOnly<i32, truncstorei16, "STORE16_I32">;
364defm : StorePatGlobalAddrOffOnly<i64, truncstorei8, "STORE8_I64">;
365defm : StorePatGlobalAddrOffOnly<i64, truncstorei16, "STORE16_I64">;
366defm : StorePatGlobalAddrOffOnly<i64, truncstorei32, "STORE32_I64">;
367
368multiclass MemoryOps<WebAssemblyRegClass rc, string B> {
369// Current memory size.
370defm MEMORY_SIZE_A#B : I<(outs rc:$dst), (ins i32imm:$flags),
371                         (outs), (ins i32imm:$flags),
372                         [(set rc:$dst,
373                           (int_wasm_memory_size (i32 imm:$flags)))],
374                         "memory.size\t$dst, $flags", "memory.size\t$flags",
375                         0x3f>;
376
377// Grow memory.
378defm MEMORY_GROW_A#B : I<(outs rc:$dst), (ins i32imm:$flags, rc:$delta),
379                         (outs), (ins i32imm:$flags),
380                         [(set rc:$dst,
381                           (int_wasm_memory_grow (i32 imm:$flags),
382                             rc:$delta))],
383                         "memory.grow\t$dst, $flags, $delta",
384                         "memory.grow\t$flags", 0x40>;
385}
386
387defm : MemoryOps<I32, "32">;
388defm : MemoryOps<I64, "64">;
389