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:
WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine & TM,CodeGenOpt::Level OptLevel)42 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
43 CodeGenOpt::Level OptLevel)
44 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {
45 }
46
getPassName() const47 StringRef getPassName() const override {
48 return "WebAssembly Instruction Selection";
49 }
50
checkForInvalidNodes(const Function & F)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
runOnMachineFunction(MachineFunction & MF)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
PreprocessISelDAG()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
Select(SDNode * Node)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
SelectInlineAsmMemoryOperand(const SDValue & Op,unsigned ConstraintID,std::vector<SDValue> & OutOps)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.
createWebAssemblyISelDag(WebAssemblyTargetMachine & TM,CodeGenOpt::Level OptLevel)253 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
254 CodeGenOpt::Level OptLevel) {
255 return new WebAssemblyDAGToDAGISel(TM, OptLevel);
256 }
257