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
WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine & TM,CodeGenOptLevel OptLevel)490b57cec5SDimitry Andric WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
505f757f3fSDimitry Andric CodeGenOptLevel OptLevel)
51bdd1243dSDimitry Andric : SelectionDAGISel(ID, TM, OptLevel), Subtarget(nullptr) {}
520b57cec5SDimitry Andric
runOnMachineFunction(MachineFunction & MF)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
675f757f3fSDimitry Andric bool SelectInlineAsmMemoryOperand(const SDValue &Op,
685f757f3fSDimitry Andric InlineAsm::ConstraintCode ConstraintID,
690b57cec5SDimitry Andric std::vector<SDValue> &OutOps) override;
700b57cec5SDimitry Andric
71bdd1243dSDimitry Andric bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);
72bdd1243dSDimitry Andric bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);
73bdd1243dSDimitry Andric
740b57cec5SDimitry Andric // Include the pieces autogenerated from the target description.
750b57cec5SDimitry Andric #include "WebAssemblyGenDAGISel.inc"
760b57cec5SDimitry Andric
770b57cec5SDimitry Andric private:
780b57cec5SDimitry Andric // add select functions here...
79bdd1243dSDimitry Andric
80bdd1243dSDimitry Andric bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op,
81bdd1243dSDimitry Andric SDValue &Offset, SDValue &Addr);
82bdd1243dSDimitry Andric bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset,
83bdd1243dSDimitry Andric SDValue &Addr);
840b57cec5SDimitry Andric };
850b57cec5SDimitry Andric } // end anonymous namespace
860b57cec5SDimitry Andric
87bdd1243dSDimitry Andric char WebAssemblyDAGToDAGISel::ID;
88bdd1243dSDimitry Andric
INITIALIZE_PASS(WebAssemblyDAGToDAGISel,DEBUG_TYPE,PASS_NAME,false,false)89bdd1243dSDimitry Andric INITIALIZE_PASS(WebAssemblyDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
90bdd1243dSDimitry Andric
91fe6060f1SDimitry Andric void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
92fe6060f1SDimitry Andric // Stack objects that should be allocated to locals are hoisted to WebAssembly
93fe6060f1SDimitry Andric // locals when they are first used. However for those without uses, we hoist
94fe6060f1SDimitry Andric // them here. It would be nice if there were some hook to do this when they
95fe6060f1SDimitry Andric // are added to the MachineFrameInfo, but that's not the case right now.
96fe6060f1SDimitry Andric MachineFrameInfo &FrameInfo = MF->getFrameInfo();
97fe6060f1SDimitry Andric for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
98fe6060f1SDimitry Andric WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
99fe6060f1SDimitry Andric
100fe6060f1SDimitry Andric SelectionDAGISel::PreprocessISelDAG();
101fe6060f1SDimitry Andric }
102fe6060f1SDimitry Andric
getTagSymNode(int Tag,SelectionDAG * DAG)103349cc55cSDimitry Andric static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {
104349cc55cSDimitry Andric assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP);
105349cc55cSDimitry Andric auto &MF = DAG->getMachineFunction();
106349cc55cSDimitry Andric const auto &TLI = DAG->getTargetLoweringInfo();
107349cc55cSDimitry Andric MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());
108349cc55cSDimitry Andric const char *SymName = Tag == WebAssembly::CPP_EXCEPTION
109349cc55cSDimitry Andric ? MF.createExternalSymbolName("__cpp_exception")
110349cc55cSDimitry Andric : MF.createExternalSymbolName("__c_longjmp");
111349cc55cSDimitry Andric return DAG->getTargetExternalSymbol(SymName, PtrVT);
112349cc55cSDimitry Andric }
113349cc55cSDimitry Andric
Select(SDNode * Node)1140b57cec5SDimitry Andric void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
1150b57cec5SDimitry Andric // If we have a custom node, we already have selected!
1160b57cec5SDimitry Andric if (Node->isMachineOpcode()) {
1170b57cec5SDimitry Andric LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
1180b57cec5SDimitry Andric Node->setNodeId(-1);
1190b57cec5SDimitry Andric return;
1200b57cec5SDimitry Andric }
1210b57cec5SDimitry Andric
1225ffd83dbSDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
1235ffd83dbSDimitry Andric auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
1245ffd83dbSDimitry Andric : WebAssembly::GLOBAL_GET_I32;
1255ffd83dbSDimitry Andric
1260b57cec5SDimitry Andric // Few custom selection stuff.
1270b57cec5SDimitry Andric SDLoc DL(Node);
1280b57cec5SDimitry Andric MachineFunction &MF = CurDAG->getMachineFunction();
1290b57cec5SDimitry Andric switch (Node->getOpcode()) {
1300b57cec5SDimitry Andric case ISD::ATOMIC_FENCE: {
1310b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
1320b57cec5SDimitry Andric break;
1330b57cec5SDimitry Andric
134349cc55cSDimitry Andric uint64_t SyncScopeID = Node->getConstantOperandVal(2);
1358bcb0991SDimitry Andric MachineSDNode *Fence = nullptr;
1360b57cec5SDimitry Andric switch (SyncScopeID) {
1378bcb0991SDimitry Andric case SyncScope::SingleThread:
1380b57cec5SDimitry Andric // We lower a single-thread fence to a pseudo compiler barrier instruction
1390b57cec5SDimitry Andric // preventing instruction reordering. This will not be emitted in final
1400b57cec5SDimitry Andric // binary.
1418bcb0991SDimitry Andric Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
1420b57cec5SDimitry Andric DL, // debug loc
1430b57cec5SDimitry Andric MVT::Other, // outchain type
1440b57cec5SDimitry Andric Node->getOperand(0) // inchain
1450b57cec5SDimitry Andric );
1468bcb0991SDimitry Andric break;
1478bcb0991SDimitry Andric case SyncScope::System:
1488bcb0991SDimitry Andric // Currently wasm only supports sequentially consistent atomics, so we
1498bcb0991SDimitry Andric // always set the order to 0 (sequentially consistent).
1508bcb0991SDimitry Andric Fence = CurDAG->getMachineNode(
1518bcb0991SDimitry Andric WebAssembly::ATOMIC_FENCE,
1520b57cec5SDimitry Andric DL, // debug loc
1530b57cec5SDimitry Andric MVT::Other, // outchain type
1548bcb0991SDimitry Andric CurDAG->getTargetConstant(0, DL, MVT::i32), // order
1550b57cec5SDimitry Andric Node->getOperand(0) // inchain
1568bcb0991SDimitry Andric );
1578bcb0991SDimitry Andric break;
1580b57cec5SDimitry Andric default:
1590b57cec5SDimitry Andric llvm_unreachable("Unknown scope!");
1600b57cec5SDimitry Andric }
1618bcb0991SDimitry Andric
1628bcb0991SDimitry Andric ReplaceNode(Node, Fence);
1638bcb0991SDimitry Andric CurDAG->RemoveDeadNode(Node);
1648bcb0991SDimitry Andric return;
1650b57cec5SDimitry Andric }
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: {
168349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(0);
1690b57cec5SDimitry Andric switch (IntNo) {
1700b57cec5SDimitry Andric case Intrinsic::wasm_tls_size: {
1710b57cec5SDimitry Andric MachineSDNode *TLSSize = CurDAG->getMachineNode(
1725ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT,
1735ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));
1740b57cec5SDimitry Andric ReplaceNode(Node, TLSSize);
1750b57cec5SDimitry Andric return;
1760b57cec5SDimitry Andric }
177349cc55cSDimitry Andric
1788bcb0991SDimitry Andric case Intrinsic::wasm_tls_align: {
1798bcb0991SDimitry Andric MachineSDNode *TLSAlign = CurDAG->getMachineNode(
1805ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT,
1815ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));
1828bcb0991SDimitry Andric ReplaceNode(Node, TLSAlign);
1838bcb0991SDimitry Andric return;
1848bcb0991SDimitry Andric }
1858bcb0991SDimitry Andric }
1868bcb0991SDimitry Andric break;
1878bcb0991SDimitry Andric }
188349cc55cSDimitry Andric
1898bcb0991SDimitry Andric case ISD::INTRINSIC_W_CHAIN: {
190349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(1);
191349cc55cSDimitry Andric const auto &TLI = CurDAG->getTargetLoweringInfo();
192349cc55cSDimitry Andric MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
1938bcb0991SDimitry Andric switch (IntNo) {
1948bcb0991SDimitry Andric case Intrinsic::wasm_tls_base: {
1958bcb0991SDimitry Andric MachineSDNode *TLSBase = CurDAG->getMachineNode(
1965ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, MVT::Other,
1978bcb0991SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
1988bcb0991SDimitry Andric Node->getOperand(0));
1998bcb0991SDimitry Andric ReplaceNode(Node, TLSBase);
2008bcb0991SDimitry Andric return;
2018bcb0991SDimitry Andric }
202349cc55cSDimitry Andric
203349cc55cSDimitry Andric case Intrinsic::wasm_catch: {
204349cc55cSDimitry Andric int Tag = Node->getConstantOperandVal(2);
205349cc55cSDimitry Andric SDValue SymNode = getTagSymNode(Tag, CurDAG);
206349cc55cSDimitry Andric MachineSDNode *Catch =
207349cc55cSDimitry Andric CurDAG->getMachineNode(WebAssembly::CATCH, DL,
208349cc55cSDimitry Andric {
209349cc55cSDimitry Andric PtrVT, // exception pointer
210349cc55cSDimitry Andric MVT::Other // outchain type
211349cc55cSDimitry Andric },
212349cc55cSDimitry Andric {
213349cc55cSDimitry Andric SymNode, // exception symbol
214349cc55cSDimitry Andric Node->getOperand(0) // inchain
215349cc55cSDimitry Andric });
216349cc55cSDimitry Andric ReplaceNode(Node, Catch);
217349cc55cSDimitry Andric return;
218349cc55cSDimitry Andric }
2190b57cec5SDimitry Andric }
2200b57cec5SDimitry Andric break;
2210b57cec5SDimitry Andric }
222349cc55cSDimitry Andric
223349cc55cSDimitry Andric case ISD::INTRINSIC_VOID: {
224349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(1);
225349cc55cSDimitry Andric switch (IntNo) {
226349cc55cSDimitry Andric case Intrinsic::wasm_throw: {
227349cc55cSDimitry Andric int Tag = Node->getConstantOperandVal(2);
228349cc55cSDimitry Andric SDValue SymNode = getTagSymNode(Tag, CurDAG);
229349cc55cSDimitry Andric MachineSDNode *Throw =
230349cc55cSDimitry Andric CurDAG->getMachineNode(WebAssembly::THROW, DL,
231349cc55cSDimitry Andric MVT::Other, // outchain type
232349cc55cSDimitry Andric {
233349cc55cSDimitry Andric SymNode, // exception symbol
234349cc55cSDimitry Andric Node->getOperand(3), // thrown value
235349cc55cSDimitry Andric Node->getOperand(0) // inchain
236349cc55cSDimitry Andric });
237349cc55cSDimitry Andric ReplaceNode(Node, Throw);
238349cc55cSDimitry Andric return;
239349cc55cSDimitry Andric }
240349cc55cSDimitry Andric }
241349cc55cSDimitry Andric break;
242349cc55cSDimitry Andric }
243349cc55cSDimitry Andric
2445ffd83dbSDimitry Andric case WebAssemblyISD::CALL:
2455ffd83dbSDimitry Andric case WebAssemblyISD::RET_CALL: {
2465ffd83dbSDimitry Andric // CALL has both variable operands and variable results, but ISel only
2475ffd83dbSDimitry Andric // supports one or the other. Split calls into two nodes glued together, one
2485ffd83dbSDimitry Andric // for the operands and one for the results. These two nodes will be
2495ffd83dbSDimitry Andric // recombined in a custom inserter hook into a single MachineInstr.
2505ffd83dbSDimitry Andric SmallVector<SDValue, 16> Ops;
2515ffd83dbSDimitry Andric for (size_t i = 1; i < Node->getNumOperands(); ++i) {
2525ffd83dbSDimitry Andric SDValue Op = Node->getOperand(i);
25306c3fb27SDimitry Andric // Remove the wrapper when the call target is a function, an external
25406c3fb27SDimitry Andric // symbol (which will be lowered to a library function), or an alias of
25506c3fb27SDimitry Andric // a function. If the target is not a function/external symbol, we
25606c3fb27SDimitry Andric // shouldn't remove the wrapper, because we cannot call it directly and
25706c3fb27SDimitry Andric // instead we want it to be loaded with a CONST instruction and called
25806c3fb27SDimitry Andric // with a call_indirect later.
25906c3fb27SDimitry Andric if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) {
26006c3fb27SDimitry Andric SDValue NewOp = Op->getOperand(0);
26106c3fb27SDimitry Andric if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(NewOp.getNode())) {
26206c3fb27SDimitry Andric if (isa<Function>(
26306c3fb27SDimitry Andric GlobalOp->getGlobal()->stripPointerCastsAndAliases()))
26406c3fb27SDimitry Andric Op = NewOp;
26506c3fb27SDimitry Andric } else if (isa<ExternalSymbolSDNode>(NewOp.getNode())) {
26606c3fb27SDimitry Andric Op = NewOp;
26706c3fb27SDimitry Andric }
26806c3fb27SDimitry Andric }
2695ffd83dbSDimitry Andric Ops.push_back(Op);
2705ffd83dbSDimitry Andric }
2715ffd83dbSDimitry Andric
2725ffd83dbSDimitry Andric // Add the chain last
2735ffd83dbSDimitry Andric Ops.push_back(Node->getOperand(0));
2745ffd83dbSDimitry Andric MachineSDNode *CallParams =
2755ffd83dbSDimitry Andric CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);
2765ffd83dbSDimitry Andric
2775ffd83dbSDimitry Andric unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL
2785ffd83dbSDimitry Andric ? WebAssembly::CALL_RESULTS
2795ffd83dbSDimitry Andric : WebAssembly::RET_CALL_RESULTS;
2805ffd83dbSDimitry Andric
2815ffd83dbSDimitry Andric SDValue Link(CallParams, 0);
2825ffd83dbSDimitry Andric MachineSDNode *CallResults =
2835ffd83dbSDimitry Andric CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);
2845ffd83dbSDimitry Andric ReplaceNode(Node, CallResults);
2855ffd83dbSDimitry Andric return;
2865ffd83dbSDimitry Andric }
2870b57cec5SDimitry Andric
2880b57cec5SDimitry Andric default:
2890b57cec5SDimitry Andric break;
2900b57cec5SDimitry Andric }
2910b57cec5SDimitry Andric
2920b57cec5SDimitry Andric // Select the default instruction.
2930b57cec5SDimitry Andric SelectCode(Node);
2940b57cec5SDimitry Andric }
2950b57cec5SDimitry Andric
SelectInlineAsmMemoryOperand(const SDValue & Op,InlineAsm::ConstraintCode ConstraintID,std::vector<SDValue> & OutOps)2960b57cec5SDimitry Andric bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
2975f757f3fSDimitry Andric const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
2985f757f3fSDimitry Andric std::vector<SDValue> &OutOps) {
2990b57cec5SDimitry Andric switch (ConstraintID) {
3005f757f3fSDimitry Andric case InlineAsm::ConstraintCode::m:
3010b57cec5SDimitry Andric // We just support simple memory operands that just have a single address
3020b57cec5SDimitry Andric // operand and need no special handling.
3030b57cec5SDimitry Andric OutOps.push_back(Op);
3040b57cec5SDimitry Andric return false;
3050b57cec5SDimitry Andric default:
3060b57cec5SDimitry Andric break;
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric
3090b57cec5SDimitry Andric return true;
3100b57cec5SDimitry Andric }
3110b57cec5SDimitry Andric
SelectAddrAddOperands(MVT OffsetType,SDValue N,SDValue & Offset,SDValue & Addr)312bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N,
313bdd1243dSDimitry Andric SDValue &Offset,
314bdd1243dSDimitry Andric SDValue &Addr) {
315bdd1243dSDimitry Andric assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op");
316bdd1243dSDimitry Andric
317bdd1243dSDimitry Andric // WebAssembly constant offsets are performed as unsigned with infinite
318bdd1243dSDimitry Andric // precision, so we need to check for NoUnsignedWrap so that we don't fold an
319bdd1243dSDimitry Andric // offset for an add that needs wrapping.
320bdd1243dSDimitry Andric if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap())
321bdd1243dSDimitry Andric return false;
322bdd1243dSDimitry Andric
323bdd1243dSDimitry Andric // Folds constants in an add into the offset.
324bdd1243dSDimitry Andric for (size_t i = 0; i < 2; ++i) {
325bdd1243dSDimitry Andric SDValue Op = N.getOperand(i);
326bdd1243dSDimitry Andric SDValue OtherOp = N.getOperand(i == 0 ? 1 : 0);
327bdd1243dSDimitry Andric
328bdd1243dSDimitry Andric if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op)) {
329bdd1243dSDimitry Andric Offset =
330bdd1243dSDimitry Andric CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), OffsetType);
331bdd1243dSDimitry Andric Addr = OtherOp;
332bdd1243dSDimitry Andric return true;
333bdd1243dSDimitry Andric }
334bdd1243dSDimitry Andric }
335bdd1243dSDimitry Andric return false;
336bdd1243dSDimitry Andric }
337bdd1243dSDimitry Andric
SelectAddrOperands(MVT AddrType,unsigned ConstOpc,SDValue N,SDValue & Offset,SDValue & Addr)338bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType,
339bdd1243dSDimitry Andric unsigned ConstOpc, SDValue N,
340bdd1243dSDimitry Andric SDValue &Offset,
341bdd1243dSDimitry Andric SDValue &Addr) {
342bdd1243dSDimitry Andric SDLoc DL(N);
343bdd1243dSDimitry Andric
344bdd1243dSDimitry Andric // Fold target global addresses into the offset.
345bdd1243dSDimitry Andric if (!TM.isPositionIndependent()) {
346bdd1243dSDimitry Andric SDValue Op(N);
347bdd1243dSDimitry Andric if (Op.getOpcode() == WebAssemblyISD::Wrapper)
348bdd1243dSDimitry Andric Op = Op.getOperand(0);
349bdd1243dSDimitry Andric
350bdd1243dSDimitry Andric if (Op.getOpcode() == ISD::TargetGlobalAddress) {
351bdd1243dSDimitry Andric Offset = Op;
352bdd1243dSDimitry Andric Addr = SDValue(
353bdd1243dSDimitry Andric CurDAG->getMachineNode(ConstOpc, DL, AddrType,
354bdd1243dSDimitry Andric CurDAG->getTargetConstant(0, DL, AddrType)),
355bdd1243dSDimitry Andric 0);
356bdd1243dSDimitry Andric return true;
357bdd1243dSDimitry Andric }
358bdd1243dSDimitry Andric }
359bdd1243dSDimitry Andric
360bdd1243dSDimitry Andric // Fold anything inside an add into the offset.
361bdd1243dSDimitry Andric if (N.getOpcode() == ISD::ADD &&
362bdd1243dSDimitry Andric SelectAddrAddOperands(AddrType, N, Offset, Addr))
363bdd1243dSDimitry Andric return true;
364bdd1243dSDimitry Andric
365bdd1243dSDimitry Andric // Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be
366bdd1243dSDimitry Andric // zero and fold them into the offset too.
367bdd1243dSDimitry Andric if (N.getOpcode() == ISD::OR) {
368bdd1243dSDimitry Andric bool OrIsAdd;
369bdd1243dSDimitry Andric if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
370bdd1243dSDimitry Andric OrIsAdd =
371bdd1243dSDimitry Andric CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
372bdd1243dSDimitry Andric } else {
373bdd1243dSDimitry Andric KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
374bdd1243dSDimitry Andric KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
375bdd1243dSDimitry Andric OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0;
376bdd1243dSDimitry Andric }
377bdd1243dSDimitry Andric
378bdd1243dSDimitry Andric if (OrIsAdd && SelectAddrAddOperands(AddrType, N, Offset, Addr))
379bdd1243dSDimitry Andric return true;
380bdd1243dSDimitry Andric }
381bdd1243dSDimitry Andric
382bdd1243dSDimitry Andric // Fold constant addresses into the offset.
383bdd1243dSDimitry Andric if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N)) {
384bdd1243dSDimitry Andric Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, AddrType);
385bdd1243dSDimitry Andric Addr = SDValue(
386bdd1243dSDimitry Andric CurDAG->getMachineNode(ConstOpc, DL, AddrType,
387bdd1243dSDimitry Andric CurDAG->getTargetConstant(0, DL, AddrType)),
388bdd1243dSDimitry Andric 0);
389bdd1243dSDimitry Andric return true;
390bdd1243dSDimitry Andric }
391bdd1243dSDimitry Andric
392bdd1243dSDimitry Andric // Else it's a plain old load/store with no offset.
393bdd1243dSDimitry Andric Offset = CurDAG->getTargetConstant(0, DL, AddrType);
394bdd1243dSDimitry Andric Addr = N;
395bdd1243dSDimitry Andric return true;
396bdd1243dSDimitry Andric }
397bdd1243dSDimitry Andric
SelectAddrOperands32(SDValue Op,SDValue & Offset,SDValue & Addr)398bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset,
399bdd1243dSDimitry Andric SDValue &Addr) {
400bdd1243dSDimitry Andric return SelectAddrOperands(MVT::i32, WebAssembly::CONST_I32, Op, Offset, Addr);
401bdd1243dSDimitry Andric }
402bdd1243dSDimitry Andric
SelectAddrOperands64(SDValue Op,SDValue & Offset,SDValue & Addr)403bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,
404bdd1243dSDimitry Andric SDValue &Addr) {
405bdd1243dSDimitry Andric return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr);
406bdd1243dSDimitry Andric }
407bdd1243dSDimitry Andric
4080b57cec5SDimitry Andric /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
4090b57cec5SDimitry Andric /// for instruction scheduling.
createWebAssemblyISelDag(WebAssemblyTargetMachine & TM,CodeGenOptLevel OptLevel)4100b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
4115f757f3fSDimitry Andric CodeGenOptLevel OptLevel) {
4120b57cec5SDimitry Andric return new WebAssemblyDAGToDAGISel(TM, OptLevel);
4130b57cec5SDimitry Andric }
414