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