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