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