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 WebAssemblyDAGToDAGISel() = delete; 46bdd1243dSDimitry Andric 470b57cec5SDimitry Andric WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, 485f757f3fSDimitry Andric CodeGenOptLevel OptLevel) 490fca6ea1SDimitry Andric : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {} 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override { 520b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" 530b57cec5SDimitry Andric "********** Function: " 540b57cec5SDimitry Andric << MF.getName() << '\n'); 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); 578bcb0991SDimitry Andric 580b57cec5SDimitry Andric return SelectionDAGISel::runOnMachineFunction(MF); 590b57cec5SDimitry Andric } 600b57cec5SDimitry Andric 61fe6060f1SDimitry Andric void PreprocessISelDAG() override; 62fe6060f1SDimitry Andric 630b57cec5SDimitry Andric void Select(SDNode *Node) override; 640b57cec5SDimitry Andric 655f757f3fSDimitry Andric bool SelectInlineAsmMemoryOperand(const SDValue &Op, 665f757f3fSDimitry Andric InlineAsm::ConstraintCode ConstraintID, 670b57cec5SDimitry Andric std::vector<SDValue> &OutOps) override; 680b57cec5SDimitry Andric 69bdd1243dSDimitry Andric bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr); 70bdd1243dSDimitry Andric bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr); 71bdd1243dSDimitry Andric 720b57cec5SDimitry Andric // Include the pieces autogenerated from the target description. 730b57cec5SDimitry Andric #include "WebAssemblyGenDAGISel.inc" 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric private: 760b57cec5SDimitry Andric // add select functions here... 77bdd1243dSDimitry Andric 78bdd1243dSDimitry Andric bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op, 79bdd1243dSDimitry Andric SDValue &Offset, SDValue &Addr); 80bdd1243dSDimitry Andric bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset, 81bdd1243dSDimitry Andric SDValue &Addr); 820b57cec5SDimitry Andric }; 830fca6ea1SDimitry Andric 840fca6ea1SDimitry Andric class WebAssemblyDAGToDAGISelLegacy : public SelectionDAGISelLegacy { 850fca6ea1SDimitry Andric public: 860fca6ea1SDimitry Andric static char ID; 870fca6ea1SDimitry Andric explicit WebAssemblyDAGToDAGISelLegacy(WebAssemblyTargetMachine &TM, 880fca6ea1SDimitry Andric CodeGenOptLevel OptLevel) 890fca6ea1SDimitry Andric : SelectionDAGISelLegacy( 900fca6ea1SDimitry Andric ID, std::make_unique<WebAssemblyDAGToDAGISel>(TM, OptLevel)) {} 910fca6ea1SDimitry Andric }; 920b57cec5SDimitry Andric } // end anonymous namespace 930b57cec5SDimitry Andric 940fca6ea1SDimitry Andric char WebAssemblyDAGToDAGISelLegacy::ID; 95bdd1243dSDimitry Andric 960fca6ea1SDimitry Andric INITIALIZE_PASS(WebAssemblyDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, 970fca6ea1SDimitry Andric false) 98bdd1243dSDimitry Andric 99fe6060f1SDimitry Andric void WebAssemblyDAGToDAGISel::PreprocessISelDAG() { 100fe6060f1SDimitry Andric // Stack objects that should be allocated to locals are hoisted to WebAssembly 101fe6060f1SDimitry Andric // locals when they are first used. However for those without uses, we hoist 102fe6060f1SDimitry Andric // them here. It would be nice if there were some hook to do this when they 103fe6060f1SDimitry Andric // are added to the MachineFrameInfo, but that's not the case right now. 104fe6060f1SDimitry Andric MachineFrameInfo &FrameInfo = MF->getFrameInfo(); 105fe6060f1SDimitry Andric for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++) 106fe6060f1SDimitry Andric WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx); 107fe6060f1SDimitry Andric 108fe6060f1SDimitry Andric SelectionDAGISel::PreprocessISelDAG(); 109fe6060f1SDimitry Andric } 110fe6060f1SDimitry Andric 111349cc55cSDimitry Andric static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) { 112349cc55cSDimitry Andric assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP); 113349cc55cSDimitry Andric auto &MF = DAG->getMachineFunction(); 114349cc55cSDimitry Andric const auto &TLI = DAG->getTargetLoweringInfo(); 115349cc55cSDimitry Andric MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout()); 116349cc55cSDimitry Andric const char *SymName = Tag == WebAssembly::CPP_EXCEPTION 117349cc55cSDimitry Andric ? MF.createExternalSymbolName("__cpp_exception") 118349cc55cSDimitry Andric : MF.createExternalSymbolName("__c_longjmp"); 119349cc55cSDimitry Andric return DAG->getTargetExternalSymbol(SymName, PtrVT); 120349cc55cSDimitry Andric } 121349cc55cSDimitry Andric 1220b57cec5SDimitry Andric void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { 1230b57cec5SDimitry Andric // If we have a custom node, we already have selected! 1240b57cec5SDimitry Andric if (Node->isMachineOpcode()) { 1250b57cec5SDimitry Andric LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); 1260b57cec5SDimitry Andric Node->setNodeId(-1); 1270b57cec5SDimitry Andric return; 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1305ffd83dbSDimitry Andric MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); 1315ffd83dbSDimitry Andric auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 1325ffd83dbSDimitry Andric : WebAssembly::GLOBAL_GET_I32; 1335ffd83dbSDimitry Andric 1340b57cec5SDimitry Andric // Few custom selection stuff. 1350b57cec5SDimitry Andric SDLoc DL(Node); 1360b57cec5SDimitry Andric MachineFunction &MF = CurDAG->getMachineFunction(); 1370b57cec5SDimitry Andric switch (Node->getOpcode()) { 1380b57cec5SDimitry Andric case ISD::ATOMIC_FENCE: { 1390b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) 1400b57cec5SDimitry Andric break; 1410b57cec5SDimitry Andric 142349cc55cSDimitry Andric uint64_t SyncScopeID = Node->getConstantOperandVal(2); 1438bcb0991SDimitry Andric MachineSDNode *Fence = nullptr; 1440b57cec5SDimitry Andric switch (SyncScopeID) { 1458bcb0991SDimitry Andric case SyncScope::SingleThread: 1460b57cec5SDimitry Andric // We lower a single-thread fence to a pseudo compiler barrier instruction 1470b57cec5SDimitry Andric // preventing instruction reordering. This will not be emitted in final 1480b57cec5SDimitry Andric // binary. 1498bcb0991SDimitry Andric Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, 1500b57cec5SDimitry Andric DL, // debug loc 1510b57cec5SDimitry Andric MVT::Other, // outchain type 1520b57cec5SDimitry Andric Node->getOperand(0) // inchain 1530b57cec5SDimitry Andric ); 1548bcb0991SDimitry Andric break; 1558bcb0991SDimitry Andric case SyncScope::System: 1568bcb0991SDimitry Andric // Currently wasm only supports sequentially consistent atomics, so we 1578bcb0991SDimitry Andric // always set the order to 0 (sequentially consistent). 1588bcb0991SDimitry Andric Fence = CurDAG->getMachineNode( 1598bcb0991SDimitry Andric WebAssembly::ATOMIC_FENCE, 1600b57cec5SDimitry Andric DL, // debug loc 1610b57cec5SDimitry Andric MVT::Other, // outchain type 1628bcb0991SDimitry Andric CurDAG->getTargetConstant(0, DL, MVT::i32), // order 1630b57cec5SDimitry Andric Node->getOperand(0) // inchain 1648bcb0991SDimitry Andric ); 1658bcb0991SDimitry Andric break; 1660b57cec5SDimitry Andric default: 1670b57cec5SDimitry Andric llvm_unreachable("Unknown scope!"); 1680b57cec5SDimitry Andric } 1698bcb0991SDimitry Andric 1708bcb0991SDimitry Andric ReplaceNode(Node, Fence); 1718bcb0991SDimitry Andric CurDAG->RemoveDeadNode(Node); 1728bcb0991SDimitry Andric return; 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: { 176349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(0); 1770b57cec5SDimitry Andric switch (IntNo) { 1780b57cec5SDimitry Andric case Intrinsic::wasm_tls_size: { 1790b57cec5SDimitry Andric MachineSDNode *TLSSize = CurDAG->getMachineNode( 1805ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, 1815ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_size", PtrVT)); 1820b57cec5SDimitry Andric ReplaceNode(Node, TLSSize); 1830b57cec5SDimitry Andric return; 1840b57cec5SDimitry Andric } 185349cc55cSDimitry Andric 1868bcb0991SDimitry Andric case Intrinsic::wasm_tls_align: { 1878bcb0991SDimitry Andric MachineSDNode *TLSAlign = CurDAG->getMachineNode( 1885ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, 1895ffd83dbSDimitry Andric CurDAG->getTargetExternalSymbol("__tls_align", PtrVT)); 1908bcb0991SDimitry Andric ReplaceNode(Node, TLSAlign); 1918bcb0991SDimitry Andric return; 1928bcb0991SDimitry Andric } 1938bcb0991SDimitry Andric } 1948bcb0991SDimitry Andric break; 1958bcb0991SDimitry Andric } 196349cc55cSDimitry Andric 1978bcb0991SDimitry Andric case ISD::INTRINSIC_W_CHAIN: { 198349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(1); 199349cc55cSDimitry Andric const auto &TLI = CurDAG->getTargetLoweringInfo(); 200349cc55cSDimitry Andric MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout()); 2018bcb0991SDimitry Andric switch (IntNo) { 2028bcb0991SDimitry Andric case Intrinsic::wasm_tls_base: { 2038bcb0991SDimitry Andric MachineSDNode *TLSBase = CurDAG->getMachineNode( 2045ffd83dbSDimitry Andric GlobalGetIns, DL, PtrVT, MVT::Other, 2058bcb0991SDimitry Andric CurDAG->getTargetExternalSymbol("__tls_base", PtrVT), 2068bcb0991SDimitry Andric Node->getOperand(0)); 2078bcb0991SDimitry Andric ReplaceNode(Node, TLSBase); 2088bcb0991SDimitry Andric return; 2098bcb0991SDimitry Andric } 210349cc55cSDimitry Andric 211349cc55cSDimitry Andric case Intrinsic::wasm_catch: { 212349cc55cSDimitry Andric int Tag = Node->getConstantOperandVal(2); 213349cc55cSDimitry Andric SDValue SymNode = getTagSymNode(Tag, CurDAG); 214349cc55cSDimitry Andric MachineSDNode *Catch = 215349cc55cSDimitry Andric CurDAG->getMachineNode(WebAssembly::CATCH, DL, 216349cc55cSDimitry Andric { 217349cc55cSDimitry Andric PtrVT, // exception pointer 218349cc55cSDimitry Andric MVT::Other // outchain type 219349cc55cSDimitry Andric }, 220349cc55cSDimitry Andric { 221349cc55cSDimitry Andric SymNode, // exception symbol 222349cc55cSDimitry Andric Node->getOperand(0) // inchain 223349cc55cSDimitry Andric }); 224349cc55cSDimitry Andric ReplaceNode(Node, Catch); 225349cc55cSDimitry Andric return; 226349cc55cSDimitry Andric } 2270b57cec5SDimitry Andric } 2280b57cec5SDimitry Andric break; 2290b57cec5SDimitry Andric } 230349cc55cSDimitry Andric 231349cc55cSDimitry Andric case ISD::INTRINSIC_VOID: { 232349cc55cSDimitry Andric unsigned IntNo = Node->getConstantOperandVal(1); 233349cc55cSDimitry Andric switch (IntNo) { 234349cc55cSDimitry Andric case Intrinsic::wasm_throw: { 235349cc55cSDimitry Andric int Tag = Node->getConstantOperandVal(2); 236349cc55cSDimitry Andric SDValue SymNode = getTagSymNode(Tag, CurDAG); 237349cc55cSDimitry Andric MachineSDNode *Throw = 238349cc55cSDimitry Andric CurDAG->getMachineNode(WebAssembly::THROW, DL, 239349cc55cSDimitry Andric MVT::Other, // outchain type 240349cc55cSDimitry Andric { 241349cc55cSDimitry Andric SymNode, // exception symbol 242349cc55cSDimitry Andric Node->getOperand(3), // thrown value 243349cc55cSDimitry Andric Node->getOperand(0) // inchain 244349cc55cSDimitry Andric }); 245349cc55cSDimitry Andric ReplaceNode(Node, Throw); 246349cc55cSDimitry Andric return; 247349cc55cSDimitry Andric } 248*415efcecSDimitry Andric case Intrinsic::wasm_rethrow: { 249*415efcecSDimitry Andric // RETHROW's BB argument will be populated in LateEHPrepare. Just use a 250*415efcecSDimitry Andric // '0' as a placeholder for now. 251*415efcecSDimitry Andric MachineSDNode *Rethrow = CurDAG->getMachineNode( 252*415efcecSDimitry Andric WebAssembly::RETHROW, DL, 253*415efcecSDimitry Andric MVT::Other, // outchain type 254*415efcecSDimitry Andric { 255*415efcecSDimitry Andric CurDAG->getConstant(0, DL, MVT::i32), // placeholder 256*415efcecSDimitry Andric Node->getOperand(0) // inchain 257*415efcecSDimitry Andric }); 258*415efcecSDimitry Andric ReplaceNode(Node, Rethrow); 259*415efcecSDimitry Andric return; 260*415efcecSDimitry Andric } 261349cc55cSDimitry Andric } 262349cc55cSDimitry Andric break; 263349cc55cSDimitry Andric } 264349cc55cSDimitry Andric 2655ffd83dbSDimitry Andric case WebAssemblyISD::CALL: 2665ffd83dbSDimitry Andric case WebAssemblyISD::RET_CALL: { 2675ffd83dbSDimitry Andric // CALL has both variable operands and variable results, but ISel only 2685ffd83dbSDimitry Andric // supports one or the other. Split calls into two nodes glued together, one 2695ffd83dbSDimitry Andric // for the operands and one for the results. These two nodes will be 2705ffd83dbSDimitry Andric // recombined in a custom inserter hook into a single MachineInstr. 2715ffd83dbSDimitry Andric SmallVector<SDValue, 16> Ops; 2725ffd83dbSDimitry Andric for (size_t i = 1; i < Node->getNumOperands(); ++i) { 2735ffd83dbSDimitry Andric SDValue Op = Node->getOperand(i); 27406c3fb27SDimitry Andric // Remove the wrapper when the call target is a function, an external 27506c3fb27SDimitry Andric // symbol (which will be lowered to a library function), or an alias of 27606c3fb27SDimitry Andric // a function. If the target is not a function/external symbol, we 27706c3fb27SDimitry Andric // shouldn't remove the wrapper, because we cannot call it directly and 27806c3fb27SDimitry Andric // instead we want it to be loaded with a CONST instruction and called 27906c3fb27SDimitry Andric // with a call_indirect later. 28006c3fb27SDimitry Andric if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) { 28106c3fb27SDimitry Andric SDValue NewOp = Op->getOperand(0); 28206c3fb27SDimitry Andric if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(NewOp.getNode())) { 28306c3fb27SDimitry Andric if (isa<Function>( 28406c3fb27SDimitry Andric GlobalOp->getGlobal()->stripPointerCastsAndAliases())) 28506c3fb27SDimitry Andric Op = NewOp; 28606c3fb27SDimitry Andric } else if (isa<ExternalSymbolSDNode>(NewOp.getNode())) { 28706c3fb27SDimitry Andric Op = NewOp; 28806c3fb27SDimitry Andric } 28906c3fb27SDimitry Andric } 2905ffd83dbSDimitry Andric Ops.push_back(Op); 2915ffd83dbSDimitry Andric } 2925ffd83dbSDimitry Andric 2935ffd83dbSDimitry Andric // Add the chain last 2945ffd83dbSDimitry Andric Ops.push_back(Node->getOperand(0)); 2955ffd83dbSDimitry Andric MachineSDNode *CallParams = 2965ffd83dbSDimitry Andric CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops); 2975ffd83dbSDimitry Andric 2985ffd83dbSDimitry Andric unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL 2995ffd83dbSDimitry Andric ? WebAssembly::CALL_RESULTS 3005ffd83dbSDimitry Andric : WebAssembly::RET_CALL_RESULTS; 3015ffd83dbSDimitry Andric 3025ffd83dbSDimitry Andric SDValue Link(CallParams, 0); 3035ffd83dbSDimitry Andric MachineSDNode *CallResults = 3045ffd83dbSDimitry Andric CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link); 3055ffd83dbSDimitry Andric ReplaceNode(Node, CallResults); 3065ffd83dbSDimitry Andric return; 3075ffd83dbSDimitry Andric } 3080b57cec5SDimitry Andric 3090b57cec5SDimitry Andric default: 3100b57cec5SDimitry Andric break; 3110b57cec5SDimitry Andric } 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric // Select the default instruction. 3140b57cec5SDimitry Andric SelectCode(Node); 3150b57cec5SDimitry Andric } 3160b57cec5SDimitry Andric 3170b57cec5SDimitry Andric bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( 3185f757f3fSDimitry Andric const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, 3195f757f3fSDimitry Andric std::vector<SDValue> &OutOps) { 3200b57cec5SDimitry Andric switch (ConstraintID) { 3215f757f3fSDimitry Andric case InlineAsm::ConstraintCode::m: 3220b57cec5SDimitry Andric // We just support simple memory operands that just have a single address 3230b57cec5SDimitry Andric // operand and need no special handling. 3240b57cec5SDimitry Andric OutOps.push_back(Op); 3250b57cec5SDimitry Andric return false; 3260b57cec5SDimitry Andric default: 3270b57cec5SDimitry Andric break; 3280b57cec5SDimitry Andric } 3290b57cec5SDimitry Andric 3300b57cec5SDimitry Andric return true; 3310b57cec5SDimitry Andric } 3320b57cec5SDimitry Andric 333bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N, 334bdd1243dSDimitry Andric SDValue &Offset, 335bdd1243dSDimitry Andric SDValue &Addr) { 336bdd1243dSDimitry Andric assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op"); 337bdd1243dSDimitry Andric 338bdd1243dSDimitry Andric // WebAssembly constant offsets are performed as unsigned with infinite 339bdd1243dSDimitry Andric // precision, so we need to check for NoUnsignedWrap so that we don't fold an 340bdd1243dSDimitry Andric // offset for an add that needs wrapping. 341bdd1243dSDimitry Andric if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap()) 342bdd1243dSDimitry Andric return false; 343bdd1243dSDimitry Andric 344bdd1243dSDimitry Andric // Folds constants in an add into the offset. 345bdd1243dSDimitry Andric for (size_t i = 0; i < 2; ++i) { 346bdd1243dSDimitry Andric SDValue Op = N.getOperand(i); 347bdd1243dSDimitry Andric SDValue OtherOp = N.getOperand(i == 0 ? 1 : 0); 348bdd1243dSDimitry Andric 349bdd1243dSDimitry Andric if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op)) { 350bdd1243dSDimitry Andric Offset = 351bdd1243dSDimitry Andric CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), OffsetType); 352bdd1243dSDimitry Andric Addr = OtherOp; 353bdd1243dSDimitry Andric return true; 354bdd1243dSDimitry Andric } 355bdd1243dSDimitry Andric } 356bdd1243dSDimitry Andric return false; 357bdd1243dSDimitry Andric } 358bdd1243dSDimitry Andric 359bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType, 360bdd1243dSDimitry Andric unsigned ConstOpc, SDValue N, 361bdd1243dSDimitry Andric SDValue &Offset, 362bdd1243dSDimitry Andric SDValue &Addr) { 363bdd1243dSDimitry Andric SDLoc DL(N); 364bdd1243dSDimitry Andric 365bdd1243dSDimitry Andric // Fold target global addresses into the offset. 366bdd1243dSDimitry Andric if (!TM.isPositionIndependent()) { 367bdd1243dSDimitry Andric SDValue Op(N); 368bdd1243dSDimitry Andric if (Op.getOpcode() == WebAssemblyISD::Wrapper) 369bdd1243dSDimitry Andric Op = Op.getOperand(0); 370bdd1243dSDimitry Andric 371bdd1243dSDimitry Andric if (Op.getOpcode() == ISD::TargetGlobalAddress) { 372bdd1243dSDimitry Andric Offset = Op; 373bdd1243dSDimitry Andric Addr = SDValue( 374bdd1243dSDimitry Andric CurDAG->getMachineNode(ConstOpc, DL, AddrType, 375bdd1243dSDimitry Andric CurDAG->getTargetConstant(0, DL, AddrType)), 376bdd1243dSDimitry Andric 0); 377bdd1243dSDimitry Andric return true; 378bdd1243dSDimitry Andric } 379bdd1243dSDimitry Andric } 380bdd1243dSDimitry Andric 381bdd1243dSDimitry Andric // Fold anything inside an add into the offset. 382bdd1243dSDimitry Andric if (N.getOpcode() == ISD::ADD && 383bdd1243dSDimitry Andric SelectAddrAddOperands(AddrType, N, Offset, Addr)) 384bdd1243dSDimitry Andric return true; 385bdd1243dSDimitry Andric 386bdd1243dSDimitry Andric // Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be 387bdd1243dSDimitry Andric // zero and fold them into the offset too. 388bdd1243dSDimitry Andric if (N.getOpcode() == ISD::OR) { 389bdd1243dSDimitry Andric bool OrIsAdd; 390bdd1243dSDimitry Andric if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N.getOperand(1))) { 391bdd1243dSDimitry Andric OrIsAdd = 392bdd1243dSDimitry Andric CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue()); 393bdd1243dSDimitry Andric } else { 394bdd1243dSDimitry Andric KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0); 395bdd1243dSDimitry Andric KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0); 396bdd1243dSDimitry Andric OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0; 397bdd1243dSDimitry Andric } 398bdd1243dSDimitry Andric 399bdd1243dSDimitry Andric if (OrIsAdd && SelectAddrAddOperands(AddrType, N, Offset, Addr)) 400bdd1243dSDimitry Andric return true; 401bdd1243dSDimitry Andric } 402bdd1243dSDimitry Andric 403bdd1243dSDimitry Andric // Fold constant addresses into the offset. 404bdd1243dSDimitry Andric if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N)) { 405bdd1243dSDimitry Andric Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, AddrType); 406bdd1243dSDimitry Andric Addr = SDValue( 407bdd1243dSDimitry Andric CurDAG->getMachineNode(ConstOpc, DL, AddrType, 408bdd1243dSDimitry Andric CurDAG->getTargetConstant(0, DL, AddrType)), 409bdd1243dSDimitry Andric 0); 410bdd1243dSDimitry Andric return true; 411bdd1243dSDimitry Andric } 412bdd1243dSDimitry Andric 413bdd1243dSDimitry Andric // Else it's a plain old load/store with no offset. 414bdd1243dSDimitry Andric Offset = CurDAG->getTargetConstant(0, DL, AddrType); 415bdd1243dSDimitry Andric Addr = N; 416bdd1243dSDimitry Andric return true; 417bdd1243dSDimitry Andric } 418bdd1243dSDimitry Andric 419bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset, 420bdd1243dSDimitry Andric SDValue &Addr) { 421bdd1243dSDimitry Andric return SelectAddrOperands(MVT::i32, WebAssembly::CONST_I32, Op, Offset, Addr); 422bdd1243dSDimitry Andric } 423bdd1243dSDimitry Andric 424bdd1243dSDimitry Andric bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset, 425bdd1243dSDimitry Andric SDValue &Addr) { 426bdd1243dSDimitry Andric return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr); 427bdd1243dSDimitry Andric } 428bdd1243dSDimitry Andric 4290b57cec5SDimitry Andric /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready 4300b57cec5SDimitry Andric /// for instruction scheduling. 4310b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, 4325f757f3fSDimitry Andric CodeGenOptLevel OptLevel) { 4330fca6ea1SDimitry Andric return new WebAssemblyDAGToDAGISelLegacy(TM, OptLevel); 4340b57cec5SDimitry Andric } 435