xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (revision aa1a8ff2d6dbc51ef058f46f3db5a8bb77967145)
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 #define PASS_NAME "WebAssembly Instruction Selection"
33 
34 //===--------------------------------------------------------------------===//
35 /// WebAssembly-specific code to select WebAssembly machine instructions for
36 /// SelectionDAG operations.
37 ///
38 namespace {
39 class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
40   /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
41   /// right decision when generating code for different targets.
42   const WebAssemblySubtarget *Subtarget;
43 
44 public:
45   static char ID;
46 
47   WebAssemblyDAGToDAGISel() = delete;
48 
49   WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
50                           CodeGenOptLevel OptLevel)
51       : SelectionDAGISel(ID, TM, OptLevel), Subtarget(nullptr) {}
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,
68                                     InlineAsm::ConstraintCode ConstraintID,
69                                     std::vector<SDValue> &OutOps) override;
70 
71   bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);
72   bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);
73 
74 // Include the pieces autogenerated from the target description.
75 #include "WebAssemblyGenDAGISel.inc"
76 
77 private:
78   // add select functions here...
79 
80   bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op,
81                           SDValue &Offset, SDValue &Addr);
82   bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset,
83                              SDValue &Addr);
84 };
85 } // end anonymous namespace
86 
87 char WebAssemblyDAGToDAGISel::ID;
88 
89 INITIALIZE_PASS(WebAssemblyDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
90 
91 void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
92   // Stack objects that should be allocated to locals are hoisted to WebAssembly
93   // locals when they are first used.  However for those without uses, we hoist
94   // them here.  It would be nice if there were some hook to do this when they
95   // are added to the MachineFrameInfo, but that's not the case right now.
96   MachineFrameInfo &FrameInfo = MF->getFrameInfo();
97   for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
98     WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
99 
100   SelectionDAGISel::PreprocessISelDAG();
101 }
102 
103 static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
104   assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP);
105   auto &MF = DAG->getMachineFunction();
106   const auto &TLI = DAG->getTargetLoweringInfo();
107   MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
108   const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
109                             ? MF.createExternalSymbolName("__cpp_exception")
110                             : MF.createExternalSymbolName("__c_longjmp");
111   return DAG->getTargetExternalSymbol(SymName, PtrVT);
112 }
113 
114 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
115   // If we have a custom node, we already have selected!
116   if (Node->isMachineOpcode()) {
117     LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
118     Node->setNodeId(-1);
119     return;
120   }
121 
122   MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
123   auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
124                                         : WebAssembly::GLOBAL_GET_I32;
125 
126   // Few custom selection stuff.
127   SDLoc DL(Node);
128   MachineFunction &MF = CurDAG->getMachineFunction();
129   switch (Node->getOpcode()) {
130   case ISD::ATOMIC_FENCE: {
131     if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
132       break;
133 
134     uint64_t SyncScopeID = Node->getConstantOperandVal(2);
135     MachineSDNode *Fence = nullptr;
136     switch (SyncScopeID) {
137     case SyncScope::SingleThread:
138       // We lower a single-thread fence to a pseudo compiler barrier instruction
139       // preventing instruction reordering. This will not be emitted in final
140       // binary.
141       Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
142                                      DL,                 // debug loc
143                                      MVT::Other,         // outchain type
144                                      Node->getOperand(0) // inchain
145       );
146       break;
147     case SyncScope::System:
148       // Currently wasm only supports sequentially consistent atomics, so we
149       // always set the order to 0 (sequentially consistent).
150       Fence = CurDAG->getMachineNode(
151           WebAssembly::ATOMIC_FENCE,
152           DL,                                         // debug loc
153           MVT::Other,                                 // outchain type
154           CurDAG->getTargetConstant(0, DL, MVT::i32), // order
155           Node->getOperand(0)                         // inchain
156       );
157       break;
158     default:
159       llvm_unreachable("Unknown scope!");
160     }
161 
162     ReplaceNode(Node, Fence);
163     CurDAG->RemoveDeadNode(Node);
164     return;
165   }
166 
167   case ISD::INTRINSIC_WO_CHAIN: {
168     unsigned IntNo = Node->getConstantOperandVal(0);
169     switch (IntNo) {
170     case Intrinsic::wasm_tls_size: {
171       MachineSDNode *TLSSize = CurDAG->getMachineNode(
172           GlobalGetIns, DL, PtrVT,
173           CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
174       ReplaceNode(Node, TLSSize);
175       return;
176     }
177 
178     case Intrinsic::wasm_tls_align: {
179       MachineSDNode *TLSAlign = CurDAG->getMachineNode(
180           GlobalGetIns, DL, PtrVT,
181           CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
182       ReplaceNode(Node, TLSAlign);
183       return;
184     }
185     }
186     break;
187   }
188 
189   case ISD::INTRINSIC_W_CHAIN: {
190     unsigned IntNo = Node->getConstantOperandVal(1);
191     const auto &TLI = CurDAG->getTargetLoweringInfo();
192     MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
193     switch (IntNo) {
194     case Intrinsic::wasm_tls_base: {
195       MachineSDNode *TLSBase = CurDAG->getMachineNode(
196           GlobalGetIns, DL, PtrVT, MVT::Other,
197           CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
198           Node->getOperand(0));
199       ReplaceNode(Node, TLSBase);
200       return;
201     }
202 
203     case Intrinsic::wasm_catch: {
204       int Tag = Node->getConstantOperandVal(2);
205       SDValue SymNode = getTagSymNode(Tag, CurDAG);
206       MachineSDNode *Catch =
207           CurDAG->getMachineNode(WebAssembly::CATCH, DL,
208                                  {
209                                      PtrVT,     // exception pointer
210                                      MVT::Other // outchain type
211                                  },
212                                  {
213                                      SymNode,            // exception symbol
214                                      Node->getOperand(0) // inchain
215                                  });
216       ReplaceNode(Node, Catch);
217       return;
218     }
219     }
220     break;
221   }
222 
223   case ISD::INTRINSIC_VOID: {
224     unsigned IntNo = Node->getConstantOperandVal(1);
225     switch (IntNo) {
226     case Intrinsic::wasm_throw: {
227       int Tag = Node->getConstantOperandVal(2);
228       SDValue SymNode = getTagSymNode(Tag, CurDAG);
229       MachineSDNode *Throw =
230           CurDAG->getMachineNode(WebAssembly::THROW, DL,
231                                  MVT::Other, // outchain type
232                                  {
233                                      SymNode,             // exception symbol
234                                      Node->getOperand(3), // thrown value
235                                      Node->getOperand(0)  // inchain
236                                  });
237       ReplaceNode(Node, Throw);
238       return;
239     }
240     }
241     break;
242   }
243 
244   case WebAssemblyISD::CALL:
245   case WebAssemblyISD::RET_CALL: {
246     // CALL has both variable operands and variable results, but ISel only
247     // supports one or the other. Split calls into two nodes glued together, one
248     // for the operands and one for the results. These two nodes will be
249     // recombined in a custom inserter hook into a single MachineInstr.
250     SmallVector<SDValue, 16> Ops;
251     for (size_t i = 1; i < Node->getNumOperands(); ++i) {
252       SDValue Op = Node->getOperand(i);
253       // Remove the wrapper when the call target is a function, an external
254       // symbol (which will be lowered to a library function), or an alias of
255       // a function. If the target is not a function/external symbol, we
256       // shouldn't remove the wrapper, because we cannot call it directly and
257       // instead we want it to be loaded with a CONST instruction and called
258       // with a call_indirect later.
259       if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) {
260         SDValue NewOp = Op->getOperand(0);
261         if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(NewOp.getNode())) {
262           if (isa<Function>(
263                   GlobalOp->getGlobal()->stripPointerCastsAndAliases()))
264             Op = NewOp;
265         } else if (isa<ExternalSymbolSDNode>(NewOp.getNode())) {
266           Op = NewOp;
267         }
268       }
269       Ops.push_back(Op);
270     }
271 
272     // Add the chain last
273     Ops.push_back(Node->getOperand(0));
274     MachineSDNode *CallParams =
275         CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
276 
277     unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
278                            ? WebAssembly::CALL_RESULTS
279                            : WebAssembly::RET_CALL_RESULTS;
280 
281     SDValue Link(CallParams, 0);
282     MachineSDNode *CallResults =
283         CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
284     ReplaceNode(Node, CallResults);
285     return;
286   }
287 
288   default:
289     break;
290   }
291 
292   // Select the default instruction.
293   SelectCode(Node);
294 }
295 
296 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
297     const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
298     std::vector<SDValue> &OutOps) {
299   switch (ConstraintID) {
300   case InlineAsm::ConstraintCode::m:
301     // We just support simple memory operands that just have a single address
302     // operand and need no special handling.
303     OutOps.push_back(Op);
304     return false;
305   default:
306     break;
307   }
308 
309   return true;
310 }
311 
312 bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N,
313                                                     SDValue &Offset,
314                                                     SDValue &Addr) {
315   assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op");
316 
317   // WebAssembly constant offsets are performed as unsigned with infinite
318   // precision, so we need to check for NoUnsignedWrap so that we don't fold an
319   // offset for an add that needs wrapping.
320   if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap())
321     return false;
322 
323   // Folds constants in an add into the offset.
324   for (size_t i = 0; i < 2; ++i) {
325     SDValue Op = N.getOperand(i);
326     SDValue OtherOp = N.getOperand(i == 0 ? 1 : 0);
327 
328     if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op)) {
329       Offset =
330           CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), OffsetType);
331       Addr = OtherOp;
332       return true;
333     }
334   }
335   return false;
336 }
337 
338 bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType,
339                                                  unsigned ConstOpc, SDValue N,
340                                                  SDValue &Offset,
341                                                  SDValue &Addr) {
342   SDLoc DL(N);
343 
344   // Fold target global addresses into the offset.
345   if (!TM.isPositionIndependent()) {
346     SDValue Op(N);
347     if (Op.getOpcode() == WebAssemblyISD::Wrapper)
348       Op = Op.getOperand(0);
349 
350     if (Op.getOpcode() == ISD::TargetGlobalAddress) {
351       Offset = Op;
352       Addr = SDValue(
353           CurDAG->getMachineNode(ConstOpc, DL, AddrType,
354                                  CurDAG->getTargetConstant(0, DL, AddrType)),
355           0);
356       return true;
357     }
358   }
359 
360   // Fold anything inside an add into the offset.
361   if (N.getOpcode() == ISD::ADD &&
362       SelectAddrAddOperands(AddrType, N, Offset, Addr))
363     return true;
364 
365   // Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be
366   // zero and fold them into the offset too.
367   if (N.getOpcode() == ISD::OR) {
368     bool OrIsAdd;
369     if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
370       OrIsAdd =
371           CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
372     } else {
373       KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
374       KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
375       OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0;
376     }
377 
378     if (OrIsAdd && SelectAddrAddOperands(AddrType, N, Offset, Addr))
379       return true;
380   }
381 
382   // Fold constant addresses into the offset.
383   if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N)) {
384     Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, AddrType);
385     Addr = SDValue(
386         CurDAG->getMachineNode(ConstOpc, DL, AddrType,
387                                CurDAG->getTargetConstant(0, DL, AddrType)),
388         0);
389     return true;
390   }
391 
392   // Else it's a plain old load/store with no offset.
393   Offset = CurDAG->getTargetConstant(0, DL, AddrType);
394   Addr = N;
395   return true;
396 }
397 
398 bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset,
399                                                    SDValue &Addr) {
400   return SelectAddrOperands(MVT::i32, WebAssembly::CONST_I32, Op, Offset, Addr);
401 }
402 
403 bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,
404                                                    SDValue &Addr) {
405   return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr);
406 }
407 
408 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
409 /// for instruction scheduling.
410 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
411                                              CodeGenOptLevel OptLevel) {
412   return new WebAssemblyDAGToDAGISel(TM, OptLevel);
413 }
414