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 "WebAssemblyISelLowering.h" 17 #include "WebAssemblyTargetMachine.h" 18 #include "llvm/CodeGen/MachineFrameInfo.h" 19 #include "llvm/CodeGen/SelectionDAGISel.h" 20 #include "llvm/CodeGen/WasmEHFuncInfo.h" 21 #include "llvm/IR/DiagnosticInfo.h" 22 #include "llvm/IR/Function.h" // To access function attributes. 23 #include "llvm/IR/IntrinsicsWebAssembly.h" 24 #include "llvm/Support/Debug.h" 25 #include "llvm/Support/KnownBits.h" 26 #include "llvm/Support/MathExtras.h" 27 #include "llvm/Support/raw_ostream.h" 28 29 using namespace llvm; 30 31 #define DEBUG_TYPE "wasm-isel" 32 33 //===--------------------------------------------------------------------===// 34 /// WebAssembly-specific code to select WebAssembly machine instructions for 35 /// SelectionDAG operations. 36 /// 37 namespace { 38 class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 39 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 40 /// right decision when generating code for different targets. 41 const WebAssemblySubtarget *Subtarget; 42 43 public: 44 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 45 CodeGenOpt::Level OptLevel) 46 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 47 } 48 49 StringRef getPassName() const override { 50 return "WebAssembly Instruction Selection"; 51 } 52 53 bool runOnMachineFunction(MachineFunction &MF) override { 54 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 55 "********** Function: " 56 << MF.getName() << '\n'); 57 58 Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 59 60 return SelectionDAGISel::runOnMachineFunction(MF); 61 } 62 63 void PreprocessISelDAG() override; 64 65 void Select(SDNode *Node) override; 66 67 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 68 std::vector<SDValue> &OutOps) override; 69 bool SelectExternRefAddr(const SDValue &Addr, const SDValue &Base); 70 71 // Include the pieces autogenerated from the target description. 72 #include "WebAssemblyGenDAGISel.inc" 73 74 private: 75 // add select functions here... 76 }; 77 } // end anonymous namespace 78 79 void WebAssemblyDAGToDAGISel::PreprocessISelDAG() { 80 // Stack objects that should be allocated to locals are hoisted to WebAssembly 81 // locals when they are first used. However for those without uses, we hoist 82 // them here. It would be nice if there were some hook to do this when they 83 // are added to the MachineFrameInfo, but that's not the case right now. 84 MachineFrameInfo &FrameInfo = MF->getFrameInfo(); 85 for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++) 86 WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx); 87 88 SelectionDAGISel::PreprocessISelDAG(); 89 } 90 91 static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) { 92 assert(Tag == WebAssembly::CPP_EXCEPTION); 93 auto &MF = DAG->getMachineFunction(); 94 const auto &TLI = DAG->getTargetLoweringInfo(); 95 MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout()); 96 const char *SymName = MF.createExternalSymbolName("__cpp_exception"); 97 return DAG->getTargetExternalSymbol(SymName, PtrVT); 98 } 99 100 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 101 // If we have a custom node, we already have selected! 102 if (Node->isMachineOpcode()) { 103 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 104 Node->setNodeId(-1); 105 return; 106 } 107 108 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 109 auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 110 : WebAssembly::GLOBAL_GET_I32; 111 112 // Few custom selection stuff. 113 SDLoc DL(Node); 114 MachineFunction &MF = CurDAG->getMachineFunction(); 115 switch (Node->getOpcode()) { 116 case ISD::ATOMIC_FENCE: { 117 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 118 break; 119 120 uint64_t SyncScopeID = Node->getConstantOperandVal(2); 121 MachineSDNode *Fence = nullptr; 122 switch (SyncScopeID) { 123 case SyncScope::SingleThread: 124 // We lower a single-thread fence to a pseudo compiler barrier instruction 125 // preventing instruction reordering. This will not be emitted in final 126 // binary. 127 Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 128 DL, // debug loc 129 MVT::Other, // outchain type 130 Node->getOperand(0) // inchain 131 ); 132 break; 133 case SyncScope::System: 134 // Currently wasm only supports sequentially consistent atomics, so we 135 // always set the order to 0 (sequentially consistent). 136 Fence = CurDAG->getMachineNode( 137 WebAssembly::ATOMIC_FENCE, 138 DL, // debug loc 139 MVT::Other, // outchain type 140 CurDAG->getTargetConstant(0, DL, MVT::i32), // order 141 Node->getOperand(0) // inchain 142 ); 143 break; 144 default: 145 llvm_unreachable("Unknown scope!"); 146 } 147 148 ReplaceNode(Node, Fence); 149 CurDAG->RemoveDeadNode(Node); 150 return; 151 } 152 153 case ISD::INTRINSIC_WO_CHAIN: { 154 unsigned IntNo = Node->getConstantOperandVal(0); 155 switch (IntNo) { 156 case Intrinsic::wasm_tls_size: { 157 MachineSDNode *TLSSize = CurDAG->getMachineNode( 158 GlobalGetIns, DL, PtrVT, 159 CurDAG->getTargetExternalSymbol("__tls_size", PtrVT)); 160 ReplaceNode(Node, TLSSize); 161 return; 162 } 163 164 case Intrinsic::wasm_tls_align: { 165 MachineSDNode *TLSAlign = CurDAG->getMachineNode( 166 GlobalGetIns, DL, PtrVT, 167 CurDAG->getTargetExternalSymbol("__tls_align", PtrVT)); 168 ReplaceNode(Node, TLSAlign); 169 return; 170 } 171 } 172 break; 173 } 174 175 case ISD::INTRINSIC_W_CHAIN: { 176 unsigned IntNo = Node->getConstantOperandVal(1); 177 const auto &TLI = CurDAG->getTargetLoweringInfo(); 178 MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout()); 179 switch (IntNo) { 180 case Intrinsic::wasm_tls_base: { 181 MachineSDNode *TLSBase = CurDAG->getMachineNode( 182 GlobalGetIns, DL, PtrVT, MVT::Other, 183 CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 184 Node->getOperand(0)); 185 ReplaceNode(Node, TLSBase); 186 return; 187 } 188 189 case Intrinsic::wasm_catch_exn: { 190 int Tag = Node->getConstantOperandVal(2); 191 SDValue SymNode = getTagSymNode(Tag, CurDAG); 192 MachineSDNode *Catch = 193 CurDAG->getMachineNode(WebAssembly::CATCH, DL, 194 { 195 PtrVT, // exception pointer 196 MVT::Other // outchain type 197 }, 198 { 199 SymNode, // exception symbol 200 Node->getOperand(0) // inchain 201 }); 202 ReplaceNode(Node, Catch); 203 return; 204 } 205 } 206 break; 207 } 208 209 case ISD::INTRINSIC_VOID: { 210 unsigned IntNo = Node->getConstantOperandVal(1); 211 switch (IntNo) { 212 case Intrinsic::wasm_throw: { 213 int Tag = Node->getConstantOperandVal(2); 214 SDValue SymNode = getTagSymNode(Tag, CurDAG); 215 MachineSDNode *Throw = 216 CurDAG->getMachineNode(WebAssembly::THROW, DL, 217 MVT::Other, // outchain type 218 { 219 SymNode, // exception symbol 220 Node->getOperand(3), // thrown value 221 Node->getOperand(0) // inchain 222 }); 223 ReplaceNode(Node, Throw); 224 return; 225 } 226 } 227 break; 228 } 229 230 case WebAssemblyISD::CALL: 231 case WebAssemblyISD::RET_CALL: { 232 // CALL has both variable operands and variable results, but ISel only 233 // supports one or the other. Split calls into two nodes glued together, one 234 // for the operands and one for the results. These two nodes will be 235 // recombined in a custom inserter hook into a single MachineInstr. 236 SmallVector<SDValue, 16> Ops; 237 for (size_t i = 1; i < Node->getNumOperands(); ++i) { 238 SDValue Op = Node->getOperand(i); 239 if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) 240 Op = Op->getOperand(0); 241 Ops.push_back(Op); 242 } 243 244 // Add the chain last 245 Ops.push_back(Node->getOperand(0)); 246 MachineSDNode *CallParams = 247 CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops); 248 249 unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL 250 ? WebAssembly::CALL_RESULTS 251 : WebAssembly::RET_CALL_RESULTS; 252 253 SDValue Link(CallParams, 0); 254 MachineSDNode *CallResults = 255 CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link); 256 ReplaceNode(Node, CallResults); 257 return; 258 } 259 260 default: 261 break; 262 } 263 264 // Select the default instruction. 265 SelectCode(Node); 266 } 267 268 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 269 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 270 switch (ConstraintID) { 271 case InlineAsm::Constraint_m: 272 // We just support simple memory operands that just have a single address 273 // operand and need no special handling. 274 OutOps.push_back(Op); 275 return false; 276 default: 277 break; 278 } 279 280 return true; 281 } 282 283 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 284 /// for instruction scheduling. 285 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 286 CodeGenOpt::Level OptLevel) { 287 return new WebAssemblyDAGToDAGISel(TM, OptLevel); 288 } 289