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