WebAssemblyISelDAGToDAG.cpp revision 353358
1285163Sdim//- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//
2285163Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6285163Sdim//
7285163Sdim//===----------------------------------------------------------------------===//
8285163Sdim///
9285163Sdim/// \file
10341825Sdim/// This file defines an instruction selector for the WebAssembly target.
11285163Sdim///
12285163Sdim//===----------------------------------------------------------------------===//
13285163Sdim
14321369Sdim#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
15285163Sdim#include "WebAssembly.h"
16285163Sdim#include "WebAssemblyTargetMachine.h"
17285163Sdim#include "llvm/CodeGen/SelectionDAGISel.h"
18353358Sdim#include "llvm/IR/DiagnosticInfo.h"
19285163Sdim#include "llvm/IR/Function.h" // To access function attributes.
20285163Sdim#include "llvm/Support/Debug.h"
21321369Sdim#include "llvm/Support/KnownBits.h"
22285163Sdim#include "llvm/Support/MathExtras.h"
23285163Sdim#include "llvm/Support/raw_ostream.h"
24285163Sdimusing namespace llvm;
25285163Sdim
26285163Sdim#define DEBUG_TYPE "wasm-isel"
27285163Sdim
28285163Sdim//===--------------------------------------------------------------------===//
29285163Sdim/// WebAssembly-specific code to select WebAssembly machine instructions for
30285163Sdim/// SelectionDAG operations.
31285163Sdim///
32285163Sdimnamespace {
33285163Sdimclass WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
34285163Sdim  /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
35285163Sdim  /// right decision when generating code for different targets.
36285163Sdim  const WebAssemblySubtarget *Subtarget;
37285163Sdim
38285163Sdim  bool ForCodeSize;
39285163Sdim
40285163Sdimpublic:
41353358Sdim  WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
42285163Sdim                          CodeGenOpt::Level OptLevel)
43353358Sdim      : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr), ForCodeSize(false) {
44285163Sdim  }
45285163Sdim
46314564Sdim  StringRef getPassName() const override {
47285163Sdim    return "WebAssembly Instruction Selection";
48285163Sdim  }
49285163Sdim
50285163Sdim  bool runOnMachineFunction(MachineFunction &MF) override {
51344779Sdim    LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
52344779Sdim                         "********** Function: "
53344779Sdim                      << MF.getName() << '\n');
54344779Sdim
55353358Sdim    ForCodeSize = MF.getFunction().hasOptSize();
56285163Sdim    Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
57285163Sdim    return SelectionDAGISel::runOnMachineFunction(MF);
58285163Sdim  }
59285163Sdim
60309124Sdim  void Select(SDNode *Node) override;
61285163Sdim
62296417Sdim  bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
63296417Sdim                                    std::vector<SDValue> &OutOps) override;
64296417Sdim
65296417Sdim// Include the pieces autogenerated from the target description.
66296417Sdim#include "WebAssemblyGenDAGISel.inc"
67296417Sdim
68285163Sdimprivate:
69285163Sdim  // add select functions here...
70285163Sdim};
71285163Sdim} // end anonymous namespace
72285163Sdim
73309124Sdimvoid WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
74296417Sdim  // If we have a custom node, we already have selected!
75296417Sdim  if (Node->isMachineOpcode()) {
76341825Sdim    LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
77296417Sdim    Node->setNodeId(-1);
78309124Sdim    return;
79296417Sdim  }
80296417Sdim
81353358Sdim  // Few custom selection stuff.
82353358Sdim  SDLoc DL(Node);
83353358Sdim  MachineFunction &MF = CurDAG->getMachineFunction();
84296417Sdim  switch (Node->getOpcode()) {
85353358Sdim  case ISD::ATOMIC_FENCE: {
86353358Sdim    if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
87353358Sdim      break;
88353358Sdim
89353358Sdim    uint64_t SyncScopeID =
90353358Sdim        cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue();
91353358Sdim    switch (SyncScopeID) {
92353358Sdim    case SyncScope::SingleThread: {
93353358Sdim      // We lower a single-thread fence to a pseudo compiler barrier instruction
94353358Sdim      // preventing instruction reordering. This will not be emitted in final
95353358Sdim      // binary.
96353358Sdim      MachineSDNode *Fence =
97353358Sdim          CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
98353358Sdim                                 DL,                 // debug loc
99353358Sdim                                 MVT::Other,         // outchain type
100353358Sdim                                 Node->getOperand(0) // inchain
101353358Sdim          );
102353358Sdim      ReplaceNode(Node, Fence);
103353358Sdim      CurDAG->RemoveDeadNode(Node);
104353358Sdim      return;
105353358Sdim    }
106353358Sdim
107353358Sdim    case SyncScope::System: {
108353358Sdim      // For non-emscripten systems, we have not decided on what we should
109353358Sdim      // traslate fences to yet.
110353358Sdim      if (!Subtarget->getTargetTriple().isOSEmscripten())
111353358Sdim        report_fatal_error(
112353358Sdim            "ATOMIC_FENCE is not yet supported in non-emscripten OSes");
113353358Sdim
114353358Sdim      // Wasm does not have a fence instruction, but because all atomic
115353358Sdim      // instructions in wasm are sequentially consistent, we translate a
116353358Sdim      // fence to an idempotent atomic RMW instruction to a linear memory
117353358Sdim      // address. All atomic instructions in wasm are sequentially consistent,
118353358Sdim      // but this is to ensure a fence also prevents reordering of non-atomic
119353358Sdim      // instructions in the VM. Even though LLVM IR's fence instruction does
120353358Sdim      // not say anything about its relationship with non-atomic instructions,
121353358Sdim      // we think this is more user-friendly.
122353358Sdim      //
123353358Sdim      // While any address can work, here we use a value stored in
124353358Sdim      // __stack_pointer wasm global because there's high chance that area is
125353358Sdim      // in cache.
126353358Sdim      //
127353358Sdim      // So the selected instructions will be in the form of:
128353358Sdim      //   %addr = get_global $__stack_pointer
129353358Sdim      //   %0 = i32.const 0
130353358Sdim      //   i32.atomic.rmw.or %addr, %0
131353358Sdim      SDValue StackPtrSym = CurDAG->getTargetExternalSymbol(
132353358Sdim          "__stack_pointer", TLI->getPointerTy(CurDAG->getDataLayout()));
133353358Sdim      MachineSDNode *GetGlobal =
134353358Sdim          CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, // opcode
135353358Sdim                                 DL,                          // debug loc
136353358Sdim                                 MVT::i32,                    // result type
137353358Sdim                                 StackPtrSym // __stack_pointer symbol
138353358Sdim          );
139353358Sdim
140353358Sdim      SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32);
141353358Sdim      auto *MMO = MF.getMachineMemOperand(
142353358Sdim          MachinePointerInfo::getUnknownStack(MF),
143353358Sdim          // FIXME Volatile isn't really correct, but currently all LLVM
144353358Sdim          // atomic instructions are treated as volatiles in the backend, so
145353358Sdim          // we should be consistent.
146353358Sdim          MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad |
147353358Sdim              MachineMemOperand::MOStore,
148353358Sdim          4, 4, AAMDNodes(), nullptr, SyncScope::System,
149353358Sdim          AtomicOrdering::SequentiallyConsistent);
150353358Sdim      MachineSDNode *Const0 =
151353358Sdim          CurDAG->getMachineNode(WebAssembly::CONST_I32, DL, MVT::i32, Zero);
152353358Sdim      MachineSDNode *AtomicRMW = CurDAG->getMachineNode(
153353358Sdim          WebAssembly::ATOMIC_RMW_OR_I32, // opcode
154353358Sdim          DL,                             // debug loc
155353358Sdim          MVT::i32,                       // result type
156353358Sdim          MVT::Other,                     // outchain type
157353358Sdim          {
158353358Sdim              Zero,                  // alignment
159353358Sdim              Zero,                  // offset
160353358Sdim              SDValue(GetGlobal, 0), // __stack_pointer
161353358Sdim              SDValue(Const0, 0),    // OR with 0 to make it idempotent
162353358Sdim              Node->getOperand(0)    // inchain
163353358Sdim          });
164353358Sdim
165353358Sdim      CurDAG->setNodeMemRefs(AtomicRMW, {MMO});
166353358Sdim      ReplaceUses(SDValue(Node, 0), SDValue(AtomicRMW, 1));
167353358Sdim      CurDAG->RemoveDeadNode(Node);
168353358Sdim      return;
169353358Sdim    }
170353358Sdim    default:
171353358Sdim      llvm_unreachable("Unknown scope!");
172353358Sdim    }
173353358Sdim  }
174353358Sdim
175353358Sdim  case ISD::GlobalTLSAddress: {
176353358Sdim    const auto *GA = cast<GlobalAddressSDNode>(Node);
177353358Sdim
178353358Sdim    if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
179353358Sdim      report_fatal_error("cannot use thread-local storage without bulk memory",
180353358Sdim                         false);
181353358Sdim
182353358Sdim    // Currently Emscripten does not support dynamic linking with threads.
183353358Sdim    // Therefore, if we have thread-local storage, only the local-exec model
184353358Sdim    // is possible.
185353358Sdim    // TODO: remove this and implement proper TLS models once Emscripten
186353358Sdim    // supports dynamic linking with threads.
187353358Sdim    if (GA->getGlobal()->getThreadLocalMode() !=
188353358Sdim            GlobalValue::LocalExecTLSModel &&
189353358Sdim        !Subtarget->getTargetTriple().isOSEmscripten()) {
190353358Sdim      report_fatal_error("only -ftls-model=local-exec is supported for now on "
191353358Sdim                         "non-Emscripten OSes: variable " +
192353358Sdim                             GA->getGlobal()->getName(),
193353358Sdim                         false);
194353358Sdim    }
195353358Sdim
196353358Sdim    MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
197353358Sdim    assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
198353358Sdim
199353358Sdim    SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT);
200353358Sdim    SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress(
201353358Sdim        GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0);
202353358Sdim
203353358Sdim    MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32,
204353358Sdim                                                    DL, MVT::i32, TLSBaseSym);
205353358Sdim    MachineSDNode *TLSOffset = CurDAG->getMachineNode(
206353358Sdim        WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym);
207353358Sdim    MachineSDNode *TLSAddress =
208353358Sdim        CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32,
209353358Sdim                               SDValue(TLSBase, 0), SDValue(TLSOffset, 0));
210353358Sdim    ReplaceNode(Node, TLSAddress);
211353358Sdim    return;
212353358Sdim  }
213353358Sdim
214353358Sdim  case ISD::INTRINSIC_WO_CHAIN: {
215353358Sdim    unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue();
216353358Sdim    switch (IntNo) {
217353358Sdim    case Intrinsic::wasm_tls_size: {
218353358Sdim      MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
219353358Sdim      assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
220353358Sdim
221353358Sdim      MachineSDNode *TLSSize = CurDAG->getMachineNode(
222353358Sdim          WebAssembly::GLOBAL_GET_I32, DL, PtrVT,
223353358Sdim          CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32));
224353358Sdim      ReplaceNode(Node, TLSSize);
225353358Sdim      return;
226353358Sdim    }
227353358Sdim    }
228353358Sdim    break;
229353358Sdim  }
230353358Sdim
231296417Sdim  default:
232296417Sdim    break;
233296417Sdim  }
234296417Sdim
235296417Sdim  // Select the default instruction.
236309124Sdim  SelectCode(Node);
237285163Sdim}
238285163Sdim
239296417Sdimbool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
240296417Sdim    const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
241296417Sdim  switch (ConstraintID) {
242296417Sdim  case InlineAsm::Constraint_i:
243296417Sdim  case InlineAsm::Constraint_m:
244296417Sdim    // We just support simple memory operands that just have a single address
245296417Sdim    // operand and need no special handling.
246296417Sdim    OutOps.push_back(Op);
247296417Sdim    return false;
248296417Sdim  default:
249296417Sdim    break;
250296417Sdim  }
251296417Sdim
252296417Sdim  return true;
253296417Sdim}
254296417Sdim
255285163Sdim/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
256285163Sdim/// for instruction scheduling.
257285163SdimFunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
258285163Sdim                                             CodeGenOpt::Level OptLevel) {
259285163Sdim  return new WebAssemblyDAGToDAGISel(TM, OptLevel);
260285163Sdim}
261