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 || WebAssembly::C_LONGJMP);
93   auto &MF = DAG->getMachineFunction();
94   const auto &TLI = DAG->getTargetLoweringInfo();
95   MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
96   const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
97                             ? MF.createExternalSymbolName("__cpp_exception")
98                             : MF.createExternalSymbolName("__c_longjmp");
99   return DAG->getTargetExternalSymbol(SymName, PtrVT);
100 }
101 
102 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
103   // If we have a custom node, we already have selected!
104   if (Node->isMachineOpcode()) {
105     LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
106     Node->setNodeId(-1);
107     return;
108   }
109 
110   MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
111   auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
112                                         : WebAssembly::GLOBAL_GET_I32;
113 
114   // Few custom selection stuff.
115   SDLoc DL(Node);
116   MachineFunction &MF = CurDAG->getMachineFunction();
117   switch (Node->getOpcode()) {
118   case ISD::ATOMIC_FENCE: {
119     if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
120       break;
121 
122     uint64_t SyncScopeID = Node->getConstantOperandVal(2);
123     MachineSDNode *Fence = nullptr;
124     switch (SyncScopeID) {
125     case SyncScope::SingleThread:
126       // We lower a single-thread fence to a pseudo compiler barrier instruction
127       // preventing instruction reordering. This will not be emitted in final
128       // binary.
129       Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
130                                      DL,                 // debug loc
131                                      MVT::Other,         // outchain type
132                                      Node->getOperand(0) // inchain
133       );
134       break;
135     case SyncScope::System:
136       // Currently wasm only supports sequentially consistent atomics, so we
137       // always set the order to 0 (sequentially consistent).
138       Fence = CurDAG->getMachineNode(
139           WebAssembly::ATOMIC_FENCE,
140           DL,                                         // debug loc
141           MVT::Other,                                 // outchain type
142           CurDAG->getTargetConstant(0, DL, MVT::i32), // order
143           Node->getOperand(0)                         // inchain
144       );
145       break;
146     default:
147       llvm_unreachable("Unknown scope!");
148     }
149 
150     ReplaceNode(Node, Fence);
151     CurDAG->RemoveDeadNode(Node);
152     return;
153   }
154 
155   case ISD::INTRINSIC_WO_CHAIN: {
156     unsigned IntNo = Node->getConstantOperandVal(0);
157     switch (IntNo) {
158     case Intrinsic::wasm_tls_size: {
159       MachineSDNode *TLSSize = CurDAG->getMachineNode(
160           GlobalGetIns, DL, PtrVT,
161           CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
162       ReplaceNode(Node, TLSSize);
163       return;
164     }
165 
166     case Intrinsic::wasm_tls_align: {
167       MachineSDNode *TLSAlign = CurDAG->getMachineNode(
168           GlobalGetIns, DL, PtrVT,
169           CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
170       ReplaceNode(Node, TLSAlign);
171       return;
172     }
173     }
174     break;
175   }
176 
177   case ISD::INTRINSIC_W_CHAIN: {
178     unsigned IntNo = Node->getConstantOperandVal(1);
179     const auto &TLI = CurDAG->getTargetLoweringInfo();
180     MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
181     switch (IntNo) {
182     case Intrinsic::wasm_tls_base: {
183       MachineSDNode *TLSBase = CurDAG->getMachineNode(
184           GlobalGetIns, DL, PtrVT, MVT::Other,
185           CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
186           Node->getOperand(0));
187       ReplaceNode(Node, TLSBase);
188       return;
189     }
190 
191     case Intrinsic::wasm_catch: {
192       int Tag = Node->getConstantOperandVal(2);
193       SDValue SymNode = getTagSymNode(Tag, CurDAG);
194       MachineSDNode *Catch =
195           CurDAG->getMachineNode(WebAssembly::CATCH, DL,
196                                  {
197                                      PtrVT,     // exception pointer
198                                      MVT::Other // outchain type
199                                  },
200                                  {
201                                      SymNode,            // exception symbol
202                                      Node->getOperand(0) // inchain
203                                  });
204       ReplaceNode(Node, Catch);
205       return;
206     }
207     }
208     break;
209   }
210 
211   case ISD::INTRINSIC_VOID: {
212     unsigned IntNo = Node->getConstantOperandVal(1);
213     switch (IntNo) {
214     case Intrinsic::wasm_throw: {
215       int Tag = Node->getConstantOperandVal(2);
216       SDValue SymNode = getTagSymNode(Tag, CurDAG);
217       MachineSDNode *Throw =
218           CurDAG->getMachineNode(WebAssembly::THROW, DL,
219                                  MVT::Other, // outchain type
220                                  {
221                                      SymNode,             // exception symbol
222                                      Node->getOperand(3), // thrown value
223                                      Node->getOperand(0)  // inchain
224                                  });
225       ReplaceNode(Node, Throw);
226       return;
227     }
228     }
229     break;
230   }
231 
232   case WebAssemblyISD::CALL:
233   case WebAssemblyISD::RET_CALL: {
234     // CALL has both variable operands and variable results, but ISel only
235     // supports one or the other. Split calls into two nodes glued together, one
236     // for the operands and one for the results. These two nodes will be
237     // recombined in a custom inserter hook into a single MachineInstr.
238     SmallVector<SDValue, 16> Ops;
239     for (size_t i = 1; i < Node->getNumOperands(); ++i) {
240       SDValue Op = Node->getOperand(i);
241       if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper)
242         Op = Op->getOperand(0);
243       Ops.push_back(Op);
244     }
245 
246     // Add the chain last
247     Ops.push_back(Node->getOperand(0));
248     MachineSDNode *CallParams =
249         CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
250 
251     unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
252                            ? WebAssembly::CALL_RESULTS
253                            : WebAssembly::RET_CALL_RESULTS;
254 
255     SDValue Link(CallParams, 0);
256     MachineSDNode *CallResults =
257         CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
258     ReplaceNode(Node, CallResults);
259     return;
260   }
261 
262   default:
263     break;
264   }
265 
266   // Select the default instruction.
267   SelectCode(Node);
268 }
269 
270 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
271     const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
272   switch (ConstraintID) {
273   case InlineAsm::Constraint_m:
274     // We just support simple memory operands that just have a single address
275     // operand and need no special handling.
276     OutOps.push_back(Op);
277     return false;
278   default:
279     break;
280   }
281 
282   return true;
283 }
284 
285 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
286 /// for instruction scheduling.
287 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
288                                              CodeGenOpt::Level OptLevel) {
289   return new WebAssemblyDAGToDAGISel(TM, OptLevel);
290 }
291