xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file
100b57cec5SDimitry Andric /// This file defines an instruction selector for the WebAssembly target.
110b57cec5SDimitry Andric ///
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
150b57cec5SDimitry Andric #include "WebAssembly.h"
16fe6060f1SDimitry Andric #include "WebAssemblyISelLowering.h"
170b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h"
18fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h"
190b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAGISel.h"
20349cc55cSDimitry Andric #include "llvm/CodeGen/WasmEHFuncInfo.h"
210b57cec5SDimitry Andric #include "llvm/IR/DiagnosticInfo.h"
220b57cec5SDimitry Andric #include "llvm/IR/Function.h" // To access function attributes.
23480093f4SDimitry Andric #include "llvm/IR/IntrinsicsWebAssembly.h"
240b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
250b57cec5SDimitry Andric #include "llvm/Support/KnownBits.h"
260b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
270b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
28349cc55cSDimitry Andric 
290b57cec5SDimitry Andric using namespace llvm;
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-isel"
32bdd1243dSDimitry Andric #define PASS_NAME "WebAssembly Instruction Selection"
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric //===--------------------------------------------------------------------===//
350b57cec5SDimitry Andric /// WebAssembly-specific code to select WebAssembly machine instructions for
360b57cec5SDimitry Andric /// SelectionDAG operations.
370b57cec5SDimitry Andric ///
380b57cec5SDimitry Andric namespace {
390b57cec5SDimitry Andric class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
400b57cec5SDimitry Andric   /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
410b57cec5SDimitry Andric   /// right decision when generating code for different targets.
420b57cec5SDimitry Andric   const WebAssemblySubtarget *Subtarget;
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric public:
45bdd1243dSDimitry Andric   static char ID;
46bdd1243dSDimitry Andric 
47bdd1243dSDimitry Andric   WebAssemblyDAGToDAGISel() = delete;
48bdd1243dSDimitry Andric 
490b57cec5SDimitry Andric   WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
500b57cec5SDimitry Andric                           CodeGenOpt::Level OptLevel)
51bdd1243dSDimitry Andric       : SelectionDAGISel(ID, TM, OptLevel), Subtarget(nullptr) {}
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override {
540b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
550b57cec5SDimitry Andric                          "********** Function: "
560b57cec5SDimitry Andric                       << MF.getName() << '\n');
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric     Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
598bcb0991SDimitry Andric 
600b57cec5SDimitry Andric     return SelectionDAGISel::runOnMachineFunction(MF);
610b57cec5SDimitry Andric   }
620b57cec5SDimitry Andric 
63fe6060f1SDimitry Andric   void PreprocessISelDAG() override;
64fe6060f1SDimitry Andric 
650b57cec5SDimitry Andric   void Select(SDNode *Node) override;
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric   bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
680b57cec5SDimitry Andric                                     std::vector<SDValue> &OutOps) override;
690b57cec5SDimitry Andric 
70bdd1243dSDimitry Andric   bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);
71bdd1243dSDimitry Andric   bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);
72bdd1243dSDimitry Andric 
730b57cec5SDimitry Andric // Include the pieces autogenerated from the target description.
740b57cec5SDimitry Andric #include "WebAssemblyGenDAGISel.inc"
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric private:
770b57cec5SDimitry Andric   // add select functions here...
78bdd1243dSDimitry Andric 
79bdd1243dSDimitry Andric   bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op,
80bdd1243dSDimitry Andric                           SDValue &Offset, SDValue &Addr);
81bdd1243dSDimitry Andric   bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset,
82bdd1243dSDimitry Andric                              SDValue &Addr);
830b57cec5SDimitry Andric };
840b57cec5SDimitry Andric } // end anonymous namespace
850b57cec5SDimitry Andric 
86bdd1243dSDimitry Andric char WebAssemblyDAGToDAGISel::ID;
87bdd1243dSDimitry Andric 
88bdd1243dSDimitry Andric INITIALIZE_PASS(WebAssemblyDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
89bdd1243dSDimitry Andric 
90fe6060f1SDimitry Andric void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
91fe6060f1SDimitry Andric   // Stack objects that should be allocated to locals are hoisted to WebAssembly
92fe6060f1SDimitry Andric   // locals when they are first used.  However for those without uses, we hoist
93fe6060f1SDimitry Andric   // them here.  It would be nice if there were some hook to do this when they
94fe6060f1SDimitry Andric   // are added to the MachineFrameInfo, but that's not the case right now.
95fe6060f1SDimitry Andric   MachineFrameInfo &FrameInfo = MF->getFrameInfo();
96fe6060f1SDimitry Andric   for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
97fe6060f1SDimitry Andric     WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
98fe6060f1SDimitry Andric 
99fe6060f1SDimitry Andric   SelectionDAGISel::PreprocessISelDAG();
100fe6060f1SDimitry Andric }
101fe6060f1SDimitry Andric 
102349cc55cSDimitry Andric static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
103349cc55cSDimitry Andric   assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP);
104349cc55cSDimitry Andric   auto &MF = DAG->getMachineFunction();
105349cc55cSDimitry Andric   const auto &TLI = DAG->getTargetLoweringInfo();
106349cc55cSDimitry Andric   MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
107349cc55cSDimitry Andric   const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
108349cc55cSDimitry Andric                             ? MF.createExternalSymbolName("__cpp_exception")
109349cc55cSDimitry Andric                             : MF.createExternalSymbolName("__c_longjmp");
110349cc55cSDimitry Andric   return DAG->getTargetExternalSymbol(SymName, PtrVT);
111349cc55cSDimitry Andric }
112349cc55cSDimitry Andric 
1130b57cec5SDimitry Andric void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
1140b57cec5SDimitry Andric   // If we have a custom node, we already have selected!
1150b57cec5SDimitry Andric   if (Node->isMachineOpcode()) {
1160b57cec5SDimitry Andric     LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
1170b57cec5SDimitry Andric     Node->setNodeId(-1);
1180b57cec5SDimitry Andric     return;
1190b57cec5SDimitry Andric   }
1200b57cec5SDimitry Andric 
1215ffd83dbSDimitry Andric   MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
1225ffd83dbSDimitry Andric   auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
1235ffd83dbSDimitry Andric                                         : WebAssembly::GLOBAL_GET_I32;
1245ffd83dbSDimitry Andric 
1250b57cec5SDimitry Andric   // Few custom selection stuff.
1260b57cec5SDimitry Andric   SDLoc DL(Node);
1270b57cec5SDimitry Andric   MachineFunction &MF = CurDAG->getMachineFunction();
1280b57cec5SDimitry Andric   switch (Node->getOpcode()) {
1290b57cec5SDimitry Andric   case ISD::ATOMIC_FENCE: {
1300b57cec5SDimitry Andric     if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
1310b57cec5SDimitry Andric       break;
1320b57cec5SDimitry Andric 
133349cc55cSDimitry Andric     uint64_t SyncScopeID = Node->getConstantOperandVal(2);
1348bcb0991SDimitry Andric     MachineSDNode *Fence = nullptr;
1350b57cec5SDimitry Andric     switch (SyncScopeID) {
1368bcb0991SDimitry Andric     case SyncScope::SingleThread:
1370b57cec5SDimitry Andric       // We lower a single-thread fence to a pseudo compiler barrier instruction
1380b57cec5SDimitry Andric       // preventing instruction reordering. This will not be emitted in final
1390b57cec5SDimitry Andric       // binary.
1408bcb0991SDimitry Andric       Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
1410b57cec5SDimitry Andric                                      DL,                 // debug loc
1420b57cec5SDimitry Andric                                      MVT::Other,         // outchain type
1430b57cec5SDimitry Andric                                      Node->getOperand(0) // inchain
1440b57cec5SDimitry Andric       );
1458bcb0991SDimitry Andric       break;
1468bcb0991SDimitry Andric     case SyncScope::System:
1478bcb0991SDimitry Andric       // Currently wasm only supports sequentially consistent atomics, so we
1488bcb0991SDimitry Andric       // always set the order to 0 (sequentially consistent).
1498bcb0991SDimitry Andric       Fence = CurDAG->getMachineNode(
1508bcb0991SDimitry Andric           WebAssembly::ATOMIC_FENCE,
1510b57cec5SDimitry Andric           DL,                                         // debug loc
1520b57cec5SDimitry Andric           MVT::Other,                                 // outchain type
1538bcb0991SDimitry Andric           CurDAG->getTargetConstant(0, DL, MVT::i32), // order
1540b57cec5SDimitry Andric           Node->getOperand(0)                         // inchain
1558bcb0991SDimitry Andric       );
1568bcb0991SDimitry Andric       break;
1570b57cec5SDimitry Andric     default:
1580b57cec5SDimitry Andric       llvm_unreachable("Unknown scope!");
1590b57cec5SDimitry Andric     }
1608bcb0991SDimitry Andric 
1618bcb0991SDimitry Andric     ReplaceNode(Node, Fence);
1628bcb0991SDimitry Andric     CurDAG->RemoveDeadNode(Node);
1638bcb0991SDimitry Andric     return;
1640b57cec5SDimitry Andric   }
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   case ISD::INTRINSIC_WO_CHAIN: {
167349cc55cSDimitry Andric     unsigned IntNo = Node->getConstantOperandVal(0);
1680b57cec5SDimitry Andric     switch (IntNo) {
1690b57cec5SDimitry Andric     case Intrinsic::wasm_tls_size: {
1700b57cec5SDimitry Andric       MachineSDNode *TLSSize = CurDAG->getMachineNode(
1715ffd83dbSDimitry Andric           GlobalGetIns, DL, PtrVT,
1725ffd83dbSDimitry Andric           CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
1730b57cec5SDimitry Andric       ReplaceNode(Node, TLSSize);
1740b57cec5SDimitry Andric       return;
1750b57cec5SDimitry Andric     }
176349cc55cSDimitry Andric 
1778bcb0991SDimitry Andric     case Intrinsic::wasm_tls_align: {
1788bcb0991SDimitry Andric       MachineSDNode *TLSAlign = CurDAG->getMachineNode(
1795ffd83dbSDimitry Andric           GlobalGetIns, DL, PtrVT,
1805ffd83dbSDimitry Andric           CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
1818bcb0991SDimitry Andric       ReplaceNode(Node, TLSAlign);
1828bcb0991SDimitry Andric       return;
1838bcb0991SDimitry Andric     }
1848bcb0991SDimitry Andric     }
1858bcb0991SDimitry Andric     break;
1868bcb0991SDimitry Andric   }
187349cc55cSDimitry Andric 
1888bcb0991SDimitry Andric   case ISD::INTRINSIC_W_CHAIN: {
189349cc55cSDimitry Andric     unsigned IntNo = Node->getConstantOperandVal(1);
190349cc55cSDimitry Andric     const auto &TLI = CurDAG->getTargetLoweringInfo();
191349cc55cSDimitry Andric     MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
1928bcb0991SDimitry Andric     switch (IntNo) {
1938bcb0991SDimitry Andric     case Intrinsic::wasm_tls_base: {
1948bcb0991SDimitry Andric       MachineSDNode *TLSBase = CurDAG->getMachineNode(
1955ffd83dbSDimitry Andric           GlobalGetIns, DL, PtrVT, MVT::Other,
1968bcb0991SDimitry Andric           CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
1978bcb0991SDimitry Andric           Node->getOperand(0));
1988bcb0991SDimitry Andric       ReplaceNode(Node, TLSBase);
1998bcb0991SDimitry Andric       return;
2008bcb0991SDimitry Andric     }
201349cc55cSDimitry Andric 
202349cc55cSDimitry Andric     case Intrinsic::wasm_catch: {
203349cc55cSDimitry Andric       int Tag = Node->getConstantOperandVal(2);
204349cc55cSDimitry Andric       SDValue SymNode = getTagSymNode(Tag, CurDAG);
205349cc55cSDimitry Andric       MachineSDNode *Catch =
206349cc55cSDimitry Andric           CurDAG->getMachineNode(WebAssembly::CATCH, DL,
207349cc55cSDimitry Andric                                  {
208349cc55cSDimitry Andric                                      PtrVT,     // exception pointer
209349cc55cSDimitry Andric                                      MVT::Other // outchain type
210349cc55cSDimitry Andric                                  },
211349cc55cSDimitry Andric                                  {
212349cc55cSDimitry Andric                                      SymNode,            // exception symbol
213349cc55cSDimitry Andric                                      Node->getOperand(0) // inchain
214349cc55cSDimitry Andric                                  });
215349cc55cSDimitry Andric       ReplaceNode(Node, Catch);
216349cc55cSDimitry Andric       return;
217349cc55cSDimitry Andric     }
2180b57cec5SDimitry Andric     }
2190b57cec5SDimitry Andric     break;
2200b57cec5SDimitry Andric   }
221349cc55cSDimitry Andric 
222349cc55cSDimitry Andric   case ISD::INTRINSIC_VOID: {
223349cc55cSDimitry Andric     unsigned IntNo = Node->getConstantOperandVal(1);
224349cc55cSDimitry Andric     switch (IntNo) {
225349cc55cSDimitry Andric     case Intrinsic::wasm_throw: {
226349cc55cSDimitry Andric       int Tag = Node->getConstantOperandVal(2);
227349cc55cSDimitry Andric       SDValue SymNode = getTagSymNode(Tag, CurDAG);
228349cc55cSDimitry Andric       MachineSDNode *Throw =
229349cc55cSDimitry Andric           CurDAG->getMachineNode(WebAssembly::THROW, DL,
230349cc55cSDimitry Andric                                  MVT::Other, // outchain type
231349cc55cSDimitry Andric                                  {
232349cc55cSDimitry Andric                                      SymNode,             // exception symbol
233349cc55cSDimitry Andric                                      Node->getOperand(3), // thrown value
234349cc55cSDimitry Andric                                      Node->getOperand(0)  // inchain
235349cc55cSDimitry Andric                                  });
236349cc55cSDimitry Andric       ReplaceNode(Node, Throw);
237349cc55cSDimitry Andric       return;
238349cc55cSDimitry Andric     }
239349cc55cSDimitry Andric     }
240349cc55cSDimitry Andric     break;
241349cc55cSDimitry Andric   }
242349cc55cSDimitry Andric 
2435ffd83dbSDimitry Andric   case WebAssemblyISD::CALL:
2445ffd83dbSDimitry Andric   case WebAssemblyISD::RET_CALL: {
2455ffd83dbSDimitry Andric     // CALL has both variable operands and variable results, but ISel only
2465ffd83dbSDimitry Andric     // supports one or the other. Split calls into two nodes glued together, one
2475ffd83dbSDimitry Andric     // for the operands and one for the results. These two nodes will be
2485ffd83dbSDimitry Andric     // recombined in a custom inserter hook into a single MachineInstr.
2495ffd83dbSDimitry Andric     SmallVector<SDValue, 16> Ops;
2505ffd83dbSDimitry Andric     for (size_t i = 1; i < Node->getNumOperands(); ++i) {
2515ffd83dbSDimitry Andric       SDValue Op = Node->getOperand(i);
252*06c3fb27SDimitry Andric       // Remove the wrapper when the call target is a function, an external
253*06c3fb27SDimitry Andric       // symbol (which will be lowered to a library function), or an alias of
254*06c3fb27SDimitry Andric       // a function. If the target is not a function/external symbol, we
255*06c3fb27SDimitry Andric       // shouldn't remove the wrapper, because we cannot call it directly and
256*06c3fb27SDimitry Andric       // instead we want it to be loaded with a CONST instruction and called
257*06c3fb27SDimitry Andric       // with a call_indirect later.
258*06c3fb27SDimitry Andric       if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) {
259*06c3fb27SDimitry Andric         SDValue NewOp = Op->getOperand(0);
260*06c3fb27SDimitry Andric         if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(NewOp.getNode())) {
261*06c3fb27SDimitry Andric           if (isa<Function>(
262*06c3fb27SDimitry Andric                   GlobalOp->getGlobal()->stripPointerCastsAndAliases()))
263*06c3fb27SDimitry Andric             Op = NewOp;
264*06c3fb27SDimitry Andric         } else if (isa<ExternalSymbolSDNode>(NewOp.getNode())) {
265*06c3fb27SDimitry Andric           Op = NewOp;
266*06c3fb27SDimitry Andric         }
267*06c3fb27SDimitry Andric       }
2685ffd83dbSDimitry Andric       Ops.push_back(Op);
2695ffd83dbSDimitry Andric     }
2705ffd83dbSDimitry Andric 
2715ffd83dbSDimitry Andric     // Add the chain last
2725ffd83dbSDimitry Andric     Ops.push_back(Node->getOperand(0));
2735ffd83dbSDimitry Andric     MachineSDNode *CallParams =
2745ffd83dbSDimitry Andric         CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
2755ffd83dbSDimitry Andric 
2765ffd83dbSDimitry Andric     unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
2775ffd83dbSDimitry Andric                            ? WebAssembly::CALL_RESULTS
2785ffd83dbSDimitry Andric                            : WebAssembly::RET_CALL_RESULTS;
2795ffd83dbSDimitry Andric 
2805ffd83dbSDimitry Andric     SDValue Link(CallParams, 0);
2815ffd83dbSDimitry Andric     MachineSDNode *CallResults =
2825ffd83dbSDimitry Andric         CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
2835ffd83dbSDimitry Andric     ReplaceNode(Node, CallResults);
2845ffd83dbSDimitry Andric     return;
2855ffd83dbSDimitry Andric   }
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric   default:
2880b57cec5SDimitry Andric     break;
2890b57cec5SDimitry Andric   }
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric   // Select the default instruction.
2920b57cec5SDimitry Andric   SelectCode(Node);
2930b57cec5SDimitry Andric }
2940b57cec5SDimitry Andric 
2950b57cec5SDimitry Andric bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
2960b57cec5SDimitry Andric     const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
2970b57cec5SDimitry Andric   switch (ConstraintID) {
2980b57cec5SDimitry Andric   case InlineAsm::Constraint_m:
2990b57cec5SDimitry Andric     // We just support simple memory operands that just have a single address
3000b57cec5SDimitry Andric     // operand and need no special handling.
3010b57cec5SDimitry Andric     OutOps.push_back(Op);
3020b57cec5SDimitry Andric     return false;
3030b57cec5SDimitry Andric   default:
3040b57cec5SDimitry Andric     break;
3050b57cec5SDimitry Andric   }
3060b57cec5SDimitry Andric 
3070b57cec5SDimitry Andric   return true;
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric 
310bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N,
311bdd1243dSDimitry Andric                                                     SDValue &Offset,
312bdd1243dSDimitry Andric                                                     SDValue &Addr) {
313bdd1243dSDimitry Andric   assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op");
314bdd1243dSDimitry Andric 
315bdd1243dSDimitry Andric   // WebAssembly constant offsets are performed as unsigned with infinite
316bdd1243dSDimitry Andric   // precision, so we need to check for NoUnsignedWrap so that we don't fold an
317bdd1243dSDimitry Andric   // offset for an add that needs wrapping.
318bdd1243dSDimitry Andric   if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap())
319bdd1243dSDimitry Andric     return false;
320bdd1243dSDimitry Andric 
321bdd1243dSDimitry Andric   // Folds constants in an add into the offset.
322bdd1243dSDimitry Andric   for (size_t i = 0; i < 2; ++i) {
323bdd1243dSDimitry Andric     SDValue Op = N.getOperand(i);
324bdd1243dSDimitry Andric     SDValue OtherOp = N.getOperand(i == 0 ? 1 : 0);
325bdd1243dSDimitry Andric 
326bdd1243dSDimitry Andric     if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op)) {
327bdd1243dSDimitry Andric       Offset =
328bdd1243dSDimitry Andric           CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), OffsetType);
329bdd1243dSDimitry Andric       Addr = OtherOp;
330bdd1243dSDimitry Andric       return true;
331bdd1243dSDimitry Andric     }
332bdd1243dSDimitry Andric   }
333bdd1243dSDimitry Andric   return false;
334bdd1243dSDimitry Andric }
335bdd1243dSDimitry Andric 
336bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType,
337bdd1243dSDimitry Andric                                                  unsigned ConstOpc, SDValue N,
338bdd1243dSDimitry Andric                                                  SDValue &Offset,
339bdd1243dSDimitry Andric                                                  SDValue &Addr) {
340bdd1243dSDimitry Andric   SDLoc DL(N);
341bdd1243dSDimitry Andric 
342bdd1243dSDimitry Andric   // Fold target global addresses into the offset.
343bdd1243dSDimitry Andric   if (!TM.isPositionIndependent()) {
344bdd1243dSDimitry Andric     SDValue Op(N);
345bdd1243dSDimitry Andric     if (Op.getOpcode() == WebAssemblyISD::Wrapper)
346bdd1243dSDimitry Andric       Op = Op.getOperand(0);
347bdd1243dSDimitry Andric 
348bdd1243dSDimitry Andric     if (Op.getOpcode() == ISD::TargetGlobalAddress) {
349bdd1243dSDimitry Andric       Offset = Op;
350bdd1243dSDimitry Andric       Addr = SDValue(
351bdd1243dSDimitry Andric           CurDAG->getMachineNode(ConstOpc, DL, AddrType,
352bdd1243dSDimitry Andric                                  CurDAG->getTargetConstant(0, DL, AddrType)),
353bdd1243dSDimitry Andric           0);
354bdd1243dSDimitry Andric       return true;
355bdd1243dSDimitry Andric     }
356bdd1243dSDimitry Andric   }
357bdd1243dSDimitry Andric 
358bdd1243dSDimitry Andric   // Fold anything inside an add into the offset.
359bdd1243dSDimitry Andric   if (N.getOpcode() == ISD::ADD &&
360bdd1243dSDimitry Andric       SelectAddrAddOperands(AddrType, N, Offset, Addr))
361bdd1243dSDimitry Andric     return true;
362bdd1243dSDimitry Andric 
363bdd1243dSDimitry Andric   // Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be
364bdd1243dSDimitry Andric   // zero and fold them into the offset too.
365bdd1243dSDimitry Andric   if (N.getOpcode() == ISD::OR) {
366bdd1243dSDimitry Andric     bool OrIsAdd;
367bdd1243dSDimitry Andric     if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
368bdd1243dSDimitry Andric       OrIsAdd =
369bdd1243dSDimitry Andric           CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
370bdd1243dSDimitry Andric     } else {
371bdd1243dSDimitry Andric       KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
372bdd1243dSDimitry Andric       KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
373bdd1243dSDimitry Andric       OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0;
374bdd1243dSDimitry Andric     }
375bdd1243dSDimitry Andric 
376bdd1243dSDimitry Andric     if (OrIsAdd && SelectAddrAddOperands(AddrType, N, Offset, Addr))
377bdd1243dSDimitry Andric       return true;
378bdd1243dSDimitry Andric   }
379bdd1243dSDimitry Andric 
380bdd1243dSDimitry Andric   // Fold constant addresses into the offset.
381bdd1243dSDimitry Andric   if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N)) {
382bdd1243dSDimitry Andric     Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, AddrType);
383bdd1243dSDimitry Andric     Addr = SDValue(
384bdd1243dSDimitry Andric         CurDAG->getMachineNode(ConstOpc, DL, AddrType,
385bdd1243dSDimitry Andric                                CurDAG->getTargetConstant(0, DL, AddrType)),
386bdd1243dSDimitry Andric         0);
387bdd1243dSDimitry Andric     return true;
388bdd1243dSDimitry Andric   }
389bdd1243dSDimitry Andric 
390bdd1243dSDimitry Andric   // Else it's a plain old load/store with no offset.
391bdd1243dSDimitry Andric   Offset = CurDAG->getTargetConstant(0, DL, AddrType);
392bdd1243dSDimitry Andric   Addr = N;
393bdd1243dSDimitry Andric   return true;
394bdd1243dSDimitry Andric }
395bdd1243dSDimitry Andric 
396bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset,
397bdd1243dSDimitry Andric                                                    SDValue &Addr) {
398bdd1243dSDimitry Andric   return SelectAddrOperands(MVT::i32, WebAssembly::CONST_I32, Op, Offset, Addr);
399bdd1243dSDimitry Andric }
400bdd1243dSDimitry Andric 
401bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,
402bdd1243dSDimitry Andric                                                    SDValue &Addr) {
403bdd1243dSDimitry Andric   return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr);
404bdd1243dSDimitry Andric }
405bdd1243dSDimitry Andric 
4060b57cec5SDimitry Andric /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
4070b57cec5SDimitry Andric /// for instruction scheduling.
4080b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
4090b57cec5SDimitry Andric                                              CodeGenOpt::Level OptLevel) {
4100b57cec5SDimitry Andric   return new WebAssemblyDAGToDAGISel(TM, OptLevel);
4110b57cec5SDimitry Andric }
412