text: //===-- LanaiInstPrinter.cpp - Convert Lanai MCInst to asm syntax ---------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  //
  // This class prints an Lanai MCInst to a .s file.
  //
  //===----------------------------------------------------------------------===//
  
  #include "LanaiInstPrinter.h"
  #include "Lanai.h"
  #include "MCTargetDesc/LanaiMCExpr.h"
  #include "llvm/MC/MCAsmInfo.h"
  #include "llvm/MC/MCExpr.h"
  #include "llvm/MC/MCInst.h"
  #include "llvm/MC/MCSymbol.h"
  #include "llvm/Support/ErrorHandling.h"
  #include "llvm/Support/FormattedStream.h"
  
  using namespace llvm;
  
  #define DEBUG_TYPE "asm-printer"
  
  // Include the auto-generated portion of the assembly writer.
  #define PRINT_ALIAS_INSTR
  #include "LanaiGenAsmWriter.inc"
  
  void LanaiInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const {
    OS << StringRef(getRegisterName(RegNo)).lower();
  }
  
  bool LanaiInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
                                   StringRef Alias, unsigned OpNo0,
                                   unsigned OpNo1) {
    OS << "\t" << Alias << " ";
    printOperand(MI, OpNo0, OS);
    OS << ", ";
    printOperand(MI, OpNo1, OS);
    return true;
  }
  
  static bool usesGivenOffset(const MCInst *MI, int AddOffset) {
    unsigned AluCode = MI->getOperand(3).getImm();
    return LPAC::encodeLanaiAluCode(AluCode) == LPAC::ADD &&
           (MI->getOperand(2).getImm() == AddOffset ||
            MI->getOperand(2).getImm() == -AddOffset);
  }
  
  static bool isPreIncrementForm(const MCInst *MI, int AddOffset) {
    unsigned AluCode = MI->getOperand(3).getImm();
    return LPAC::isPreOp(AluCode) && usesGivenOffset(MI, AddOffset);
  }
  
  static bool isPostIncrementForm(const MCInst *MI, int AddOffset) {
    unsigned AluCode = MI->getOperand(3).getImm();
    return LPAC::isPostOp(AluCode) && usesGivenOffset(MI, AddOffset);
  }
  
  static StringRef decIncOperator(const MCInst *MI) {
    if (MI->getOperand(2).getImm() < 0)
      return "--";
    return "++";
  }
  
  bool LanaiInstPrinter::printMemoryLoadIncrement(const MCInst *MI,
                                                  raw_ostream &OS,
                                                  StringRef Opcode,
                                                  int AddOffset) {
    if (isPreIncrementForm(MI, AddOffset)) {
      OS << "\t" << Opcode << "\t[" << decIncOperator(MI) << "%"
         << getRegisterName(MI->getOperand(1).getReg()) << "], %"
         << getRegisterName(MI->getOperand(0).getReg());
      return true;
    }
    if (isPostIncrementForm(MI, AddOffset)) {
      OS << "\t" << Opcode << "\t[%"
         << getRegisterName(MI->getOperand(1).getReg()) << decIncOperator(MI)
         << "], %" << getRegisterName(MI->getOperand(0).getReg());
      return true;
    }
    return false;
  }
  
  bool LanaiInstPrinter::printMemoryStoreIncrement(const MCInst *MI,
                                                   raw_ostream &OS,
                                                   StringRef Opcode,
                                                   int AddOffset) {
    if (isPreIncrementForm(MI, AddOffset)) {
      OS << "\t" << Opcode << "\t%" << getRegisterName(MI->getOperand(0).getReg())
         << ", [" << decIncOperator(MI) << "%"
         << getRegisterName(MI->getOperand(1).getReg()) << "]";
      return true;
    }
    if (isPostIncrementForm(MI, AddOffset)) {
      OS << "\t" << Opcode << "\t%" << getRegisterName(MI->getOperand(0).getReg())
         << ", [%" << getRegisterName(MI->getOperand(1).getReg())
         << decIncOperator(MI) << "]";
      return true;
    }
    return false;
  }
  
  bool LanaiInstPrinter::printAlias(const MCInst *MI, raw_ostream &OS) {
    switch (MI->getOpcode()) {
    case Lanai::LDW_RI:
      // ld 4[*%rN], %rX => ld [++imm], %rX
      // ld -4[*%rN], %rX => ld [--imm], %rX
      // ld 4[%rN*], %rX => ld [imm++], %rX
      // ld -4[%rN*], %rX => ld [imm--], %rX
      return printMemoryLoadIncrement(MI, OS, "ld", 4);
    case Lanai::LDHs_RI:
      return printMemoryLoadIncrement(MI, OS, "ld.h", 2);
    case Lanai::LDHz_RI:
      return printMemoryLoadIncrement(MI, OS, "uld.h", 2);
    case Lanai::LDBs_RI:
      return printMemoryLoadIncrement(MI, OS, "ld.b", 1);
    case Lanai::LDBz_RI:
      return printMemoryLoadIncrement(MI, OS, "uld.b", 1);
    case Lanai::SW_RI:
      // st %rX, 4[*%rN] => st %rX, [++imm]
      // st %rX, -4[*%rN] => st %rX, [--imm]
      // st %rX, 4[%rN*] => st %rX, [imm++]
      // st %rX, -4[%rN*] => st %rX, [imm--]
      return printMemoryStoreIncrement(MI, OS, "st", 4);
    case Lanai::STH_RI:
      return printMemoryStoreIncrement(MI, OS, "st.h", 2);
    case Lanai::STB_RI:
      return printMemoryStoreIncrement(MI, OS, "st.b", 1);
    default:
      return false;
    }
  }
  
  void LanaiInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
                                   StringRef Annotation,
                                   const MCSubtargetInfo &STI) {
    if (!printAlias(MI, OS) && !printAliasInstr(MI, OS))
      printInstruction(MI, OS);
    printAnnotation(OS, Annotation);
  }
  
  void LanaiInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                      raw_ostream &OS, const char *Modifier) {
    assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");
    const MCOperand &Op = MI->getOperand(OpNo);
    if (Op.isReg())
      OS << "%" << getRegisterName(Op.getReg());
    else if (Op.isImm())
      OS << formatHex(Op.getImm());
    else {
      assert(Op.isExpr() && "Expected an expression");
      Op.getExpr()->print(OS, &MAI);
    }
  }
  
  void LanaiInstPrinter::printMemImmOperand(const MCInst *MI, unsigned OpNo,
                                            raw_ostream &OS) {
    const MCOperand &Op = MI->getOperand(OpNo);
    if (Op.isImm()) {
      OS << '[' << formatHex(Op.getImm()) << ']';
    } else {
      // Symbolic operand will be lowered to immediate value by linker
      assert(Op.isExpr() && "Expected an expression");
      OS << '[';
      Op.getExpr()->print(OS, &MAI);
      OS << ']';
    }
  }
  
  void LanaiInstPrinter::printHi16ImmOperand(const MCInst *MI, unsigned OpNo,
                                             raw_ostream &OS) {
    const MCOperand &Op = MI->getOperand(OpNo);
    if (Op.isImm()) {
      OS << formatHex(Op.getImm() << 16);
    } else {
      // Symbolic operand will be lowered to immediate value by linker
      assert(Op.isExpr() && "Expected an expression");
      Op.getExpr()->print(OS, &MAI);
    }
  }
  
  void LanaiInstPrinter::printHi16AndImmOperand(const MCInst *MI, unsigned OpNo,
                                                raw_ostream &OS) {
    const MCOperand &Op = MI->getOperand(OpNo);
    if (Op.isImm()) {
      OS << formatHex((Op.getImm() << 16) | 0xffff);
    } else {
      // Symbolic operand will be lowered to immediate value by linker
      assert(Op.isExpr() && "Expected an expression");
      Op.getExpr()->print(OS, &MAI);
    }
  }
  
  void LanaiInstPrinter::printLo16AndImmOperand(const MCInst *MI, unsigned OpNo,
                                                raw_ostream &OS) {
    const MCOperand &Op = MI->getOperand(OpNo);
    if (Op.isImm()) {
      OS << formatHex(0xffff0000 | Op.getImm());
    } else {
      // Symbolic operand will be lowered to immediate value by linker
      assert(Op.isExpr() && "Expected an expression");
      Op.getExpr()->print(OS, &MAI);
    }
  }
  
  static void printMemoryBaseRegister(raw_ostream &OS, const unsigned AluCode,
                                      const MCOperand &RegOp) {
    assert(RegOp.isReg() && "Register operand expected");
    OS << "[";
    if (LPAC::isPreOp(AluCode))
      OS << "*";
    OS << "%" << LanaiInstPrinter::getRegisterName(RegOp.getReg());
    if (LPAC::isPostOp(AluCode))
      OS << "*";
    OS << "]";
  }
  
  template <unsigned SizeInBits>
  static void printMemoryImmediateOffset(const MCAsmInfo &MAI,
                                         const MCOperand &OffsetOp,
                                         raw_ostream &OS) {
    assert((OffsetOp.isImm() || OffsetOp.isExpr()) && "Immediate expected");
    if (OffsetOp.isImm()) {
      assert(isInt<SizeInBits>(OffsetOp.getImm()) && "Constant value truncated");
      OS << OffsetOp.getImm();
    } else
      OffsetOp.getExpr()->print(OS, &MAI);
  }
  
  void LanaiInstPrinter::printMemRiOperand(const MCInst *MI, int OpNo,
                                           raw_ostream &OS,
                                           const char *Modifier) {
    const MCOperand &RegOp = MI->getOperand(OpNo);
    const MCOperand &OffsetOp = MI->getOperand(OpNo + 1);
    const MCOperand &AluOp = MI->getOperand(OpNo + 2);
    const unsigned AluCode = AluOp.getImm();
  
    // Offset
    printMemoryImmediateOffset<16>(MAI, OffsetOp, OS);
  
    // Register
    printMemoryBaseRegister(OS, AluCode, RegOp);
  }
  
  void LanaiInstPrinter::printMemRrOperand(const MCInst *MI, int OpNo,
                                           raw_ostream &OS,
                                           const char *Modifier) {
    const MCOperand &RegOp = MI->getOperand(OpNo);
    const MCOperand &OffsetOp = MI->getOperand(OpNo + 1);
    const MCOperand &AluOp = MI->getOperand(OpNo + 2);
    const unsigned AluCode = AluOp.getImm();
    assert(OffsetOp.isReg() && RegOp.isReg() && "Registers expected.");
  
    // [ Base OP Offset ]
    OS << "[";
    if (LPAC::isPreOp(AluCode))
      OS << "*";
    OS << "%" << getRegisterName(RegOp.getReg());
    if (LPAC::isPostOp(AluCode))
      OS << "*";
    OS << " " << LPAC::lanaiAluCodeToString(AluCode) << " ";
    OS << "%" << getRegisterName(OffsetOp.getReg());
    OS << "]";
  }
  
  void LanaiInstPrinter::printMemSplsOperand(const MCInst *MI, int OpNo,
                                             raw_ostream &OS,
                                             const char *Modifier) {
    const MCOperand &RegOp = MI->getOperand(OpNo);
    const MCOperand &OffsetOp = MI->getOperand(OpNo + 1);
    const MCOperand &AluOp = MI->getOperand(OpNo + 2);
    const unsigned AluCode = AluOp.getImm();
  
    // Offset
    printMemoryImmediateOffset<10>(MAI, OffsetOp, OS);
  
    // Register
    printMemoryBaseRegister(OS, AluCode, RegOp);
  }
  
  void LanaiInstPrinter::printCCOperand(const MCInst *MI, int OpNo,
                                        raw_ostream &OS) {
    LPCC::CondCode CC =
        static_cast<LPCC::CondCode>(MI->getOperand(OpNo).getImm());
    // Handle the undefined value here for printing so we don't abort().
    if (CC >= LPCC::UNKNOWN)
      OS << "<und>";
    else
      OS << lanaiCondCodeToString(CC);
  }
  
  void LanaiInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNo,
                                               raw_ostream &OS) {
    LPCC::CondCode CC =
        static_cast<LPCC::CondCode>(MI->getOperand(OpNo).getImm());
    // Handle the undefined value here for printing so we don't abort().
    if (CC >= LPCC::UNKNOWN)
      OS << "<und>";
    else if (CC != LPCC::ICC_T)
      OS << "." << lanaiCondCodeToString(CC);
  }
  