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