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" 16*fe6060f1SDimitry Andric #include "WebAssemblyISelLowering.h" 170b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h" 18*fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 190b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAGISel.h" 200b57cec5SDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 210b57cec5SDimitry Andric #include "llvm/IR/Function.h" // To access function attributes. 22480093f4SDimitry Andric #include "llvm/IR/IntrinsicsWebAssembly.h" 230b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 240b57cec5SDimitry Andric #include "llvm/Support/KnownBits.h" 250b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 260b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 270b57cec5SDimitry Andric using namespace llvm; 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-isel" 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 320b57cec5SDimitry Andric /// WebAssembly-specific code to select WebAssembly machine instructions for 330b57cec5SDimitry Andric /// SelectionDAG operations. 340b57cec5SDimitry Andric /// 350b57cec5SDimitry Andric namespace { 360b57cec5SDimitry Andric class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 370b57cec5SDimitry Andric /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 380b57cec5SDimitry Andric /// right decision when generating code for different targets. 390b57cec5SDimitry Andric const WebAssemblySubtarget *Subtarget; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric public: 420b57cec5SDimitry Andric WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 430b57cec5SDimitry Andric CodeGenOpt::Level OptLevel) 44480093f4SDimitry Andric : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 450b57cec5SDimitry Andric } 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric StringRef getPassName() const override { 480b57cec5SDimitry Andric return "WebAssembly Instruction Selection"; 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 51*fe6060f1SDimitry Andric void checkForInvalidNodes(const Function &F) { 52*fe6060f1SDimitry Andric // This function will check for uses of ptrtoint on reference types and 53*fe6060f1SDimitry Andric // report a fatal error if these are found. 54*fe6060f1SDimitry Andric for (const BasicBlock &BB : F) { 55*fe6060f1SDimitry Andric for (const Instruction &I : BB) { 56*fe6060f1SDimitry Andric if (const PtrToIntInst *PTI = dyn_cast<const PtrToIntInst>(&I)) { 57*fe6060f1SDimitry Andric const Value *V = PTI->getPointerOperand(); 58*fe6060f1SDimitry Andric if (WebAssemblyTargetLowering::isFuncrefType(V->getType()) || 59*fe6060f1SDimitry Andric WebAssemblyTargetLowering::isExternrefType(V->getType())) 60*fe6060f1SDimitry Andric report_fatal_error("ptrtoint not allowed on reference types"); 61*fe6060f1SDimitry Andric } else if (const IntToPtrInst *ITP = dyn_cast<const IntToPtrInst>(&I)) { 62*fe6060f1SDimitry Andric if (WebAssemblyTargetLowering::isFuncrefType(ITP->getDestTy()) || 63*fe6060f1SDimitry Andric WebAssemblyTargetLowering::isExternrefType(ITP->getDestTy())) 64*fe6060f1SDimitry Andric report_fatal_error("inttoptr not allowed on reference types"); 65*fe6060f1SDimitry Andric } 66*fe6060f1SDimitry Andric } 67*fe6060f1SDimitry Andric } 68*fe6060f1SDimitry Andric } 69*fe6060f1SDimitry Andric 700b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override { 710b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 720b57cec5SDimitry Andric "********** Function: " 730b57cec5SDimitry Andric << MF.getName() << '\n'); 740b57cec5SDimitry Andric 75*fe6060f1SDimitry Andric checkForInvalidNodes(MF.getFunction()); 76*fe6060f1SDimitry Andric 770b57cec5SDimitry Andric Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 788bcb0991SDimitry Andric 790b57cec5SDimitry Andric return SelectionDAGISel::runOnMachineFunction(MF); 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric 82*fe6060f1SDimitry Andric void PreprocessISelDAG() override; 83*fe6060f1SDimitry Andric 840b57cec5SDimitry Andric void Select(SDNode *Node) override; 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 870b57cec5SDimitry Andric std::vector<SDValue> &OutOps) override; 88*fe6060f1SDimitry Andric bool SelectExternRefAddr(const SDValue &Addr, const SDValue &Base); 890b57cec5SDimitry Andric 900b57cec5SDimitry Andric // Include the pieces autogenerated from the target description. 910b57cec5SDimitry Andric #include "WebAssemblyGenDAGISel.inc" 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric private: 940b57cec5SDimitry Andric // add select functions here... 950b57cec5SDimitry Andric }; 960b57cec5SDimitry Andric } // end anonymous namespace 970b57cec5SDimitry Andric 98*fe6060f1SDimitry Andric void WebAssemblyDAGToDAGISel::PreprocessISelDAG() { 99*fe6060f1SDimitry Andric // Stack objects that should be allocated to locals are hoisted to WebAssembly 100*fe6060f1SDimitry Andric // locals when they are first used. However for those without uses, we hoist 101*fe6060f1SDimitry Andric // them here. It would be nice if there were some hook to do this when they 102*fe6060f1SDimitry Andric // are added to the MachineFrameInfo, but that's not the case right now. 103*fe6060f1SDimitry Andric MachineFrameInfo &FrameInfo = MF->getFrameInfo(); 104*fe6060f1SDimitry Andric for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++) 105*fe6060f1SDimitry Andric WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx); 106*fe6060f1SDimitry Andric 107*fe6060f1SDimitry Andric SelectionDAGISel::PreprocessISelDAG(); 108*fe6060f1SDimitry Andric } 109*fe6060f1SDimitry Andric 1100b57cec5SDimitry Andric void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 1110b57cec5SDimitry Andric // If we have a custom node, we already have selected! 1120b57cec5SDimitry Andric if (Node->isMachineOpcode()) { 1130b57cec5SDimitry Andric LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 1140b57cec5SDimitry Andric Node->setNodeId(-1); 1150b57cec5SDimitry Andric return; 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric 1185ffd83dbSDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1195ffd83dbSDimitry Andric auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 1205ffd83dbSDimitry Andric : WebAssembly::GLOBAL_GET_I32; 1215ffd83dbSDimitry Andric 1220b57cec5SDimitry Andric // Few custom selection stuff. 1230b57cec5SDimitry Andric SDLoc DL(Node); 1240b57cec5SDimitry Andric MachineFunction &MF = CurDAG->getMachineFunction(); 1250b57cec5SDimitry Andric switch (Node->getOpcode()) { 1260b57cec5SDimitry Andric case ISD::ATOMIC_FENCE: { 1270b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 1280b57cec5SDimitry Andric break; 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric uint64_t SyncScopeID = 1310b57cec5SDimitry Andric cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 1328bcb0991SDimitry Andric MachineSDNode *Fence = nullptr; 1330b57cec5SDimitry Andric switch (SyncScopeID) { 1348bcb0991SDimitry Andric case SyncScope::SingleThread: 1350b57cec5SDimitry Andric // We lower a single-thread fence to a pseudo compiler barrier instruction 1360b57cec5SDimitry Andric // preventing instruction reordering. This will not be emitted in final 1370b57cec5SDimitry Andric // binary. 1388bcb0991SDimitry Andric Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 1390b57cec5SDimitry Andric DL, // debug loc 1400b57cec5SDimitry Andric MVT::Other, // outchain type 1410b57cec5SDimitry Andric Node->getOperand(0) // inchain 1420b57cec5SDimitry Andric ); 1438bcb0991SDimitry Andric break; 1448bcb0991SDimitry Andric case SyncScope::System: 1458bcb0991SDimitry Andric // Currently wasm only supports sequentially consistent atomics, so we 1468bcb0991SDimitry Andric // always set the order to 0 (sequentially consistent). 1478bcb0991SDimitry Andric Fence = CurDAG->getMachineNode( 1488bcb0991SDimitry Andric WebAssembly::ATOMIC_FENCE, 1490b57cec5SDimitry Andric DL, // debug loc 1500b57cec5SDimitry Andric MVT::Other, // outchain type 1518bcb0991SDimitry Andric CurDAG->getTargetConstant(0, DL, MVT::i32), // order 1520b57cec5SDimitry Andric Node->getOperand(0) // inchain 1538bcb0991SDimitry Andric ); 1548bcb0991SDimitry Andric break; 1550b57cec5SDimitry Andric default: 1560b57cec5SDimitry Andric llvm_unreachable("Unknown scope!"); 1570b57cec5SDimitry Andric } 1588bcb0991SDimitry Andric 1598bcb0991SDimitry Andric ReplaceNode(Node, Fence); 1608bcb0991SDimitry Andric CurDAG->RemoveDeadNode(Node); 1618bcb0991SDimitry Andric return; 1620b57cec5SDimitry Andric } 1630b57cec5SDimitry Andric 1640b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: { 1650b57cec5SDimitry Andric unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 1660b57cec5SDimitry Andric switch (IntNo) { 1670b57cec5SDimitry Andric case Intrinsic::wasm_tls_size: { 1680b57cec5SDimitry Andric MachineSDNode *TLSSize = CurDAG->getMachineNode( 1695ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, 1705ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_size", PtrVT)); 1710b57cec5SDimitry Andric ReplaceNode(Node, TLSSize); 1720b57cec5SDimitry Andric return; 1730b57cec5SDimitry Andric } 1748bcb0991SDimitry Andric case Intrinsic::wasm_tls_align: { 1758bcb0991SDimitry Andric MachineSDNode *TLSAlign = CurDAG->getMachineNode( 1765ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, 1775ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_align", PtrVT)); 1788bcb0991SDimitry Andric ReplaceNode(Node, TLSAlign); 1798bcb0991SDimitry Andric return; 1808bcb0991SDimitry Andric } 1818bcb0991SDimitry Andric } 1828bcb0991SDimitry Andric break; 1838bcb0991SDimitry Andric } 1848bcb0991SDimitry Andric case ISD::INTRINSIC_W_CHAIN: { 1858bcb0991SDimitry Andric unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 1868bcb0991SDimitry Andric switch (IntNo) { 1878bcb0991SDimitry Andric case Intrinsic::wasm_tls_base: { 1888bcb0991SDimitry Andric MachineSDNode *TLSBase = CurDAG->getMachineNode( 1895ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, MVT::Other, 1908bcb0991SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 1918bcb0991SDimitry Andric Node->getOperand(0)); 1928bcb0991SDimitry Andric ReplaceNode(Node, TLSBase); 1938bcb0991SDimitry Andric return; 1948bcb0991SDimitry Andric } 1950b57cec5SDimitry Andric } 1960b57cec5SDimitry Andric break; 1970b57cec5SDimitry Andric } 1985ffd83dbSDimitry Andric case WebAssemblyISD::CALL: 1995ffd83dbSDimitry Andric case WebAssemblyISD::RET_CALL: { 2005ffd83dbSDimitry Andric // CALL has both variable operands and variable results, but ISel only 2015ffd83dbSDimitry Andric // supports one or the other. Split calls into two nodes glued together, one 2025ffd83dbSDimitry Andric // for the operands and one for the results. These two nodes will be 2035ffd83dbSDimitry Andric // recombined in a custom inserter hook into a single MachineInstr. 2045ffd83dbSDimitry Andric SmallVector<SDValue, 16> Ops; 2055ffd83dbSDimitry Andric for (size_t i = 1; i < Node->getNumOperands(); ++i) { 2065ffd83dbSDimitry Andric SDValue Op = Node->getOperand(i); 2075ffd83dbSDimitry Andric if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) 2085ffd83dbSDimitry Andric Op = Op->getOperand(0); 2095ffd83dbSDimitry Andric Ops.push_back(Op); 2105ffd83dbSDimitry Andric } 2115ffd83dbSDimitry Andric 2125ffd83dbSDimitry Andric // Add the chain last 2135ffd83dbSDimitry Andric Ops.push_back(Node->getOperand(0)); 2145ffd83dbSDimitry Andric MachineSDNode *CallParams = 2155ffd83dbSDimitry Andric CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops); 2165ffd83dbSDimitry Andric 2175ffd83dbSDimitry Andric unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL 2185ffd83dbSDimitry Andric ? WebAssembly::CALL_RESULTS 2195ffd83dbSDimitry Andric : WebAssembly::RET_CALL_RESULTS; 2205ffd83dbSDimitry Andric 2215ffd83dbSDimitry Andric SDValue Link(CallParams, 0); 2225ffd83dbSDimitry Andric MachineSDNode *CallResults = 2235ffd83dbSDimitry Andric CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link); 2245ffd83dbSDimitry Andric ReplaceNode(Node, CallResults); 2255ffd83dbSDimitry Andric return; 2265ffd83dbSDimitry Andric } 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric default: 2290b57cec5SDimitry Andric break; 2300b57cec5SDimitry Andric } 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric // Select the default instruction. 2330b57cec5SDimitry Andric SelectCode(Node); 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 2370b57cec5SDimitry Andric const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 2380b57cec5SDimitry Andric switch (ConstraintID) { 2390b57cec5SDimitry Andric case InlineAsm::Constraint_m: 2400b57cec5SDimitry Andric // We just support simple memory operands that just have a single address 2410b57cec5SDimitry Andric // operand and need no special handling. 2420b57cec5SDimitry Andric OutOps.push_back(Op); 2430b57cec5SDimitry Andric return false; 2440b57cec5SDimitry Andric default: 2450b57cec5SDimitry Andric break; 2460b57cec5SDimitry Andric } 2470b57cec5SDimitry Andric 2480b57cec5SDimitry Andric return true; 2490b57cec5SDimitry Andric } 2500b57cec5SDimitry Andric 2510b57cec5SDimitry Andric /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 2520b57cec5SDimitry Andric /// for instruction scheduling. 2530b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 2540b57cec5SDimitry Andric CodeGenOpt::Level OptLevel) { 2550b57cec5SDimitry Andric return new WebAssemblyDAGToDAGISel(TM, OptLevel); 2560b57cec5SDimitry Andric } 257