1//- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//
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/// This file defines an instruction selector for the WebAssembly target.
11///
12//===----------------------------------------------------------------------===//
13
14#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
15#include "WebAssembly.h"
16#include "WebAssemblyISelLowering.h"
17#include "WebAssemblyTargetMachine.h"
18#include "WebAssemblyUtilities.h"
19#include "llvm/BinaryFormat/Wasm.h"
20#include "llvm/CodeGen/MachineFrameInfo.h"
21#include "llvm/CodeGen/SelectionDAGISel.h"
22#include "llvm/CodeGen/WasmEHFuncInfo.h"
23#include "llvm/IR/DiagnosticInfo.h"
24#include "llvm/IR/Function.h" // To access function attributes.
25#include "llvm/IR/IntrinsicsWebAssembly.h"
26#include "llvm/MC/MCSymbolWasm.h"
27#include "llvm/Support/Debug.h"
28#include "llvm/Support/KnownBits.h"
29#include "llvm/Support/raw_ostream.h"
30
31using namespace llvm;
32
33#define DEBUG_TYPE "wasm-isel"
34#define PASS_NAME "WebAssembly Instruction Selection"
35
36//===--------------------------------------------------------------------===//
37/// WebAssembly-specific code to select WebAssembly machine instructions for
38/// SelectionDAG operations.
39///
40namespace {
41class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
42 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
43 /// right decision when generating code for different targets.
44 const WebAssemblySubtarget *Subtarget;
45
46public:
47 WebAssemblyDAGToDAGISel() = delete;
48
49 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
50 CodeGenOptLevel OptLevel)
51 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {}
52
53 bool runOnMachineFunction(MachineFunction &MF) override {
54 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
55 "********** Function: "
56 << MF.getName() << '\n');
57
58 Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
59
60 return SelectionDAGISel::runOnMachineFunction(mf&: MF);
61 }
62
63 void PreprocessISelDAG() override;
64
65 void Select(SDNode *Node) override;
66
67 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
68 InlineAsm::ConstraintCode ConstraintID,
69 std::vector<SDValue> &OutOps) override;
70
71 bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);
72 bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);
73 bool SelectAtomicAddrOperands(SDNode *Op, SDValue N, SDValue &Offset,
74 SDValue &Addr, SDValue &Order, bool Is64);
75 bool SelectAtomicAddrOperands32(SDNode *Op, SDValue N, SDValue &Offset,
76 SDValue &Addr, SDValue &Order);
77 bool SelectAtomicAddrOperands64(SDNode *Op, SDValue N, SDValue &Offset,
78 SDValue &Addr, SDValue &Order);
79
80// Include the pieces autogenerated from the target description.
81#include "WebAssemblyGenDAGISel.inc"
82
83private:
84 // add select functions here...
85
86 bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op,
87 SDValue &Offset, SDValue &Addr);
88 bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset,
89 SDValue &Addr);
90};
91
92class WebAssemblyDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
93public:
94 static char ID;
95 explicit WebAssemblyDAGToDAGISelLegacy(WebAssemblyTargetMachine &TM,
96 CodeGenOptLevel OptLevel)
97 : SelectionDAGISelLegacy(
98 ID, std::make_unique<WebAssemblyDAGToDAGISel>(args&: TM, args&: OptLevel)) {}
99};
100} // end anonymous namespace
101
102char WebAssemblyDAGToDAGISelLegacy::ID;
103
104INITIALIZE_PASS(WebAssemblyDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false,
105 false)
106
107void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
108 // Stack objects that should be allocated to locals are hoisted to WebAssembly
109 // locals when they are first used. However for those without uses, we hoist
110 // them here. It would be nice if there were some hook to do this when they
111 // are added to the MachineFrameInfo, but that's not the case right now.
112 MachineFrameInfo &FrameInfo = MF->getFrameInfo();
113 for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
114 WebAssemblyFrameLowering::getLocalForStackObject(MF&: *MF, FrameIndex: Idx);
115
116 SelectionDAGISel::PreprocessISelDAG();
117}
118
119static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
120 assert(Tag == WebAssembly::CPP_EXCEPTION || Tag == WebAssembly::C_LONGJMP);
121 auto &MF = DAG->getMachineFunction();
122 const auto &TLI = DAG->getTargetLoweringInfo();
123 MVT PtrVT = TLI.getPointerTy(DL: DAG->getDataLayout());
124 const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
125 ? MF.createExternalSymbolName(Name: "__cpp_exception")
126 : MF.createExternalSymbolName(Name: "__c_longjmp");
127 return DAG->getTargetExternalSymbol(Sym: SymName, VT: PtrVT);
128}
129
130static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL,
131 SmallVector<MVT, 4> &Returns,
132 SmallVector<MVT, 4> &Params) {
133 auto toWasmValType = [](MVT VT) {
134 if (VT == MVT::i32) {
135 return wasm::ValType::I32;
136 }
137 if (VT == MVT::i64) {
138 return wasm::ValType::I64;
139 }
140 if (VT == MVT::f32) {
141 return wasm::ValType::F32;
142 }
143 if (VT == MVT::f64) {
144 return wasm::ValType::F64;
145 }
146 if (VT == MVT::externref) {
147 return wasm::ValType::EXTERNREF;
148 }
149 if (VT == MVT::funcref) {
150 return wasm::ValType::FUNCREF;
151 }
152 if (VT == MVT::exnref) {
153 return wasm::ValType::EXNREF;
154 }
155 LLVM_DEBUG(errs() << "Unhandled type for llvm.wasm.ref.test.func: " << VT
156 << "\n");
157 llvm_unreachable("Unhandled type for llvm.wasm.ref.test.func");
158 };
159 auto NParams = Params.size();
160 auto NReturns = Returns.size();
161 auto BitWidth = (NParams + NReturns + 2) * 64;
162 auto Sig = APInt(BitWidth, 0);
163
164 // Annoying special case: if getSignificantBits() <= 64 then InstrEmitter will
165 // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we
166 // always emit a CImm. So xor NParams with 0x7ffffff to ensure
167 // getSignificantBits() > 64
168 Sig |= NReturns ^ 0x7ffffff;
169 for (auto &Return : Returns) {
170 auto V = toWasmValType(Return);
171 Sig <<= 64;
172 Sig |= (int64_t)V;
173 }
174 Sig <<= 64;
175 Sig |= NParams;
176 for (auto &Param : Params) {
177 auto V = toWasmValType(Param);
178 Sig <<= 64;
179 Sig |= (int64_t)V;
180 }
181 return Sig;
182}
183
184static unsigned getWebAssemblyMemoryOrder(AtomicOrdering Ordering) {
185 unsigned OrderVal = wasm::WASM_MEM_ORDER_SEQ_CST;
186 switch (Ordering) {
187 case AtomicOrdering::Unordered:
188 case AtomicOrdering::Monotonic:
189 case AtomicOrdering::Acquire:
190 case AtomicOrdering::Release:
191 case AtomicOrdering::AcquireRelease:
192 OrderVal = wasm::WASM_MEM_ORDER_ACQ_REL;
193 break;
194 case AtomicOrdering::SequentiallyConsistent:
195 OrderVal = wasm::WASM_MEM_ORDER_SEQ_CST;
196 break;
197 default:
198 llvm_unreachable("Invalid atomic ordering");
199 }
200 return OrderVal;
201}
202
203void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
204 // If we have a custom node, we already have selected!
205 if (Node->isMachineOpcode()) {
206 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
207 Node->setNodeId(-1);
208 return;
209 }
210
211 MVT PtrVT = TLI->getPointerTy(DL: CurDAG->getDataLayout());
212 auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
213 : WebAssembly::GLOBAL_GET_I32;
214
215 SDLoc DL(Node);
216 MachineFunction &MF = CurDAG->getMachineFunction();
217 switch (Node->getOpcode()) {
218 case ISD::ATOMIC_FENCE: {
219 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
220 break;
221
222 uint64_t SyncScopeID = Node->getConstantOperandVal(Num: 2);
223 MachineSDNode *Fence = nullptr;
224 switch (SyncScopeID) {
225 case SyncScope::SingleThread:
226 // We lower a single-thread fence to a pseudo compiler barrier instruction
227 // preventing instruction reordering. This will not be emitted in final
228 // binary.
229 Fence = CurDAG->getMachineNode(Opcode: WebAssembly::COMPILER_FENCE,
230 dl: DL, // debug loc
231 VT: MVT::Other, // outchain type
232 Op1: Node->getOperand(Num: 0) // inchain
233 );
234 break;
235 case SyncScope::System: {
236 unsigned Order = wasm::WASM_MEM_ORDER_SEQ_CST;
237 if (MF.getSubtarget<WebAssemblySubtarget>().hasRelaxedAtomics()) {
238 auto Ordering =
239 static_cast<AtomicOrdering>(Node->getConstantOperandVal(Num: 1));
240 Order = getWebAssemblyMemoryOrder(Ordering);
241 }
242 Fence = CurDAG->getMachineNode(
243 Opcode: WebAssembly::ATOMIC_FENCE,
244 dl: DL, // debug loc
245 VT: MVT::Other, // outchain type
246 Op1: CurDAG->getTargetConstant(Val: Order, DL, VT: MVT::i32), // order
247 Op2: Node->getOperand(Num: 0) // inchain
248 );
249 break;
250 }
251 default:
252 llvm_unreachable("Unknown scope!");
253 }
254
255 ReplaceNode(F: Node, T: Fence);
256 CurDAG->RemoveDeadNode(N: Node);
257 return;
258 }
259
260 case ISD::INTRINSIC_WO_CHAIN: {
261 unsigned IntNo = Node->getConstantOperandVal(Num: 0);
262 switch (IntNo) {
263 case Intrinsic::wasm_tls_size: {
264 MachineSDNode *TLSSize = CurDAG->getMachineNode(
265 Opcode: GlobalGetIns, dl: DL, VT: PtrVT,
266 Op1: CurDAG->getTargetExternalSymbol(Sym: "__tls_size", VT: PtrVT));
267 ReplaceNode(F: Node, T: TLSSize);
268 return;
269 }
270
271 case Intrinsic::wasm_tls_align: {
272 MachineSDNode *TLSAlign = CurDAG->getMachineNode(
273 Opcode: GlobalGetIns, dl: DL, VT: PtrVT,
274 Op1: CurDAG->getTargetExternalSymbol(Sym: "__tls_align", VT: PtrVT));
275 ReplaceNode(F: Node, T: TLSAlign);
276 return;
277 }
278 case Intrinsic::wasm_ref_test_func: {
279 // First emit the TABLE_GET instruction to convert function pointer ==>
280 // funcref
281 MachineFunction &MF = CurDAG->getMachineFunction();
282 auto PtrVT = MVT::getIntegerVT(BitWidth: MF.getDataLayout().getPointerSizeInBits());
283 MCSymbol *Table = WebAssembly::getOrCreateFunctionTableSymbol(
284 Ctx&: MF.getContext(), Subtarget);
285 SDValue TableSym = CurDAG->getMCSymbol(Sym: Table, VT: PtrVT);
286 SDValue FuncPtr = Node->getOperand(Num: 1);
287 if (Subtarget->hasAddr64() && FuncPtr.getValueType() == MVT::i64) {
288 // table.get expects an i32 but on 64 bit platforms the function pointer
289 // is an i64. In that case, i32.wrap_i64 to convert.
290 FuncPtr = SDValue(CurDAG->getMachineNode(Opcode: WebAssembly::I32_WRAP_I64, dl: DL,
291 VT: MVT::i32, Op1: FuncPtr),
292 0);
293 }
294 SDValue FuncRef =
295 SDValue(CurDAG->getMachineNode(Opcode: WebAssembly::TABLE_GET_FUNCREF, dl: DL,
296 VT: MVT::funcref, Op1: TableSym, Op2: FuncPtr),
297 0);
298
299 // Encode the signature information into the type index placeholder.
300 // This gets decoded and converted into the actual type signature in
301 // WebAssemblyMCInstLower.cpp.
302 SmallVector<MVT, 4> Params;
303 SmallVector<MVT, 4> Returns;
304
305 bool IsParam = false;
306 // Operand 0 is the return register, Operand 1 is the function pointer.
307 // The remaining operands encode the type of the function we are testing
308 // for.
309 for (unsigned I = 2, E = Node->getNumOperands(); I < E; ++I) {
310 MVT VT = Node->getOperand(Num: I).getValueType().getSimpleVT();
311 if (VT == MVT::Untyped) {
312 IsParam = true;
313 continue;
314 }
315 if (IsParam) {
316 Params.push_back(Elt: VT);
317 } else {
318 Returns.push_back(Elt: VT);
319 }
320 }
321 auto Sig = encodeFunctionSignature(DAG: CurDAG, DL, Returns, Params);
322
323 auto SigOp = CurDAG->getTargetConstant(
324 Val: Sig, DL, VT: EVT::getIntegerVT(Context&: *CurDAG->getContext(), BitWidth: Sig.getBitWidth()));
325 MachineSDNode *RefTestNode = CurDAG->getMachineNode(
326 Opcode: WebAssembly::REF_TEST_FUNCREF, dl: DL, VT: MVT::i32, Ops: {SigOp, FuncRef});
327 ReplaceNode(F: Node, T: RefTestNode);
328 return;
329 }
330 }
331 break;
332 }
333
334 case ISD::INTRINSIC_W_CHAIN: {
335 unsigned IntNo = Node->getConstantOperandVal(Num: 1);
336 const auto &TLI = CurDAG->getTargetLoweringInfo();
337 MVT PtrVT = TLI.getPointerTy(DL: CurDAG->getDataLayout());
338 switch (IntNo) {
339 case Intrinsic::wasm_tls_base: {
340 MachineSDNode *TLSBase = CurDAG->getMachineNode(
341 Opcode: GlobalGetIns, dl: DL, VT1: PtrVT, VT2: MVT::Other,
342 Op1: CurDAG->getTargetExternalSymbol(Sym: "__tls_base", VT: PtrVT),
343 Op2: Node->getOperand(Num: 0));
344 ReplaceNode(F: Node, T: TLSBase);
345 return;
346 }
347
348 case Intrinsic::wasm_catch: {
349 int Tag = Node->getConstantOperandVal(Num: 2);
350 SDValue SymNode = getTagSymNode(Tag, DAG: CurDAG);
351 unsigned CatchOpcode = WebAssembly::WasmUseLegacyEH
352 ? WebAssembly::CATCH_LEGACY
353 : WebAssembly::CATCH;
354 MachineSDNode *Catch =
355 CurDAG->getMachineNode(Opcode: CatchOpcode, dl: DL,
356 ResultTys: {
357 PtrVT, // exception pointer
358 MVT::Other // outchain type
359 },
360 Ops: {
361 SymNode, // exception symbol
362 Node->getOperand(Num: 0) // inchain
363 });
364 ReplaceNode(F: Node, T: Catch);
365 return;
366 }
367 }
368 break;
369 }
370
371 case ISD::INTRINSIC_VOID: {
372 unsigned IntNo = Node->getConstantOperandVal(Num: 1);
373 switch (IntNo) {
374 case Intrinsic::wasm_throw: {
375 int Tag = Node->getConstantOperandVal(Num: 2);
376 SDValue SymNode = getTagSymNode(Tag, DAG: CurDAG);
377 MachineSDNode *Throw =
378 CurDAG->getMachineNode(Opcode: WebAssembly::THROW, dl: DL,
379 VT: MVT::Other, // outchain type
380 Ops: {
381 SymNode, // exception symbol
382 Node->getOperand(Num: 3), // thrown value
383 Node->getOperand(Num: 0) // inchain
384 });
385 ReplaceNode(F: Node, T: Throw);
386 return;
387 }
388 case Intrinsic::wasm_rethrow: {
389 // RETHROW's BB argument will be populated in LateEHPrepare. Just use a
390 // '0' as a placeholder for now.
391 MachineSDNode *Rethrow = CurDAG->getMachineNode(
392 Opcode: WebAssembly::RETHROW, dl: DL,
393 VT: MVT::Other, // outchain type
394 Ops: {
395 CurDAG->getConstant(Val: 0, DL, VT: MVT::i32), // placeholder
396 Node->getOperand(Num: 0) // inchain
397 });
398 ReplaceNode(F: Node, T: Rethrow);
399 return;
400 }
401 }
402 break;
403 }
404
405 case WebAssemblyISD::CALL:
406 case WebAssemblyISD::RET_CALL: {
407 // CALL has both variable operands and variable results, but ISel only
408 // supports one or the other. Split calls into two nodes glued together, one
409 // for the operands and one for the results. These two nodes will be
410 // recombined in a custom inserter hook into a single MachineInstr.
411 SmallVector<SDValue, 16> Ops;
412 for (size_t i = 1; i < Node->getNumOperands(); ++i) {
413 SDValue Op = Node->getOperand(Num: i);
414 // Remove the wrapper when the call target is a function, an external
415 // symbol (which will be lowered to a library function), or an alias of
416 // a function. If the target is not a function/external symbol, we
417 // shouldn't remove the wrapper, because we cannot call it directly and
418 // instead we want it to be loaded with a CONST instruction and called
419 // with a call_indirect later.
420 if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) {
421 SDValue NewOp = Op->getOperand(Num: 0);
422 if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(Val: NewOp.getNode())) {
423 if (isa<Function>(
424 Val: GlobalOp->getGlobal()->stripPointerCastsAndAliases()))
425 Op = NewOp;
426 } else if (isa<ExternalSymbolSDNode>(Val: NewOp.getNode())) {
427 Op = NewOp;
428 }
429 }
430 Ops.push_back(Elt: Op);
431 }
432
433 // Add the chain last
434 Ops.push_back(Elt: Node->getOperand(Num: 0));
435 MachineSDNode *CallParams =
436 CurDAG->getMachineNode(Opcode: WebAssembly::CALL_PARAMS, dl: DL, VT: MVT::Glue, Ops);
437
438 unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
439 ? WebAssembly::CALL_RESULTS
440 : WebAssembly::RET_CALL_RESULTS;
441
442 SDValue Link(CallParams, 0);
443 MachineSDNode *CallResults =
444 CurDAG->getMachineNode(Opcode: Results, dl: DL, VTs: Node->getVTList(), Ops: Link);
445 ReplaceNode(F: Node, T: CallResults);
446 return;
447 }
448
449 default:
450 break;
451 }
452
453 // Select the default instruction.
454 SelectCode(N: Node);
455}
456
457bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
458 const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
459 std::vector<SDValue> &OutOps) {
460 switch (ConstraintID) {
461 case InlineAsm::ConstraintCode::m:
462 // We just support simple memory operands that just have a single address
463 // operand and need no special handling.
464 OutOps.push_back(x: Op);
465 return false;
466 default:
467 break;
468 }
469
470 return true;
471}
472
473bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N,
474 SDValue &Offset,
475 SDValue &Addr) {
476 assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op");
477
478 // WebAssembly constant offsets are performed as unsigned with infinite
479 // precision, so we need to check for NoUnsignedWrap so that we don't fold an
480 // offset for an add that needs wrapping.
481 if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap())
482 return false;
483
484 for (size_t i = 0; i < 2; ++i) {
485 SDValue Op = N.getOperand(i);
486 SDValue OtherOp = N.getOperand(i: i == 0 ? 1 : 0);
487
488 // Folds constants in an add into the offset.
489 if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val&: Op)) {
490 Offset =
491 CurDAG->getTargetConstant(Val: CN->getZExtValue(), DL: SDLoc(N), VT: OffsetType);
492 Addr = OtherOp;
493 return true;
494 }
495
496 // Fold target global addresses into the offset.
497 if (!TM.isPositionIndependent()) {
498 if (Op.getOpcode() == WebAssemblyISD::Wrapper)
499 Op = Op.getOperand(i: 0);
500
501 if (Op.getOpcode() == ISD::TargetGlobalAddress) {
502 Addr = OtherOp;
503 Offset = Op;
504 return true;
505 }
506 }
507 }
508 return false;
509}
510
511bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType,
512 unsigned ConstOpc, SDValue N,
513 SDValue &Offset,
514 SDValue &Addr) {
515 SDLoc DL(N);
516
517 // Fold target global addresses into the offset.
518 if (!TM.isPositionIndependent()) {
519 SDValue Op(N);
520 if (Op.getOpcode() == WebAssemblyISD::Wrapper)
521 Op = Op.getOperand(i: 0);
522
523 if (Op.getOpcode() == ISD::TargetGlobalAddress) {
524 Offset = Op;
525 Addr = SDValue(
526 CurDAG->getMachineNode(Opcode: ConstOpc, dl: DL, VT: AddrType,
527 Op1: CurDAG->getTargetConstant(Val: 0, DL, VT: AddrType)),
528 0);
529 return true;
530 }
531 }
532
533 // Fold anything inside an add into the offset.
534 if (N.getOpcode() == ISD::ADD &&
535 SelectAddrAddOperands(OffsetType: AddrType, N, Offset, Addr))
536 return true;
537
538 // Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be
539 // zero and fold them into the offset too.
540 if (N.getOpcode() == ISD::OR) {
541 bool OrIsAdd;
542 if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val: N.getOperand(i: 1))) {
543 OrIsAdd =
544 CurDAG->MaskedValueIsZero(Op: N->getOperand(Num: 0), Mask: CN->getAPIntValue());
545 } else {
546 KnownBits Known0 = CurDAG->computeKnownBits(Op: N->getOperand(Num: 0), Depth: 0);
547 KnownBits Known1 = CurDAG->computeKnownBits(Op: N->getOperand(Num: 1), Depth: 0);
548 OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0;
549 }
550
551 if (OrIsAdd && SelectAddrAddOperands(OffsetType: AddrType, N, Offset, Addr))
552 return true;
553 }
554
555 // Fold constant addresses into the offset.
556 if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val&: N)) {
557 Offset = CurDAG->getTargetConstant(Val: CN->getZExtValue(), DL, VT: AddrType);
558 Addr = SDValue(
559 CurDAG->getMachineNode(Opcode: ConstOpc, dl: DL, VT: AddrType,
560 Op1: CurDAG->getTargetConstant(Val: 0, DL, VT: AddrType)),
561 0);
562 return true;
563 }
564
565 // Else it's a plain old load/store with no offset.
566 Offset = CurDAG->getTargetConstant(Val: 0, DL, VT: AddrType);
567 Addr = N;
568 return true;
569}
570
571bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset,
572 SDValue &Addr) {
573 return SelectAddrOperands(AddrType: MVT::i32, ConstOpc: WebAssembly::CONST_I32, N: Op, Offset, Addr);
574}
575
576bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,
577 SDValue &Addr) {
578 return SelectAddrOperands(AddrType: MVT::i64, ConstOpc: WebAssembly::CONST_I64, N: Op, Offset, Addr);
579}
580
581static MemSDNode *findMemSDNode(SDNode *N) {
582 while (N) {
583 if (auto *MemNode = dyn_cast<MemSDNode>(Val: N))
584 return MemNode;
585 switch (N->getOpcode()) {
586 case ISD::ZERO_EXTEND:
587 case ISD::SIGN_EXTEND:
588 case ISD::ANY_EXTEND:
589 case ISD::SIGN_EXTEND_INREG:
590 case ISD::AssertZext:
591 case ISD::AssertSext:
592 case ISD::TRUNCATE:
593 case ISD::BITCAST:
594 case ISD::AND:
595 N = N->getOperand(Num: 0).getNode();
596 break;
597 default:
598 return nullptr;
599 }
600 }
601 return nullptr;
602}
603
604bool WebAssemblyDAGToDAGISel::SelectAtomicAddrOperands(SDNode *Op, SDValue N,
605 SDValue &Offset,
606 SDValue &Addr,
607 SDValue &Order,
608 bool Is64) {
609 auto *MemNode = findMemSDNode(N: Op);
610 if (!MemNode)
611 return false;
612
613 bool Match = Is64 ? SelectAddrOperands64(Op: N, Offset, Addr)
614 : SelectAddrOperands32(Op: N, Offset, Addr);
615 if (!Match)
616 return false;
617
618 auto Ordering = MemNode->getMergedOrdering();
619 unsigned OrderVal = wasm::WASM_MEM_ORDER_SEQ_CST;
620 if (Subtarget->hasRelaxedAtomics())
621 OrderVal = getWebAssemblyMemoryOrder(Ordering);
622 Order = CurDAG->getTargetConstant(Val: OrderVal, DL: SDLoc(Op), VT: MVT::i32);
623 return true;
624}
625
626bool WebAssemblyDAGToDAGISel::SelectAtomicAddrOperands32(SDNode *Op, SDValue N,
627 SDValue &Offset,
628 SDValue &Addr,
629 SDValue &Order) {
630 return SelectAtomicAddrOperands(Op, N, Offset, Addr, Order, /*Is64=*/false);
631}
632
633bool WebAssemblyDAGToDAGISel::SelectAtomicAddrOperands64(SDNode *Op, SDValue N,
634 SDValue &Offset,
635 SDValue &Addr,
636 SDValue &Order) {
637 return SelectAtomicAddrOperands(Op, N, Offset, Addr, Order, /*Is64=*/true);
638}
639
640/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
641/// for instruction scheduling.
642FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
643 CodeGenOptLevel OptLevel) {
644 return new WebAssemblyDAGToDAGISelLegacy(TM, OptLevel);
645}
646