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" 160b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h" 170b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAGISel.h" 180b57cec5SDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 190b57cec5SDimitry Andric #include "llvm/IR/Function.h" // To access function attributes. 20*480093f4SDimitry Andric #include "llvm/IR/IntrinsicsWebAssembly.h" 210b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 220b57cec5SDimitry Andric #include "llvm/Support/KnownBits.h" 230b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 240b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 250b57cec5SDimitry Andric using namespace llvm; 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-isel" 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric //===--------------------------------------------------------------------===// 300b57cec5SDimitry Andric /// WebAssembly-specific code to select WebAssembly machine instructions for 310b57cec5SDimitry Andric /// SelectionDAG operations. 320b57cec5SDimitry Andric /// 330b57cec5SDimitry Andric namespace { 340b57cec5SDimitry Andric class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 350b57cec5SDimitry Andric /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 360b57cec5SDimitry Andric /// right decision when generating code for different targets. 370b57cec5SDimitry Andric const WebAssemblySubtarget *Subtarget; 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric public: 400b57cec5SDimitry Andric WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 410b57cec5SDimitry Andric CodeGenOpt::Level OptLevel) 42*480093f4SDimitry Andric : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 430b57cec5SDimitry Andric } 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric StringRef getPassName() const override { 460b57cec5SDimitry Andric return "WebAssembly Instruction Selection"; 470b57cec5SDimitry Andric } 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override { 500b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 510b57cec5SDimitry Andric "********** Function: " 520b57cec5SDimitry Andric << MF.getName() << '\n'); 530b57cec5SDimitry Andric 540b57cec5SDimitry Andric Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 558bcb0991SDimitry Andric 568bcb0991SDimitry Andric // Wasm64 is not fully supported right now (and is not specified) 578bcb0991SDimitry Andric if (Subtarget->hasAddr64()) 588bcb0991SDimitry Andric report_fatal_error( 598bcb0991SDimitry Andric "64-bit WebAssembly (wasm64) is not currently supported"); 608bcb0991SDimitry Andric 610b57cec5SDimitry Andric return SelectionDAGISel::runOnMachineFunction(MF); 620b57cec5SDimitry Andric } 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric void Select(SDNode *Node) override; 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 670b57cec5SDimitry Andric std::vector<SDValue> &OutOps) override; 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric // Include the pieces autogenerated from the target description. 700b57cec5SDimitry Andric #include "WebAssemblyGenDAGISel.inc" 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric private: 730b57cec5SDimitry Andric // add select functions here... 740b57cec5SDimitry Andric }; 750b57cec5SDimitry Andric } // end anonymous namespace 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 780b57cec5SDimitry Andric // If we have a custom node, we already have selected! 790b57cec5SDimitry Andric if (Node->isMachineOpcode()) { 800b57cec5SDimitry Andric LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 810b57cec5SDimitry Andric Node->setNodeId(-1); 820b57cec5SDimitry Andric return; 830b57cec5SDimitry Andric } 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric // Few custom selection stuff. 860b57cec5SDimitry Andric SDLoc DL(Node); 870b57cec5SDimitry Andric MachineFunction &MF = CurDAG->getMachineFunction(); 880b57cec5SDimitry Andric switch (Node->getOpcode()) { 890b57cec5SDimitry Andric case ISD::ATOMIC_FENCE: { 900b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 910b57cec5SDimitry Andric break; 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric uint64_t SyncScopeID = 940b57cec5SDimitry Andric cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 958bcb0991SDimitry Andric MachineSDNode *Fence = nullptr; 960b57cec5SDimitry Andric switch (SyncScopeID) { 978bcb0991SDimitry Andric case SyncScope::SingleThread: 980b57cec5SDimitry Andric // We lower a single-thread fence to a pseudo compiler barrier instruction 990b57cec5SDimitry Andric // preventing instruction reordering. This will not be emitted in final 1000b57cec5SDimitry Andric // binary. 1018bcb0991SDimitry Andric Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 1020b57cec5SDimitry Andric DL, // debug loc 1030b57cec5SDimitry Andric MVT::Other, // outchain type 1040b57cec5SDimitry Andric Node->getOperand(0) // inchain 1050b57cec5SDimitry Andric ); 1068bcb0991SDimitry Andric break; 1078bcb0991SDimitry Andric case SyncScope::System: 1088bcb0991SDimitry Andric // Currently wasm only supports sequentially consistent atomics, so we 1098bcb0991SDimitry Andric // always set the order to 0 (sequentially consistent). 1108bcb0991SDimitry Andric Fence = CurDAG->getMachineNode( 1118bcb0991SDimitry Andric WebAssembly::ATOMIC_FENCE, 1120b57cec5SDimitry Andric DL, // debug loc 1130b57cec5SDimitry Andric MVT::Other, // outchain type 1148bcb0991SDimitry Andric CurDAG->getTargetConstant(0, DL, MVT::i32), // order 1150b57cec5SDimitry Andric Node->getOperand(0) // inchain 1168bcb0991SDimitry Andric ); 1178bcb0991SDimitry Andric break; 1180b57cec5SDimitry Andric default: 1190b57cec5SDimitry Andric llvm_unreachable("Unknown scope!"); 1200b57cec5SDimitry Andric } 1218bcb0991SDimitry Andric 1228bcb0991SDimitry Andric ReplaceNode(Node, Fence); 1238bcb0991SDimitry Andric CurDAG->RemoveDeadNode(Node); 1248bcb0991SDimitry Andric return; 1250b57cec5SDimitry Andric } 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric case ISD::GlobalTLSAddress: { 1280b57cec5SDimitry Andric const auto *GA = cast<GlobalAddressSDNode>(Node); 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 1310b57cec5SDimitry Andric report_fatal_error("cannot use thread-local storage without bulk memory", 1320b57cec5SDimitry Andric false); 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric // Currently Emscripten does not support dynamic linking with threads. 1350b57cec5SDimitry Andric // Therefore, if we have thread-local storage, only the local-exec model 1360b57cec5SDimitry Andric // is possible. 1370b57cec5SDimitry Andric // TODO: remove this and implement proper TLS models once Emscripten 1380b57cec5SDimitry Andric // supports dynamic linking with threads. 1390b57cec5SDimitry Andric if (GA->getGlobal()->getThreadLocalMode() != 1400b57cec5SDimitry Andric GlobalValue::LocalExecTLSModel && 1410b57cec5SDimitry Andric !Subtarget->getTargetTriple().isOSEmscripten()) { 1420b57cec5SDimitry Andric report_fatal_error("only -ftls-model=local-exec is supported for now on " 1430b57cec5SDimitry Andric "non-Emscripten OSes: variable " + 1440b57cec5SDimitry Andric GA->getGlobal()->getName(), 1450b57cec5SDimitry Andric false); 1460b57cec5SDimitry Andric } 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1490b57cec5SDimitry Andric assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); 1520b57cec5SDimitry Andric SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( 1530b57cec5SDimitry Andric GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, 1560b57cec5SDimitry Andric DL, MVT::i32, TLSBaseSym); 1570b57cec5SDimitry Andric MachineSDNode *TLSOffset = CurDAG->getMachineNode( 1580b57cec5SDimitry Andric WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); 1590b57cec5SDimitry Andric MachineSDNode *TLSAddress = 1600b57cec5SDimitry Andric CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, 1610b57cec5SDimitry Andric SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); 1620b57cec5SDimitry Andric ReplaceNode(Node, TLSAddress); 1630b57cec5SDimitry Andric return; 1640b57cec5SDimitry Andric } 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: { 1670b57cec5SDimitry Andric unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 1680b57cec5SDimitry Andric switch (IntNo) { 1690b57cec5SDimitry Andric case Intrinsic::wasm_tls_size: { 1700b57cec5SDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1710b57cec5SDimitry Andric assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 1720b57cec5SDimitry Andric 1730b57cec5SDimitry Andric MachineSDNode *TLSSize = CurDAG->getMachineNode( 1740b57cec5SDimitry Andric WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 1750b57cec5SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); 1760b57cec5SDimitry Andric ReplaceNode(Node, TLSSize); 1770b57cec5SDimitry Andric return; 1780b57cec5SDimitry Andric } 1798bcb0991SDimitry Andric case Intrinsic::wasm_tls_align: { 1808bcb0991SDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1818bcb0991SDimitry Andric assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 1828bcb0991SDimitry Andric 1838bcb0991SDimitry Andric MachineSDNode *TLSAlign = CurDAG->getMachineNode( 1848bcb0991SDimitry Andric WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 1858bcb0991SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_align", MVT::i32)); 1868bcb0991SDimitry Andric ReplaceNode(Node, TLSAlign); 1878bcb0991SDimitry Andric return; 1888bcb0991SDimitry Andric } 1898bcb0991SDimitry Andric } 1908bcb0991SDimitry Andric break; 1918bcb0991SDimitry Andric } 1928bcb0991SDimitry Andric case ISD::INTRINSIC_W_CHAIN: { 1938bcb0991SDimitry Andric unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 1948bcb0991SDimitry Andric switch (IntNo) { 1958bcb0991SDimitry Andric case Intrinsic::wasm_tls_base: { 1968bcb0991SDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1978bcb0991SDimitry Andric assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 1988bcb0991SDimitry Andric 1998bcb0991SDimitry Andric MachineSDNode *TLSBase = CurDAG->getMachineNode( 2008bcb0991SDimitry Andric WebAssembly::GLOBAL_GET_I32, DL, MVT::i32, MVT::Other, 2018bcb0991SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 2028bcb0991SDimitry Andric Node->getOperand(0)); 2038bcb0991SDimitry Andric ReplaceNode(Node, TLSBase); 2048bcb0991SDimitry Andric return; 2058bcb0991SDimitry Andric } 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric break; 2080b57cec5SDimitry Andric } 2090b57cec5SDimitry Andric 2100b57cec5SDimitry Andric default: 2110b57cec5SDimitry Andric break; 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric // Select the default instruction. 2150b57cec5SDimitry Andric SelectCode(Node); 2160b57cec5SDimitry Andric } 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 2190b57cec5SDimitry Andric const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 2200b57cec5SDimitry Andric switch (ConstraintID) { 2210b57cec5SDimitry Andric case InlineAsm::Constraint_m: 2220b57cec5SDimitry Andric // We just support simple memory operands that just have a single address 2230b57cec5SDimitry Andric // operand and need no special handling. 2240b57cec5SDimitry Andric OutOps.push_back(Op); 2250b57cec5SDimitry Andric return false; 2260b57cec5SDimitry Andric default: 2270b57cec5SDimitry Andric break; 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric return true; 2310b57cec5SDimitry Andric } 2320b57cec5SDimitry Andric 2330b57cec5SDimitry Andric /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 2340b57cec5SDimitry Andric /// for instruction scheduling. 2350b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 2360b57cec5SDimitry Andric CodeGenOpt::Level OptLevel) { 2370b57cec5SDimitry Andric return new WebAssemblyDAGToDAGISel(TM, OptLevel); 2380b57cec5SDimitry Andric } 239