WebAssemblyISelDAGToDAG.cpp revision 353358
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. 20285163Sdim#include "llvm/Support/Debug.h" 21321369Sdim#include "llvm/Support/KnownBits.h" 22285163Sdim#include "llvm/Support/MathExtras.h" 23285163Sdim#include "llvm/Support/raw_ostream.h" 24285163Sdimusing namespace llvm; 25285163Sdim 26285163Sdim#define DEBUG_TYPE "wasm-isel" 27285163Sdim 28285163Sdim//===--------------------------------------------------------------------===// 29285163Sdim/// WebAssembly-specific code to select WebAssembly machine instructions for 30285163Sdim/// SelectionDAG operations. 31285163Sdim/// 32285163Sdimnamespace { 33285163Sdimclass WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 34285163Sdim /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 35285163Sdim /// right decision when generating code for different targets. 36285163Sdim const WebAssemblySubtarget *Subtarget; 37285163Sdim 38285163Sdim bool ForCodeSize; 39285163Sdim 40285163Sdimpublic: 41353358Sdim WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 42285163Sdim CodeGenOpt::Level OptLevel) 43353358Sdim : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr), ForCodeSize(false) { 44285163Sdim } 45285163Sdim 46314564Sdim StringRef getPassName() const override { 47285163Sdim return "WebAssembly Instruction Selection"; 48285163Sdim } 49285163Sdim 50285163Sdim bool runOnMachineFunction(MachineFunction &MF) override { 51344779Sdim LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 52344779Sdim "********** Function: " 53344779Sdim << MF.getName() << '\n'); 54344779Sdim 55353358Sdim ForCodeSize = MF.getFunction().hasOptSize(); 56285163Sdim Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 57285163Sdim return SelectionDAGISel::runOnMachineFunction(MF); 58285163Sdim } 59285163Sdim 60309124Sdim void Select(SDNode *Node) override; 61285163Sdim 62296417Sdim bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 63296417Sdim std::vector<SDValue> &OutOps) override; 64296417Sdim 65296417Sdim// Include the pieces autogenerated from the target description. 66296417Sdim#include "WebAssemblyGenDAGISel.inc" 67296417Sdim 68285163Sdimprivate: 69285163Sdim // add select functions here... 70285163Sdim}; 71285163Sdim} // end anonymous namespace 72285163Sdim 73309124Sdimvoid WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 74296417Sdim // If we have a custom node, we already have selected! 75296417Sdim if (Node->isMachineOpcode()) { 76341825Sdim LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 77296417Sdim Node->setNodeId(-1); 78309124Sdim return; 79296417Sdim } 80296417Sdim 81353358Sdim // Few custom selection stuff. 82353358Sdim SDLoc DL(Node); 83353358Sdim MachineFunction &MF = CurDAG->getMachineFunction(); 84296417Sdim switch (Node->getOpcode()) { 85353358Sdim case ISD::ATOMIC_FENCE: { 86353358Sdim if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 87353358Sdim break; 88353358Sdim 89353358Sdim uint64_t SyncScopeID = 90353358Sdim cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 91353358Sdim switch (SyncScopeID) { 92353358Sdim case SyncScope::SingleThread: { 93353358Sdim // We lower a single-thread fence to a pseudo compiler barrier instruction 94353358Sdim // preventing instruction reordering. This will not be emitted in final 95353358Sdim // binary. 96353358Sdim MachineSDNode *Fence = 97353358Sdim CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 98353358Sdim DL, // debug loc 99353358Sdim MVT::Other, // outchain type 100353358Sdim Node->getOperand(0) // inchain 101353358Sdim ); 102353358Sdim ReplaceNode(Node, Fence); 103353358Sdim CurDAG->RemoveDeadNode(Node); 104353358Sdim return; 105353358Sdim } 106353358Sdim 107353358Sdim case SyncScope::System: { 108353358Sdim // For non-emscripten systems, we have not decided on what we should 109353358Sdim // traslate fences to yet. 110353358Sdim if (!Subtarget->getTargetTriple().isOSEmscripten()) 111353358Sdim report_fatal_error( 112353358Sdim "ATOMIC_FENCE is not yet supported in non-emscripten OSes"); 113353358Sdim 114353358Sdim // Wasm does not have a fence instruction, but because all atomic 115353358Sdim // instructions in wasm are sequentially consistent, we translate a 116353358Sdim // fence to an idempotent atomic RMW instruction to a linear memory 117353358Sdim // address. All atomic instructions in wasm are sequentially consistent, 118353358Sdim // but this is to ensure a fence also prevents reordering of non-atomic 119353358Sdim // instructions in the VM. Even though LLVM IR's fence instruction does 120353358Sdim // not say anything about its relationship with non-atomic instructions, 121353358Sdim // we think this is more user-friendly. 122353358Sdim // 123353358Sdim // While any address can work, here we use a value stored in 124353358Sdim // __stack_pointer wasm global because there's high chance that area is 125353358Sdim // in cache. 126353358Sdim // 127353358Sdim // So the selected instructions will be in the form of: 128353358Sdim // %addr = get_global $__stack_pointer 129353358Sdim // %0 = i32.const 0 130353358Sdim // i32.atomic.rmw.or %addr, %0 131353358Sdim SDValue StackPtrSym = CurDAG->getTargetExternalSymbol( 132353358Sdim "__stack_pointer", TLI->getPointerTy(CurDAG->getDataLayout())); 133353358Sdim MachineSDNode *GetGlobal = 134353358Sdim CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, // opcode 135353358Sdim DL, // debug loc 136353358Sdim MVT::i32, // result type 137353358Sdim StackPtrSym // __stack_pointer symbol 138353358Sdim ); 139353358Sdim 140353358Sdim SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32); 141353358Sdim auto *MMO = MF.getMachineMemOperand( 142353358Sdim MachinePointerInfo::getUnknownStack(MF), 143353358Sdim // FIXME Volatile isn't really correct, but currently all LLVM 144353358Sdim // atomic instructions are treated as volatiles in the backend, so 145353358Sdim // we should be consistent. 146353358Sdim MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad | 147353358Sdim MachineMemOperand::MOStore, 148353358Sdim 4, 4, AAMDNodes(), nullptr, SyncScope::System, 149353358Sdim AtomicOrdering::SequentiallyConsistent); 150353358Sdim MachineSDNode *Const0 = 151353358Sdim CurDAG->getMachineNode(WebAssembly::CONST_I32, DL, MVT::i32, Zero); 152353358Sdim MachineSDNode *AtomicRMW = CurDAG->getMachineNode( 153353358Sdim WebAssembly::ATOMIC_RMW_OR_I32, // opcode 154353358Sdim DL, // debug loc 155353358Sdim MVT::i32, // result type 156353358Sdim MVT::Other, // outchain type 157353358Sdim { 158353358Sdim Zero, // alignment 159353358Sdim Zero, // offset 160353358Sdim SDValue(GetGlobal, 0), // __stack_pointer 161353358Sdim SDValue(Const0, 0), // OR with 0 to make it idempotent 162353358Sdim Node->getOperand(0) // inchain 163353358Sdim }); 164353358Sdim 165353358Sdim CurDAG->setNodeMemRefs(AtomicRMW, {MMO}); 166353358Sdim ReplaceUses(SDValue(Node, 0), SDValue(AtomicRMW, 1)); 167353358Sdim CurDAG->RemoveDeadNode(Node); 168353358Sdim return; 169353358Sdim } 170353358Sdim default: 171353358Sdim llvm_unreachable("Unknown scope!"); 172353358Sdim } 173353358Sdim } 174353358Sdim 175353358Sdim case ISD::GlobalTLSAddress: { 176353358Sdim const auto *GA = cast<GlobalAddressSDNode>(Node); 177353358Sdim 178353358Sdim if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 179353358Sdim report_fatal_error("cannot use thread-local storage without bulk memory", 180353358Sdim false); 181353358Sdim 182353358Sdim // Currently Emscripten does not support dynamic linking with threads. 183353358Sdim // Therefore, if we have thread-local storage, only the local-exec model 184353358Sdim // is possible. 185353358Sdim // TODO: remove this and implement proper TLS models once Emscripten 186353358Sdim // supports dynamic linking with threads. 187353358Sdim if (GA->getGlobal()->getThreadLocalMode() != 188353358Sdim GlobalValue::LocalExecTLSModel && 189353358Sdim !Subtarget->getTargetTriple().isOSEmscripten()) { 190353358Sdim report_fatal_error("only -ftls-model=local-exec is supported for now on " 191353358Sdim "non-Emscripten OSes: variable " + 192353358Sdim GA->getGlobal()->getName(), 193353358Sdim false); 194353358Sdim } 195353358Sdim 196353358Sdim MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 197353358Sdim assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 198353358Sdim 199353358Sdim SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); 200353358Sdim SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( 201353358Sdim GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); 202353358Sdim 203353358Sdim MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, 204353358Sdim DL, MVT::i32, TLSBaseSym); 205353358Sdim MachineSDNode *TLSOffset = CurDAG->getMachineNode( 206353358Sdim WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); 207353358Sdim MachineSDNode *TLSAddress = 208353358Sdim CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, 209353358Sdim SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); 210353358Sdim ReplaceNode(Node, TLSAddress); 211353358Sdim return; 212353358Sdim } 213353358Sdim 214353358Sdim case ISD::INTRINSIC_WO_CHAIN: { 215353358Sdim unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 216353358Sdim switch (IntNo) { 217353358Sdim case Intrinsic::wasm_tls_size: { 218353358Sdim MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 219353358Sdim assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 220353358Sdim 221353358Sdim MachineSDNode *TLSSize = CurDAG->getMachineNode( 222353358Sdim WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 223353358Sdim CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); 224353358Sdim ReplaceNode(Node, TLSSize); 225353358Sdim return; 226353358Sdim } 227353358Sdim } 228353358Sdim break; 229353358Sdim } 230353358Sdim 231296417Sdim default: 232296417Sdim break; 233296417Sdim } 234296417Sdim 235296417Sdim // Select the default instruction. 236309124Sdim SelectCode(Node); 237285163Sdim} 238285163Sdim 239296417Sdimbool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 240296417Sdim const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 241296417Sdim switch (ConstraintID) { 242296417Sdim case InlineAsm::Constraint_i: 243296417Sdim case InlineAsm::Constraint_m: 244296417Sdim // We just support simple memory operands that just have a single address 245296417Sdim // operand and need no special handling. 246296417Sdim OutOps.push_back(Op); 247296417Sdim return false; 248296417Sdim default: 249296417Sdim break; 250296417Sdim } 251296417Sdim 252296417Sdim return true; 253296417Sdim} 254296417Sdim 255285163Sdim/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 256285163Sdim/// for instruction scheduling. 257285163SdimFunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 258285163Sdim CodeGenOpt::Level OptLevel) { 259285163Sdim return new WebAssemblyDAGToDAGISel(TM, OptLevel); 260285163Sdim} 261