| //===-- M68kISelDAGToDAG.cpp - M68k Dag to Dag Inst Selector ----*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// This file defines an instruction selector for the M68K target. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "M68k.h" | |
| #include "M68kMachineFunction.h" | |
| #include "M68kRegisterInfo.h" | |
| #include "M68kTargetMachine.h" | |
| #include "llvm/CodeGen/MachineConstantPool.h" | |
| #include "llvm/CodeGen/MachineFrameInfo.h" | |
| #include "llvm/CodeGen/MachineFunction.h" | |
| #include "llvm/CodeGen/MachineInstrBuilder.h" | |
| #include "llvm/CodeGen/MachineRegisterInfo.h" | |
| #include "llvm/CodeGen/SelectionDAGISel.h" | |
| #include "llvm/CodeGen/SelectionDAGNodes.h" | |
| #include "llvm/IR/CFG.h" | |
| #include "llvm/IR/GlobalValue.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/Intrinsics.h" | |
| #include "llvm/IR/Type.h" | |
| #include "llvm/Support/Alignment.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/MathExtras.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "m68k-isel" | |
| #define PASS_NAME "M68k DAG->DAG Pattern Instruction Selection" | |
| namespace { | |
| // For reference, the full order of operands for memory references is: | |
| // (Operand), Displacement, Base, Index, Scale | |
| struct M68kISelAddressMode { | |
| enum class AddrType { | |
| ARI, // Address Register Indirect | |
| ARIPI, // Address Register Indirect with Postincrement | |
| ARIPD, // Address Register Indirect with Postdecrement | |
| ARID, // Address Register Indirect with Displacement | |
| ARII, // Address Register Indirect with Index | |
| PCD, // Program Counter Indirect with Displacement | |
| PCI, // Program Counter Indirect with Index | |
| AL, // Absolute | |
| }; | |
| AddrType AM; | |
| enum class Base { RegBase, FrameIndexBase }; | |
| Base BaseType; | |
| int64_t Disp; | |
| // This is really a union, discriminated by BaseType! | |
| SDValue BaseReg; | |
| int BaseFrameIndex; | |
| SDValue IndexReg; | |
| unsigned Scale; | |
| const GlobalValue *GV; | |
| const Constant *CP; | |
| const BlockAddress *BlockAddr; | |
| const char *ES; | |
| MCSymbol *MCSym; | |
| int JT; | |
| Align Alignment; // CP alignment. | |
| unsigned char SymbolFlags; // M68kII::MO_* | |
| M68kISelAddressMode(AddrType AT) | |
| : AM(AT), BaseType(Base::RegBase), Disp(0), BaseFrameIndex(0), IndexReg(), | |
| Scale(1), GV(nullptr), CP(nullptr), BlockAddr(nullptr), ES(nullptr), | |
| MCSym(nullptr), JT(-1), Alignment(), SymbolFlags(M68kII::MO_NO_FLAG) {} | |
| bool hasSymbolicDisplacement() const { | |
| return GV != nullptr || CP != nullptr || ES != nullptr || | |
| MCSym != nullptr || JT != -1 || BlockAddr != nullptr; | |
| } | |
| bool hasBase() const { | |
| return BaseType == Base::FrameIndexBase || BaseReg.getNode() != nullptr; | |
| } | |
| bool hasFrameIndex() const { return BaseType == Base::FrameIndexBase; } | |
| bool hasBaseReg() const { | |
| return BaseType == Base::RegBase && BaseReg.getNode() != nullptr; | |
| } | |
| bool hasIndexReg() const { | |
| return BaseType == Base::RegBase && IndexReg.getNode() != nullptr; | |
| } | |
| /// True if address mode type supports displacement | |
| bool isDispAddrType() const { | |
| return AM == AddrType::ARII || AM == AddrType::PCI || | |
| AM == AddrType::ARID || AM == AddrType::PCD || AM == AddrType::AL; | |
| } | |
| unsigned getDispSize() const { | |
| switch (AM) { | |
| default: | |
| return 0; | |
| case AddrType::ARII: | |
| case AddrType::PCI: | |
| return 8; | |
| // These two in the next chip generations can hold upto 32 bit | |
| case AddrType::ARID: | |
| case AddrType::PCD: | |
| return 16; | |
| case AddrType::AL: | |
| return 32; | |
| } | |
| } | |
| bool hasDisp() const { return getDispSize() != 0; } | |
| bool isDisp8() const { return getDispSize() == 8; } | |
| bool isDisp16() const { return getDispSize() == 16; } | |
| bool isDisp32() const { return getDispSize() == 32; } | |
| /// Return true if this addressing mode is already PC-relative. | |
| bool isPCRelative() const { | |
| if (BaseType != Base::RegBase) | |
| return false; | |
| if (auto *RegNode = dyn_cast_or_null<RegisterSDNode>(BaseReg.getNode())) | |
| return RegNode->getReg() == M68k::PC; | |
| return false; | |
| } | |
| void setBaseReg(SDValue Reg) { | |
| BaseType = Base::RegBase; | |
| BaseReg = Reg; | |
| } | |
| void setIndexReg(SDValue Reg) { IndexReg = Reg; } | |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | |
| void dump() { | |
| dbgs() << "M68kISelAddressMode " << this; | |
| dbgs() << "\nDisp: " << Disp; | |
| dbgs() << ", BaseReg: "; | |
| if (BaseReg.getNode()) | |
| BaseReg.getNode()->dump(); | |
| else | |
| dbgs() << "null"; | |
| dbgs() << ", BaseFI: " << BaseFrameIndex; | |
| dbgs() << ", IndexReg: "; | |
| if (IndexReg.getNode()) { | |
| IndexReg.getNode()->dump(); | |
| } else { | |
| dbgs() << "null"; | |
| dbgs() << ", Scale: " << Scale; | |
| } | |
| dbgs() << '\n'; | |
| } | |
| #endif | |
| }; | |
| } // end anonymous namespace | |
| namespace { | |
| class M68kDAGToDAGISel : public SelectionDAGISel { | |
| public: | |
| static char ID; | |
| M68kDAGToDAGISel() = delete; | |
| explicit M68kDAGToDAGISel(M68kTargetMachine &TM) | |
| : SelectionDAGISel(ID, TM), Subtarget(nullptr) {} | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| bool IsProfitableToFold(SDValue N, SDNode *U, SDNode *Root) const override; | |
| private: | |
| /// Keep a pointer to the M68kSubtarget around so that we can | |
| /// make the right decision when generating code for different targets. | |
| const M68kSubtarget *Subtarget; | |
| // Include the pieces autogenerated from the target description. | |
| #include "M68kGenDAGISel.inc" | |
| /// getTargetMachine - Return a reference to the TargetMachine, casted | |
| /// to the target-specific type. | |
| const M68kTargetMachine &getTargetMachine() { | |
| return static_cast<const M68kTargetMachine &>(TM); | |
| } | |
| void Select(SDNode *N) override; | |
| // Insert instructions to initialize the global base register in the | |
| // first MBB of the function. | |
| // HMM... do i need this? | |
| void initGlobalBaseReg(MachineFunction &MF); | |
| bool foldOffsetIntoAddress(uint64_t Offset, M68kISelAddressMode &AM); | |
| bool matchLoadInAddress(LoadSDNode *N, M68kISelAddressMode &AM); | |
| bool matchAddress(SDValue N, M68kISelAddressMode &AM); | |
| bool matchAddressBase(SDValue N, M68kISelAddressMode &AM); | |
| bool matchAddressRecursively(SDValue N, M68kISelAddressMode &AM, | |
| unsigned Depth); | |
| bool matchADD(SDValue &N, M68kISelAddressMode &AM, unsigned Depth); | |
| bool matchWrapper(SDValue N, M68kISelAddressMode &AM); | |
| std::pair<bool, SDNode *> selectNode(SDNode *Node); | |
| bool SelectARI(SDNode *Parent, SDValue N, SDValue &Base); | |
| bool SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base); | |
| bool SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base); | |
| bool SelectARID(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base); | |
| bool SelectARII(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base, | |
| SDValue &Index); | |
| bool SelectAL(SDNode *Parent, SDValue N, SDValue &Sym); | |
| bool SelectPCD(SDNode *Parent, SDValue N, SDValue &Imm); | |
| bool SelectPCI(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index); | |
| // If Address Mode represents Frame Index store FI in Disp and | |
| // Displacement bit size in Base. These values are read symmetrically by | |
| // M68kRegisterInfo::eliminateFrameIndex method | |
| inline bool getFrameIndexAddress(M68kISelAddressMode &AM, const SDLoc &DL, | |
| SDValue &Disp, SDValue &Base) { | |
| if (AM.BaseType == M68kISelAddressMode::Base::FrameIndexBase) { | |
| Disp = getI32Imm(AM.Disp, DL); | |
| Base = CurDAG->getTargetFrameIndex( | |
| AM.BaseFrameIndex, TLI->getPointerTy(CurDAG->getDataLayout())); | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Gets a symbol plus optional displacement | |
| inline bool getSymbolicDisplacement(M68kISelAddressMode &AM, const SDLoc &DL, | |
| SDValue &Sym) { | |
| if (AM.GV) { | |
| Sym = CurDAG->getTargetGlobalAddress(AM.GV, SDLoc(), MVT::i32, AM.Disp, | |
| AM.SymbolFlags); | |
| return true; | |
| } | |
| if (AM.CP) { | |
| Sym = CurDAG->getTargetConstantPool(AM.CP, MVT::i32, AM.Alignment, | |
| AM.Disp, AM.SymbolFlags); | |
| return true; | |
| } | |
| if (AM.ES) { | |
| assert(!AM.Disp && "Non-zero displacement is ignored with ES."); | |
| Sym = CurDAG->getTargetExternalSymbol(AM.ES, MVT::i32, AM.SymbolFlags); | |
| return true; | |
| } | |
| if (AM.MCSym) { | |
| assert(!AM.Disp && "Non-zero displacement is ignored with MCSym."); | |
| assert(AM.SymbolFlags == 0 && "oo"); | |
| Sym = CurDAG->getMCSymbol(AM.MCSym, MVT::i32); | |
| return true; | |
| } | |
| if (AM.JT != -1) { | |
| assert(!AM.Disp && "Non-zero displacement is ignored with JT."); | |
| Sym = CurDAG->getTargetJumpTable(AM.JT, MVT::i32, AM.SymbolFlags); | |
| return true; | |
| } | |
| if (AM.BlockAddr) { | |
| Sym = CurDAG->getTargetBlockAddress(AM.BlockAddr, MVT::i32, AM.Disp, | |
| AM.SymbolFlags); | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// Return a target constant with the specified value of type i8. | |
| inline SDValue getI8Imm(int64_t Imm, const SDLoc &DL) { | |
| return CurDAG->getTargetConstant(Imm, DL, MVT::i8); | |
| } | |
| /// Return a target constant with the specified value of type i8. | |
| inline SDValue getI16Imm(int64_t Imm, const SDLoc &DL) { | |
| return CurDAG->getTargetConstant(Imm, DL, MVT::i16); | |
| } | |
| /// Return a target constant with the specified value, of type i32. | |
| inline SDValue getI32Imm(int64_t Imm, const SDLoc &DL) { | |
| return CurDAG->getTargetConstant(Imm, DL, MVT::i32); | |
| } | |
| /// Return a reference to the TargetInstrInfo, casted to the target-specific | |
| /// type. | |
| const M68kInstrInfo *getInstrInfo() const { | |
| return Subtarget->getInstrInfo(); | |
| } | |
| /// Return an SDNode that returns the value of the global base register. | |
| /// Output instructions required to initialize the global base register, | |
| /// if necessary. | |
| SDNode *getGlobalBaseReg(); | |
| }; | |
| char M68kDAGToDAGISel::ID; | |
| } // namespace | |
| INITIALIZE_PASS(M68kDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false) | |
| bool M68kDAGToDAGISel::IsProfitableToFold(SDValue N, SDNode *U, | |
| SDNode *Root) const { | |
| if (OptLevel == CodeGenOpt::None) | |
| return false; | |
| if (U == Root) { | |
| switch (U->getOpcode()) { | |
| default: | |
| return true; | |
| case M68kISD::SUB: | |
| case ISD::SUB: | |
| // Prefer NEG instruction when zero subtracts a value. | |
| // e.g. | |
| // move.l #0, %d0 | |
| // sub.l (4,%sp), %d0 | |
| // vs. | |
| // move.l (4,%sp), %d0 | |
| // neg.l %d0 | |
| if (llvm::isNullConstant(U->getOperand(0))) | |
| return false; | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| bool M68kDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { | |
| Subtarget = &MF.getSubtarget<M68kSubtarget>(); | |
| return SelectionDAGISel::runOnMachineFunction(MF); | |
| } | |
| /// This pass converts a legalized DAG into a M68k-specific DAG, | |
| /// ready for instruction scheduling. | |
| FunctionPass *llvm::createM68kISelDag(M68kTargetMachine &TM) { | |
| return new M68kDAGToDAGISel(TM); | |
| } | |
| static bool doesDispFitFI(M68kISelAddressMode &AM) { | |
| if (!AM.isDispAddrType()) | |
| return false; | |
| // -1 to make sure that resolved FI will fit into Disp field | |
| return isIntN(AM.getDispSize() - 1, AM.Disp); | |
| } | |
| static bool doesDispFit(M68kISelAddressMode &AM, int64_t Val) { | |
| if (!AM.isDispAddrType()) | |
| return false; | |
| return isIntN(AM.getDispSize(), Val); | |
| } | |
| /// Return an SDNode that returns the value of the global base register. | |
| /// Output instructions required to initialize the global base register, | |
| /// if necessary. | |
| SDNode *M68kDAGToDAGISel::getGlobalBaseReg() { | |
| unsigned GlobalBaseReg = getInstrInfo()->getGlobalBaseReg(MF); | |
| auto &DL = MF->getDataLayout(); | |
| return CurDAG->getRegister(GlobalBaseReg, TLI->getPointerTy(DL)).getNode(); | |
| } | |
| bool M68kDAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset, | |
| M68kISelAddressMode &AM) { | |
| // Cannot combine ExternalSymbol displacements with integer offsets. | |
| if (Offset != 0 && (AM.ES || AM.MCSym)) | |
| return false; | |
| int64_t Val = AM.Disp + Offset; | |
| if (doesDispFit(AM, Val)) { | |
| AM.Disp = Val; | |
| return true; | |
| } | |
| return false; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Matchers | |
| //===----------------------------------------------------------------------===// | |
| /// Helper for MatchAddress. Add the specified node to the | |
| /// specified addressing mode without any further recursion. | |
| bool M68kDAGToDAGISel::matchAddressBase(SDValue N, M68kISelAddressMode &AM) { | |
| // Is the base register already occupied? | |
| if (AM.hasBase()) { | |
| // If so, check to see if the scale index register is set. | |
| if (!AM.hasIndexReg()) { | |
| AM.IndexReg = N; | |
| AM.Scale = 1; | |
| return true; | |
| } | |
| // Otherwise, we cannot select it. | |
| return false; | |
| } | |
| // Default, generate it as a register. | |
| AM.BaseType = M68kISelAddressMode::Base::RegBase; | |
| AM.BaseReg = N; | |
| return true; | |
| } | |
| /// TODO Add TLS support | |
| bool M68kDAGToDAGISel::matchLoadInAddress(LoadSDNode *N, | |
| M68kISelAddressMode &AM) { | |
| return false; | |
| } | |
| bool M68kDAGToDAGISel::matchAddressRecursively(SDValue N, | |
| M68kISelAddressMode &AM, | |
| unsigned Depth) { | |
| SDLoc DL(N); | |
| // Limit recursion. | |
| if (Depth > 5) | |
| return matchAddressBase(N, AM); | |
| // If this is already a %PC relative address, we can only merge immediates | |
| // into it. Instead of handling this in every case, we handle it here. | |
| // PC relative addressing: %PC + 16-bit displacement! | |
| if (AM.isPCRelative()) { | |
| // FIXME JumpTable and ExternalSymbol address currently don't like | |
| // displacements. It isn't very important, but should be fixed for | |
| // consistency. | |
| if (ConstantSDNode *Cst = dyn_cast<ConstantSDNode>(N)) | |
| if (foldOffsetIntoAddress(Cst->getSExtValue(), AM)) | |
| return true; | |
| return false; | |
| } | |
| switch (N.getOpcode()) { | |
| default: | |
| break; | |
| case ISD::Constant: { | |
| uint64_t Val = cast<ConstantSDNode>(N)->getSExtValue(); | |
| if (foldOffsetIntoAddress(Val, AM)) | |
| return true; | |
| break; | |
| } | |
| case M68kISD::Wrapper: | |
| case M68kISD::WrapperPC: | |
| if (matchWrapper(N, AM)) | |
| return true; | |
| break; | |
| case ISD::LOAD: | |
| if (matchLoadInAddress(cast<LoadSDNode>(N), AM)) | |
| return true; | |
| break; | |
| case ISD::OR: | |
| // We want to look through a transform in InstCombine and DAGCombiner that | |
| // turns 'add' into 'or', so we can treat this 'or' exactly like an 'add'. | |
| // Example: (or (and x, 1), (shl y, 3)) --> (add (and x, 1), (shl y, 3)) | |
| // An 'lea' can then be used to match the shift (multiply) and add: | |
| // and $1, %esi | |
| // lea (%rsi, %rdi, 8), %rax | |
| if (CurDAG->haveNoCommonBitsSet(N.getOperand(0), N.getOperand(1)) && | |
| matchADD(N, AM, Depth)) | |
| return true; | |
| break; | |
| case ISD::ADD: | |
| if (matchADD(N, AM, Depth)) | |
| return true; | |
| break; | |
| case ISD::FrameIndex: | |
| if (AM.isDispAddrType() && | |
| AM.BaseType == M68kISelAddressMode::Base::RegBase && | |
| AM.BaseReg.getNode() == nullptr && doesDispFitFI(AM)) { | |
| AM.BaseType = M68kISelAddressMode::Base::FrameIndexBase; | |
| AM.BaseFrameIndex = cast<FrameIndexSDNode>(N)->getIndex(); | |
| return true; | |
| } | |
| break; | |
| } | |
| return matchAddressBase(N, AM); | |
| } | |
| /// Add the specified node to the specified addressing mode, returning true if | |
| /// it cannot be done. This just pattern matches for the addressing mode. | |
| bool M68kDAGToDAGISel::matchAddress(SDValue N, M68kISelAddressMode &AM) { | |
| // TODO: Post-processing: Convert lea(,%reg,2) to lea(%reg,%reg), which has | |
| // a smaller encoding and avoids a scaled-index. | |
| // And make sure it is an indexed mode | |
| // TODO: Post-processing: Convert foo to foo(%pc), even in non-PIC mode, | |
| // because it has a smaller encoding. | |
| // Make sure this must be done only if PC* modes are currently being matched | |
| return matchAddressRecursively(N, AM, 0); | |
| } | |
| bool M68kDAGToDAGISel::matchADD(SDValue &N, M68kISelAddressMode &AM, | |
| unsigned Depth) { | |
| // Add an artificial use to this node so that we can keep track of | |
| // it if it gets CSE'd with a different node. | |
| HandleSDNode Handle(N); | |
| M68kISelAddressMode Backup = AM; | |
| if (matchAddressRecursively(N.getOperand(0), AM, Depth + 1) && | |
| matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1)) { | |
| return true; | |
| } | |
| AM = Backup; | |
| // Try again after commuting the operands. | |
| if (matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1) && | |
| matchAddressRecursively(Handle.getValue().getOperand(0), AM, Depth + 1)) { | |
| return true; | |
| } | |
| AM = Backup; | |
| // If we couldn't fold both operands into the address at the same time, | |
| // see if we can just put each operand into a register and fold at least | |
| // the add. | |
| if (!AM.hasBase() && !AM.hasIndexReg()) { | |
| N = Handle.getValue(); | |
| AM.BaseReg = N.getOperand(0); | |
| AM.IndexReg = N.getOperand(1); | |
| AM.Scale = 1; | |
| return true; | |
| } | |
| N = Handle.getValue(); | |
| return false; | |
| } | |
| /// Try to match M68kISD::Wrapper and M68kISD::WrapperPC nodes into an | |
| /// addressing mode. These wrap things that will resolve down into a symbol | |
| /// reference. If no match is possible, this returns true, otherwise it returns | |
| /// false. | |
| bool M68kDAGToDAGISel::matchWrapper(SDValue N, M68kISelAddressMode &AM) { | |
| // If the addressing mode already has a symbol as the displacement, we can | |
| // never match another symbol. | |
| if (AM.hasSymbolicDisplacement()) | |
| return false; | |
| SDValue N0 = N.getOperand(0); | |
| if (N.getOpcode() == M68kISD::WrapperPC) { | |
| // If cannot match here just restore the old version | |
| M68kISelAddressMode Backup = AM; | |
| if (AM.hasBase()) { | |
| return false; | |
| } | |
| if (auto *G = dyn_cast<GlobalAddressSDNode>(N0)) { | |
| AM.GV = G->getGlobal(); | |
| AM.SymbolFlags = G->getTargetFlags(); | |
| if (!foldOffsetIntoAddress(G->getOffset(), AM)) { | |
| AM = Backup; | |
| return false; | |
| } | |
| } else if (auto *CP = dyn_cast<ConstantPoolSDNode>(N0)) { | |
| AM.CP = CP->getConstVal(); | |
| AM.Alignment = CP->getAlign(); | |
| AM.SymbolFlags = CP->getTargetFlags(); | |
| if (!foldOffsetIntoAddress(CP->getOffset(), AM)) { | |
| AM = Backup; | |
| return false; | |
| } | |
| } else if (auto *S = dyn_cast<ExternalSymbolSDNode>(N0)) { | |
| AM.ES = S->getSymbol(); | |
| AM.SymbolFlags = S->getTargetFlags(); | |
| } else if (auto *S = dyn_cast<MCSymbolSDNode>(N0)) { | |
| AM.MCSym = S->getMCSymbol(); | |
| } else if (auto *J = dyn_cast<JumpTableSDNode>(N0)) { | |
| AM.JT = J->getIndex(); | |
| AM.SymbolFlags = J->getTargetFlags(); | |
| } else if (auto *BA = dyn_cast<BlockAddressSDNode>(N0)) { | |
| AM.BlockAddr = BA->getBlockAddress(); | |
| AM.SymbolFlags = BA->getTargetFlags(); | |
| if (!foldOffsetIntoAddress(BA->getOffset(), AM)) { | |
| AM = Backup; | |
| return false; | |
| } | |
| } else | |
| llvm_unreachable("Unhandled symbol reference node."); | |
| AM.setBaseReg(CurDAG->getRegister(M68k::PC, MVT::i32)); | |
| return true; | |
| } | |
| // This wrapper requires 32bit disp/imm field for Medium CM | |
| if (!AM.isDisp32()) { | |
| return false; | |
| } | |
| if (N.getOpcode() == M68kISD::Wrapper) { | |
| if (auto *G = dyn_cast<GlobalAddressSDNode>(N0)) { | |
| AM.GV = G->getGlobal(); | |
| AM.Disp += G->getOffset(); | |
| AM.SymbolFlags = G->getTargetFlags(); | |
| } else if (auto *CP = dyn_cast<ConstantPoolSDNode>(N0)) { | |
| AM.CP = CP->getConstVal(); | |
| AM.Alignment = CP->getAlign(); | |
| AM.Disp += CP->getOffset(); | |
| AM.SymbolFlags = CP->getTargetFlags(); | |
| } else if (auto *S = dyn_cast<ExternalSymbolSDNode>(N0)) { | |
| AM.ES = S->getSymbol(); | |
| AM.SymbolFlags = S->getTargetFlags(); | |
| } else if (auto *S = dyn_cast<MCSymbolSDNode>(N0)) { | |
| AM.MCSym = S->getMCSymbol(); | |
| } else if (auto *J = dyn_cast<JumpTableSDNode>(N0)) { | |
| AM.JT = J->getIndex(); | |
| AM.SymbolFlags = J->getTargetFlags(); | |
| } else if (auto *BA = dyn_cast<BlockAddressSDNode>(N0)) { | |
| AM.BlockAddr = BA->getBlockAddress(); | |
| AM.Disp += BA->getOffset(); | |
| AM.SymbolFlags = BA->getTargetFlags(); | |
| } else | |
| llvm_unreachable("Unhandled symbol reference node."); | |
| return true; | |
| } | |
| return false; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Selectors | |
| //===----------------------------------------------------------------------===// | |
| void M68kDAGToDAGISel::Select(SDNode *Node) { | |
| unsigned Opcode = Node->getOpcode(); | |
| SDLoc DL(Node); | |
| LLVM_DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << '\n'); | |
| if (Node->isMachineOpcode()) { | |
| LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << '\n'); | |
| Node->setNodeId(-1); | |
| return; // Already selected. | |
| } | |
| switch (Opcode) { | |
| default: | |
| break; | |
| case M68kISD::GLOBAL_BASE_REG: | |
| ReplaceNode(Node, getGlobalBaseReg()); | |
| return; | |
| } | |
| SelectCode(Node); | |
| } | |
| bool M68kDAGToDAGISel::SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::ARIPI: "); | |
| LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n"); | |
| return false; | |
| } | |
| bool M68kDAGToDAGISel::SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::ARIPD: "); | |
| LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n"); | |
| return false; | |
| } | |
| bool M68kDAGToDAGISel::SelectARID(SDNode *Parent, SDValue N, SDValue &Disp, | |
| SDValue &Base) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::ARID: "); | |
| M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARID); | |
| if (!matchAddress(N, AM)) | |
| return false; | |
| if (AM.isPCRelative()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n"); | |
| return false; | |
| } | |
| // If this is a frame index, grab it | |
| if (getFrameIndexAddress(AM, SDLoc(N), Disp, Base)) { | |
| LLVM_DEBUG(dbgs() << "SUCCESS matched FI\n"); | |
| return true; | |
| } | |
| if (AM.hasIndexReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n"); | |
| return false; | |
| } | |
| if (!AM.hasBaseReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: No Base reg\n"); | |
| return false; | |
| } | |
| if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) { | |
| assert(!AM.Disp && "Should not be any displacement"); | |
| LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n"); | |
| return true; | |
| } | |
| // Give a chance to AddrType::ARI | |
| if (AM.Disp == 0) { | |
| LLVM_DEBUG(dbgs() << "REJECT: No displacement\n"); | |
| return false; | |
| } | |
| Base = AM.BaseReg; | |
| Disp = getI16Imm(AM.Disp, SDLoc(N)); | |
| LLVM_DEBUG(dbgs() << "SUCCESS\n"); | |
| return true; | |
| } | |
| static bool isAddressBase(const SDValue &N) { | |
| switch (N.getOpcode()) { | |
| case ISD::ADD: | |
| case ISD::ADDC: | |
| return llvm::any_of(N.getNode()->ops(), | |
| [](const SDUse &U) { return isAddressBase(U.get()); }); | |
| case M68kISD::Wrapper: | |
| case M68kISD::WrapperPC: | |
| case M68kISD::GLOBAL_BASE_REG: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool M68kDAGToDAGISel::SelectARII(SDNode *Parent, SDValue N, SDValue &Disp, | |
| SDValue &Base, SDValue &Index) { | |
| M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARII); | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::ARII: "); | |
| if (!matchAddress(N, AM)) | |
| return false; | |
| if (AM.isPCRelative()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: PC relative\n"); | |
| return false; | |
| } | |
| if (!AM.hasIndexReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: No Index\n"); | |
| return false; | |
| } | |
| if (!AM.hasBaseReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: No Base\n"); | |
| return false; | |
| } | |
| if (!isAddressBase(AM.BaseReg) && isAddressBase(AM.IndexReg)) { | |
| Base = AM.IndexReg; | |
| Index = AM.BaseReg; | |
| } else { | |
| Base = AM.BaseReg; | |
| Index = AM.IndexReg; | |
| } | |
| if (AM.hasSymbolicDisplacement()) { | |
| LLVM_DEBUG(dbgs() << "REJECT, Cannot match symbolic displacement\n"); | |
| return false; | |
| } | |
| // The idea here is that we want to use AddrType::ARII without displacement | |
| // only if necessary like memory operations, otherwise this must be lowered | |
| // into addition | |
| if (AM.Disp == 0 && (!Parent || (Parent->getOpcode() != ISD::LOAD && | |
| Parent->getOpcode() != ISD::STORE))) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Displacement is Zero\n"); | |
| return false; | |
| } | |
| Disp = getI8Imm(AM.Disp, SDLoc(N)); | |
| LLVM_DEBUG(dbgs() << "SUCCESS\n"); | |
| return true; | |
| } | |
| bool M68kDAGToDAGISel::SelectAL(SDNode *Parent, SDValue N, SDValue &Sym) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::AL: "); | |
| M68kISelAddressMode AM(M68kISelAddressMode::AddrType::AL); | |
| if (!matchAddress(N, AM)) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Match failed\n"); | |
| return false; | |
| } | |
| if (AM.isPCRelative()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n"); | |
| return false; | |
| } | |
| if (AM.hasBase()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match Base\n"); | |
| return false; | |
| } | |
| if (AM.hasIndexReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n"); | |
| return false; | |
| } | |
| if (getSymbolicDisplacement(AM, SDLoc(N), Sym)) { | |
| LLVM_DEBUG(dbgs() << "SUCCESS: Matched symbol\n"); | |
| return true; | |
| } | |
| if (AM.Disp) { | |
| Sym = getI32Imm(AM.Disp, SDLoc(N)); | |
| LLVM_DEBUG(dbgs() << "SUCCESS\n"); | |
| return true; | |
| } | |
| LLVM_DEBUG(dbgs() << "REJECT: Not Symbol or Disp\n"); | |
| return false; | |
| ; | |
| } | |
| bool M68kDAGToDAGISel::SelectPCD(SDNode *Parent, SDValue N, SDValue &Disp) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::PCD: "); | |
| M68kISelAddressMode AM(M68kISelAddressMode::AddrType::PCD); | |
| if (!matchAddress(N, AM)) | |
| return false; | |
| if (!AM.isPCRelative()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n"); | |
| return false; | |
| } | |
| if (AM.hasIndexReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n"); | |
| return false; | |
| } | |
| if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) { | |
| LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n"); | |
| return true; | |
| } | |
| Disp = getI16Imm(AM.Disp, SDLoc(N)); | |
| LLVM_DEBUG(dbgs() << "SUCCESS\n"); | |
| return true; | |
| } | |
| bool M68kDAGToDAGISel::SelectPCI(SDNode *Parent, SDValue N, SDValue &Disp, | |
| SDValue &Index) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::PCI: "); | |
| M68kISelAddressMode AM(M68kISelAddressMode::AddrType::PCI); | |
| if (!matchAddress(N, AM)) | |
| return false; | |
| if (!AM.isPCRelative()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n"); | |
| return false; | |
| } | |
| if (!AM.hasIndexReg()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: No Index\n"); | |
| return false; | |
| } | |
| Index = AM.IndexReg; | |
| if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) { | |
| assert(!AM.Disp && "Should not be any displacement"); | |
| LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n"); | |
| return true; | |
| } | |
| Disp = getI8Imm(AM.Disp, SDLoc(N)); | |
| LLVM_DEBUG(dbgs() << "SUCCESS\n"); | |
| return true; | |
| } | |
| bool M68kDAGToDAGISel::SelectARI(SDNode *Parent, SDValue N, SDValue &Base) { | |
| LLVM_DEBUG(dbgs() << "Selecting AddrType::ARI: "); | |
| M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARI); | |
| if (!matchAddress(N, AM)) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Match failed\n"); | |
| return false; | |
| } | |
| if (AM.isPCRelative()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n"); | |
| return false; | |
| } | |
| // AddrType::ARI does not use these | |
| if (AM.hasIndexReg() || AM.Disp != 0) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index or Disp\n"); | |
| return false; | |
| } | |
| // Must be matched by AddrType::AL | |
| if (AM.hasSymbolicDisplacement()) { | |
| LLVM_DEBUG(dbgs() << "REJECT: Cannot match Symbolic Disp\n"); | |
| return false; | |
| } | |
| if (AM.hasBaseReg()) { | |
| Base = AM.BaseReg; | |
| LLVM_DEBUG(dbgs() << "SUCCESS\n"); | |
| return true; | |
| } | |
| return false; | |
| } |