capstone/arch/Mips/MipsInstPrinter.c
Rot127 0c90fe13f5
Replace assert with CS_ASSERT in modules (#2478)
* Replace asserts with macros for AArch64, Alpha, LoongArch, Mips, SystemZ inc files.

* Add missing clearing of MCInst

* Ensure correct dir name is used.

* Replace asserts in inc files for PPC, ARM, TriCore

* Replace all asserts in modules with CS_ASSERT.

Also enable the CS_ASSERTs if CMAKE_BUILD_TYPE=Debug

* Formatting
2024-09-25 14:58:06 +08:00

635 lines
16 KiB
C

/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */
/* Rot127 <unisono@quyllur.org> 2022-2023 */
/* Automatically translated source file from LLVM. */
/* LLVM-commit: <commit> */
/* LLVM-tag: <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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <capstone/platform.h>
#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);
}
}