1 // WebAssemblyInstrMemory.td-WebAssembly Memory codegen support -*- tablegen -*-
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
7 //===----------------------------------------------------------------------===//
10 /// WebAssembly Memory operand code-gen constructs.
12 //===----------------------------------------------------------------------===//
16 // - WebAssemblyTargetLowering having to do with atomics
17 // - Each has optional alignment.
19 // WebAssembly has i8/i16/i32/i64/f32/f64 memory types, but doesn't have i8/i16
20 // local types. These memory-only types instead zero- or sign-extend into local
21 // types when loading, and truncate when storing.
23 // WebAssembly constant offsets are performed as unsigned with infinite
24 // precision, so we need to check for NoUnsignedWrap so that we don't fold an
25 // offset for an add that needs wrapping.
26 def regPlusImm : PatFrag<(ops node:$addr, node:$off),
27 (add node:$addr, node:$off),
28 [{ return N->getFlags().hasNoUnsignedWrap(); }]>;
30 // Treat an 'or' node as an 'add' if the or'ed bits are known to be zero.
31 def or_is_add : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{
32 if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N->getOperand(1)))
33 return CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
35 KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
36 KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
37 return (~Known0.Zero & ~Known1.Zero) == 0;
40 // We don't need a regPlusES because external symbols never have constant
41 // offsets folded into them, so we can just use add.
43 // Defines atomic and non-atomic loads, regular and extending.
44 multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode> {
45 let mayLoad = 1, UseNamedOperandTable = 1 in
46 defm "": 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>;
54 // FIXME: When we can break syntax compatibility, reorder the fields in the
55 // asmstrings to match the binary encoding.
56 defm LOAD_I32 : WebAssemblyLoad<I32, "i32.load", 0x28>;
57 defm LOAD_I64 : WebAssemblyLoad<I64, "i64.load", 0x29>;
58 defm LOAD_F32 : WebAssemblyLoad<F32, "f32.load", 0x2a>;
59 defm LOAD_F64 : WebAssemblyLoad<F64, "f64.load", 0x2b>;
61 // Select loads with no constant offset.
62 class LoadPatNoOffset<ValueType ty, PatFrag kind, NI inst> :
63 Pat<(ty (kind I32:$addr)), (inst 0, 0, I32:$addr)>;
65 def : LoadPatNoOffset<i32, load, LOAD_I32>;
66 def : LoadPatNoOffset<i64, load, LOAD_I64>;
67 def : LoadPatNoOffset<f32, load, LOAD_F32>;
68 def : LoadPatNoOffset<f64, load, LOAD_F64>;
71 // Select loads with a constant offset.
73 // Pattern with address + immediate offset
74 class LoadPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> :
75 Pat<(ty (kind (operand I32:$addr, imm:$off))), (inst 0, imm:$off, I32:$addr)>;
77 def : LoadPatImmOff<i32, load, regPlusImm, LOAD_I32>;
78 def : LoadPatImmOff<i64, load, regPlusImm, LOAD_I64>;
79 def : LoadPatImmOff<f32, load, regPlusImm, LOAD_F32>;
80 def : LoadPatImmOff<f64, load, regPlusImm, LOAD_F64>;
81 def : LoadPatImmOff<i32, load, or_is_add, LOAD_I32>;
82 def : LoadPatImmOff<i64, load, or_is_add, LOAD_I64>;
83 def : LoadPatImmOff<f32, load, or_is_add, LOAD_F32>;
84 def : LoadPatImmOff<f64, load, or_is_add, LOAD_F64>;
86 // Select loads with just a constant offset.
87 class LoadPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> :
88 Pat<(ty (kind imm:$off)), (inst 0, imm:$off, (CONST_I32 0))>;
90 def : LoadPatOffsetOnly<i32, load, LOAD_I32>;
91 def : LoadPatOffsetOnly<i64, load, LOAD_I64>;
92 def : LoadPatOffsetOnly<f32, load, LOAD_F32>;
93 def : LoadPatOffsetOnly<f64, load, LOAD_F64>;
95 class LoadPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
96 Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))),
97 (inst 0, tglobaladdr:$off, (CONST_I32 0))>, Requires<[IsNotPIC]>;
99 def : LoadPatGlobalAddrOffOnly<i32, load, LOAD_I32>;
100 def : LoadPatGlobalAddrOffOnly<i64, load, LOAD_I64>;
101 def : LoadPatGlobalAddrOffOnly<f32, load, LOAD_F32>;
102 def : LoadPatGlobalAddrOffOnly<f64, load, LOAD_F64>;
105 defm LOAD8_S_I32 : WebAssemblyLoad<I32, "i32.load8_s", 0x2c>;
106 defm LOAD8_U_I32 : WebAssemblyLoad<I32, "i32.load8_u", 0x2d>;
107 defm LOAD16_S_I32 : WebAssemblyLoad<I32, "i32.load16_s", 0x2e>;
108 defm LOAD16_U_I32 : WebAssemblyLoad<I32, "i32.load16_u", 0x2f>;
109 defm LOAD8_S_I64 : WebAssemblyLoad<I64, "i64.load8_s", 0x30>;
110 defm LOAD8_U_I64 : WebAssemblyLoad<I64, "i64.load8_u", 0x31>;
111 defm LOAD16_S_I64 : WebAssemblyLoad<I64, "i64.load16_s", 0x32>;
112 defm LOAD16_U_I64 : WebAssemblyLoad<I64, "i64.load16_u", 0x33>;
113 defm LOAD32_S_I64 : WebAssemblyLoad<I64, "i64.load32_s", 0x34>;
114 defm LOAD32_U_I64 : WebAssemblyLoad<I64, "i64.load32_u", 0x35>;
116 // Select extending loads with no constant offset.
117 def : LoadPatNoOffset<i32, sextloadi8, LOAD8_S_I32>;
118 def : LoadPatNoOffset<i32, zextloadi8, LOAD8_U_I32>;
119 def : LoadPatNoOffset<i32, sextloadi16, LOAD16_S_I32>;
120 def : LoadPatNoOffset<i32, zextloadi16, LOAD16_U_I32>;
121 def : LoadPatNoOffset<i64, sextloadi8, LOAD8_S_I64>;
122 def : LoadPatNoOffset<i64, zextloadi8, LOAD8_U_I64>;
123 def : LoadPatNoOffset<i64, sextloadi16, LOAD16_S_I64>;
124 def : LoadPatNoOffset<i64, zextloadi16, LOAD16_U_I64>;
125 def : LoadPatNoOffset<i64, sextloadi32, LOAD32_S_I64>;
126 def : LoadPatNoOffset<i64, zextloadi32, LOAD32_U_I64>;
128 // Select extending loads with a constant offset.
129 def : LoadPatImmOff<i32, sextloadi8, regPlusImm, LOAD8_S_I32>;
130 def : LoadPatImmOff<i32, zextloadi8, regPlusImm, LOAD8_U_I32>;
131 def : LoadPatImmOff<i32, sextloadi16, regPlusImm, LOAD16_S_I32>;
132 def : LoadPatImmOff<i32, zextloadi16, regPlusImm, LOAD16_U_I32>;
133 def : LoadPatImmOff<i64, sextloadi8, regPlusImm, LOAD8_S_I64>;
134 def : LoadPatImmOff<i64, zextloadi8, regPlusImm, LOAD8_U_I64>;
135 def : LoadPatImmOff<i64, sextloadi16, regPlusImm, LOAD16_S_I64>;
136 def : LoadPatImmOff<i64, zextloadi16, regPlusImm, LOAD16_U_I64>;
137 def : LoadPatImmOff<i64, sextloadi32, regPlusImm, LOAD32_S_I64>;
138 def : LoadPatImmOff<i64, zextloadi32, regPlusImm, LOAD32_U_I64>;
140 def : LoadPatImmOff<i32, sextloadi8, or_is_add, LOAD8_S_I32>;
141 def : LoadPatImmOff<i32, zextloadi8, or_is_add, LOAD8_U_I32>;
142 def : LoadPatImmOff<i32, sextloadi16, or_is_add, LOAD16_S_I32>;
143 def : LoadPatImmOff<i32, zextloadi16, or_is_add, LOAD16_U_I32>;
144 def : LoadPatImmOff<i64, sextloadi8, or_is_add, LOAD8_S_I64>;
145 def : LoadPatImmOff<i64, zextloadi8, or_is_add, LOAD8_U_I64>;
146 def : LoadPatImmOff<i64, sextloadi16, or_is_add, LOAD16_S_I64>;
147 def : LoadPatImmOff<i64, zextloadi16, or_is_add, LOAD16_U_I64>;
148 def : LoadPatImmOff<i64, sextloadi32, or_is_add, LOAD32_S_I64>;
149 def : LoadPatImmOff<i64, zextloadi32, or_is_add, LOAD32_U_I64>;
151 // Select extending loads with just a constant offset.
152 def : LoadPatOffsetOnly<i32, sextloadi8, LOAD8_S_I32>;
153 def : LoadPatOffsetOnly<i32, zextloadi8, LOAD8_U_I32>;
154 def : LoadPatOffsetOnly<i32, sextloadi16, LOAD16_S_I32>;
155 def : LoadPatOffsetOnly<i32, zextloadi16, LOAD16_U_I32>;
157 def : LoadPatOffsetOnly<i64, sextloadi8, LOAD8_S_I64>;
158 def : LoadPatOffsetOnly<i64, zextloadi8, LOAD8_U_I64>;
159 def : LoadPatOffsetOnly<i64, sextloadi16, LOAD16_S_I64>;
160 def : LoadPatOffsetOnly<i64, zextloadi16, LOAD16_U_I64>;
161 def : LoadPatOffsetOnly<i64, sextloadi32, LOAD32_S_I64>;
162 def : LoadPatOffsetOnly<i64, zextloadi32, LOAD32_U_I64>;
164 def : LoadPatGlobalAddrOffOnly<i32, sextloadi8, LOAD8_S_I32>;
165 def : LoadPatGlobalAddrOffOnly<i32, zextloadi8, LOAD8_U_I32>;
166 def : LoadPatGlobalAddrOffOnly<i32, sextloadi16, LOAD16_S_I32>;
167 def : LoadPatGlobalAddrOffOnly<i32, zextloadi16, LOAD16_U_I32>;
168 def : LoadPatGlobalAddrOffOnly<i64, sextloadi8, LOAD8_S_I64>;
169 def : LoadPatGlobalAddrOffOnly<i64, zextloadi8, LOAD8_U_I64>;
170 def : LoadPatGlobalAddrOffOnly<i64, sextloadi16, LOAD16_S_I64>;
171 def : LoadPatGlobalAddrOffOnly<i64, zextloadi16, LOAD16_U_I64>;
172 def : LoadPatGlobalAddrOffOnly<i64, sextloadi32, LOAD32_S_I64>;
173 def : LoadPatGlobalAddrOffOnly<i64, zextloadi32, LOAD32_U_I64>;
175 // Resolve "don't care" extending loads to zero-extending loads. This is
176 // somewhat arbitrary, but zero-extending is conceptually simpler.
178 // Select "don't care" extending loads with no constant offset.
179 def : LoadPatNoOffset<i32, extloadi8, LOAD8_U_I32>;
180 def : LoadPatNoOffset<i32, extloadi16, LOAD16_U_I32>;
181 def : LoadPatNoOffset<i64, extloadi8, LOAD8_U_I64>;
182 def : LoadPatNoOffset<i64, extloadi16, LOAD16_U_I64>;
183 def : LoadPatNoOffset<i64, extloadi32, LOAD32_U_I64>;
185 // Select "don't care" extending loads with a constant offset.
186 def : LoadPatImmOff<i32, extloadi8, regPlusImm, LOAD8_U_I32>;
187 def : LoadPatImmOff<i32, extloadi16, regPlusImm, LOAD16_U_I32>;
188 def : LoadPatImmOff<i64, extloadi8, regPlusImm, LOAD8_U_I64>;
189 def : LoadPatImmOff<i64, extloadi16, regPlusImm, LOAD16_U_I64>;
190 def : LoadPatImmOff<i64, extloadi32, regPlusImm, LOAD32_U_I64>;
191 def : LoadPatImmOff<i32, extloadi8, or_is_add, LOAD8_U_I32>;
192 def : LoadPatImmOff<i32, extloadi16, or_is_add, LOAD16_U_I32>;
193 def : LoadPatImmOff<i64, extloadi8, or_is_add, LOAD8_U_I64>;
194 def : LoadPatImmOff<i64, extloadi16, or_is_add, LOAD16_U_I64>;
195 def : LoadPatImmOff<i64, extloadi32, or_is_add, LOAD32_U_I64>;
197 // Select "don't care" extending loads with just a constant offset.
198 def : LoadPatOffsetOnly<i32, extloadi8, LOAD8_U_I32>;
199 def : LoadPatOffsetOnly<i32, extloadi16, LOAD16_U_I32>;
200 def : LoadPatOffsetOnly<i64, extloadi8, LOAD8_U_I64>;
201 def : LoadPatOffsetOnly<i64, extloadi16, LOAD16_U_I64>;
202 def : LoadPatOffsetOnly<i64, extloadi32, LOAD32_U_I64>;
203 def : LoadPatGlobalAddrOffOnly<i32, extloadi8, LOAD8_U_I32>;
204 def : LoadPatGlobalAddrOffOnly<i32, extloadi16, LOAD16_U_I32>;
205 def : LoadPatGlobalAddrOffOnly<i64, extloadi8, LOAD8_U_I64>;
206 def : LoadPatGlobalAddrOffOnly<i64, extloadi16, LOAD16_U_I64>;
207 def : LoadPatGlobalAddrOffOnly<i64, extloadi32, LOAD32_U_I64>;
209 // Defines atomic and non-atomic stores, regular and truncating
210 multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode> {
211 let mayStore = 1, UseNamedOperandTable = 1 in
213 (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
215 (ins P2Align:$p2align, offset32_op:$off), [],
216 !strconcat(Name, "\t${off}(${addr})${p2align}, $val"),
217 !strconcat(Name, "\t${off}${p2align}"), Opcode>;
220 // Note: WebAssembly inverts SelectionDAG's usual operand order.
221 defm STORE_I32 : WebAssemblyStore<I32, "i32.store", 0x36>;
222 defm STORE_I64 : WebAssemblyStore<I64, "i64.store", 0x37>;
223 defm STORE_F32 : WebAssemblyStore<F32, "f32.store", 0x38>;
224 defm STORE_F64 : WebAssemblyStore<F64, "f64.store", 0x39>;
226 // Select stores with no constant offset.
227 class StorePatNoOffset<ValueType ty, PatFrag node, NI inst> :
228 Pat<(node ty:$val, I32:$addr), (inst 0, 0, I32:$addr, ty:$val)>;
230 def : StorePatNoOffset<i32, store, STORE_I32>;
231 def : StorePatNoOffset<i64, store, STORE_I64>;
232 def : StorePatNoOffset<f32, store, STORE_F32>;
233 def : StorePatNoOffset<f64, store, STORE_F64>;
235 // Select stores with a constant offset.
236 class StorePatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> :
237 Pat<(kind ty:$val, (operand I32:$addr, imm:$off)),
238 (inst 0, imm:$off, I32:$addr, ty:$val)>;
240 def : StorePatImmOff<i32, store, regPlusImm, STORE_I32>;
241 def : StorePatImmOff<i64, store, regPlusImm, STORE_I64>;
242 def : StorePatImmOff<f32, store, regPlusImm, STORE_F32>;
243 def : StorePatImmOff<f64, store, regPlusImm, STORE_F64>;
244 def : StorePatImmOff<i32, store, or_is_add, STORE_I32>;
245 def : StorePatImmOff<i64, store, or_is_add, STORE_I64>;
246 def : StorePatImmOff<f32, store, or_is_add, STORE_F32>;
247 def : StorePatImmOff<f64, store, or_is_add, STORE_F64>;
249 // Select stores with just a constant offset.
250 class StorePatOffsetOnly<ValueType ty, PatFrag kind, NI inst> :
251 Pat<(kind ty:$val, imm:$off), (inst 0, imm:$off, (CONST_I32 0), ty:$val)>;
252 def : StorePatOffsetOnly<i32, store, STORE_I32>;
253 def : StorePatOffsetOnly<i64, store, STORE_I64>;
254 def : StorePatOffsetOnly<f32, store, STORE_F32>;
255 def : StorePatOffsetOnly<f64, store, STORE_F64>;
257 class StorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
258 Pat<(kind ty:$val, (WebAssemblywrapper tglobaladdr:$off)),
259 (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>, Requires<[IsNotPIC]>;
260 def : StorePatGlobalAddrOffOnly<i32, store, STORE_I32>;
261 def : StorePatGlobalAddrOffOnly<i64, store, STORE_I64>;
262 def : StorePatGlobalAddrOffOnly<f32, store, STORE_F32>;
263 def : StorePatGlobalAddrOffOnly<f64, store, STORE_F64>;
266 defm STORE8_I32 : WebAssemblyStore<I32, "i32.store8", 0x3a>;
267 defm STORE16_I32 : WebAssemblyStore<I32, "i32.store16", 0x3b>;
268 defm STORE8_I64 : WebAssemblyStore<I64, "i64.store8", 0x3c>;
269 defm STORE16_I64 : WebAssemblyStore<I64, "i64.store16", 0x3d>;
270 defm STORE32_I64 : WebAssemblyStore<I64, "i64.store32", 0x3e>;
272 // Select truncating stores with no constant offset.
273 def : StorePatNoOffset<i32, truncstorei8, STORE8_I32>;
274 def : StorePatNoOffset<i32, truncstorei16, STORE16_I32>;
275 def : StorePatNoOffset<i64, truncstorei8, STORE8_I64>;
276 def : StorePatNoOffset<i64, truncstorei16, STORE16_I64>;
277 def : StorePatNoOffset<i64, truncstorei32, STORE32_I64>;
279 // Select truncating stores with a constant offset.
280 def : StorePatImmOff<i32, truncstorei8, regPlusImm, STORE8_I32>;
281 def : StorePatImmOff<i32, truncstorei16, regPlusImm, STORE16_I32>;
282 def : StorePatImmOff<i64, truncstorei8, regPlusImm, STORE8_I64>;
283 def : StorePatImmOff<i64, truncstorei16, regPlusImm, STORE16_I64>;
284 def : StorePatImmOff<i64, truncstorei32, regPlusImm, STORE32_I64>;
285 def : StorePatImmOff<i32, truncstorei8, or_is_add, STORE8_I32>;
286 def : StorePatImmOff<i32, truncstorei16, or_is_add, STORE16_I32>;
287 def : StorePatImmOff<i64, truncstorei8, or_is_add, STORE8_I64>;
288 def : StorePatImmOff<i64, truncstorei16, or_is_add, STORE16_I64>;
289 def : StorePatImmOff<i64, truncstorei32, or_is_add, STORE32_I64>;
291 // Select truncating stores with just a constant offset.
292 def : StorePatOffsetOnly<i32, truncstorei8, STORE8_I32>;
293 def : StorePatOffsetOnly<i32, truncstorei16, STORE16_I32>;
294 def : StorePatOffsetOnly<i64, truncstorei8, STORE8_I64>;
295 def : StorePatOffsetOnly<i64, truncstorei16, STORE16_I64>;
296 def : StorePatOffsetOnly<i64, truncstorei32, STORE32_I64>;
297 def : StorePatGlobalAddrOffOnly<i32, truncstorei8, STORE8_I32>;
298 def : StorePatGlobalAddrOffOnly<i32, truncstorei16, STORE16_I32>;
299 def : StorePatGlobalAddrOffOnly<i64, truncstorei8, STORE8_I64>;
300 def : StorePatGlobalAddrOffOnly<i64, truncstorei16, STORE16_I64>;
301 def : StorePatGlobalAddrOffOnly<i64, truncstorei32, STORE32_I64>;
303 // Current memory size.
304 defm MEMORY_SIZE_I32 : I<(outs I32:$dst), (ins i32imm:$flags),
305 (outs), (ins i32imm:$flags),
307 (int_wasm_memory_size (i32 imm:$flags)))],
308 "memory.size\t$dst, $flags", "memory.size\t$flags",
310 Requires<[HasAddr32]>;
313 defm MEMORY_GROW_I32 : I<(outs I32:$dst), (ins i32imm:$flags, I32:$delta),
314 (outs), (ins i32imm:$flags),
316 (int_wasm_memory_grow (i32 imm:$flags),
318 "memory.grow\t$dst, $flags, $delta",
319 "memory.grow\t$flags", 0x40>,
320 Requires<[HasAddr32]>;