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/Support/Debug.h" 21 #include "llvm/Support/KnownBits.h" 22 #include "llvm/Support/MathExtras.h" 23 #include "llvm/Support/raw_ostream.h" 24 using namespace llvm; 25 26 #define DEBUG_TYPE "wasm-isel" 27 28 //===--------------------------------------------------------------------===// 29 /// WebAssembly-specific code to select WebAssembly machine instructions for 30 /// SelectionDAG operations. 31 /// 32 namespace { 33 class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 34 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 35 /// right decision when generating code for different targets. 36 const WebAssemblySubtarget *Subtarget; 37 38 bool ForCodeSize; 39 40 public: 41 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 42 CodeGenOpt::Level OptLevel) 43 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr), ForCodeSize(false) { 44 } 45 46 StringRef getPassName() const override { 47 return "WebAssembly Instruction Selection"; 48 } 49 50 bool runOnMachineFunction(MachineFunction &MF) override { 51 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 52 "********** Function: " 53 << MF.getName() << '\n'); 54 55 ForCodeSize = MF.getFunction().hasOptSize(); 56 Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 57 return SelectionDAGISel::runOnMachineFunction(MF); 58 } 59 60 void Select(SDNode *Node) override; 61 62 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 63 std::vector<SDValue> &OutOps) override; 64 65 // Include the pieces autogenerated from the target description. 66 #include "WebAssemblyGenDAGISel.inc" 67 68 private: 69 // add select functions here... 70 }; 71 } // end anonymous namespace 72 73 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 74 // If we have a custom node, we already have selected! 75 if (Node->isMachineOpcode()) { 76 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 77 Node->setNodeId(-1); 78 return; 79 } 80 81 // Few custom selection stuff. 82 SDLoc DL(Node); 83 MachineFunction &MF = CurDAG->getMachineFunction(); 84 switch (Node->getOpcode()) { 85 case ISD::ATOMIC_FENCE: { 86 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 87 break; 88 89 uint64_t SyncScopeID = 90 cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 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 MachineSDNode *Fence = 97 CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 98 DL, // debug loc 99 MVT::Other, // outchain type 100 Node->getOperand(0) // inchain 101 ); 102 ReplaceNode(Node, Fence); 103 CurDAG->RemoveDeadNode(Node); 104 return; 105 } 106 107 case SyncScope::System: { 108 // For non-emscripten systems, we have not decided on what we should 109 // traslate fences to yet. 110 if (!Subtarget->getTargetTriple().isOSEmscripten()) 111 report_fatal_error( 112 "ATOMIC_FENCE is not yet supported in non-emscripten OSes"); 113 114 // Wasm does not have a fence instruction, but because all atomic 115 // instructions in wasm are sequentially consistent, we translate a 116 // fence to an idempotent atomic RMW instruction to a linear memory 117 // address. All atomic instructions in wasm are sequentially consistent, 118 // but this is to ensure a fence also prevents reordering of non-atomic 119 // instructions in the VM. Even though LLVM IR's fence instruction does 120 // not say anything about its relationship with non-atomic instructions, 121 // we think this is more user-friendly. 122 // 123 // While any address can work, here we use a value stored in 124 // __stack_pointer wasm global because there's high chance that area is 125 // in cache. 126 // 127 // So the selected instructions will be in the form of: 128 // %addr = get_global $__stack_pointer 129 // %0 = i32.const 0 130 // i32.atomic.rmw.or %addr, %0 131 SDValue StackPtrSym = CurDAG->getTargetExternalSymbol( 132 "__stack_pointer", TLI->getPointerTy(CurDAG->getDataLayout())); 133 MachineSDNode *GetGlobal = 134 CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, // opcode 135 DL, // debug loc 136 MVT::i32, // result type 137 StackPtrSym // __stack_pointer symbol 138 ); 139 140 SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32); 141 auto *MMO = MF.getMachineMemOperand( 142 MachinePointerInfo::getUnknownStack(MF), 143 // FIXME Volatile isn't really correct, but currently all LLVM 144 // atomic instructions are treated as volatiles in the backend, so 145 // we should be consistent. 146 MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad | 147 MachineMemOperand::MOStore, 148 4, 4, AAMDNodes(), nullptr, SyncScope::System, 149 AtomicOrdering::SequentiallyConsistent); 150 MachineSDNode *Const0 = 151 CurDAG->getMachineNode(WebAssembly::CONST_I32, DL, MVT::i32, Zero); 152 MachineSDNode *AtomicRMW = CurDAG->getMachineNode( 153 WebAssembly::ATOMIC_RMW_OR_I32, // opcode 154 DL, // debug loc 155 MVT::i32, // result type 156 MVT::Other, // outchain type 157 { 158 Zero, // alignment 159 Zero, // offset 160 SDValue(GetGlobal, 0), // __stack_pointer 161 SDValue(Const0, 0), // OR with 0 to make it idempotent 162 Node->getOperand(0) // inchain 163 }); 164 165 CurDAG->setNodeMemRefs(AtomicRMW, {MMO}); 166 ReplaceUses(SDValue(Node, 0), SDValue(AtomicRMW, 1)); 167 CurDAG->RemoveDeadNode(Node); 168 return; 169 } 170 default: 171 llvm_unreachable("Unknown scope!"); 172 } 173 } 174 175 case ISD::GlobalTLSAddress: { 176 const auto *GA = cast<GlobalAddressSDNode>(Node); 177 178 if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 179 report_fatal_error("cannot use thread-local storage without bulk memory", 180 false); 181 182 // Currently Emscripten does not support dynamic linking with threads. 183 // Therefore, if we have thread-local storage, only the local-exec model 184 // is possible. 185 // TODO: remove this and implement proper TLS models once Emscripten 186 // supports dynamic linking with threads. 187 if (GA->getGlobal()->getThreadLocalMode() != 188 GlobalValue::LocalExecTLSModel && 189 !Subtarget->getTargetTriple().isOSEmscripten()) { 190 report_fatal_error("only -ftls-model=local-exec is supported for now on " 191 "non-Emscripten OSes: variable " + 192 GA->getGlobal()->getName(), 193 false); 194 } 195 196 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 197 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 198 199 SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); 200 SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( 201 GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); 202 203 MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, 204 DL, MVT::i32, TLSBaseSym); 205 MachineSDNode *TLSOffset = CurDAG->getMachineNode( 206 WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); 207 MachineSDNode *TLSAddress = 208 CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, 209 SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); 210 ReplaceNode(Node, TLSAddress); 211 return; 212 } 213 214 case ISD::INTRINSIC_WO_CHAIN: { 215 unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); 216 switch (IntNo) { 217 case Intrinsic::wasm_tls_size: { 218 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 219 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); 220 221 MachineSDNode *TLSSize = CurDAG->getMachineNode( 222 WebAssembly::GLOBAL_GET_I32, DL, PtrVT, 223 CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); 224 ReplaceNode(Node, TLSSize); 225 return; 226 } 227 } 228 break; 229 } 230 231 default: 232 break; 233 } 234 235 // Select the default instruction. 236 SelectCode(Node); 237 } 238 239 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 240 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 241 switch (ConstraintID) { 242 case InlineAsm::Constraint_i: 243 case InlineAsm::Constraint_m: 244 // We just support simple memory operands that just have a single address 245 // operand and need no special handling. 246 OutOps.push_back(Op); 247 return false; 248 default: 249 break; 250 } 251 252 return true; 253 } 254 255 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 256 /// for instruction scheduling. 257 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 258 CodeGenOpt::Level OptLevel) { 259 return new WebAssemblyDAGToDAGISel(TM, OptLevel); 260 } 261