/* Capstone Disassembly Engine, http://www.capstone-engine.org */ /* By Nguyen Anh Quynh , 2013-2022, */ /* Rot127 2022-2023 */ /* Automatically translated source file from LLVM. */ /* LLVM-commit: */ /* LLVM-tag: */ /* Only small edits allowed. */ /* For multiple similar edits, please create a Patch for the translator. */ /* Capstone's C++ file translator: */ /* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */ //===-- 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 #include #include #include #include "MipsMapping.h" #include "MipsInstPrinter.h" #define GET_SUBTARGETINFO_ENUM #include "MipsGenSubtargetInfo.inc" #define GET_INSTRINFO_ENUM #include "MipsGenInstrInfo.inc" #define GET_REGINFO_ENUM #include "MipsGenRegisterInfo.inc" #define CONCAT(a, b) CONCAT_(a, b) #define CONCAT_(a, b) a##_##b #define DEBUG_TYPE "asm-printer" #define PRINT_ALIAS_INSTR #include "MipsGenAsmWriter.inc" static bool isReg(const MCInst *MI, unsigned OpNo, unsigned R) { return MCOperand_getReg(MCInst_getOperand((MCInst *)MI, (OpNo))) == R; } static const char *MipsFCCToString(Mips_CondCode CC) { switch (CC) { case Mips_FCOND_F: case Mips_FCOND_T: return "f"; case Mips_FCOND_UN: case Mips_FCOND_OR: return "un"; case Mips_FCOND_OEQ: case Mips_FCOND_UNE: return "eq"; case Mips_FCOND_UEQ: case Mips_FCOND_ONE: return "ueq"; case Mips_FCOND_OLT: case Mips_FCOND_UGE: return "olt"; case Mips_FCOND_ULT: case Mips_FCOND_OGE: return "ult"; case Mips_FCOND_OLE: case Mips_FCOND_UGT: return "ole"; case Mips_FCOND_ULE: case Mips_FCOND_OGT: return "ule"; case Mips_FCOND_SF: case Mips_FCOND_ST: return "sf"; case Mips_FCOND_NGLE: case Mips_FCOND_GLE: return "ngle"; case Mips_FCOND_SEQ: case Mips_FCOND_SNE: return "seq"; case Mips_FCOND_NGL: case Mips_FCOND_GL: return "ngl"; case Mips_FCOND_LT: case Mips_FCOND_NLT: return "lt"; case Mips_FCOND_NGE: case Mips_FCOND_GE: return "nge"; case Mips_FCOND_LE: case Mips_FCOND_NLE: return "le"; case Mips_FCOND_NGT: case Mips_FCOND_GT: return "ngt"; } CS_ASSERT_RET_VAL(0 && "Impossible condition code!", NULL); return ""; } const char *Mips_LLVM_getRegisterName(unsigned RegNo, bool noRegName); static void printRegName(MCInst *MI, SStream *OS, MCRegister Reg) { int syntax_opt = MI->csh->syntax; if (!(syntax_opt & CS_OPT_SYNTAX_NO_DOLLAR)) { SStream_concat1(OS, '$'); } SStream_concat0(OS, Mips_LLVM_getRegisterName(Reg, syntax_opt & CS_OPT_SYNTAX_NOREGNAME)); } void Mips_LLVM_printInst(MCInst *MI, uint64_t Address, SStream *O) { bool useAliasDetails = map_use_alias_details(MI); if (!useAliasDetails) { SStream_Close(O); printInstruction(MI, Address, O); SStream_Open(O); map_set_fill_detail_ops(MI, false); } if (printAliasInstr(MI, Address, O) || printAlias4(MI, Address, O)) { MCInst_setIsAlias(MI, true); } else { printInstruction(MI, Address, O); } if (!useAliasDetails) { map_set_fill_detail_ops(MI, true); } } void printOperand(MCInst *MI, unsigned OpNo, SStream *O) { switch (MCInst_getOpcode(MI)) { default: break; case Mips_AND16_NM: case Mips_XOR16_NM: case Mips_OR16_NM: if (MCInst_getNumOperands(MI) == 2 && OpNo == 2) OpNo = 0; // rt, rs -> rt, rs, rt break; case Mips_ADDu4x4_NM: case Mips_MUL4x4_NM: if (MCInst_getNumOperands(MI) == 2 && OpNo > 0) OpNo = OpNo - 1; // rt, rs -> rt, rt, rs break; } MCOperand *Op = MCInst_getOperand(MI, (OpNo)); if (MCOperand_isReg(Op)) { add_cs_detail(MI, Mips_OP_GROUP_Operand, OpNo); printRegName(MI, O, MCOperand_getReg(Op)); return; } if (MCOperand_isImm(Op)) { switch (MCInst_getOpcode(MI)) { case Mips_LI48_NM: case Mips_ANDI16_NM: case Mips_ANDI_NM: case Mips_ORI_NM: case Mips_XORI_NM: case Mips_TEQ_NM: case Mips_TNE_NM: case Mips_SIGRIE_NM: case Mips_SDBBP_NM: case Mips_SDBBP16_NM: case Mips_BREAK_NM: case Mips_BREAK16_NM: case Mips_SYSCALL_NM: case Mips_SYSCALL16_NM: case Mips_WAIT_NM: CONCAT(printUImm, CONCAT(32, 0)) (MI, OpNo, O); break; default: add_cs_detail(MI, Mips_OP_GROUP_Operand, OpNo); printInt64(O, MCOperand_getImm(Op)); break; } return; } } static void printJumpOperand(MCInst *MI, unsigned OpNo, SStream *O) { add_cs_detail(MI, Mips_OP_GROUP_JumpOperand, OpNo); MCOperand *Op = MCInst_getOperand(MI, (OpNo)); if (MCOperand_isReg(Op)) return printRegName(MI, O, MCOperand_getReg(Op)); // only the upper bits are needed. uint64_t Base = MI->address & ~0x0fffffffull; uint64_t Target = MCOperand_getImm(Op); printInt64(O, Base | Target); } static void printBranchOperand(MCInst *MI, uint64_t Address, unsigned OpNo, SStream *O) { add_cs_detail(MI, Mips_OP_GROUP_BranchOperand, OpNo); MCOperand *Op = MCInst_getOperand(MI, (OpNo)); if (MCOperand_isReg(Op)) return printRegName(MI, O, MCOperand_getReg(Op)); uint64_t Target = Address + MCOperand_getImm(Op); printInt64(O, Target); } #define DEFINE_printUImm(Bits) \ static void CONCAT(printUImm, CONCAT(Bits, 0))(MCInst * MI, int opNum, \ SStream *O) \ { \ add_cs_detail(MI, CONCAT(Mips_OP_GROUP_UImm, CONCAT(Bits, 0)), opNum); \ MCOperand *MO = MCInst_getOperand(MI, (opNum)); \ if (MCOperand_isImm(MO)) { \ uint64_t Imm = MCOperand_getImm(MO); \ Imm &= (((uint64_t)1) << Bits) - 1; \ printUInt64(O, Imm); \ return; \ } \ MCOperand *Op = MCInst_getOperand(MI, (opNum)); \ printRegName(MI, O, MCOperand_getReg(Op)); \ } #define DEFINE_printUImm_2(Bits, Offset) \ static void CONCAT(printUImm, CONCAT(Bits, Offset))(MCInst * MI, int opNum, \ SStream *O) \ { \ add_cs_detail(MI, CONCAT(Mips_OP_GROUP_UImm, CONCAT(Bits, Offset)), \ opNum); \ MCOperand *MO = MCInst_getOperand(MI, (opNum)); \ if (MCOperand_isImm(MO)) { \ uint64_t Imm = MCOperand_getImm(MO); \ Imm -= Offset; \ Imm &= (1 << Bits) - 1; \ Imm += Offset; \ printUInt64(O, Imm); \ return; \ } \ MCOperand *Op = MCInst_getOperand(MI, (opNum)); \ printRegName(MI, O, MCOperand_getReg(Op)); \ } DEFINE_printUImm(0); DEFINE_printUImm(1); DEFINE_printUImm(10); DEFINE_printUImm(12); DEFINE_printUImm(16); DEFINE_printUImm(2); DEFINE_printUImm(20); DEFINE_printUImm(26); DEFINE_printUImm(3); DEFINE_printUImm(32); DEFINE_printUImm(4); DEFINE_printUImm(5); DEFINE_printUImm(6); DEFINE_printUImm(7); DEFINE_printUImm(8); DEFINE_printUImm_2(2, 1); DEFINE_printUImm_2(5, 1); DEFINE_printUImm_2(5, 32); DEFINE_printUImm_2(5, 33); DEFINE_printUImm_2(6, 1); DEFINE_printUImm_2(6, 2); static void printMemOperand(MCInst *MI, int opNum, SStream *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 (MCInst_getOpcode(MI)) { 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 = MCInst_getNumOperands(MI) - 2; break; } set_mem_access(MI, true); // Index register is encoded as immediate value // in case of nanoMIPS indexed instructions switch (MCInst_getOpcode(MI)) { // No offset needed for paired LL/SC case Mips_LLWP_NM: case Mips_SCWP_NM: break; case Mips_LWX_NM: case Mips_LWXS_NM: case Mips_LWXS16_NM: case Mips_LBX_NM: case Mips_LBUX_NM: case Mips_LHX_NM: case Mips_LHUX_NM: case Mips_LHXS_NM: case Mips_LHUXS_NM: case Mips_SWX_NM: case Mips_SWXS_NM: case Mips_SBX_NM: case Mips_SHX_NM: case Mips_SHXS_NM: if (!MCOperand_isReg(MCInst_getOperand(MI, (opNum + 1)))) { add_cs_detail(MI, Mips_OP_GROUP_MemOperand, (opNum + 1)); printRegName(MI, O, MCOperand_getImm(MCInst_getOperand( MI, (opNum + 1)))); break; } // Fall through default: printOperand((MCInst *)MI, opNum + 1, O); break; } SStream_concat0(O, "("); printOperand((MCInst *)MI, opNum, O); SStream_concat0(O, ")"); set_mem_access(MI, false); } static void printMemOperandEA(MCInst *MI, int opNum, SStream *O) { // when using stack locations for not load/store instructions // print the same way as all normal 3 operand instructions. printOperand((MCInst *)MI, opNum, O); SStream_concat0(O, ", "); printOperand((MCInst *)MI, opNum + 1, O); } static void printFCCOperand(MCInst *MI, int opNum, SStream *O) { MCOperand *MO = MCInst_getOperand(MI, (opNum)); SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO))); } static bool printAlias(const char *Str, const MCInst *MI, uint64_t Address, unsigned OpNo, SStream *OS, bool IsBranch) { SStream_concat(OS, "%s%s", "\t", Str); SStream_concat0(OS, "\t"); if (IsBranch) printBranchOperand((MCInst *)MI, Address, OpNo, OS); else printOperand((MCInst *)MI, OpNo, OS); return true; } static bool printAlias2(const char *Str, const MCInst *MI, uint64_t Address, unsigned OpNo0, unsigned OpNo1, SStream *OS, bool IsBranch) { printAlias(Str, MI, Address, OpNo0, OS, IsBranch); SStream_concat0(OS, ", "); if (IsBranch) printBranchOperand((MCInst *)MI, Address, OpNo1, OS); else printOperand((MCInst *)MI, OpNo1, OS); return true; } static bool printAlias3(const char *Str, const MCInst *MI, uint64_t Address, unsigned OpNo0, unsigned OpNo1, unsigned OpNo2, SStream *OS) { printAlias(Str, MI, Address, OpNo0, OS, false); SStream_concat0(OS, ", "); printOperand((MCInst *)MI, OpNo1, OS); SStream_concat0(OS, ", "); printOperand((MCInst *)MI, OpNo2, OS); return true; } static bool printAlias4(const MCInst *MI, uint64_t Address, SStream *OS) { switch (MCInst_getOpcode(MI)) { case Mips_BEQ: case Mips_BEQ_MM: // beq $zero, $zero, $L2 => b $L2 // beq $r0, $zero, $L2 => beqz $r0, $L2 return (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO) && printAlias("b", MI, Address, 2, OS, true)) || (isReg(MI, 1, Mips_ZERO) && printAlias2("beqz", MI, Address, 0, 2, OS, true)); case Mips_BEQ64: // beq $r0, $zero, $L2 => beqz $r0, $L2 return isReg(MI, 1, Mips_ZERO_64) && printAlias2("beqz", MI, Address, 0, 2, OS, true); case Mips_BNE: case Mips_BNE_MM: // bne $r0, $zero, $L2 => bnez $r0, $L2 return isReg(MI, 1, Mips_ZERO) && printAlias2("bnez", MI, Address, 0, 2, OS, true); case Mips_BNE64: // bne $r0, $zero, $L2 => bnez $r0, $L2 return isReg(MI, 1, Mips_ZERO_64) && printAlias2("bnez", MI, Address, 0, 2, OS, true); case Mips_BGEZAL: // bgezal $zero, $L1 => bal $L1 return isReg(MI, 0, Mips_ZERO) && printAlias("bal", MI, Address, 1, OS, true); case Mips_BC1T: // bc1t $fcc0, $L1 => bc1t $L1 return isReg(MI, 0, Mips_FCC0) && printAlias("bc1t", MI, Address, 1, OS, true); case Mips_BC1F: // bc1f $fcc0, $L1 => bc1f $L1 return isReg(MI, 0, Mips_FCC0) && printAlias("bc1f", MI, Address, 1, OS, true); case Mips_JALR: // jalr $zero, $r1 => jr $r1 // jalr $ra, $r1 => jalr $r1 return (isReg(MI, 0, Mips_ZERO) && printAlias("jr", MI, Address, 1, OS, false)) || (isReg(MI, 0, Mips_RA) && printAlias("jalr", MI, Address, 1, OS, false)); case Mips_JALR64: // jalr $zero, $r1 => jr $r1 // jalr $ra, $r1 => jalr $r1 return (isReg(MI, 0, Mips_ZERO_64) && printAlias("jr", MI, Address, 1, OS, false)) || (isReg(MI, 0, Mips_RA_64) && printAlias("jalr", MI, Address, 1, OS, false)); case Mips_NOR: case Mips_NOR_MM: case Mips_NOR_MMR6: // nor $r0, $r1, $zero => not $r0, $r1 return isReg(MI, 2, Mips_ZERO) && printAlias2("not", MI, Address, 0, 1, OS, false); case Mips_NOR64: // nor $r0, $r1, $zero => not $r0, $r1 return isReg(MI, 2, Mips_ZERO_64) && printAlias2("not", MI, Address, 0, 1, OS, false); case Mips_OR: case Mips_ADDu: // or $r0, $r1, $zero => move $r0, $r1 // addu $r0, $r1, $zero => move $r0, $r1 return isReg(MI, 2, Mips_ZERO) && printAlias2("move", MI, Address, 0, 1, OS, false); case Mips_LI48_NM: case Mips_LI16_NM: // li[16/48] $r0, imm => li $r0, imm return printAlias2("li", MI, Address, 0, 1, OS, false); case Mips_ADDIU_NM: case Mips_ADDIUNEG_NM: if (isReg(MI, 1, Mips_ZERO_NM)) return printAlias2("li", MI, Address, 0, 2, OS, false); else return printAlias3("addiu", MI, Address, 0, 1, 2, OS); case Mips_ADDIU48_NM: case Mips_ADDIURS5_NM: case Mips_ADDIUR1SP_NM: case Mips_ADDIUR2_NM: case Mips_ADDIUGPB_NM: case Mips_ADDIUGPW_NM: return printAlias3("addiu", MI, Address, 0, 1, 2, OS); case Mips_ANDI16_NM: case Mips_ANDI_NM: // andi[16/32] $r0, $r1, imm => andi $r0, $r1, imm return printAlias3("andi", MI, Address, 0, 1, 2, OS); default: return false; } } static void printRegisterList(MCInst *MI, int opNum, SStream *O) { // - 2 because register List is always first operand of instruction and it is // always followed by memory operand (base + offset). add_cs_detail(MI, Mips_OP_GROUP_RegisterList, opNum); for (int i = opNum, e = MCInst_getNumOperands(MI) - 2; i != e; ++i) { if (i != opNum) SStream_concat0(O, ", "); printRegName(MI, O, MCOperand_getReg(MCInst_getOperand(MI, (i)))); } } static void printNanoMipsRegisterList(MCInst *MI, int OpNum, SStream *O) { add_cs_detail(MI, Mips_OP_GROUP_NanoMipsRegisterList, OpNum); for (unsigned I = OpNum; I < MCInst_getNumOperands(MI); I++) { SStream_concat0(O, ", "); printRegName(MI, O, MCOperand_getReg(MCInst_getOperand(MI, (I)))); } } static void printHi20(MCInst *MI, int OpNum, SStream *O) { MCOperand *MO = MCInst_getOperand(MI, (OpNum)); if (MCOperand_isImm(MO)) { add_cs_detail(MI, Mips_OP_GROUP_Hi20, OpNum); SStream_concat0(O, "%hi("); printUInt64(O, MCOperand_getImm(MO)); SStream_concat0(O, ")"); } else printOperand(MI, OpNum, O); } static void printHi20PCRel(MCInst *MI, uint64_t Address, int OpNum, SStream *O) { MCOperand *MO = MCInst_getOperand(MI, (OpNum)); if (MCOperand_isImm(MO)) { add_cs_detail(MI, Mips_OP_GROUP_Hi20PCRel, OpNum); SStream_concat0(O, "%pcrel_hi("); printUInt64(O, MCOperand_getImm(MO) + Address); SStream_concat0(O, ")"); } else printOperand(MI, OpNum, O); } static void printPCRel(MCInst *MI, uint64_t Address, int OpNum, SStream *O) { MCOperand *MO = MCInst_getOperand(MI, (OpNum)); if (MCOperand_isImm(MO)) { add_cs_detail(MI, Mips_OP_GROUP_PCRel, OpNum); printUInt64(O, MCOperand_getImm(MO) + Address); } else printOperand(MI, OpNum, O); } const char *Mips_LLVM_getRegisterName(unsigned RegNo, bool noRegName) { if (!RegNo || RegNo >= MIPS_REG_ENDING) { return NULL; } if (noRegName) { return getRegisterName(RegNo); } switch(RegNo) { case MIPS_REG_AT: case MIPS_REG_AT_64: return "at"; case MIPS_REG_A0: case MIPS_REG_A0_64: return "a0"; case MIPS_REG_A1: case MIPS_REG_A1_64: return "a1"; case MIPS_REG_A2: case MIPS_REG_A2_64: return "a2"; case MIPS_REG_A3: case MIPS_REG_A3_64: return "a3"; case MIPS_REG_K0: case MIPS_REG_K0_64: return "k0"; case MIPS_REG_K1: case MIPS_REG_K1_64: return "k1"; case MIPS_REG_S0: case MIPS_REG_S0_64: return "s0"; case MIPS_REG_S1: case MIPS_REG_S1_64: return "s1"; case MIPS_REG_S2: case MIPS_REG_S2_64: return "s2"; case MIPS_REG_S3: case MIPS_REG_S3_64: return "s3"; case MIPS_REG_S4: case MIPS_REG_S4_64: return "s4"; case MIPS_REG_S5: case MIPS_REG_S5_64: return "s5"; case MIPS_REG_S6: case MIPS_REG_S6_64: return "s6"; case MIPS_REG_S7: case MIPS_REG_S7_64: return "s7"; case MIPS_REG_T0: case MIPS_REG_T0_64: return "t0"; case MIPS_REG_T1: case MIPS_REG_T1_64: return "t1"; case MIPS_REG_T2: case MIPS_REG_T2_64: return "t2"; case MIPS_REG_T3: case MIPS_REG_T3_64: return "t3"; case MIPS_REG_T4: case MIPS_REG_T4_64: return "t4"; case MIPS_REG_T5: case MIPS_REG_T5_64: return "t5"; case MIPS_REG_T6: case MIPS_REG_T6_64: return "t6"; case MIPS_REG_T7: case MIPS_REG_T7_64: return "t7"; case MIPS_REG_T8: case MIPS_REG_T8_64: return "t8"; case MIPS_REG_T9: case MIPS_REG_T9_64: return "t9"; case MIPS_REG_V0: case MIPS_REG_V0_64: return "v0"; case MIPS_REG_V1: case MIPS_REG_V1_64: return "v1"; default: return getRegisterName(RegNo); } }