110e730a2SDan Gohman //- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//
210e730a2SDan Gohman //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
610e730a2SDan Gohman //
710e730a2SDan Gohman //===----------------------------------------------------------------------===//
810e730a2SDan Gohman ///
910e730a2SDan Gohman /// \file
105f8f34e4SAdrian Prantl /// This file defines an instruction selector for the WebAssembly target.
1110e730a2SDan Gohman ///
1210e730a2SDan Gohman //===----------------------------------------------------------------------===//
1310e730a2SDan Gohman 
1410e730a2SDan Gohman #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
156bda14b3SChandler Carruth #include "WebAssembly.h"
1646667a10SPaulo Matos #include "WebAssemblyISelLowering.h"
1710e730a2SDan Gohman #include "WebAssemblyTargetMachine.h"
1882f92e35SAndy Wingo #include "llvm/CodeGen/MachineFrameInfo.h"
1910e730a2SDan Gohman #include "llvm/CodeGen/SelectionDAGISel.h"
2031a71a39SHeejin Ahn #include "llvm/CodeGen/WasmEHFuncInfo.h"
2142bba4b8SGuanzhong Chen #include "llvm/IR/DiagnosticInfo.h"
2210e730a2SDan Gohman #include "llvm/IR/Function.h" // To access function attributes.
235d986953SReid Kleckner #include "llvm/IR/IntrinsicsWebAssembly.h"
2410e730a2SDan Gohman #include "llvm/Support/Debug.h"
25053cf4daSCraig Topper #include "llvm/Support/KnownBits.h"
2610e730a2SDan Gohman #include "llvm/Support/MathExtras.h"
2710e730a2SDan Gohman #include "llvm/Support/raw_ostream.h"
28d3a0a65bSPaulo Matos 
2910e730a2SDan Gohman using namespace llvm;
3010e730a2SDan Gohman 
3110e730a2SDan Gohman #define DEBUG_TYPE "wasm-isel"
3210e730a2SDan Gohman 
3310e730a2SDan Gohman //===--------------------------------------------------------------------===//
3410e730a2SDan Gohman /// WebAssembly-specific code to select WebAssembly machine instructions for
3510e730a2SDan Gohman /// SelectionDAG operations.
3610e730a2SDan Gohman ///
3710e730a2SDan Gohman namespace {
3810e730a2SDan Gohman class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
3910e730a2SDan Gohman   /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
4010e730a2SDan Gohman   /// right decision when generating code for different targets.
4110e730a2SDan Gohman   const WebAssemblySubtarget *Subtarget;
4210e730a2SDan Gohman 
4310e730a2SDan Gohman public:
WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine & TM,CodeGenOpt::Level OptLevel)4418c56a07SHeejin Ahn   WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
4510e730a2SDan Gohman                           CodeGenOpt::Level OptLevel)
4670a3c9f5SHiroshi Yamauchi       : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {
4710e730a2SDan Gohman   }
4810e730a2SDan Gohman 
getPassName() const49117296c0SMehdi Amini   StringRef getPassName() const override {
5010e730a2SDan Gohman     return "WebAssembly Instruction Selection";
5110e730a2SDan Gohman   }
5210e730a2SDan Gohman 
runOnMachineFunction(MachineFunction & MF)5310e730a2SDan Gohman   bool runOnMachineFunction(MachineFunction &MF) override {
54569f0909SHeejin Ahn     LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
55569f0909SHeejin Ahn                          "********** Function: "
56569f0909SHeejin Ahn                       << MF.getName() << '\n');
57569f0909SHeejin Ahn 
5810e730a2SDan Gohman     Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
595b74c39dSThomas Lively 
6010e730a2SDan Gohman     return SelectionDAGISel::runOnMachineFunction(MF);
6110e730a2SDan Gohman   }
6210e730a2SDan Gohman 
6382f92e35SAndy Wingo   void PreprocessISelDAG() override;
6482f92e35SAndy Wingo 
65c6afd4bbSJustin Bogner   void Select(SDNode *Node) override;
6610e730a2SDan Gohman 
67f19ed562SDan Gohman   bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
68f19ed562SDan Gohman                                     std::vector<SDValue> &OutOps) override;
69f19ed562SDan Gohman 
70b9073fb2SJF Bastien // Include the pieces autogenerated from the target description.
71b9073fb2SJF Bastien #include "WebAssemblyGenDAGISel.inc"
72b9073fb2SJF Bastien 
7310e730a2SDan Gohman private:
7410e730a2SDan Gohman   // add select functions here...
7510e730a2SDan Gohman };
7610e730a2SDan Gohman } // end anonymous namespace
7710e730a2SDan Gohman 
PreprocessISelDAG()7882f92e35SAndy Wingo void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
7982f92e35SAndy Wingo   // Stack objects that should be allocated to locals are hoisted to WebAssembly
8082f92e35SAndy Wingo   // locals when they are first used.  However for those without uses, we hoist
8182f92e35SAndy Wingo   // them here.  It would be nice if there were some hook to do this when they
8282f92e35SAndy Wingo   // are added to the MachineFrameInfo, but that's not the case right now.
8382f92e35SAndy Wingo   MachineFrameInfo &FrameInfo = MF->getFrameInfo();
8482f92e35SAndy Wingo   for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
8582f92e35SAndy Wingo     WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
8682f92e35SAndy Wingo 
8782f92e35SAndy Wingo   SelectionDAGISel::PreprocessISelDAG();
8882f92e35SAndy Wingo }
8982f92e35SAndy Wingo 
getTagSymNode(int Tag,SelectionDAG * DAG)9031a71a39SHeejin Ahn static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
91*28780e59SHeejin Ahn   assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP);
9231a71a39SHeejin Ahn   auto &MF = DAG->getMachineFunction();
9331a71a39SHeejin Ahn   const auto &TLI = DAG->getTargetLoweringInfo();
9431a71a39SHeejin Ahn   MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
95*28780e59SHeejin Ahn   const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
96*28780e59SHeejin Ahn                             ? MF.createExternalSymbolName("__cpp_exception")
97*28780e59SHeejin Ahn                             : MF.createExternalSymbolName("__c_longjmp");
9831a71a39SHeejin Ahn   return DAG->getTargetExternalSymbol(SymName, PtrVT);
9931a71a39SHeejin Ahn }
10031a71a39SHeejin Ahn 
Select(SDNode * Node)101c6afd4bbSJustin Bogner void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
102b9073fb2SJF Bastien   // If we have a custom node, we already have selected!
103b9073fb2SJF Bastien   if (Node->isMachineOpcode()) {
104d34e60caSNicola Zaghen     LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
105b9073fb2SJF Bastien     Node->setNodeId(-1);
106c6afd4bbSJustin Bogner     return;
107b9073fb2SJF Bastien   }
108b9073fb2SJF Bastien 
109b9a539c0SWouter van Oortmerssen   MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
110b9a539c0SWouter van Oortmerssen   auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
111b9a539c0SWouter van Oortmerssen                                         : WebAssembly::GLOBAL_GET_I32;
112b9a539c0SWouter van Oortmerssen 
11355146585SHeejin Ahn   // Few custom selection stuff.
11455146585SHeejin Ahn   SDLoc DL(Node);
11555146585SHeejin Ahn   MachineFunction &MF = CurDAG->getMachineFunction();
116b9073fb2SJF Bastien   switch (Node->getOpcode()) {
11755146585SHeejin Ahn   case ISD::ATOMIC_FENCE: {
11855146585SHeejin Ahn     if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
11955146585SHeejin Ahn       break;
12055146585SHeejin Ahn 
121aa0b0fbbSHeejin Ahn     uint64_t SyncScopeID = Node->getConstantOperandVal(2);
122d85fd5a3SHeejin Ahn     MachineSDNode *Fence = nullptr;
12355146585SHeejin Ahn     switch (SyncScopeID) {
124d85fd5a3SHeejin Ahn     case SyncScope::SingleThread:
12555146585SHeejin Ahn       // We lower a single-thread fence to a pseudo compiler barrier instruction
12655146585SHeejin Ahn       // preventing instruction reordering. This will not be emitted in final
12755146585SHeejin Ahn       // binary.
128d85fd5a3SHeejin Ahn       Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
12955146585SHeejin Ahn                                      DL,                 // debug loc
13055146585SHeejin Ahn                                      MVT::Other,         // outchain type
13155146585SHeejin Ahn                                      Node->getOperand(0) // inchain
13255146585SHeejin Ahn       );
133d85fd5a3SHeejin Ahn       break;
134d85fd5a3SHeejin Ahn     case SyncScope::System:
135d85fd5a3SHeejin Ahn       // Currently wasm only supports sequentially consistent atomics, so we
136d85fd5a3SHeejin Ahn       // always set the order to 0 (sequentially consistent).
137d85fd5a3SHeejin Ahn       Fence = CurDAG->getMachineNode(
138d85fd5a3SHeejin Ahn           WebAssembly::ATOMIC_FENCE,
13955146585SHeejin Ahn           DL,                                         // debug loc
14055146585SHeejin Ahn           MVT::Other,                                 // outchain type
141d85fd5a3SHeejin Ahn           CurDAG->getTargetConstant(0, DL, MVT::i32), // order
14255146585SHeejin Ahn           Node->getOperand(0)                         // inchain
143d85fd5a3SHeejin Ahn       );
144d85fd5a3SHeejin Ahn       break;
14555146585SHeejin Ahn     default:
14655146585SHeejin Ahn       llvm_unreachable("Unknown scope!");
14755146585SHeejin Ahn     }
148d85fd5a3SHeejin Ahn 
149d85fd5a3SHeejin Ahn     ReplaceNode(Node, Fence);
150d85fd5a3SHeejin Ahn     CurDAG->RemoveDeadNode(Node);
151d85fd5a3SHeejin Ahn     return;
15255146585SHeejin Ahn   }
15355146585SHeejin Ahn 
15442bba4b8SGuanzhong Chen   case ISD::INTRINSIC_WO_CHAIN: {
155aa0b0fbbSHeejin Ahn     unsigned IntNo = Node->getConstantOperandVal(0);
15642bba4b8SGuanzhong Chen     switch (IntNo) {
15742bba4b8SGuanzhong Chen     case Intrinsic::wasm_tls_size: {
15842bba4b8SGuanzhong Chen       MachineSDNode *TLSSize = CurDAG->getMachineNode(
159b9a539c0SWouter van Oortmerssen           GlobalGetIns, DL, PtrVT,
160b9a539c0SWouter van Oortmerssen           CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
16142bba4b8SGuanzhong Chen       ReplaceNode(Node, TLSSize);
16242bba4b8SGuanzhong Chen       return;
16342bba4b8SGuanzhong Chen     }
16431a71a39SHeejin Ahn 
1655204f761SGuanzhong Chen     case Intrinsic::wasm_tls_align: {
1665204f761SGuanzhong Chen       MachineSDNode *TLSAlign = CurDAG->getMachineNode(
167b9a539c0SWouter van Oortmerssen           GlobalGetIns, DL, PtrVT,
168b9a539c0SWouter van Oortmerssen           CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
1695204f761SGuanzhong Chen       ReplaceNode(Node, TLSAlign);
1705204f761SGuanzhong Chen       return;
1715204f761SGuanzhong Chen     }
17242bba4b8SGuanzhong Chen     }
17342bba4b8SGuanzhong Chen     break;
17442bba4b8SGuanzhong Chen   }
17531a71a39SHeejin Ahn 
176801fa8e6SGuanzhong Chen   case ISD::INTRINSIC_W_CHAIN: {
177aa0b0fbbSHeejin Ahn     unsigned IntNo = Node->getConstantOperandVal(1);
17831a71a39SHeejin Ahn     const auto &TLI = CurDAG->getTargetLoweringInfo();
17931a71a39SHeejin Ahn     MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
180801fa8e6SGuanzhong Chen     switch (IntNo) {
181801fa8e6SGuanzhong Chen     case Intrinsic::wasm_tls_base: {
182801fa8e6SGuanzhong Chen       MachineSDNode *TLSBase = CurDAG->getMachineNode(
183b9a539c0SWouter van Oortmerssen           GlobalGetIns, DL, PtrVT, MVT::Other,
184801fa8e6SGuanzhong Chen           CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
185801fa8e6SGuanzhong Chen           Node->getOperand(0));
186801fa8e6SGuanzhong Chen       ReplaceNode(Node, TLSBase);
187801fa8e6SGuanzhong Chen       return;
188801fa8e6SGuanzhong Chen     }
18931a71a39SHeejin Ahn 
190c2c9a3fdSHeejin Ahn     case Intrinsic::wasm_catch: {
19131a71a39SHeejin Ahn       int Tag = Node->getConstantOperandVal(2);
19231a71a39SHeejin Ahn       SDValue SymNode = getTagSymNode(Tag, CurDAG);
19331a71a39SHeejin Ahn       MachineSDNode *Catch =
19431a71a39SHeejin Ahn           CurDAG->getMachineNode(WebAssembly::CATCH, DL,
19531a71a39SHeejin Ahn                                  {
19631a71a39SHeejin Ahn                                      PtrVT,     // exception pointer
19731a71a39SHeejin Ahn                                      MVT::Other // outchain type
19831a71a39SHeejin Ahn                                  },
19931a71a39SHeejin Ahn                                  {
20031a71a39SHeejin Ahn                                      SymNode,            // exception symbol
20131a71a39SHeejin Ahn                                      Node->getOperand(0) // inchain
20231a71a39SHeejin Ahn                                  });
20331a71a39SHeejin Ahn       ReplaceNode(Node, Catch);
20431a71a39SHeejin Ahn       return;
20531a71a39SHeejin Ahn     }
206801fa8e6SGuanzhong Chen     }
207801fa8e6SGuanzhong Chen     break;
208801fa8e6SGuanzhong Chen   }
20931a71a39SHeejin Ahn 
21031a71a39SHeejin Ahn   case ISD::INTRINSIC_VOID: {
21131a71a39SHeejin Ahn     unsigned IntNo = Node->getConstantOperandVal(1);
21231a71a39SHeejin Ahn     switch (IntNo) {
21331a71a39SHeejin Ahn     case Intrinsic::wasm_throw: {
21431a71a39SHeejin Ahn       int Tag = Node->getConstantOperandVal(2);
21531a71a39SHeejin Ahn       SDValue SymNode = getTagSymNode(Tag, CurDAG);
21631a71a39SHeejin Ahn       MachineSDNode *Throw =
21731a71a39SHeejin Ahn           CurDAG->getMachineNode(WebAssembly::THROW, DL,
21831a71a39SHeejin Ahn                                  MVT::Other, // outchain type
21931a71a39SHeejin Ahn                                  {
22031a71a39SHeejin Ahn                                      SymNode,             // exception symbol
22131a71a39SHeejin Ahn                                      Node->getOperand(3), // thrown value
22231a71a39SHeejin Ahn                                      Node->getOperand(0)  // inchain
22331a71a39SHeejin Ahn                                  });
22431a71a39SHeejin Ahn       ReplaceNode(Node, Throw);
22531a71a39SHeejin Ahn       return;
22631a71a39SHeejin Ahn     }
22731a71a39SHeejin Ahn     }
22831a71a39SHeejin Ahn     break;
22931a71a39SHeejin Ahn   }
23031a71a39SHeejin Ahn 
231ca9ba764SThomas Lively   case WebAssemblyISD::CALL:
232ca9ba764SThomas Lively   case WebAssemblyISD::RET_CALL: {
2337b64a590SThomas Lively     // CALL has both variable operands and variable results, but ISel only
2347b64a590SThomas Lively     // supports one or the other. Split calls into two nodes glued together, one
2357b64a590SThomas Lively     // for the operands and one for the results. These two nodes will be
236d5191096SThomas Lively     // recombined in a custom inserter hook into a single MachineInstr.
2377b64a590SThomas Lively     SmallVector<SDValue, 16> Ops;
2387b64a590SThomas Lively     for (size_t i = 1; i < Node->getNumOperands(); ++i) {
2397b64a590SThomas Lively       SDValue Op = Node->getOperand(i);
240ca9ba764SThomas Lively       if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper)
2417b64a590SThomas Lively         Op = Op->getOperand(0);
2427b64a590SThomas Lively       Ops.push_back(Op);
2437b64a590SThomas Lively     }
244ca9ba764SThomas Lively 
245ca9ba764SThomas Lively     // Add the chain last
2467b64a590SThomas Lively     Ops.push_back(Node->getOperand(0));
247d5191096SThomas Lively     MachineSDNode *CallParams =
248d5191096SThomas Lively         CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
249ca9ba764SThomas Lively 
250ca9ba764SThomas Lively     unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
251ca9ba764SThomas Lively                            ? WebAssembly::CALL_RESULTS
252ca9ba764SThomas Lively                            : WebAssembly::RET_CALL_RESULTS;
253ca9ba764SThomas Lively 
254d5191096SThomas Lively     SDValue Link(CallParams, 0);
255ca9ba764SThomas Lively     MachineSDNode *CallResults =
256ca9ba764SThomas Lively         CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
257d5191096SThomas Lively     ReplaceNode(Node, CallResults);
2587b64a590SThomas Lively     return;
2597b64a590SThomas Lively   }
26042bba4b8SGuanzhong Chen 
261b9073fb2SJF Bastien   default:
262b9073fb2SJF Bastien     break;
263b9073fb2SJF Bastien   }
264b9073fb2SJF Bastien 
265b9073fb2SJF Bastien   // Select the default instruction.
266c6afd4bbSJustin Bogner   SelectCode(Node);
26710e730a2SDan Gohman }
26810e730a2SDan Gohman 
SelectInlineAsmMemoryOperand(const SDValue & Op,unsigned ConstraintID,std::vector<SDValue> & OutOps)269f19ed562SDan Gohman bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
270f19ed562SDan Gohman     const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
271f19ed562SDan Gohman   switch (ConstraintID) {
272f19ed562SDan Gohman   case InlineAsm::Constraint_m:
273f19ed562SDan Gohman     // We just support simple memory operands that just have a single address
274f19ed562SDan Gohman     // operand and need no special handling.
275f19ed562SDan Gohman     OutOps.push_back(Op);
276f19ed562SDan Gohman     return false;
277f19ed562SDan Gohman   default:
278f19ed562SDan Gohman     break;
279f19ed562SDan Gohman   }
280f19ed562SDan Gohman 
281f19ed562SDan Gohman   return true;
282f19ed562SDan Gohman }
283f19ed562SDan Gohman 
28410e730a2SDan Gohman /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
28510e730a2SDan Gohman /// for instruction scheduling.
createWebAssemblyISelDag(WebAssemblyTargetMachine & TM,CodeGenOpt::Level OptLevel)28610e730a2SDan Gohman FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
28710e730a2SDan Gohman                                              CodeGenOpt::Level OptLevel) {
28810e730a2SDan Gohman   return new WebAssemblyDAGToDAGISel(TM, OptLevel);
28910e730a2SDan Gohman }
290