1285163Sdim//- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -// 2285163Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6285163Sdim// 7285163Sdim//===----------------------------------------------------------------------===// 8285163Sdim/// 9285163Sdim/// \file 10341825Sdim/// This file defines an instruction selector for the WebAssembly target. 11285163Sdim/// 12285163Sdim//===----------------------------------------------------------------------===// 13285163Sdim 14321369Sdim#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 15285163Sdim#include "WebAssembly.h" 16285163Sdim#include "WebAssemblyTargetMachine.h" 17285163Sdim#include "llvm/CodeGen/SelectionDAGISel.h" 18353358Sdim#include "llvm/IR/DiagnosticInfo.h" 19285163Sdim#include "llvm/IR/Function.h" // To access function attributes. 20360784Sdim#include "llvm/IR/IntrinsicsWebAssembly.h" 21285163Sdim#include "llvm/Support/Debug.h" 22321369Sdim#include "llvm/Support/KnownBits.h" 23285163Sdim#include "llvm/Support/MathExtras.h" 24285163Sdim#include "llvm/Support/raw_ostream.h" 25285163Sdimusing namespace llvm; 26285163Sdim 27285163Sdim#define DEBUG_TYPE "wasm-isel" 28285163Sdim 29285163Sdim//===--------------------------------------------------------------------===// 30285163Sdim/// WebAssembly-specific code to select WebAssembly machine instructions for 31285163Sdim/// SelectionDAG operations. 32285163Sdim/// 33285163Sdimnamespace { 34285163Sdimclass WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 35285163Sdim /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 36285163Sdim /// right decision when generating code for different targets. 37285163Sdim const WebAssemblySubtarget *Subtarget; 38285163Sdim 39285163Sdimpublic: 40353358Sdim WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 41285163Sdim CodeGenOpt::Level OptLevel) 42360784Sdim : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 43285163Sdim } 44285163Sdim 45314564Sdim StringRef getPassName() const override { 46285163Sdim return "WebAssembly Instruction Selection"; 47285163Sdim } 48285163Sdim 49285163Sdim bool runOnMachineFunction(MachineFunction &MF) override { 50344779Sdim LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 51344779Sdim "********** Function: " 52344779Sdim << MF.getName() << '\n'); 53344779Sdim 54285163Sdim Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 55360784Sdim 56360784Sdim // Wasm64 is not fully supported right now (and is not specified) 57360784Sdim if (Subtarget->hasAddr64()) 58360784Sdim report_fatal_error( 59360784Sdim "64-bit WebAssembly (wasm64) is not currently supported"); 60360784Sdim 61285163Sdim return SelectionDAGISel::runOnMachineFunction(MF); 62285163Sdim } 63285163Sdim 64309124Sdim void Select(SDNode *Node) override; 65285163Sdim 66296417Sdim bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 67296417Sdim std::vector<SDValue> &OutOps) override; 68296417Sdim 69296417Sdim// Include the pieces autogenerated from the target description. 70296417Sdim#include "WebAssemblyGenDAGISel.inc" 71296417Sdim 72285163Sdimprivate: 73285163Sdim // add select functions here... 74285163Sdim}; 75285163Sdim} // end anonymous namespace 76285163Sdim 77309124Sdimvoid WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 78296417Sdim // If we have a custom node, we already have selected! 79296417Sdim if (Node->isMachineOpcode()) { 80341825Sdim LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 81296417Sdim Node->setNodeId(-1); 82309124Sdim return; 83296417Sdim } 84296417Sdim 85353358Sdim // Few custom selection stuff. 86353358Sdim SDLoc DL(Node); 87353358Sdim MachineFunction &MF = CurDAG->getMachineFunction(); 88296417Sdim switch (Node->getOpcode()) { 89353358Sdim case ISD::ATOMIC_FENCE: { 90353358Sdim if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 91353358Sdim break; 92353358Sdim 93353358Sdim uint64_t SyncScopeID = 94353358Sdim cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 95360784Sdim MachineSDNode *Fence = nullptr; 96353358Sdim switch (SyncScopeID) { 97360784Sdim case SyncScope::SingleThread: 98353358Sdim // We lower a single-thread fence to a pseudo compiler barrier instruction 99353358Sdim // preventing instruction reordering. This will not be emitted in final 100353358Sdim // binary. 101360784Sdim Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 102360784Sdim DL, // debug loc 103360784Sdim MVT::Other, // outchain type 104360784Sdim Node->getOperand(0) // inchain 105360784Sdim ); 106360784Sdim break; 107360784Sdim case SyncScope::System: 108360784Sdim // Currently wasm only supports sequentially consistent atomics, so we 109360784Sdim // always set the order to 0 (sequentially consistent). 110360784Sdim Fence = CurDAG->getMachineNode( 111360784Sdim WebAssembly::ATOMIC_FENCE, 112360784Sdim DL, // debug loc 113360784Sdim MVT::Other, // outchain type 114360784Sdim CurDAG->getTargetConstant(0, DL, MVT::i32), // order 115360784Sdim Node->getOperand(0) // inchain 116360784Sdim ); 117360784Sdim break; 118353358Sdim default: 119353358Sdim llvm_unreachable("Unknown scope!"); 120353358Sdim } 121360784Sdim 122360784Sdim ReplaceNode(Node, Fence); 123360784Sdim CurDAG->RemoveDeadNode(Node); 124360784Sdim return; 125353358Sdim } 126353358Sdim 127353358Sdim case ISD::GlobalTLSAddress: { 128353358Sdim const auto *GA = cast<GlobalAddressSDNode>(Node); 129353358Sdim 130353358Sdim if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 131353358Sdim report_fatal_error("cannot use thread-local storage without bulk memory", 132353358Sdim false); 133353358Sdim 134353358Sdim // Currently Emscripten does not support dynamic linking with threads. 135353358Sdim // Therefore, if we have thread-local storage, only the local-exec model 136353358Sdim // is possible. 137353358Sdim // TODO: remove this and implement proper TLS models once Emscripten 138353358Sdim // supports dynamic linking with threads. 139353358Sdim if (GA->getGlobal()->getThreadLocalMode() != 140353358Sdim GlobalValue::LocalExecTLSModel && 141353358Sdim !Subtarget->getTargetTriple().isOSEmscripten()) { 142353358Sdim report_fatal_error("only -ftls-model=local-exec is supported for now on " 143353358Sdim "non-Emscripten OSes: variable " + 144353358Sdim GA->getGlobal()->getName(), 145353358Sdim false); 146353358Sdim } 147353358Sdim 148353358Sdim MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 149353358Sdim assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 150353358Sdim 151353358Sdim SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); 152353358Sdim SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( 153353358Sdim GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); 154353358Sdim 155353358Sdim MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, 156353358Sdim DL, MVT::i32, TLSBaseSym); 157353358Sdim MachineSDNode *TLSOffset = CurDAG->getMachineNode( 158353358Sdim WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); 159353358Sdim MachineSDNode *TLSAddress = 160353358Sdim CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, 161353358Sdim SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); 162353358Sdim ReplaceNode(Node, TLSAddress); 163353358Sdim return; 164353358Sdim } 165353358Sdim 166353358Sdim case ISD::INTRINSIC_WO_CHAIN: { 167353358Sdim unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 168353358Sdim switch (IntNo) { 169353358Sdim case Intrinsic::wasm_tls_size: { 170353358Sdim MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 171353358Sdim assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 172353358Sdim 173353358Sdim MachineSDNode *TLSSize = CurDAG->getMachineNode( 174353358Sdim WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 175353358Sdim CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); 176353358Sdim ReplaceNode(Node, TLSSize); 177353358Sdim return; 178353358Sdim } 179360784Sdim case Intrinsic::wasm_tls_align: { 180360784Sdim MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 181360784Sdim assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 182360784Sdim 183360784Sdim MachineSDNode *TLSAlign = CurDAG->getMachineNode( 184360784Sdim WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 185360784Sdim CurDAG->getTargetExternalSymbol("__tls_align", MVT::i32)); 186360784Sdim ReplaceNode(Node, TLSAlign); 187360784Sdim return; 188353358Sdim } 189360784Sdim } 190353358Sdim break; 191353358Sdim } 192360784Sdim case ISD::INTRINSIC_W_CHAIN: { 193360784Sdim unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 194360784Sdim switch (IntNo) { 195360784Sdim case Intrinsic::wasm_tls_base: { 196360784Sdim MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 197360784Sdim assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 198353358Sdim 199360784Sdim MachineSDNode *TLSBase = CurDAG->getMachineNode( 200360784Sdim WebAssembly::GLOBAL_GET_I32, DL, MVT::i32, MVT::Other, 201360784Sdim CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 202360784Sdim Node->getOperand(0)); 203360784Sdim ReplaceNode(Node, TLSBase); 204360784Sdim return; 205360784Sdim } 206360784Sdim } 207360784Sdim break; 208360784Sdim } 209360784Sdim 210296417Sdim default: 211296417Sdim break; 212296417Sdim } 213296417Sdim 214296417Sdim // Select the default instruction. 215309124Sdim SelectCode(Node); 216285163Sdim} 217285163Sdim 218296417Sdimbool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 219296417Sdim const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 220296417Sdim switch (ConstraintID) { 221296417Sdim case InlineAsm::Constraint_m: 222296417Sdim // We just support simple memory operands that just have a single address 223296417Sdim // operand and need no special handling. 224296417Sdim OutOps.push_back(Op); 225296417Sdim return false; 226296417Sdim default: 227296417Sdim break; 228296417Sdim } 229296417Sdim 230296417Sdim return true; 231296417Sdim} 232296417Sdim 233285163Sdim/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 234285163Sdim/// for instruction scheduling. 235285163SdimFunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 236285163Sdim CodeGenOpt::Level OptLevel) { 237285163Sdim return new WebAssemblyDAGToDAGISel(TM, OptLevel); 238285163Sdim} 239