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