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/IR/DiagnosticInfo.h" 21 #include "llvm/IR/Function.h" // To access function attributes. 22 #include "llvm/IR/IntrinsicsWebAssembly.h" 23 #include "llvm/Support/Debug.h" 24 #include "llvm/Support/KnownBits.h" 25 #include "llvm/Support/MathExtras.h" 26 #include "llvm/Support/raw_ostream.h" 27 using namespace llvm; 28 29 #define DEBUG_TYPE "wasm-isel" 30 31 //===--------------------------------------------------------------------===// 32 /// WebAssembly-specific code to select WebAssembly machine instructions for 33 /// SelectionDAG operations. 34 /// 35 namespace { 36 class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { 37 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the 38 /// right decision when generating code for different targets. 39 const WebAssemblySubtarget *Subtarget; 40 41 public: 42 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 43 CodeGenOpt::Level OptLevel) 44 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) { 45 } 46 47 StringRef getPassName() const override { 48 return "WebAssembly Instruction Selection"; 49 } 50 51 void checkForInvalidNodes(const Function &F) { 52 // This function will check for uses of ptrtoint on reference types and 53 // report a fatal error if these are found. 54 for (const BasicBlock &BB : F) { 55 for (const Instruction &I : BB) { 56 if (const PtrToIntInst *PTI = dyn_cast<const PtrToIntInst>(&I)) { 57 const Value *V = PTI->getPointerOperand(); 58 if (WebAssemblyTargetLowering::isFuncrefType(V->getType()) || 59 WebAssemblyTargetLowering::isExternrefType(V->getType())) 60 report_fatal_error("ptrtoint not allowed on reference types"); 61 } else if (const IntToPtrInst *ITP = dyn_cast<const IntToPtrInst>(&I)) { 62 if (WebAssemblyTargetLowering::isFuncrefType(ITP->getDestTy()) || 63 WebAssemblyTargetLowering::isExternrefType(ITP->getDestTy())) 64 report_fatal_error("inttoptr not allowed on reference types"); 65 } 66 } 67 } 68 } 69 70 bool runOnMachineFunction(MachineFunction &MF) override { 71 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 72 "********** Function: " 73 << MF.getName() << '\n'); 74 75 checkForInvalidNodes(MF.getFunction()); 76 77 Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 78 79 return SelectionDAGISel::runOnMachineFunction(MF); 80 } 81 82 void PreprocessISelDAG() override; 83 84 void Select(SDNode *Node) override; 85 86 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 87 std::vector<SDValue> &OutOps) override; 88 bool SelectExternRefAddr(const SDValue &Addr, const SDValue &Base); 89 90 // Include the pieces autogenerated from the target description. 91 #include "WebAssemblyGenDAGISel.inc" 92 93 private: 94 // add select functions here... 95 }; 96 } // end anonymous namespace 97 98 void WebAssemblyDAGToDAGISel::PreprocessISelDAG() { 99 // Stack objects that should be allocated to locals are hoisted to WebAssembly 100 // locals when they are first used. However for those without uses, we hoist 101 // them here. It would be nice if there were some hook to do this when they 102 // are added to the MachineFrameInfo, but that's not the case right now. 103 MachineFrameInfo &FrameInfo = MF->getFrameInfo(); 104 for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++) 105 WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx); 106 107 SelectionDAGISel::PreprocessISelDAG(); 108 } 109 110 void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 111 // If we have a custom node, we already have selected! 112 if (Node->isMachineOpcode()) { 113 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 114 Node->setNodeId(-1); 115 return; 116 } 117 118 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 119 auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 120 : WebAssembly::GLOBAL_GET_I32; 121 122 // Few custom selection stuff. 123 SDLoc DL(Node); 124 MachineFunction &MF = CurDAG->getMachineFunction(); 125 switch (Node->getOpcode()) { 126 case ISD::ATOMIC_FENCE: { 127 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 128 break; 129 130 uint64_t SyncScopeID = 131 cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); 132 MachineSDNode *Fence = nullptr; 133 switch (SyncScopeID) { 134 case SyncScope::SingleThread: 135 // We lower a single-thread fence to a pseudo compiler barrier instruction 136 // preventing instruction reordering. This will not be emitted in final 137 // binary. 138 Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 139 DL, // debug loc 140 MVT::Other, // outchain type 141 Node->getOperand(0) // inchain 142 ); 143 break; 144 case SyncScope::System: 145 // Currently wasm only supports sequentially consistent atomics, so we 146 // always set the order to 0 (sequentially consistent). 147 Fence = CurDAG->getMachineNode( 148 WebAssembly::ATOMIC_FENCE, 149 DL, // debug loc 150 MVT::Other, // outchain type 151 CurDAG->getTargetConstant(0, DL, MVT::i32), // order 152 Node->getOperand(0) // inchain 153 ); 154 break; 155 default: 156 llvm_unreachable("Unknown scope!"); 157 } 158 159 ReplaceNode(Node, Fence); 160 CurDAG->RemoveDeadNode(Node); 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