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