WebAssemblyISelDAGToDAG.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
  14. #include "WebAssembly.h"
  15. #include "WebAssemblyISelLowering.h"
  16. #include "WebAssemblyTargetMachine.h"
  17. #include "llvm/CodeGen/MachineFrameInfo.h"
  18. #include "llvm/CodeGen/SelectionDAGISel.h"
  19. #include "llvm/CodeGen/WasmEHFuncInfo.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. #define DEBUG_TYPE "wasm-isel"
  29. #define PASS_NAME "WebAssembly Instruction Selection"
  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. public:
  40. static char ID;
  41. WebAssemblyDAGToDAGISel() = delete;
  42. WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
  43. CodeGenOpt::Level OptLevel)
  44. : SelectionDAGISel(ID, TM, OptLevel), Subtarget(nullptr) {}
  45. bool runOnMachineFunction(MachineFunction &MF) override {
  46. LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
  47. "********** Function: "
  48. << MF.getName() << '\n');
  49. Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
  50. return SelectionDAGISel::runOnMachineFunction(MF);
  51. }
  52. void PreprocessISelDAG() override;
  53. void Select(SDNode *Node) override;
  54. bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
  55. std::vector<SDValue> &OutOps) override;
  56. bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);
  57. bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);
  58. // Include the pieces autogenerated from the target description.
  59. #include "WebAssemblyGenDAGISel.inc"
  60. private:
  61. // add select functions here...
  62. bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op,
  63. SDValue &Offset, SDValue &Addr);
  64. bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset,
  65. SDValue &Addr);
  66. };
  67. } // end anonymous namespace
  68. char WebAssemblyDAGToDAGISel::ID;
  69. INITIALIZE_PASS(WebAssemblyDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
  70. void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
  71. // Stack objects that should be allocated to locals are hoisted to WebAssembly
  72. // locals when they are first used. However for those without uses, we hoist
  73. // them here. It would be nice if there were some hook to do this when they
  74. // are added to the MachineFrameInfo, but that's not the case right now.
  75. MachineFrameInfo &FrameInfo = MF->getFrameInfo();
  76. for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
  77. WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
  78. SelectionDAGISel::PreprocessISelDAG();
  79. }
  80. static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
  81. assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP);
  82. auto &MF = DAG->getMachineFunction();
  83. const auto &TLI = DAG->getTargetLoweringInfo();
  84. MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
  85. const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
  86. ? MF.createExternalSymbolName("__cpp_exception")
  87. : MF.createExternalSymbolName("__c_longjmp");
  88. return DAG->getTargetExternalSymbol(SymName, PtrVT);
  89. }
  90. void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
  91. // If we have a custom node, we already have selected!
  92. if (Node->isMachineOpcode()) {
  93. LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
  94. Node->setNodeId(-1);
  95. return;
  96. }
  97. MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
  98. auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
  99. : WebAssembly::GLOBAL_GET_I32;
  100. // Few custom selection stuff.
  101. SDLoc DL(Node);
  102. MachineFunction &MF = CurDAG->getMachineFunction();
  103. switch (Node->getOpcode()) {
  104. case ISD::ATOMIC_FENCE: {
  105. if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
  106. break;
  107. uint64_t SyncScopeID = Node->getConstantOperandVal(2);
  108. MachineSDNode *Fence = nullptr;
  109. switch (SyncScopeID) {
  110. case SyncScope::SingleThread:
  111. // We lower a single-thread fence to a pseudo compiler barrier instruction
  112. // preventing instruction reordering. This will not be emitted in final
  113. // binary.
  114. Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
  115. DL, // debug loc
  116. MVT::Other, // outchain type
  117. Node->getOperand(0) // inchain
  118. );
  119. break;
  120. case SyncScope::System:
  121. // Currently wasm only supports sequentially consistent atomics, so we
  122. // always set the order to 0 (sequentially consistent).
  123. Fence = CurDAG->getMachineNode(
  124. WebAssembly::ATOMIC_FENCE,
  125. DL, // debug loc
  126. MVT::Other, // outchain type
  127. CurDAG->getTargetConstant(0, DL, MVT::i32), // order
  128. Node->getOperand(0) // inchain
  129. );
  130. break;
  131. default:
  132. llvm_unreachable("Unknown scope!");
  133. }
  134. ReplaceNode(Node, Fence);
  135. CurDAG->RemoveDeadNode(Node);
  136. return;
  137. }
  138. case ISD::INTRINSIC_WO_CHAIN: {
  139. unsigned IntNo = Node->getConstantOperandVal(0);
  140. switch (IntNo) {
  141. case Intrinsic::wasm_tls_size: {
  142. MachineSDNode *TLSSize = CurDAG->getMachineNode(
  143. GlobalGetIns, DL, PtrVT,
  144. CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
  145. ReplaceNode(Node, TLSSize);
  146. return;
  147. }
  148. case Intrinsic::wasm_tls_align: {
  149. MachineSDNode *TLSAlign = CurDAG->getMachineNode(
  150. GlobalGetIns, DL, PtrVT,
  151. CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
  152. ReplaceNode(Node, TLSAlign);
  153. return;
  154. }
  155. }
  156. break;
  157. }
  158. case ISD::INTRINSIC_W_CHAIN: {
  159. unsigned IntNo = Node->getConstantOperandVal(1);
  160. const auto &TLI = CurDAG->getTargetLoweringInfo();
  161. MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
  162. switch (IntNo) {
  163. case Intrinsic::wasm_tls_base: {
  164. MachineSDNode *TLSBase = CurDAG->getMachineNode(
  165. GlobalGetIns, DL, PtrVT, MVT::Other,
  166. CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
  167. Node->getOperand(0));
  168. ReplaceNode(Node, TLSBase);
  169. return;
  170. }
  171. case Intrinsic::wasm_catch: {
  172. int Tag = Node->getConstantOperandVal(2);
  173. SDValue SymNode = getTagSymNode(Tag, CurDAG);
  174. MachineSDNode *Catch =
  175. CurDAG->getMachineNode(WebAssembly::CATCH, DL,
  176. {
  177. PtrVT, // exception pointer
  178. MVT::Other // outchain type
  179. },
  180. {
  181. SymNode, // exception symbol
  182. Node->getOperand(0) // inchain
  183. });
  184. ReplaceNode(Node, Catch);
  185. return;
  186. }
  187. }
  188. break;
  189. }
  190. case ISD::INTRINSIC_VOID: {
  191. unsigned IntNo = Node->getConstantOperandVal(1);
  192. switch (IntNo) {
  193. case Intrinsic::wasm_throw: {
  194. int Tag = Node->getConstantOperandVal(2);
  195. SDValue SymNode = getTagSymNode(Tag, CurDAG);
  196. MachineSDNode *Throw =
  197. CurDAG->getMachineNode(WebAssembly::THROW, DL,
  198. MVT::Other, // outchain type
  199. {
  200. SymNode, // exception symbol
  201. Node->getOperand(3), // thrown value
  202. Node->getOperand(0) // inchain
  203. });
  204. ReplaceNode(Node, Throw);
  205. return;
  206. }
  207. }
  208. break;
  209. }
  210. case WebAssemblyISD::CALL:
  211. case WebAssemblyISD::RET_CALL: {
  212. // CALL has both variable operands and variable results, but ISel only
  213. // supports one or the other. Split calls into two nodes glued together, one
  214. // for the operands and one for the results. These two nodes will be
  215. // recombined in a custom inserter hook into a single MachineInstr.
  216. SmallVector<SDValue, 16> Ops;
  217. for (size_t i = 1; i < Node->getNumOperands(); ++i) {
  218. SDValue Op = Node->getOperand(i);
  219. if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper)
  220. Op = Op->getOperand(0);
  221. Ops.push_back(Op);
  222. }
  223. // Add the chain last
  224. Ops.push_back(Node->getOperand(0));
  225. MachineSDNode *CallParams =
  226. CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
  227. unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
  228. ? WebAssembly::CALL_RESULTS
  229. : WebAssembly::RET_CALL_RESULTS;
  230. SDValue Link(CallParams, 0);
  231. MachineSDNode *CallResults =
  232. CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
  233. ReplaceNode(Node, CallResults);
  234. return;
  235. }
  236. default:
  237. break;
  238. }
  239. // Select the default instruction.
  240. SelectCode(Node);
  241. }
  242. bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
  243. const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
  244. switch (ConstraintID) {
  245. case InlineAsm::Constraint_m:
  246. // We just support simple memory operands that just have a single address
  247. // operand and need no special handling.
  248. OutOps.push_back(Op);
  249. return false;
  250. default:
  251. break;
  252. }
  253. return true;
  254. }
  255. bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N,
  256. SDValue &Offset,
  257. SDValue &Addr) {
  258. assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op");
  259. // WebAssembly constant offsets are performed as unsigned with infinite
  260. // precision, so we need to check for NoUnsignedWrap so that we don't fold an
  261. // offset for an add that needs wrapping.
  262. if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap())
  263. return false;
  264. // Folds constants in an add into the offset.
  265. for (size_t i = 0; i < 2; ++i) {
  266. SDValue Op = N.getOperand(i);
  267. SDValue OtherOp = N.getOperand(i == 0 ? 1 : 0);
  268. if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op)) {
  269. Offset =
  270. CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), OffsetType);
  271. Addr = OtherOp;
  272. return true;
  273. }
  274. }
  275. return false;
  276. }
  277. bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType,
  278. unsigned ConstOpc, SDValue N,
  279. SDValue &Offset,
  280. SDValue &Addr) {
  281. SDLoc DL(N);
  282. // Fold target global addresses into the offset.
  283. if (!TM.isPositionIndependent()) {
  284. SDValue Op(N);
  285. if (Op.getOpcode() == WebAssemblyISD::Wrapper)
  286. Op = Op.getOperand(0);
  287. if (Op.getOpcode() == ISD::TargetGlobalAddress) {
  288. Offset = Op;
  289. Addr = SDValue(
  290. CurDAG->getMachineNode(ConstOpc, DL, AddrType,
  291. CurDAG->getTargetConstant(0, DL, AddrType)),
  292. 0);
  293. return true;
  294. }
  295. }
  296. // Fold anything inside an add into the offset.
  297. if (N.getOpcode() == ISD::ADD &&
  298. SelectAddrAddOperands(AddrType, N, Offset, Addr))
  299. return true;
  300. // Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be
  301. // zero and fold them into the offset too.
  302. if (N.getOpcode() == ISD::OR) {
  303. bool OrIsAdd;
  304. if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
  305. OrIsAdd =
  306. CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
  307. } else {
  308. KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
  309. KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
  310. OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0;
  311. }
  312. if (OrIsAdd && SelectAddrAddOperands(AddrType, N, Offset, Addr))
  313. return true;
  314. }
  315. // Fold constant addresses into the offset.
  316. if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N)) {
  317. Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, AddrType);
  318. Addr = SDValue(
  319. CurDAG->getMachineNode(ConstOpc, DL, AddrType,
  320. CurDAG->getTargetConstant(0, DL, AddrType)),
  321. 0);
  322. return true;
  323. }
  324. // Else it's a plain old load/store with no offset.
  325. Offset = CurDAG->getTargetConstant(0, DL, AddrType);
  326. Addr = N;
  327. return true;
  328. }
  329. bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset,
  330. SDValue &Addr) {
  331. return SelectAddrOperands(MVT::i32, WebAssembly::CONST_I32, Op, Offset, Addr);
  332. }
  333. bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,
  334. SDValue &Addr) {
  335. return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr);
  336. }
  337. /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
  338. /// for instruction scheduling.
  339. FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
  340. CodeGenOpt::Level OptLevel) {
  341. return new WebAssemblyDAGToDAGISel(TM, OptLevel);
  342. }