1 //- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file defines an instruction selector for the WebAssembly target. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 15 #include "WebAssembly.h" 16 #include "WebAssemblyTargetMachine.h" 17 #include "llvm/CodeGen/SelectionDAGISel.h" 18 #include "llvm/IR/DiagnosticInfo.h" 19 #include "llvm/IR/Function.h" // To access function attributes. 20 #include "llvm/IR/IntrinsicsWebAssembly.h" 21 #include "llvm/Support/Debug.h" 22 #include "llvm/Support/KnownBits.h" 23 #include "llvm/Support/MathExtras.h" 24 #include "llvm/Support/raw_ostream.h" 25 using namespace llvm; 26 27 #define DEBUG_TYPE "wasm-isel" 28 29 //===--------------------------------------------------------------------===// 30 /// WebAssembly-specific code to select WebAssembly machine instructions for 31 /// SelectionDAG operations. 32 /// 33 namespace { 34 class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 35 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 36 /// right decision when generating code for different targets. 37 const WebAssemblySubtarget *Subtarget; 38 39 public: 40 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 41 CodeGenOpt::Level OptLevel) 42 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 43 } 44 45 StringRef getPassName() const override { 46 return "WebAssembly Instruction Selection"; 47 } 48 49 bool runOnMachineFunction(MachineFunction &MF) override { 50 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 51 "********** Function: " 52 << MF.getName() << '\n'); 53 54 Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 55 56 // Wasm64 is not fully supported right now (and is not specified) 57 if (Subtarget->hasAddr64()) 58 report_fatal_error( 59 "64-bit WebAssembly (wasm64) is not currently supported"); 60 61 return SelectionDAGISel::runOnMachineFunction(MF); 62 } 63 64 void Select(SDNode *Node) override; 65 66 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 67 std::vector<SDValue> &OutOps) override; 68 69 // Include the pieces autogenerated from the target description. 70 #include "WebAssemblyGenDAGISel.inc" 71 72 private: 73 // add select functions here... 74 }; 75 } // end anonymous namespace 76 77 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 78 // If we have a custom node, we already have selected! 79 if (Node->isMachineOpcode()) { 80 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 81 Node->setNodeId(-1); 82 return; 83 } 84 85 // Few custom selection stuff. 86 SDLoc DL(Node); 87 MachineFunction &MF = CurDAG->getMachineFunction(); 88 switch (Node->getOpcode()) { 89 case ISD::ATOMIC_FENCE: { 90 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 91 break; 92 93 uint64_t SyncScopeID = 94 cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 95 MachineSDNode *Fence = nullptr; 96 switch (SyncScopeID) { 97 case SyncScope::SingleThread: 98 // We lower a single-thread fence to a pseudo compiler barrier instruction 99 // preventing instruction reordering. This will not be emitted in final 100 // binary. 101 Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 102 DL, // debug loc 103 MVT::Other, // outchain type 104 Node->getOperand(0) // inchain 105 ); 106 break; 107 case SyncScope::System: 108 // Currently wasm only supports sequentially consistent atomics, so we 109 // always set the order to 0 (sequentially consistent). 110 Fence = CurDAG->getMachineNode( 111 WebAssembly::ATOMIC_FENCE, 112 DL, // debug loc 113 MVT::Other, // outchain type 114 CurDAG->getTargetConstant(0, DL, MVT::i32), // order 115 Node->getOperand(0) // inchain 116 ); 117 break; 118 default: 119 llvm_unreachable("Unknown scope!"); 120 } 121 122 ReplaceNode(Node, Fence); 123 CurDAG->RemoveDeadNode(Node); 124 return; 125 } 126 127 case ISD::GlobalTLSAddress: { 128 const auto *GA = cast<GlobalAddressSDNode>(Node); 129 130 if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 131 report_fatal_error("cannot use thread-local storage without bulk memory", 132 false); 133 134 // Currently Emscripten does not support dynamic linking with threads. 135 // Therefore, if we have thread-local storage, only the local-exec model 136 // is possible. 137 // TODO: remove this and implement proper TLS models once Emscripten 138 // supports dynamic linking with threads. 139 if (GA->getGlobal()->getThreadLocalMode() != 140 GlobalValue::LocalExecTLSModel && 141 !Subtarget->getTargetTriple().isOSEmscripten()) { 142 report_fatal_error("only -ftls-model=local-exec is supported for now on " 143 "non-Emscripten OSes: variable " + 144 GA->getGlobal()->getName(), 145 false); 146 } 147 148 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 149 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 150 151 SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); 152 SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( 153 GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); 154 155 MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, 156 DL, MVT::i32, TLSBaseSym); 157 MachineSDNode *TLSOffset = CurDAG->getMachineNode( 158 WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); 159 MachineSDNode *TLSAddress = 160 CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, 161 SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); 162 ReplaceNode(Node, TLSAddress); 163 return; 164 } 165 166 case ISD::INTRINSIC_WO_CHAIN: { 167 unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 168 switch (IntNo) { 169 case Intrinsic::wasm_tls_size: { 170 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 171 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 172 173 MachineSDNode *TLSSize = CurDAG->getMachineNode( 174 WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 175 CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); 176 ReplaceNode(Node, TLSSize); 177 return; 178 } 179 case Intrinsic::wasm_tls_align: { 180 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 181 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 182 183 MachineSDNode *TLSAlign = CurDAG->getMachineNode( 184 WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 185 CurDAG->getTargetExternalSymbol("__tls_align", MVT::i32)); 186 ReplaceNode(Node, TLSAlign); 187 return; 188 } 189 } 190 break; 191 } 192 case ISD::INTRINSIC_W_CHAIN: { 193 unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 194 switch (IntNo) { 195 case Intrinsic::wasm_tls_base: { 196 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 197 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 198 199 MachineSDNode *TLSBase = CurDAG->getMachineNode( 200 WebAssembly::GLOBAL_GET_I32, DL, MVT::i32, MVT::Other, 201 CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 202 Node->getOperand(0)); 203 ReplaceNode(Node, TLSBase); 204 return; 205 } 206 } 207 break; 208 } 209 210 default: 211 break; 212 } 213 214 // Select the default instruction. 215 SelectCode(Node); 216 } 217 218 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 219 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 220 switch (ConstraintID) { 221 case InlineAsm::Constraint_m: 222 // We just support simple memory operands that just have a single address 223 // operand and need no special handling. 224 OutOps.push_back(Op); 225 return false; 226 default: 227 break; 228 } 229 230 return true; 231 } 232 233 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 234 /// for instruction scheduling. 235 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 236 CodeGenOpt::Level OptLevel) { 237 return new WebAssemblyDAGToDAGISel(TM, OptLevel); 238 } 239