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// - HasAddr64 16// - WebAssemblyTargetLowering having to do with atomics 17// - Each has optional alignment. 18 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. 22 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. 26def regPlusImm : PatFrag<(ops node:$addr, node:$off), 27 (add node:$addr, node:$off), 28 [{ return N->getFlags().hasNoUnsignedWrap(); }]>; 29 30// Treat an 'or' node as an 'add' if the or'ed bits are known to be zero. 31def 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()); 34 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; 38}]>; 39 40// We don't need a regPlusES because external symbols never have constant 41// offsets folded into them, so we can just use add. 42 43// Defines atomic and non-atomic loads, regular and extending. 44multiclass 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>; 51} 52 53// Basic load. 54// FIXME: When we can break syntax compatibility, reorder the fields in the 55// asmstrings to match the binary encoding. 56defm LOAD_I32 : WebAssemblyLoad<I32, "i32.load", 0x28>; 57defm LOAD_I64 : WebAssemblyLoad<I64, "i64.load", 0x29>; 58defm LOAD_F32 : WebAssemblyLoad<F32, "f32.load", 0x2a>; 59defm LOAD_F64 : WebAssemblyLoad<F64, "f64.load", 0x2b>; 60 61// Select loads with no constant offset. 62class LoadPatNoOffset<ValueType ty, PatFrag kind, NI inst> : 63 Pat<(ty (kind I32:$addr)), (inst 0, 0, I32:$addr)>; 64 65def : LoadPatNoOffset<i32, load, LOAD_I32>; 66def : LoadPatNoOffset<i64, load, LOAD_I64>; 67def : LoadPatNoOffset<f32, load, LOAD_F32>; 68def : LoadPatNoOffset<f64, load, LOAD_F64>; 69 70 71// Select loads with a constant offset. 72 73// Pattern with address + immediate offset 74class LoadPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> : 75 Pat<(ty (kind (operand I32:$addr, imm:$off))), (inst 0, imm:$off, I32:$addr)>; 76 77def : LoadPatImmOff<i32, load, regPlusImm, LOAD_I32>; 78def : LoadPatImmOff<i64, load, regPlusImm, LOAD_I64>; 79def : LoadPatImmOff<f32, load, regPlusImm, LOAD_F32>; 80def : LoadPatImmOff<f64, load, regPlusImm, LOAD_F64>; 81def : LoadPatImmOff<i32, load, or_is_add, LOAD_I32>; 82def : LoadPatImmOff<i64, load, or_is_add, LOAD_I64>; 83def : LoadPatImmOff<f32, load, or_is_add, LOAD_F32>; 84def : LoadPatImmOff<f64, load, or_is_add, LOAD_F64>; 85 86// Select loads with just a constant offset. 87class LoadPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> : 88 Pat<(ty (kind imm:$off)), (inst 0, imm:$off, (CONST_I32 0))>; 89 90def : LoadPatOffsetOnly<i32, load, LOAD_I32>; 91def : LoadPatOffsetOnly<i64, load, LOAD_I64>; 92def : LoadPatOffsetOnly<f32, load, LOAD_F32>; 93def : LoadPatOffsetOnly<f64, load, LOAD_F64>; 94 95class LoadPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> : 96 Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))), 97 (inst 0, tglobaladdr:$off, (CONST_I32 0))>, Requires<[IsNotPIC]>; 98 99def : LoadPatGlobalAddrOffOnly<i32, load, LOAD_I32>; 100def : LoadPatGlobalAddrOffOnly<i64, load, LOAD_I64>; 101def : LoadPatGlobalAddrOffOnly<f32, load, LOAD_F32>; 102def : LoadPatGlobalAddrOffOnly<f64, load, LOAD_F64>; 103 104// Extending load. 105defm LOAD8_S_I32 : WebAssemblyLoad<I32, "i32.load8_s", 0x2c>; 106defm LOAD8_U_I32 : WebAssemblyLoad<I32, "i32.load8_u", 0x2d>; 107defm LOAD16_S_I32 : WebAssemblyLoad<I32, "i32.load16_s", 0x2e>; 108defm LOAD16_U_I32 : WebAssemblyLoad<I32, "i32.load16_u", 0x2f>; 109defm LOAD8_S_I64 : WebAssemblyLoad<I64, "i64.load8_s", 0x30>; 110defm LOAD8_U_I64 : WebAssemblyLoad<I64, "i64.load8_u", 0x31>; 111defm LOAD16_S_I64 : WebAssemblyLoad<I64, "i64.load16_s", 0x32>; 112defm LOAD16_U_I64 : WebAssemblyLoad<I64, "i64.load16_u", 0x33>; 113defm LOAD32_S_I64 : WebAssemblyLoad<I64, "i64.load32_s", 0x34>; 114defm LOAD32_U_I64 : WebAssemblyLoad<I64, "i64.load32_u", 0x35>; 115 116// Select extending loads with no constant offset. 117def : LoadPatNoOffset<i32, sextloadi8, LOAD8_S_I32>; 118def : LoadPatNoOffset<i32, zextloadi8, LOAD8_U_I32>; 119def : LoadPatNoOffset<i32, sextloadi16, LOAD16_S_I32>; 120def : LoadPatNoOffset<i32, zextloadi16, LOAD16_U_I32>; 121def : LoadPatNoOffset<i64, sextloadi8, LOAD8_S_I64>; 122def : LoadPatNoOffset<i64, zextloadi8, LOAD8_U_I64>; 123def : LoadPatNoOffset<i64, sextloadi16, LOAD16_S_I64>; 124def : LoadPatNoOffset<i64, zextloadi16, LOAD16_U_I64>; 125def : LoadPatNoOffset<i64, sextloadi32, LOAD32_S_I64>; 126def : LoadPatNoOffset<i64, zextloadi32, LOAD32_U_I64>; 127 128// Select extending loads with a constant offset. 129def : LoadPatImmOff<i32, sextloadi8, regPlusImm, LOAD8_S_I32>; 130def : LoadPatImmOff<i32, zextloadi8, regPlusImm, LOAD8_U_I32>; 131def : LoadPatImmOff<i32, sextloadi16, regPlusImm, LOAD16_S_I32>; 132def : LoadPatImmOff<i32, zextloadi16, regPlusImm, LOAD16_U_I32>; 133def : LoadPatImmOff<i64, sextloadi8, regPlusImm, LOAD8_S_I64>; 134def : LoadPatImmOff<i64, zextloadi8, regPlusImm, LOAD8_U_I64>; 135def : LoadPatImmOff<i64, sextloadi16, regPlusImm, LOAD16_S_I64>; 136def : LoadPatImmOff<i64, zextloadi16, regPlusImm, LOAD16_U_I64>; 137def : LoadPatImmOff<i64, sextloadi32, regPlusImm, LOAD32_S_I64>; 138def : LoadPatImmOff<i64, zextloadi32, regPlusImm, LOAD32_U_I64>; 139 140def : LoadPatImmOff<i32, sextloadi8, or_is_add, LOAD8_S_I32>; 141def : LoadPatImmOff<i32, zextloadi8, or_is_add, LOAD8_U_I32>; 142def : LoadPatImmOff<i32, sextloadi16, or_is_add, LOAD16_S_I32>; 143def : LoadPatImmOff<i32, zextloadi16, or_is_add, LOAD16_U_I32>; 144def : LoadPatImmOff<i64, sextloadi8, or_is_add, LOAD8_S_I64>; 145def : LoadPatImmOff<i64, zextloadi8, or_is_add, LOAD8_U_I64>; 146def : LoadPatImmOff<i64, sextloadi16, or_is_add, LOAD16_S_I64>; 147def : LoadPatImmOff<i64, zextloadi16, or_is_add, LOAD16_U_I64>; 148def : LoadPatImmOff<i64, sextloadi32, or_is_add, LOAD32_S_I64>; 149def : LoadPatImmOff<i64, zextloadi32, or_is_add, LOAD32_U_I64>; 150 151// Select extending loads with just a constant offset. 152def : LoadPatOffsetOnly<i32, sextloadi8, LOAD8_S_I32>; 153def : LoadPatOffsetOnly<i32, zextloadi8, LOAD8_U_I32>; 154def : LoadPatOffsetOnly<i32, sextloadi16, LOAD16_S_I32>; 155def : LoadPatOffsetOnly<i32, zextloadi16, LOAD16_U_I32>; 156 157def : LoadPatOffsetOnly<i64, sextloadi8, LOAD8_S_I64>; 158def : LoadPatOffsetOnly<i64, zextloadi8, LOAD8_U_I64>; 159def : LoadPatOffsetOnly<i64, sextloadi16, LOAD16_S_I64>; 160def : LoadPatOffsetOnly<i64, zextloadi16, LOAD16_U_I64>; 161def : LoadPatOffsetOnly<i64, sextloadi32, LOAD32_S_I64>; 162def : LoadPatOffsetOnly<i64, zextloadi32, LOAD32_U_I64>; 163 164def : LoadPatGlobalAddrOffOnly<i32, sextloadi8, LOAD8_S_I32>; 165def : LoadPatGlobalAddrOffOnly<i32, zextloadi8, LOAD8_U_I32>; 166def : LoadPatGlobalAddrOffOnly<i32, sextloadi16, LOAD16_S_I32>; 167def : LoadPatGlobalAddrOffOnly<i32, zextloadi16, LOAD16_U_I32>; 168def : LoadPatGlobalAddrOffOnly<i64, sextloadi8, LOAD8_S_I64>; 169def : LoadPatGlobalAddrOffOnly<i64, zextloadi8, LOAD8_U_I64>; 170def : LoadPatGlobalAddrOffOnly<i64, sextloadi16, LOAD16_S_I64>; 171def : LoadPatGlobalAddrOffOnly<i64, zextloadi16, LOAD16_U_I64>; 172def : LoadPatGlobalAddrOffOnly<i64, sextloadi32, LOAD32_S_I64>; 173def : LoadPatGlobalAddrOffOnly<i64, zextloadi32, LOAD32_U_I64>; 174 175// Resolve "don't care" extending loads to zero-extending loads. This is 176// somewhat arbitrary, but zero-extending is conceptually simpler. 177 178// Select "don't care" extending loads with no constant offset. 179def : LoadPatNoOffset<i32, extloadi8, LOAD8_U_I32>; 180def : LoadPatNoOffset<i32, extloadi16, LOAD16_U_I32>; 181def : LoadPatNoOffset<i64, extloadi8, LOAD8_U_I64>; 182def : LoadPatNoOffset<i64, extloadi16, LOAD16_U_I64>; 183def : LoadPatNoOffset<i64, extloadi32, LOAD32_U_I64>; 184 185// Select "don't care" extending loads with a constant offset. 186def : LoadPatImmOff<i32, extloadi8, regPlusImm, LOAD8_U_I32>; 187def : LoadPatImmOff<i32, extloadi16, regPlusImm, LOAD16_U_I32>; 188def : LoadPatImmOff<i64, extloadi8, regPlusImm, LOAD8_U_I64>; 189def : LoadPatImmOff<i64, extloadi16, regPlusImm, LOAD16_U_I64>; 190def : LoadPatImmOff<i64, extloadi32, regPlusImm, LOAD32_U_I64>; 191def : LoadPatImmOff<i32, extloadi8, or_is_add, LOAD8_U_I32>; 192def : LoadPatImmOff<i32, extloadi16, or_is_add, LOAD16_U_I32>; 193def : LoadPatImmOff<i64, extloadi8, or_is_add, LOAD8_U_I64>; 194def : LoadPatImmOff<i64, extloadi16, or_is_add, LOAD16_U_I64>; 195def : LoadPatImmOff<i64, extloadi32, or_is_add, LOAD32_U_I64>; 196 197// Select "don't care" extending loads with just a constant offset. 198def : LoadPatOffsetOnly<i32, extloadi8, LOAD8_U_I32>; 199def : LoadPatOffsetOnly<i32, extloadi16, LOAD16_U_I32>; 200def : LoadPatOffsetOnly<i64, extloadi8, LOAD8_U_I64>; 201def : LoadPatOffsetOnly<i64, extloadi16, LOAD16_U_I64>; 202def : LoadPatOffsetOnly<i64, extloadi32, LOAD32_U_I64>; 203def : LoadPatGlobalAddrOffOnly<i32, extloadi8, LOAD8_U_I32>; 204def : LoadPatGlobalAddrOffOnly<i32, extloadi16, LOAD16_U_I32>; 205def : LoadPatGlobalAddrOffOnly<i64, extloadi8, LOAD8_U_I64>; 206def : LoadPatGlobalAddrOffOnly<i64, extloadi16, LOAD16_U_I64>; 207def : LoadPatGlobalAddrOffOnly<i64, extloadi32, LOAD32_U_I64>; 208 209// Defines atomic and non-atomic stores, regular and truncating 210multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode> { 211 let mayStore = 1, UseNamedOperandTable = 1 in 212 defm "" : I<(outs), 213 (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val), 214 (outs), 215 (ins P2Align:$p2align, offset32_op:$off), [], 216 !strconcat(Name, "\t${off}(${addr})${p2align}, $val"), 217 !strconcat(Name, "\t${off}${p2align}"), Opcode>; 218} 219// Basic store. 220// Note: WebAssembly inverts SelectionDAG's usual operand order. 221defm STORE_I32 : WebAssemblyStore<I32, "i32.store", 0x36>; 222defm STORE_I64 : WebAssemblyStore<I64, "i64.store", 0x37>; 223defm STORE_F32 : WebAssemblyStore<F32, "f32.store", 0x38>; 224defm STORE_F64 : WebAssemblyStore<F64, "f64.store", 0x39>; 225 226// Select stores with no constant offset. 227class StorePatNoOffset<ValueType ty, PatFrag node, NI inst> : 228 Pat<(node ty:$val, I32:$addr), (inst 0, 0, I32:$addr, ty:$val)>; 229 230def : StorePatNoOffset<i32, store, STORE_I32>; 231def : StorePatNoOffset<i64, store, STORE_I64>; 232def : StorePatNoOffset<f32, store, STORE_F32>; 233def : StorePatNoOffset<f64, store, STORE_F64>; 234 235// Select stores with a constant offset. 236class 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)>; 239 240def : StorePatImmOff<i32, store, regPlusImm, STORE_I32>; 241def : StorePatImmOff<i64, store, regPlusImm, STORE_I64>; 242def : StorePatImmOff<f32, store, regPlusImm, STORE_F32>; 243def : StorePatImmOff<f64, store, regPlusImm, STORE_F64>; 244def : StorePatImmOff<i32, store, or_is_add, STORE_I32>; 245def : StorePatImmOff<i64, store, or_is_add, STORE_I64>; 246def : StorePatImmOff<f32, store, or_is_add, STORE_F32>; 247def : StorePatImmOff<f64, store, or_is_add, STORE_F64>; 248 249// Select stores with just a constant offset. 250class StorePatOffsetOnly<ValueType ty, PatFrag kind, NI inst> : 251 Pat<(kind ty:$val, imm:$off), (inst 0, imm:$off, (CONST_I32 0), ty:$val)>; 252def : StorePatOffsetOnly<i32, store, STORE_I32>; 253def : StorePatOffsetOnly<i64, store, STORE_I64>; 254def : StorePatOffsetOnly<f32, store, STORE_F32>; 255def : StorePatOffsetOnly<f64, store, STORE_F64>; 256 257class 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]>; 260def : StorePatGlobalAddrOffOnly<i32, store, STORE_I32>; 261def : StorePatGlobalAddrOffOnly<i64, store, STORE_I64>; 262def : StorePatGlobalAddrOffOnly<f32, store, STORE_F32>; 263def : StorePatGlobalAddrOffOnly<f64, store, STORE_F64>; 264 265// Truncating store. 266defm STORE8_I32 : WebAssemblyStore<I32, "i32.store8", 0x3a>; 267defm STORE16_I32 : WebAssemblyStore<I32, "i32.store16", 0x3b>; 268defm STORE8_I64 : WebAssemblyStore<I64, "i64.store8", 0x3c>; 269defm STORE16_I64 : WebAssemblyStore<I64, "i64.store16", 0x3d>; 270defm STORE32_I64 : WebAssemblyStore<I64, "i64.store32", 0x3e>; 271 272// Select truncating stores with no constant offset. 273def : StorePatNoOffset<i32, truncstorei8, STORE8_I32>; 274def : StorePatNoOffset<i32, truncstorei16, STORE16_I32>; 275def : StorePatNoOffset<i64, truncstorei8, STORE8_I64>; 276def : StorePatNoOffset<i64, truncstorei16, STORE16_I64>; 277def : StorePatNoOffset<i64, truncstorei32, STORE32_I64>; 278 279// Select truncating stores with a constant offset. 280def : StorePatImmOff<i32, truncstorei8, regPlusImm, STORE8_I32>; 281def : StorePatImmOff<i32, truncstorei16, regPlusImm, STORE16_I32>; 282def : StorePatImmOff<i64, truncstorei8, regPlusImm, STORE8_I64>; 283def : StorePatImmOff<i64, truncstorei16, regPlusImm, STORE16_I64>; 284def : StorePatImmOff<i64, truncstorei32, regPlusImm, STORE32_I64>; 285def : StorePatImmOff<i32, truncstorei8, or_is_add, STORE8_I32>; 286def : StorePatImmOff<i32, truncstorei16, or_is_add, STORE16_I32>; 287def : StorePatImmOff<i64, truncstorei8, or_is_add, STORE8_I64>; 288def : StorePatImmOff<i64, truncstorei16, or_is_add, STORE16_I64>; 289def : StorePatImmOff<i64, truncstorei32, or_is_add, STORE32_I64>; 290 291// Select truncating stores with just a constant offset. 292def : StorePatOffsetOnly<i32, truncstorei8, STORE8_I32>; 293def : StorePatOffsetOnly<i32, truncstorei16, STORE16_I32>; 294def : StorePatOffsetOnly<i64, truncstorei8, STORE8_I64>; 295def : StorePatOffsetOnly<i64, truncstorei16, STORE16_I64>; 296def : StorePatOffsetOnly<i64, truncstorei32, STORE32_I64>; 297def : StorePatGlobalAddrOffOnly<i32, truncstorei8, STORE8_I32>; 298def : StorePatGlobalAddrOffOnly<i32, truncstorei16, STORE16_I32>; 299def : StorePatGlobalAddrOffOnly<i64, truncstorei8, STORE8_I64>; 300def : StorePatGlobalAddrOffOnly<i64, truncstorei16, STORE16_I64>; 301def : StorePatGlobalAddrOffOnly<i64, truncstorei32, STORE32_I64>; 302 303// Current memory size. 304defm MEMORY_SIZE_I32 : I<(outs I32:$dst), (ins i32imm:$flags), 305 (outs), (ins i32imm:$flags), 306 [(set I32:$dst, 307 (int_wasm_memory_size (i32 imm:$flags)))], 308 "memory.size\t$dst, $flags", "memory.size\t$flags", 309 0x3f>, 310 Requires<[HasAddr32]>; 311 312// Grow memory. 313defm MEMORY_GROW_I32 : I<(outs I32:$dst), (ins i32imm:$flags, I32:$delta), 314 (outs), (ins i32imm:$flags), 315 [(set I32:$dst, 316 (int_wasm_memory_grow (i32 imm:$flags), 317 I32:$delta))], 318 "memory.grow\t$dst, $flags, $delta", 319 "memory.grow\t$flags", 0x40>, 320 Requires<[HasAddr32]>; 321