10b57cec5SDimitry Andric //- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric /// 90b57cec5SDimitry Andric /// \file 100b57cec5SDimitry Andric /// This file defines an instruction selector for the WebAssembly target. 110b57cec5SDimitry Andric /// 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 150b57cec5SDimitry Andric #include "WebAssembly.h" 16fe6060f1SDimitry Andric #include "WebAssemblyISelLowering.h" 170b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h" 18fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 190b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAGISel.h" 20*349cc55cSDimitry Andric #include "llvm/CodeGen/WasmEHFuncInfo.h" 210b57cec5SDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 220b57cec5SDimitry Andric #include "llvm/IR/Function.h" // To access function attributes. 23480093f4SDimitry Andric #include "llvm/IR/IntrinsicsWebAssembly.h" 240b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 250b57cec5SDimitry Andric #include "llvm/Support/KnownBits.h" 260b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 270b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 28*349cc55cSDimitry Andric 290b57cec5SDimitry Andric using namespace llvm; 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-isel" 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 340b57cec5SDimitry Andric /// WebAssembly-specific code to select WebAssembly machine instructions for 350b57cec5SDimitry Andric /// SelectionDAG operations. 360b57cec5SDimitry Andric /// 370b57cec5SDimitry Andric namespace { 380b57cec5SDimitry Andric class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 390b57cec5SDimitry Andric /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 400b57cec5SDimitry Andric /// right decision when generating code for different targets. 410b57cec5SDimitry Andric const WebAssemblySubtarget *Subtarget; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric public: 440b57cec5SDimitry Andric WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 450b57cec5SDimitry Andric CodeGenOpt::Level OptLevel) 46480093f4SDimitry Andric : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 470b57cec5SDimitry Andric } 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric StringRef getPassName() const override { 500b57cec5SDimitry Andric return "WebAssembly Instruction Selection"; 510b57cec5SDimitry Andric } 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override { 540b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 550b57cec5SDimitry Andric "********** Function: " 560b57cec5SDimitry Andric << MF.getName() << '\n'); 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 598bcb0991SDimitry Andric 600b57cec5SDimitry Andric return SelectionDAGISel::runOnMachineFunction(MF); 610b57cec5SDimitry Andric } 620b57cec5SDimitry Andric 63fe6060f1SDimitry Andric void PreprocessISelDAG() override; 64fe6060f1SDimitry Andric 650b57cec5SDimitry Andric void Select(SDNode *Node) override; 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 680b57cec5SDimitry Andric std::vector<SDValue> &OutOps) override; 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric // Include the pieces autogenerated from the target description. 710b57cec5SDimitry Andric #include "WebAssemblyGenDAGISel.inc" 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric private: 740b57cec5SDimitry Andric // add select functions here... 750b57cec5SDimitry Andric }; 760b57cec5SDimitry Andric } // end anonymous namespace 770b57cec5SDimitry Andric 78fe6060f1SDimitry Andric void WebAssemblyDAGToDAGISel::PreprocessISelDAG() { 79fe6060f1SDimitry Andric // Stack objects that should be allocated to locals are hoisted to WebAssembly 80fe6060f1SDimitry Andric // locals when they are first used. However for those without uses, we hoist 81fe6060f1SDimitry Andric // them here. It would be nice if there were some hook to do this when they 82fe6060f1SDimitry Andric // are added to the MachineFrameInfo, but that's not the case right now. 83fe6060f1SDimitry Andric MachineFrameInfo &FrameInfo = MF->getFrameInfo(); 84fe6060f1SDimitry Andric for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++) 85fe6060f1SDimitry Andric WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx); 86fe6060f1SDimitry Andric 87fe6060f1SDimitry Andric SelectionDAGISel::PreprocessISelDAG(); 88fe6060f1SDimitry Andric } 89fe6060f1SDimitry Andric 90*349cc55cSDimitry Andric static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) { 91*349cc55cSDimitry Andric assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP); 92*349cc55cSDimitry Andric auto &MF = DAG->getMachineFunction(); 93*349cc55cSDimitry Andric const auto &TLI = DAG->getTargetLoweringInfo(); 94*349cc55cSDimitry Andric MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout()); 95*349cc55cSDimitry Andric const char *SymName = Tag == WebAssembly::CPP_EXCEPTION 96*349cc55cSDimitry Andric ? MF.createExternalSymbolName("__cpp_exception") 97*349cc55cSDimitry Andric : MF.createExternalSymbolName("__c_longjmp"); 98*349cc55cSDimitry Andric return DAG->getTargetExternalSymbol(SymName, PtrVT); 99*349cc55cSDimitry Andric } 100*349cc55cSDimitry Andric 1010b57cec5SDimitry Andric void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 1020b57cec5SDimitry Andric // If we have a custom node, we already have selected! 1030b57cec5SDimitry Andric if (Node->isMachineOpcode()) { 1040b57cec5SDimitry Andric LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 1050b57cec5SDimitry Andric Node->setNodeId(-1); 1060b57cec5SDimitry Andric return; 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric 1095ffd83dbSDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1105ffd83dbSDimitry Andric auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 1115ffd83dbSDimitry Andric : WebAssembly::GLOBAL_GET_I32; 1125ffd83dbSDimitry Andric 1130b57cec5SDimitry Andric // Few custom selection stuff. 1140b57cec5SDimitry Andric SDLoc DL(Node); 1150b57cec5SDimitry Andric MachineFunction &MF = CurDAG->getMachineFunction(); 1160b57cec5SDimitry Andric switch (Node->getOpcode()) { 1170b57cec5SDimitry Andric case ISD::ATOMIC_FENCE: { 1180b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 1190b57cec5SDimitry Andric break; 1200b57cec5SDimitry Andric 121*349cc55cSDimitry Andric uint64_t SyncScopeID = Node->getConstantOperandVal(2); 1228bcb0991SDimitry Andric MachineSDNode *Fence = nullptr; 1230b57cec5SDimitry Andric switch (SyncScopeID) { 1248bcb0991SDimitry Andric case SyncScope::SingleThread: 1250b57cec5SDimitry Andric // We lower a single-thread fence to a pseudo compiler barrier instruction 1260b57cec5SDimitry Andric // preventing instruction reordering. This will not be emitted in final 1270b57cec5SDimitry Andric // binary. 1288bcb0991SDimitry Andric Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 1290b57cec5SDimitry Andric DL, // debug loc 1300b57cec5SDimitry Andric MVT::Other, // outchain type 1310b57cec5SDimitry Andric Node->getOperand(0) // inchain 1320b57cec5SDimitry Andric ); 1338bcb0991SDimitry Andric break; 1348bcb0991SDimitry Andric case SyncScope::System: 1358bcb0991SDimitry Andric // Currently wasm only supports sequentially consistent atomics, so we 1368bcb0991SDimitry Andric // always set the order to 0 (sequentially consistent). 1378bcb0991SDimitry Andric Fence = CurDAG->getMachineNode( 1388bcb0991SDimitry Andric WebAssembly::ATOMIC_FENCE, 1390b57cec5SDimitry Andric DL, // debug loc 1400b57cec5SDimitry Andric MVT::Other, // outchain type 1418bcb0991SDimitry Andric CurDAG->getTargetConstant(0, DL, MVT::i32), // order 1420b57cec5SDimitry Andric Node->getOperand(0) // inchain 1438bcb0991SDimitry Andric ); 1448bcb0991SDimitry Andric break; 1450b57cec5SDimitry Andric default: 1460b57cec5SDimitry Andric llvm_unreachable("Unknown scope!"); 1470b57cec5SDimitry Andric } 1488bcb0991SDimitry Andric 1498bcb0991SDimitry Andric ReplaceNode(Node, Fence); 1508bcb0991SDimitry Andric CurDAG->RemoveDeadNode(Node); 1518bcb0991SDimitry Andric return; 1520b57cec5SDimitry Andric } 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: { 155*349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(0); 1560b57cec5SDimitry Andric switch (IntNo) { 1570b57cec5SDimitry Andric case Intrinsic::wasm_tls_size: { 1580b57cec5SDimitry Andric MachineSDNode *TLSSize = CurDAG->getMachineNode( 1595ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, 1605ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_size", PtrVT)); 1610b57cec5SDimitry Andric ReplaceNode(Node, TLSSize); 1620b57cec5SDimitry Andric return; 1630b57cec5SDimitry Andric } 164*349cc55cSDimitry Andric 1658bcb0991SDimitry Andric case Intrinsic::wasm_tls_align: { 1668bcb0991SDimitry Andric MachineSDNode *TLSAlign = CurDAG->getMachineNode( 1675ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, 1685ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_align", PtrVT)); 1698bcb0991SDimitry Andric ReplaceNode(Node, TLSAlign); 1708bcb0991SDimitry Andric return; 1718bcb0991SDimitry Andric } 1728bcb0991SDimitry Andric } 1738bcb0991SDimitry Andric break; 1748bcb0991SDimitry Andric } 175*349cc55cSDimitry Andric 1768bcb0991SDimitry Andric case ISD::INTRINSIC_W_CHAIN: { 177*349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(1); 178*349cc55cSDimitry Andric const auto &TLI = CurDAG->getTargetLoweringInfo(); 179*349cc55cSDimitry Andric MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout()); 1808bcb0991SDimitry Andric switch (IntNo) { 1818bcb0991SDimitry Andric case Intrinsic::wasm_tls_base: { 1828bcb0991SDimitry Andric MachineSDNode *TLSBase = CurDAG->getMachineNode( 1835ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, MVT::Other, 1848bcb0991SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 1858bcb0991SDimitry Andric Node->getOperand(0)); 1868bcb0991SDimitry Andric ReplaceNode(Node, TLSBase); 1878bcb0991SDimitry Andric return; 1888bcb0991SDimitry Andric } 189*349cc55cSDimitry Andric 190*349cc55cSDimitry Andric case Intrinsic::wasm_catch: { 191*349cc55cSDimitry Andric int Tag = Node->getConstantOperandVal(2); 192*349cc55cSDimitry Andric SDValue SymNode = getTagSymNode(Tag, CurDAG); 193*349cc55cSDimitry Andric MachineSDNode *Catch = 194*349cc55cSDimitry Andric CurDAG->getMachineNode(WebAssembly::CATCH, DL, 195*349cc55cSDimitry Andric { 196*349cc55cSDimitry Andric PtrVT, // exception pointer 197*349cc55cSDimitry Andric MVT::Other // outchain type 198*349cc55cSDimitry Andric }, 199*349cc55cSDimitry Andric { 200*349cc55cSDimitry Andric SymNode, // exception symbol 201*349cc55cSDimitry Andric Node->getOperand(0) // inchain 202*349cc55cSDimitry Andric }); 203*349cc55cSDimitry Andric ReplaceNode(Node, Catch); 204*349cc55cSDimitry Andric return; 205*349cc55cSDimitry Andric } 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric break; 2080b57cec5SDimitry Andric } 209*349cc55cSDimitry Andric 210*349cc55cSDimitry Andric case ISD::INTRINSIC_VOID: { 211*349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(1); 212*349cc55cSDimitry Andric switch (IntNo) { 213*349cc55cSDimitry Andric case Intrinsic::wasm_throw: { 214*349cc55cSDimitry Andric int Tag = Node->getConstantOperandVal(2); 215*349cc55cSDimitry Andric SDValue SymNode = getTagSymNode(Tag, CurDAG); 216*349cc55cSDimitry Andric MachineSDNode *Throw = 217*349cc55cSDimitry Andric CurDAG->getMachineNode(WebAssembly::THROW, DL, 218*349cc55cSDimitry Andric MVT::Other, // outchain type 219*349cc55cSDimitry Andric { 220*349cc55cSDimitry Andric SymNode, // exception symbol 221*349cc55cSDimitry Andric Node->getOperand(3), // thrown value 222*349cc55cSDimitry Andric Node->getOperand(0) // inchain 223*349cc55cSDimitry Andric }); 224*349cc55cSDimitry Andric ReplaceNode(Node, Throw); 225*349cc55cSDimitry Andric return; 226*349cc55cSDimitry Andric } 227*349cc55cSDimitry Andric } 228*349cc55cSDimitry Andric break; 229*349cc55cSDimitry Andric } 230*349cc55cSDimitry Andric 2315ffd83dbSDimitry Andric case WebAssemblyISD::CALL: 2325ffd83dbSDimitry Andric case WebAssemblyISD::RET_CALL: { 2335ffd83dbSDimitry Andric // CALL has both variable operands and variable results, but ISel only 2345ffd83dbSDimitry Andric // supports one or the other. Split calls into two nodes glued together, one 2355ffd83dbSDimitry Andric // for the operands and one for the results. These two nodes will be 2365ffd83dbSDimitry Andric // recombined in a custom inserter hook into a single MachineInstr. 2375ffd83dbSDimitry Andric SmallVector<SDValue, 16> Ops; 2385ffd83dbSDimitry Andric for (size_t i = 1; i < Node->getNumOperands(); ++i) { 2395ffd83dbSDimitry Andric SDValue Op = Node->getOperand(i); 2405ffd83dbSDimitry Andric if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) 2415ffd83dbSDimitry Andric Op = Op->getOperand(0); 2425ffd83dbSDimitry Andric Ops.push_back(Op); 2435ffd83dbSDimitry Andric } 2445ffd83dbSDimitry Andric 2455ffd83dbSDimitry Andric // Add the chain last 2465ffd83dbSDimitry Andric Ops.push_back(Node->getOperand(0)); 2475ffd83dbSDimitry Andric MachineSDNode *CallParams = 2485ffd83dbSDimitry Andric CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops); 2495ffd83dbSDimitry Andric 2505ffd83dbSDimitry Andric unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL 2515ffd83dbSDimitry Andric ? WebAssembly::CALL_RESULTS 2525ffd83dbSDimitry Andric : WebAssembly::RET_CALL_RESULTS; 2535ffd83dbSDimitry Andric 2545ffd83dbSDimitry Andric SDValue Link(CallParams, 0); 2555ffd83dbSDimitry Andric MachineSDNode *CallResults = 2565ffd83dbSDimitry Andric CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link); 2575ffd83dbSDimitry Andric ReplaceNode(Node, CallResults); 2585ffd83dbSDimitry Andric return; 2595ffd83dbSDimitry Andric } 2600b57cec5SDimitry Andric 2610b57cec5SDimitry Andric default: 2620b57cec5SDimitry Andric break; 2630b57cec5SDimitry Andric } 2640b57cec5SDimitry Andric 2650b57cec5SDimitry Andric // Select the default instruction. 2660b57cec5SDimitry Andric SelectCode(Node); 2670b57cec5SDimitry Andric } 2680b57cec5SDimitry Andric 2690b57cec5SDimitry Andric bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 2700b57cec5SDimitry Andric const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 2710b57cec5SDimitry Andric switch (ConstraintID) { 2720b57cec5SDimitry Andric case InlineAsm::Constraint_m: 2730b57cec5SDimitry Andric // We just support simple memory operands that just have a single address 2740b57cec5SDimitry Andric // operand and need no special handling. 2750b57cec5SDimitry Andric OutOps.push_back(Op); 2760b57cec5SDimitry Andric return false; 2770b57cec5SDimitry Andric default: 2780b57cec5SDimitry Andric break; 2790b57cec5SDimitry Andric } 2800b57cec5SDimitry Andric 2810b57cec5SDimitry Andric return true; 2820b57cec5SDimitry Andric } 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 2850b57cec5SDimitry Andric /// for instruction scheduling. 2860b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 2870b57cec5SDimitry Andric CodeGenOpt::Level OptLevel) { 2880b57cec5SDimitry Andric return new WebAssemblyDAGToDAGISel(TM, OptLevel); 2890b57cec5SDimitry Andric } 290