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 || WebAssembly::C_LONGJMP); 93 auto &MF = DAG->getMachineFunction(); 94 const auto &TLI = DAG->getTargetLoweringInfo(); 95 MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout()); 96 const char *SymName = Tag == WebAssembly::CPP_EXCEPTION 97 ? MF.createExternalSymbolName("__cpp_exception") 98 : MF.createExternalSymbolName("__c_longjmp"); 99 return DAG->getTargetExternalSymbol(SymName, PtrVT); 100 } 101 102 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 103 // If we have a custom node, we already have selected! 104 if (Node->isMachineOpcode()) { 105 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 106 Node->setNodeId(-1); 107 return; 108 } 109 110 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 111 auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 112 : WebAssembly::GLOBAL_GET_I32; 113 114 // Few custom selection stuff. 115 SDLoc DL(Node); 116 MachineFunction &MF = CurDAG->getMachineFunction(); 117 switch (Node->getOpcode()) { 118 case ISD::ATOMIC_FENCE: { 119 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 120 break; 121 122 uint64_t SyncScopeID = Node->getConstantOperandVal(2); 123 MachineSDNode *Fence = nullptr; 124 switch (SyncScopeID) { 125 case SyncScope::SingleThread: 126 // We lower a single-thread fence to a pseudo compiler barrier instruction 127 // preventing instruction reordering. This will not be emitted in final 128 // binary. 129 Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 130 DL, // debug loc 131 MVT::Other, // outchain type 132 Node->getOperand(0) // inchain 133 ); 134 break; 135 case SyncScope::System: 136 // Currently wasm only supports sequentially consistent atomics, so we 137 // always set the order to 0 (sequentially consistent). 138 Fence = CurDAG->getMachineNode( 139 WebAssembly::ATOMIC_FENCE, 140 DL, // debug loc 141 MVT::Other, // outchain type 142 CurDAG->getTargetConstant(0, DL, MVT::i32), // order 143 Node->getOperand(0) // inchain 144 ); 145 break; 146 default: 147 llvm_unreachable("Unknown scope!"); 148 } 149 150 ReplaceNode(Node, Fence); 151 CurDAG->RemoveDeadNode(Node); 152 return; 153 } 154 155 case ISD::INTRINSIC_WO_CHAIN: { 156 unsigned IntNo = Node->getConstantOperandVal(0); 157 switch (IntNo) { 158 case Intrinsic::wasm_tls_size: { 159 MachineSDNode *TLSSize = CurDAG->getMachineNode( 160 GlobalGetIns, DL, PtrVT, 161 CurDAG->getTargetExternalSymbol("__tls_size", PtrVT)); 162 ReplaceNode(Node, TLSSize); 163 return; 164 } 165 166 case Intrinsic::wasm_tls_align: { 167 MachineSDNode *TLSAlign = CurDAG->getMachineNode( 168 GlobalGetIns, DL, PtrVT, 169 CurDAG->getTargetExternalSymbol("__tls_align", PtrVT)); 170 ReplaceNode(Node, TLSAlign); 171 return; 172 } 173 } 174 break; 175 } 176 177 case ISD::INTRINSIC_W_CHAIN: { 178 unsigned IntNo = Node->getConstantOperandVal(1); 179 const auto &TLI = CurDAG->getTargetLoweringInfo(); 180 MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout()); 181 switch (IntNo) { 182 case Intrinsic::wasm_tls_base: { 183 MachineSDNode *TLSBase = CurDAG->getMachineNode( 184 GlobalGetIns, DL, PtrVT, MVT::Other, 185 CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 186 Node->getOperand(0)); 187 ReplaceNode(Node, TLSBase); 188 return; 189 } 190 191 case Intrinsic::wasm_catch: { 192 int Tag = Node->getConstantOperandVal(2); 193 SDValue SymNode = getTagSymNode(Tag, CurDAG); 194 MachineSDNode *Catch = 195 CurDAG->getMachineNode(WebAssembly::CATCH, DL, 196 { 197 PtrVT, // exception pointer 198 MVT::Other // outchain type 199 }, 200 { 201 SymNode, // exception symbol 202 Node->getOperand(0) // inchain 203 }); 204 ReplaceNode(Node, Catch); 205 return; 206 } 207 } 208 break; 209 } 210 211 case ISD::INTRINSIC_VOID: { 212 unsigned IntNo = Node->getConstantOperandVal(1); 213 switch (IntNo) { 214 case Intrinsic::wasm_throw: { 215 int Tag = Node->getConstantOperandVal(2); 216 SDValue SymNode = getTagSymNode(Tag, CurDAG); 217 MachineSDNode *Throw = 218 CurDAG->getMachineNode(WebAssembly::THROW, DL, 219 MVT::Other, // outchain type 220 { 221 SymNode, // exception symbol 222 Node->getOperand(3), // thrown value 223 Node->getOperand(0) // inchain 224 }); 225 ReplaceNode(Node, Throw); 226 return; 227 } 228 } 229 break; 230 } 231 232 case WebAssemblyISD::CALL: 233 case WebAssemblyISD::RET_CALL: { 234 // CALL has both variable operands and variable results, but ISel only 235 // supports one or the other. Split calls into two nodes glued together, one 236 // for the operands and one for the results. These two nodes will be 237 // recombined in a custom inserter hook into a single MachineInstr. 238 SmallVector<SDValue, 16> Ops; 239 for (size_t i = 1; i < Node->getNumOperands(); ++i) { 240 SDValue Op = Node->getOperand(i); 241 if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) 242 Op = Op->getOperand(0); 243 Ops.push_back(Op); 244 } 245 246 // Add the chain last 247 Ops.push_back(Node->getOperand(0)); 248 MachineSDNode *CallParams = 249 CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops); 250 251 unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL 252 ? WebAssembly::CALL_RESULTS 253 : WebAssembly::RET_CALL_RESULTS; 254 255 SDValue Link(CallParams, 0); 256 MachineSDNode *CallResults = 257 CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link); 258 ReplaceNode(Node, CallResults); 259 return; 260 } 261 262 default: 263 break; 264 } 265 266 // Select the default instruction. 267 SelectCode(Node); 268 } 269 270 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 271 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 272 switch (ConstraintID) { 273 case InlineAsm::Constraint_m: 274 // We just support simple memory operands that just have a single address 275 // operand and need no special handling. 276 OutOps.push_back(Op); 277 return false; 278 default: 279 break; 280 } 281 282 return true; 283 } 284 285 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 286 /// for instruction scheduling. 287 FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 288 CodeGenOpt::Level OptLevel) { 289 return new WebAssemblyDAGToDAGISel(TM, OptLevel); 290 } 291