| //===-- MipsInstPrinter.cpp - Convert Mips MCInst to assembly syntax ------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This class prints an Mips MCInst to a .s file. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MipsInstPrinter.h" | |
| #include "MipsInstrInfo.h" | |
| #include "MipsMCExpr.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstrInfo.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "asm-printer" | |
| #define PRINT_ALIAS_INSTR | |
| #include "MipsGenAsmWriter.inc" | |
| template<unsigned R> | |
| static bool isReg(const MCInst &MI, unsigned OpNo) { | |
| assert(MI.getOperand(OpNo).isReg() && "Register operand expected."); | |
| return MI.getOperand(OpNo).getReg() == R; | |
| } | |
| const char* Mips::MipsFCCToString(Mips::CondCode CC) { | |
| switch (CC) { | |
| case FCOND_F: | |
| case FCOND_T: return "f"; | |
| case FCOND_UN: | |
| case FCOND_OR: return "un"; | |
| case FCOND_OEQ: | |
| case FCOND_UNE: return "eq"; | |
| case FCOND_UEQ: | |
| case FCOND_ONE: return "ueq"; | |
| case FCOND_OLT: | |
| case FCOND_UGE: return "olt"; | |
| case FCOND_ULT: | |
| case FCOND_OGE: return "ult"; | |
| case FCOND_OLE: | |
| case FCOND_UGT: return "ole"; | |
| case FCOND_ULE: | |
| case FCOND_OGT: return "ule"; | |
| case FCOND_SF: | |
| case FCOND_ST: return "sf"; | |
| case FCOND_NGLE: | |
| case FCOND_GLE: return "ngle"; | |
| case FCOND_SEQ: | |
| case FCOND_SNE: return "seq"; | |
| case FCOND_NGL: | |
| case FCOND_GL: return "ngl"; | |
| case FCOND_LT: | |
| case FCOND_NLT: return "lt"; | |
| case FCOND_NGE: | |
| case FCOND_GE: return "nge"; | |
| case FCOND_LE: | |
| case FCOND_NLE: return "le"; | |
| case FCOND_NGT: | |
| case FCOND_GT: return "ngt"; | |
| } | |
| llvm_unreachable("Impossible condition code!"); | |
| } | |
| void MipsInstPrinter::printRegName(raw_ostream &OS, MCRegister Reg) const { | |
| OS << markup("<reg:") << '$' << StringRef(getRegisterName(Reg)).lower() | |
| << markup(">"); | |
| } | |
| void MipsInstPrinter::printInst(const MCInst *MI, uint64_t Address, | |
| StringRef Annot, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| switch (MI->getOpcode()) { | |
| default: | |
| break; | |
| case Mips::RDHWR: | |
| case Mips::RDHWR64: | |
| O << "\t.set\tpush\n"; | |
| O << "\t.set\tmips32r2\n"; | |
| break; | |
| case Mips::Save16: | |
| O << "\tsave\t"; | |
| printSaveRestore(MI, STI, O); | |
| O << " # 16 bit inst\n"; | |
| return; | |
| case Mips::SaveX16: | |
| O << "\tsave\t"; | |
| printSaveRestore(MI, STI, O); | |
| O << "\n"; | |
| return; | |
| case Mips::Restore16: | |
| O << "\trestore\t"; | |
| printSaveRestore(MI, STI, O); | |
| O << " # 16 bit inst\n"; | |
| return; | |
| case Mips::RestoreX16: | |
| O << "\trestore\t"; | |
| printSaveRestore(MI, STI, O); | |
| O << "\n"; | |
| return; | |
| } | |
| // Try to print any aliases first. | |
| if (!printAliasInstr(MI, Address, STI, O) && | |
| !printAlias(*MI, Address, STI, O)) | |
| printInstruction(MI, Address, STI, O); | |
| printAnnotation(O, Annot); | |
| switch (MI->getOpcode()) { | |
| default: | |
| break; | |
| case Mips::RDHWR: | |
| case Mips::RDHWR64: | |
| O << "\n\t.set\tpop"; | |
| } | |
| } | |
| void MipsInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNo); | |
| if (Op.isReg()) { | |
| printRegName(O, Op.getReg()); | |
| return; | |
| } | |
| if (Op.isImm()) { | |
| O << markup("<imm:") << formatImm(Op.getImm()) << markup(">"); | |
| return; | |
| } | |
| assert(Op.isExpr() && "unknown operand kind in printOperand"); | |
| Op.getExpr()->print(O, &MAI, true); | |
| } | |
| void MipsInstPrinter::printJumpOperand(const MCInst *MI, unsigned OpNo, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNo); | |
| if (!Op.isImm()) | |
| return printOperand(MI, OpNo, STI, O); | |
| if (PrintBranchImmAsAddress) | |
| O << markup("<imm:") << formatHex(Op.getImm()) << markup(">"); | |
| else | |
| O << markup("<imm:") << formatImm(Op.getImm()) << markup(">"); | |
| } | |
| void MipsInstPrinter::printBranchOperand(const MCInst *MI, uint64_t Address, | |
| unsigned OpNo, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNo); | |
| if (!Op.isImm()) | |
| return printOperand(MI, OpNo, STI, O); | |
| if (PrintBranchImmAsAddress) { | |
| uint64_t Target = Address + Op.getImm(); | |
| if (STI.hasFeature(Mips::FeatureMips32)) | |
| Target &= 0xffffffff; | |
| else if (STI.hasFeature(Mips::FeatureMips16)) | |
| Target &= 0xffff; | |
| O << markup("<imm:") << formatHex(Target) << markup(">"); | |
| } else { | |
| O << markup("<imm:") << formatImm(Op.getImm()) << markup(">"); | |
| } | |
| } | |
| template <unsigned Bits, unsigned Offset> | |
| void MipsInstPrinter::printUImm(const MCInst *MI, int opNum, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(opNum); | |
| if (MO.isImm()) { | |
| uint64_t Imm = MO.getImm(); | |
| Imm -= Offset; | |
| Imm &= (1 << Bits) - 1; | |
| Imm += Offset; | |
| O << markup("<imm:") << formatImm(Imm) << markup(">"); | |
| return; | |
| } | |
| printOperand(MI, opNum, STI, O); | |
| } | |
| void MipsInstPrinter::printMemOperand(const MCInst *MI, int opNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Load/Store memory operands -- imm($reg) | |
| // If PIC target the target is loaded as the | |
| // pattern lw $25,%call16($28) | |
| // opNum can be invalid if instruction had reglist as operand. | |
| // MemOperand is always last operand of instruction (base + offset). | |
| switch (MI->getOpcode()) { | |
| default: | |
| break; | |
| case Mips::SWM32_MM: | |
| case Mips::LWM32_MM: | |
| case Mips::SWM16_MM: | |
| case Mips::SWM16_MMR6: | |
| case Mips::LWM16_MM: | |
| case Mips::LWM16_MMR6: | |
| opNum = MI->getNumOperands() - 2; | |
| break; | |
| } | |
| O << markup("<mem:"); | |
| printOperand(MI, opNum + 1, STI, O); | |
| O << "("; | |
| printOperand(MI, opNum, STI, O); | |
| O << ")"; | |
| O << markup(">"); | |
| } | |
| void MipsInstPrinter::printMemOperandEA(const MCInst *MI, int opNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // when using stack locations for not load/store instructions | |
| // print the same way as all normal 3 operand instructions. | |
| printOperand(MI, opNum, STI, O); | |
| O << ", "; | |
| printOperand(MI, opNum + 1, STI, O); | |
| } | |
| void MipsInstPrinter::printFCCOperand(const MCInst *MI, int opNum, | |
| const MCSubtargetInfo & /* STI */, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(opNum); | |
| O << MipsFCCToString((Mips::CondCode)MO.getImm()); | |
| } | |
| void MipsInstPrinter:: | |
| printSHFMask(const MCInst *MI, int opNum, raw_ostream &O) { | |
| llvm_unreachable("TODO"); | |
| } | |
| bool MipsInstPrinter::printAlias(const char *Str, const MCInst &MI, | |
| uint64_t Address, unsigned OpNo, | |
| const MCSubtargetInfo &STI, raw_ostream &OS, | |
| bool IsBranch) { | |
| OS << "\t" << Str << "\t"; | |
| if (IsBranch) | |
| printBranchOperand(&MI, Address, OpNo, STI, OS); | |
| else | |
| printOperand(&MI, OpNo, STI, OS); | |
| return true; | |
| } | |
| bool MipsInstPrinter::printAlias(const char *Str, const MCInst &MI, | |
| uint64_t Address, unsigned OpNo0, | |
| unsigned OpNo1, const MCSubtargetInfo &STI, | |
| raw_ostream &OS, bool IsBranch) { | |
| printAlias(Str, MI, Address, OpNo0, STI, OS, IsBranch); | |
| OS << ", "; | |
| if (IsBranch) | |
| printBranchOperand(&MI, Address, OpNo1, STI, OS); | |
| else | |
| printOperand(&MI, OpNo1, STI, OS); | |
| return true; | |
| } | |
| bool MipsInstPrinter::printAlias(const MCInst &MI, uint64_t Address, | |
| const MCSubtargetInfo &STI, raw_ostream &OS) { | |
| switch (MI.getOpcode()) { | |
| case Mips::BEQ: | |
| case Mips::BEQ_MM: | |
| // beq $zero, $zero, $L2 => b $L2 | |
| // beq $r0, $zero, $L2 => beqz $r0, $L2 | |
| return (isReg<Mips::ZERO>(MI, 0) && isReg<Mips::ZERO>(MI, 1) && | |
| printAlias("b", MI, Address, 2, STI, OS, true)) || | |
| (isReg<Mips::ZERO>(MI, 1) && | |
| printAlias("beqz", MI, Address, 0, 2, STI, OS, true)); | |
| case Mips::BEQ64: | |
| // beq $r0, $zero, $L2 => beqz $r0, $L2 | |
| return isReg<Mips::ZERO_64>(MI, 1) && | |
| printAlias("beqz", MI, Address, 0, 2, STI, OS, true); | |
| case Mips::BNE: | |
| case Mips::BNE_MM: | |
| // bne $r0, $zero, $L2 => bnez $r0, $L2 | |
| return isReg<Mips::ZERO>(MI, 1) && | |
| printAlias("bnez", MI, Address, 0, 2, STI, OS, true); | |
| case Mips::BNE64: | |
| // bne $r0, $zero, $L2 => bnez $r0, $L2 | |
| return isReg<Mips::ZERO_64>(MI, 1) && | |
| printAlias("bnez", MI, Address, 0, 2, STI, OS, true); | |
| case Mips::BGEZAL: | |
| // bgezal $zero, $L1 => bal $L1 | |
| return isReg<Mips::ZERO>(MI, 0) && | |
| printAlias("bal", MI, Address, 1, STI, OS, true); | |
| case Mips::BC1T: | |
| // bc1t $fcc0, $L1 => bc1t $L1 | |
| return isReg<Mips::FCC0>(MI, 0) && | |
| printAlias("bc1t", MI, Address, 1, STI, OS, true); | |
| case Mips::BC1F: | |
| // bc1f $fcc0, $L1 => bc1f $L1 | |
| return isReg<Mips::FCC0>(MI, 0) && | |
| printAlias("bc1f", MI, Address, 1, STI, OS, true); | |
| case Mips::JALR: | |
| // jalr $zero, $r1 => jr $r1 | |
| // jalr $ra, $r1 => jalr $r1 | |
| return (isReg<Mips::ZERO>(MI, 0) && | |
| printAlias("jr", MI, Address, 1, STI, OS)) || | |
| (isReg<Mips::RA>(MI, 0) && | |
| printAlias("jalr", MI, Address, 1, STI, OS)); | |
| case Mips::JALR64: | |
| // jalr $zero, $r1 => jr $r1 | |
| // jalr $ra, $r1 => jalr $r1 | |
| return (isReg<Mips::ZERO_64>(MI, 0) && | |
| printAlias("jr", MI, Address, 1, STI, OS)) || | |
| (isReg<Mips::RA_64>(MI, 0) && | |
| printAlias("jalr", MI, Address, 1, STI, OS)); | |
| case Mips::NOR: | |
| case Mips::NOR_MM: | |
| case Mips::NOR_MMR6: | |
| // nor $r0, $r1, $zero => not $r0, $r1 | |
| return isReg<Mips::ZERO>(MI, 2) && | |
| printAlias("not", MI, Address, 0, 1, STI, OS); | |
| case Mips::NOR64: | |
| // nor $r0, $r1, $zero => not $r0, $r1 | |
| return isReg<Mips::ZERO_64>(MI, 2) && | |
| printAlias("not", MI, Address, 0, 1, STI, OS); | |
| case Mips::OR: | |
| case Mips::ADDu: | |
| // or $r0, $r1, $zero => move $r0, $r1 | |
| // addu $r0, $r1, $zero => move $r0, $r1 | |
| return isReg<Mips::ZERO>(MI, 2) && | |
| printAlias("move", MI, Address, 0, 1, STI, OS); | |
| default: | |
| return false; | |
| } | |
| } | |
| void MipsInstPrinter::printSaveRestore(const MCInst *MI, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { | |
| if (i != 0) O << ", "; | |
| if (MI->getOperand(i).isReg()) | |
| printRegName(O, MI->getOperand(i).getReg()); | |
| else | |
| printUImm<16>(MI, i, STI, O); | |
| } | |
| } | |
| void MipsInstPrinter::printRegisterList(const MCInst *MI, int opNum, | |
| const MCSubtargetInfo & /* STI */, | |
| raw_ostream &O) { | |
| // - 2 because register List is always first operand of instruction and it is | |
| // always followed by memory operand (base + offset). | |
| for (int i = opNum, e = MI->getNumOperands() - 2; i != e; ++i) { | |
| if (i != opNum) | |
| O << ", "; | |
| printRegName(O, MI->getOperand(i).getReg()); | |
| } | |
| } |