Compiler projects using llvm
//===-- 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"

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:
  explicit M68kDAGToDAGISel(M68kTargetMachine &TM)
      : SelectionDAGISel(TM), Subtarget(nullptr) {}

  StringRef getPassName() const override {
    return "M68k DAG->DAG Pattern Instruction Selection";
  }

  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();
};
} // namespace

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;
}