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 return SelectionDAGISel::runOnMachineFunction(MF); 57 } 58 59 void Select(SDNode *Node) override; 60 61 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 62 std::vector<SDValue> &OutOps) override; 63 64 // Include the pieces autogenerated from the target description. 65 #include "WebAssemblyGenDAGISel.inc" 66 67 private: 68 // add select functions here... 69 }; 70 } // end anonymous namespace 71 72 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 73 // If we have a custom node, we already have selected! 74 if (Node->isMachineOpcode()) { 75 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 76 Node->setNodeId(-1); 77 return; 78 } 79 80 // Few custom selection stuff. 81 SDLoc DL(Node); 82 MachineFunction &MF = CurDAG->getMachineFunction(); 83 switch (Node->getOpcode()) { 84 case ISD::ATOMIC_FENCE: { 85 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 86 break; 87 88 uint64_t SyncScopeID = 89 cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 90 MachineSDNode *Fence = nullptr; 91 switch (SyncScopeID) { 92 case SyncScope::SingleThread: 93 // We lower a single-thread fence to a pseudo compiler barrier instruction 94 // preventing instruction reordering. This will not be emitted in final 95 // binary. 96 Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 97 DL, // debug loc 98 MVT::Other, // outchain type 99 Node->getOperand(0) // inchain 100 ); 101 break; 102 case SyncScope::System: 103 // Currently wasm only supports sequentially consistent atomics, so we 104 // always set the order to 0 (sequentially consistent). 105 Fence = CurDAG->getMachineNode( 106 WebAssembly::ATOMIC_FENCE, 107 DL, // debug loc 108 MVT::Other, // outchain type 109 CurDAG->getTargetConstant(0, DL, MVT::i32), // order 110 Node->getOperand(0) // inchain 111 ); 112 break; 113 default: 114 llvm_unreachable("Unknown scope!"); 115 } 116 117 ReplaceNode(Node, Fence); 118 CurDAG->RemoveDeadNode(Node); 119 return; 120 } 121 122 case ISD::GlobalTLSAddress: { 123 const auto *GA = cast<GlobalAddressSDNode>(Node); 124 125 if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 126 report_fatal_error("cannot use thread-local storage without bulk memory", 127 false); 128 129 // Currently Emscripten does not support dynamic linking with threads. 130 // Therefore, if we have thread-local storage, only the local-exec model 131 // is possible. 132 // TODO: remove this and implement proper TLS models once Emscripten 133 // supports dynamic linking with threads. 134 if (GA->getGlobal()->getThreadLocalMode() != 135 GlobalValue::LocalExecTLSModel && 136 !Subtarget->getTargetTriple().isOSEmscripten()) { 137 report_fatal_error("only -ftls-model=local-exec is supported for now on " 138 "non-Emscripten OSes: variable " + 139 GA->getGlobal()->getName(), 140 false); 141 } 142 143 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 144 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 145 146 SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); 147 SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( 148 GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); 149 150 MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, 151 DL, MVT::i32, TLSBaseSym); 152 MachineSDNode *TLSOffset = CurDAG->getMachineNode( 153 WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); 154 MachineSDNode *TLSAddress = 155 CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, 156 SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); 157 ReplaceNode(Node, TLSAddress); 158 return; 159 } 160 161 case ISD::INTRINSIC_WO_CHAIN: { 162 unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 163 switch (IntNo) { 164 case Intrinsic::wasm_tls_size: { 165 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 166 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 167 168 MachineSDNode *TLSSize = CurDAG->getMachineNode( 169 WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 170 CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); 171 ReplaceNode(Node, TLSSize); 172 return; 173 } 174 case Intrinsic::wasm_tls_align: { 175 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 176 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 177 178 MachineSDNode *TLSAlign = CurDAG->getMachineNode( 179 WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 180 CurDAG->getTargetExternalSymbol("__tls_align", MVT::i32)); 181 ReplaceNode(Node, TLSAlign); 182 return; 183 } 184 } 185 break; 186 } 187 case ISD::INTRINSIC_W_CHAIN: { 188 unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 189 switch (IntNo) { 190 case Intrinsic::wasm_tls_base: { 191 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 192 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 193 194 MachineSDNode *TLSBase = CurDAG->getMachineNode( 195 WebAssembly::GLOBAL_GET_I32, DL, MVT::i32, MVT::Other, 196 CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 197 Node->getOperand(0)); 198 ReplaceNode(Node, TLSBase); 199 return; 200 } 201 } 202 break; 203 } 204 case WebAssemblyISD::CALL: 205 case WebAssemblyISD::RET_CALL: { 206 // CALL has both variable operands and variable results, but ISel only 207 // supports one or the other. Split calls into two nodes glued together, one 208 // for the operands and one for the results. These two nodes will be 209 // recombined in a custom inserter hook into a single MachineInstr. 210 SmallVector<SDValue, 16> Ops; 211 for (size_t i = 1; i < Node->getNumOperands(); ++i) { 212 SDValue Op = Node->getOperand(i); 213 if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) 214 Op = Op->getOperand(0); 215 Ops.push_back(Op); 216 } 217 218 // Add the chain last 219 Ops.push_back(Node->getOperand(0)); 220 MachineSDNode *CallParams = 221 CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops); 222 223 unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL 224 ? WebAssembly::CALL_RESULTS 225 : WebAssembly::RET_CALL_RESULTS; 226 227 SDValue Link(CallParams, 0); 228 MachineSDNode *CallResults = 229 CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link); 230 ReplaceNode(Node, CallResults); 231 return; 232 } 233 234 default: 235 break; 236 } 237 238 // Select the default instruction. 239 SelectCode(Node); 240 } 241 242 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 243 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 244 switch (ConstraintID) { 245 case InlineAsm::Constraint_m: 246 // We just support simple memory operands that just have a single address 247 // operand and need no special handling. 248 OutOps.push_back(Op); 249 return false; 250 default: 251 break; 252 } 253 254 return true; 255 } 256 257 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 258 /// for instruction scheduling. 259 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 260 CodeGenOpt::Level OptLevel) { 261 return new WebAssemblyDAGToDAGISel(TM, OptLevel); 262 } 263