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 "llvm/CodeGen/MachineFrameInfo.h"
19 #include "llvm/CodeGen/SelectionDAGISel.h"
20 #include "llvm/CodeGen/WasmEHFuncInfo.h"
21 #include "llvm/IR/DiagnosticInfo.h"
22 #include "llvm/IR/Function.h" // To access function attributes.
23 #include "llvm/IR/IntrinsicsWebAssembly.h"
24 #include "llvm/Support/Debug.h"
25 #include "llvm/Support/KnownBits.h"
26 #include "llvm/Support/MathExtras.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 using namespace llvm;
30 
31 #define DEBUG_TYPE "wasm-isel"
32 
33 //===--------------------------------------------------------------------===//
34 /// WebAssembly-specific code to select WebAssembly machine instructions for
35 /// SelectionDAG operations.
36 ///
37 namespace {
38 class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
39   /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
40   /// right decision when generating code for different targets.
41   const WebAssemblySubtarget *Subtarget;
42 
43 public:
44   WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
45                           CodeGenOpt::Level OptLevel)
46       : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {
47   }
48 
49   StringRef getPassName() const override {
50     return "WebAssembly Instruction Selection";
51   }
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);
61   }
62 
63   void PreprocessISelDAG() override;
64 
65   void Select(SDNode *Node) override;
66 
67   bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
68                                     std::vector<SDValue> &OutOps) override;
69   bool SelectExternRefAddr(const SDValue &Addr, const SDValue &Base);
70 
71 // Include the pieces autogenerated from the target description.
72 #include "WebAssemblyGenDAGISel.inc"
73 
74 private:
75   // add select functions here...
76 };
77 } // end anonymous namespace
78 
79 void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
80   // Stack objects that should be allocated to locals are hoisted to WebAssembly
81   // locals when they are first used.  However for those without uses, we hoist
82   // them here.  It would be nice if there were some hook to do this when they
83   // are added to the MachineFrameInfo, but that's not the case right now.
84   MachineFrameInfo &FrameInfo = MF->getFrameInfo();
85   for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
86     WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
87 
88   SelectionDAGISel::PreprocessISelDAG();
89 }
90 
91 static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
92   assert(Tag == WebAssembly::CPP_EXCEPTION);
93   auto &MF = DAG->getMachineFunction();
94   const auto &TLI = DAG->getTargetLoweringInfo();
95   MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
96   const char *SymName = MF.createExternalSymbolName("__cpp_exception");
97   return DAG->getTargetExternalSymbol(SymName, PtrVT);
98 }
99 
100 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
101   // If we have a custom node, we already have selected!
102   if (Node->isMachineOpcode()) {
103     LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
104     Node->setNodeId(-1);
105     return;
106   }
107 
108   MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
109   auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
110                                         : WebAssembly::GLOBAL_GET_I32;
111 
112   // Few custom selection stuff.
113   SDLoc DL(Node);
114   MachineFunction &MF = CurDAG->getMachineFunction();
115   switch (Node->getOpcode()) {
116   case ISD::ATOMIC_FENCE: {
117     if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
118       break;
119 
120     uint64_t SyncScopeID = Node->getConstantOperandVal(2);
121     MachineSDNode *Fence = nullptr;
122     switch (SyncScopeID) {
123     case SyncScope::SingleThread:
124       // We lower a single-thread fence to a pseudo compiler barrier instruction
125       // preventing instruction reordering. This will not be emitted in final
126       // binary.
127       Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
128                                      DL,                 // debug loc
129                                      MVT::Other,         // outchain type
130                                      Node->getOperand(0) // inchain
131       );
132       break;
133     case SyncScope::System:
134       // Currently wasm only supports sequentially consistent atomics, so we
135       // always set the order to 0 (sequentially consistent).
136       Fence = CurDAG->getMachineNode(
137           WebAssembly::ATOMIC_FENCE,
138           DL,                                         // debug loc
139           MVT::Other,                                 // outchain type
140           CurDAG->getTargetConstant(0, DL, MVT::i32), // order
141           Node->getOperand(0)                         // inchain
142       );
143       break;
144     default:
145       llvm_unreachable("Unknown scope!");
146     }
147 
148     ReplaceNode(Node, Fence);
149     CurDAG->RemoveDeadNode(Node);
150     return;
151   }
152 
153   case ISD::INTRINSIC_WO_CHAIN: {
154     unsigned IntNo = Node->getConstantOperandVal(0);
155     switch (IntNo) {
156     case Intrinsic::wasm_tls_size: {
157       MachineSDNode *TLSSize = CurDAG->getMachineNode(
158           GlobalGetIns, DL, PtrVT,
159           CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
160       ReplaceNode(Node, TLSSize);
161       return;
162     }
163 
164     case Intrinsic::wasm_tls_align: {
165       MachineSDNode *TLSAlign = CurDAG->getMachineNode(
166           GlobalGetIns, DL, PtrVT,
167           CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
168       ReplaceNode(Node, TLSAlign);
169       return;
170     }
171     }
172     break;
173   }
174 
175   case ISD::INTRINSIC_W_CHAIN: {
176     unsigned IntNo = Node->getConstantOperandVal(1);
177     const auto &TLI = CurDAG->getTargetLoweringInfo();
178     MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
179     switch (IntNo) {
180     case Intrinsic::wasm_tls_base: {
181       MachineSDNode *TLSBase = CurDAG->getMachineNode(
182           GlobalGetIns, DL, PtrVT, MVT::Other,
183           CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
184           Node->getOperand(0));
185       ReplaceNode(Node, TLSBase);
186       return;
187     }
188 
189     case Intrinsic::wasm_catch_exn: {
190       int Tag = Node->getConstantOperandVal(2);
191       SDValue SymNode = getTagSymNode(Tag, CurDAG);
192       MachineSDNode *Catch =
193           CurDAG->getMachineNode(WebAssembly::CATCH, DL,
194                                  {
195                                      PtrVT,     // exception pointer
196                                      MVT::Other // outchain type
197                                  },
198                                  {
199                                      SymNode,            // exception symbol
200                                      Node->getOperand(0) // inchain
201                                  });
202       ReplaceNode(Node, Catch);
203       return;
204     }
205     }
206     break;
207   }
208 
209   case ISD::INTRINSIC_VOID: {
210     unsigned IntNo = Node->getConstantOperandVal(1);
211     switch (IntNo) {
212     case Intrinsic::wasm_throw: {
213       int Tag = Node->getConstantOperandVal(2);
214       SDValue SymNode = getTagSymNode(Tag, CurDAG);
215       MachineSDNode *Throw =
216           CurDAG->getMachineNode(WebAssembly::THROW, DL,
217                                  MVT::Other, // outchain type
218                                  {
219                                      SymNode,             // exception symbol
220                                      Node->getOperand(3), // thrown value
221                                      Node->getOperand(0)  // inchain
222                                  });
223       ReplaceNode(Node, Throw);
224       return;
225     }
226     }
227     break;
228   }
229 
230   case WebAssemblyISD::CALL:
231   case WebAssemblyISD::RET_CALL: {
232     // CALL has both variable operands and variable results, but ISel only
233     // supports one or the other. Split calls into two nodes glued together, one
234     // for the operands and one for the results. These two nodes will be
235     // recombined in a custom inserter hook into a single MachineInstr.
236     SmallVector<SDValue, 16> Ops;
237     for (size_t i = 1; i < Node->getNumOperands(); ++i) {
238       SDValue Op = Node->getOperand(i);
239       if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper)
240         Op = Op->getOperand(0);
241       Ops.push_back(Op);
242     }
243 
244     // Add the chain last
245     Ops.push_back(Node->getOperand(0));
246     MachineSDNode *CallParams =
247         CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
248 
249     unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
250                            ? WebAssembly::CALL_RESULTS
251                            : WebAssembly::RET_CALL_RESULTS;
252 
253     SDValue Link(CallParams, 0);
254     MachineSDNode *CallResults =
255         CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
256     ReplaceNode(Node, CallResults);
257     return;
258   }
259 
260   default:
261     break;
262   }
263 
264   // Select the default instruction.
265   SelectCode(Node);
266 }
267 
268 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
269     const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
270   switch (ConstraintID) {
271   case InlineAsm::Constraint_m:
272     // We just support simple memory operands that just have a single address
273     // operand and need no special handling.
274     OutOps.push_back(Op);
275     return false;
276   default:
277     break;
278   }
279 
280   return true;
281 }
282 
283 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
284 /// for instruction scheduling.
285 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
286                                              CodeGenOpt::Level OptLevel) {
287   return new WebAssemblyDAGToDAGISel(TM, OptLevel);
288 }
289