Squashed 'external/capstone/' content from commit e46f64fa
git-subtree-dir: external/capstone git-subtree-split: e46f64fadb351e9ecd05264fab26f2772feb0994
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
/* 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 */
|
||||
|
||||
//===-- RISCVInstPrinter.cpp - Convert RISC-V MCInst to asm 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 RISC-V MCInst to a .s file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <capstone/platform.h>
|
||||
#include "../../MathExtras.h"
|
||||
|
||||
#include "RISCVMapping.h"
|
||||
#include "RISCVInstPrinter.h"
|
||||
|
||||
#define GET_SUBTARGETINFO_ENUM
|
||||
#include "RISCVGenSubtargetInfo.inc"
|
||||
|
||||
#define GET_INSTRINFO_ENUM
|
||||
#include "RISCVGenInstrInfo.inc"
|
||||
|
||||
#define GET_REGINFO_ENUM
|
||||
#include "RISCVGenRegisterInfo.inc"
|
||||
|
||||
#define GET_SysRegsList_IMPL
|
||||
#include "RISCVGenSystemOperands.inc"
|
||||
|
||||
#define GEN_UNCOMPRESS_INSTR
|
||||
#include "RISCVGenCompressedInstructionsInfo.inc"
|
||||
|
||||
#include "RISCVMapping.h"
|
||||
#include "../../Mapping.h"
|
||||
|
||||
#define CONCAT(a, b) CONCAT_(a, b)
|
||||
#define CONCAT_(a, b) a##_##b
|
||||
|
||||
#define DEBUG_TYPE "asm-printer"
|
||||
|
||||
static void printCustomAliasOperand(MCInst *MI, uint64_t Address,
|
||||
unsigned OpIdx, unsigned PrintMethodIdx,
|
||||
SStream *OS);
|
||||
static inline void printRegName(SStream *O, MCRegister Reg);
|
||||
static inline void printOperand(MCInst *MI, unsigned OpNo, SStream *O);
|
||||
// Include the auto-generated portion of the assembly writer.
|
||||
#define PRINT_ALIAS_INSTR
|
||||
#include "RISCVGenAsmWriter.inc"
|
||||
|
||||
// Print architectural register names rather than the ABI names (such as x2
|
||||
// instead of sp).
|
||||
// TODO: Make RISCVInstPrinter_doGetRegisterName non-static so that this can a
|
||||
// member.
|
||||
static bool ArchRegNames;
|
||||
|
||||
const char *doGetRegisterName(MCRegister Reg)
|
||||
{
|
||||
return getRegisterName(Reg, ArchRegNames ? RISCV_NoRegAltName :
|
||||
RISCV_ABIRegAltName);
|
||||
}
|
||||
|
||||
static inline void printRegName(SStream *O, MCRegister Reg)
|
||||
{
|
||||
SStream_concat0(markup_OS(O, Markup_Register), doGetRegisterName(Reg));
|
||||
}
|
||||
|
||||
bool haveRequiredFeatures(const RISCV_SysReg *Reg, MCInst *MI)
|
||||
{
|
||||
// Not in 32-bit mode.
|
||||
if (Reg->isRV32Only &&
|
||||
RISCV_getFeatureBits(MI->csh->mode, RISCV_Feature64Bit))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_Operand, OpNo);
|
||||
|
||||
MCOperand *MO = MCInst_getOperand(MI, (OpNo));
|
||||
|
||||
if (MCOperand_isReg(MO)) {
|
||||
printRegName(O, MCOperand_getReg(MO));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MCOperand_isImm(MO)) {
|
||||
printInt64(markup_OS(O, Markup_Immediate),
|
||||
MCOperand_getImm(MO));
|
||||
return;
|
||||
}
|
||||
|
||||
CS_ASSERT(MCOperand_isExpr(MO) &&
|
||||
"Unknown operand kind in printOperand");
|
||||
printExpr(O, MCOperand_getExpr(MO));
|
||||
}
|
||||
|
||||
void printBranchOperand(MCInst *MI, uint64_t Address, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_BranchOperand, OpNo);
|
||||
MCOperand *MO = MCInst_getOperand(MI, (OpNo));
|
||||
if (!MCOperand_isImm(MO))
|
||||
return printOperand(MI, OpNo, O);
|
||||
|
||||
if (MI->csh->PrintBranchImmAsAddress) {
|
||||
uint64_t Target = Address + MCOperand_getImm(MO);
|
||||
if (!RISCV_getFeatureBits(MI->csh->mode, RISCV_Feature64Bit))
|
||||
Target &= 0xffffffff;
|
||||
printUInt64(markup_OS(O, Markup_Target), Target);
|
||||
} else {
|
||||
printInt64(markup_OS(O, Markup_Target), MCOperand_getImm(MO));
|
||||
}
|
||||
}
|
||||
|
||||
void printCSRSystemRegister(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_CSRSystemRegister, OpNo);
|
||||
unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
const RISCV_SysReg *SysReg = RISCV_lookupSysRegByEncoding(Imm);
|
||||
if (SysReg && haveRequiredFeatures(SysReg, MI))
|
||||
SStream_concat0(markup_OS(O, Markup_Register), SysReg->Name);
|
||||
else
|
||||
printUInt64(markup_OS(O, Markup_Register), Imm);
|
||||
}
|
||||
|
||||
void printFenceArg(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FenceArg, OpNo);
|
||||
unsigned FenceArg = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
CS_ASSERT(((FenceArg >> 4) == 0) &&
|
||||
"Invalid immediate in printFenceArg");
|
||||
|
||||
if ((FenceArg & RISCVFenceField_I) != 0)
|
||||
SStream_concat0(O, "i");
|
||||
|
||||
if ((FenceArg & RISCVFenceField_O) != 0)
|
||||
SStream_concat0(O, "o");
|
||||
|
||||
if ((FenceArg & RISCVFenceField_R) != 0)
|
||||
SStream_concat0(O, "r");
|
||||
|
||||
if ((FenceArg & RISCVFenceField_W) != 0)
|
||||
SStream_concat0(O, "w");
|
||||
|
||||
if (FenceArg == 0)
|
||||
SStream_concat0(O, "0");
|
||||
}
|
||||
|
||||
void printFRMArg(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FRMArg, OpNo);
|
||||
unsigned FRMArg = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
if (!(MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT) &&
|
||||
FRMArg == RISCVFPRndMode_DYN)
|
||||
return;
|
||||
SStream_concat(O, "%s", ", ");
|
||||
SStream_concat0(O, RISCVFPRndMode_roundingModeToString(FRMArg));
|
||||
}
|
||||
|
||||
void printFRMArgLegacy(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FRMArgLegacy, OpNo);
|
||||
unsigned FRMArg = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
// Never print rounding mode if it's the default 'rne'. This ensures the
|
||||
// output can still be parsed by older tools that erroneously failed to
|
||||
// accept a rounding mode.
|
||||
if (FRMArg == RISCVFPRndMode_RNE)
|
||||
return;
|
||||
SStream_concat(O, "%s", ", ");
|
||||
SStream_concat0(O, RISCVFPRndMode_roundingModeToString(FRMArg));
|
||||
}
|
||||
|
||||
void printFPImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FPImmOperand, OpNo);
|
||||
unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
if (Imm == 1) {
|
||||
SStream_concat0(markup_OS(O, Markup_Immediate), "min");
|
||||
} else if (Imm == 30) {
|
||||
SStream_concat0(markup_OS(O, Markup_Immediate), "inf");
|
||||
} else if (Imm == 31) {
|
||||
SStream_concat0(markup_OS(O, Markup_Immediate), "nan");
|
||||
} else {
|
||||
float FPVal = getFPImm(Imm);
|
||||
// If the value is an integer, print a .0 fraction. Otherwise, use %g to
|
||||
// which will not print trailing zeros and will use scientific notation
|
||||
// if it is shorter than printing as a decimal. The smallest value requires
|
||||
// 12 digits of precision including the decimal.
|
||||
if (FPVal == (int)(FPVal))
|
||||
printfFloat(markup_OS(O, Markup_Immediate), "%.1f",
|
||||
FPVal);
|
||||
else
|
||||
printfFloat(markup_OS(O, Markup_Immediate), "%.12g",
|
||||
FPVal);
|
||||
}
|
||||
}
|
||||
|
||||
void printZeroOffsetMemOp(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_ZeroOffsetMemOp, OpNo);
|
||||
MCOperand *MO = MCInst_getOperand(MI, (OpNo));
|
||||
|
||||
CS_ASSERT(MCOperand_isReg(MO) &&
|
||||
"printZeroOffsetMemOp can only print register operands");
|
||||
SStream_concat0(O, "(");
|
||||
printRegName(O, MCOperand_getReg(MO));
|
||||
SStream_concat0(O, ")");
|
||||
}
|
||||
|
||||
void printVTypeI(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_VTypeI, OpNo);
|
||||
unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
// Print the raw immediate for reserved values: vlmul[2:0]=4, vsew[2:0]=0b1xx,
|
||||
// or non-zero in bits 8 and above.
|
||||
if (RISCVVType_getVLMUL(Imm) == RISCVII_LMUL_RESERVED ||
|
||||
RISCVVType_getSEW(Imm) > 64 || (Imm >> 8) != 0) {
|
||||
printUInt64(O, Imm);
|
||||
return;
|
||||
}
|
||||
// Print the text form.
|
||||
printVType(Imm, O);
|
||||
}
|
||||
|
||||
void printRlist(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_Rlist, OpNo);
|
||||
unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
SStream_concat0(O, "{");
|
||||
switch (Imm) {
|
||||
case RISCVZC_RLISTENCODE_RA:
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x1" : "ra"));
|
||||
break;
|
||||
case RISCVZC_RLISTENCODE_RA_S0:
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x1" : "ra"));
|
||||
SStream_concat0(O, ", ");
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x8" : "s0"));
|
||||
break;
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S1:
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x1" : "ra"));
|
||||
SStream_concat0(O, ", ");
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x8" : "s0"));
|
||||
SStream_concat0(O, "-");
|
||||
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x9" : "s1"));
|
||||
break;
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S2:
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x1" : "ra"));
|
||||
SStream_concat0(O, ", ");
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x8" : "s0"));
|
||||
SStream_concat0(O, "-");
|
||||
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x9" : "s2"));
|
||||
if (ArchRegNames) {
|
||||
SStream_concat0(O, ", ");
|
||||
SStream_concat0(markup_OS(O, Markup_Register), "x18");
|
||||
}
|
||||
break;
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S3:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S4:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S5:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S6:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S7:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S8:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S9:
|
||||
case RISCVZC_RLISTENCODE_RA_S0_S11:
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x1" : "ra"));
|
||||
SStream_concat0(O, ", ");
|
||||
SStream_concat0(markup_OS(O, Markup_Register),
|
||||
(ArchRegNames ? "x8" : "s0"));
|
||||
SStream_concat0(O, "-");
|
||||
|
||||
if (ArchRegNames) {
|
||||
SStream_concat0(markup_OS(O, Markup_Register), "x9");
|
||||
SStream_concat0(O, ", ");
|
||||
SStream_concat0(markup_OS(O, Markup_Register), "x18");
|
||||
SStream_concat0(O, "-");
|
||||
}
|
||||
SStream_concat0(
|
||||
markup_OS(O, Markup_Register),
|
||||
doGetRegisterName(
|
||||
RISCV_X19 +
|
||||
(Imm == RISCVZC_RLISTENCODE_RA_S0_S11 ?
|
||||
8 :
|
||||
Imm - RISCVZC_RLISTENCODE_RA_S0_S3)));
|
||||
break;
|
||||
default:
|
||||
CS_ASSERT(0 && "invalid register list");
|
||||
}
|
||||
SStream_concat0(O, "}");
|
||||
}
|
||||
|
||||
void printRegReg(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_RegReg, OpNo);
|
||||
MCOperand *MO = MCInst_getOperand(MI, (OpNo));
|
||||
|
||||
CS_ASSERT(MCOperand_isReg(MO) &&
|
||||
"printRegReg can only print register operands");
|
||||
if (MCOperand_getReg(MO) == RISCV_NoRegister)
|
||||
return;
|
||||
printRegName(O, MCOperand_getReg(MO));
|
||||
|
||||
SStream_concat0(O, "(");
|
||||
MCOperand *MO1 = MCInst_getOperand(MI, (OpNo + 1));
|
||||
CS_ASSERT(MCOperand_isReg(MO1) &&
|
||||
"printRegReg can only print register operands");
|
||||
printRegName(O, MCOperand_getReg(MO1));
|
||||
SStream_concat0(O, ")");
|
||||
}
|
||||
|
||||
void printSpimm(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_Spimm, OpNo);
|
||||
int64_t Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
||||
unsigned Opcode = MCInst_getOpcode(MI);
|
||||
bool IsRV64 = RISCV_getFeatureBits(MI->csh->mode, RISCV_Feature64Bit);
|
||||
bool IsEABI = RISCV_getFeatureBits(MI->csh->mode, RISCV_FeatureRVE);
|
||||
int64_t Spimm = 0;
|
||||
int64_t RlistVal = MCOperand_getImm(MCInst_getOperand(MI, (0)));
|
||||
CS_ASSERT(RlistVal != 16 && "Incorrect rlist.");
|
||||
unsigned Base = RISCVZC_getStackAdjBase(RlistVal, IsRV64, IsEABI);
|
||||
Spimm = Imm + Base;
|
||||
CS_ASSERT((Spimm >= Base && Spimm <= Base + 48) && "Incorrect spimm");
|
||||
if (Opcode == RISCV_CM_PUSH)
|
||||
Spimm = -Spimm;
|
||||
|
||||
RISCVZC_printSpimm(Spimm, markup_OS(O, Markup_Immediate));
|
||||
}
|
||||
|
||||
void printVMaskReg(MCInst *MI, unsigned OpNo, SStream *O)
|
||||
{
|
||||
RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_VMaskReg, OpNo);
|
||||
MCOperand *MO = MCInst_getOperand(MI, (OpNo));
|
||||
|
||||
CS_ASSERT(MCOperand_isReg(MO) &&
|
||||
"printVMaskReg can only print register operands");
|
||||
if (MCOperand_getReg(MO) == RISCV_NoRegister)
|
||||
return;
|
||||
SStream_concat0(O, ", ");
|
||||
printRegName(O, MCOperand_getReg(MO));
|
||||
SStream_concat0(O, ".t");
|
||||
}
|
||||
|
||||
void RISCV_LLVM_printInstruction(MCInst *MI, SStream *O,
|
||||
void * /* MCRegisterInfo* */ info)
|
||||
{
|
||||
MI->MRI = (MCRegisterInfo *)info;
|
||||
|
||||
MCInst_setIsAlias(MI, false);
|
||||
bool usesAliasDetails = map_use_alias_details(MI);
|
||||
MI->flat_insn->usesAliasDetails = usesAliasDetails;
|
||||
|
||||
/* check for a non-compressed instruction */
|
||||
MCInst Uncompressed;
|
||||
MCInst_Init(&Uncompressed, MI->csh->arch);
|
||||
|
||||
MCInst *McInstr = MI;
|
||||
bool is_uncompressed = false;
|
||||
// side-effectful check for compressed instructions that creates the equivalent uncompressed instruction in case of true
|
||||
// (LLVM doesn't generate an API for doing a pure check)
|
||||
if (uncompressInst(&Uncompressed, MI)) {
|
||||
McInstr = &Uncompressed;
|
||||
Uncompressed.address = MI->address;
|
||||
Uncompressed.MRI = MI->MRI;
|
||||
Uncompressed.csh = MI->csh;
|
||||
Uncompressed.flat_insn = MI->flat_insn;
|
||||
is_uncompressed = true;
|
||||
}
|
||||
|
||||
// print the exact instruction text and done
|
||||
bool print_exact_text =
|
||||
(MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT) ||
|
||||
(is_uncompressed &&
|
||||
MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED);
|
||||
if (print_exact_text) {
|
||||
printInstruction(MI, MI->address, O);
|
||||
} else {
|
||||
// side-effectful check for alias instructions that prints to the SStream if true
|
||||
if (printAliasInstr(McInstr, MI->address, O)) {
|
||||
MCInst_setIsAlias(MI, true);
|
||||
// do we still want the exact details even if the text is alias ?
|
||||
if (!usesAliasDetails && detail_is_set(MI)) {
|
||||
// disable actual printing
|
||||
SStream_Close(O);
|
||||
// discard the alias operands
|
||||
memset(MI->flat_insn->detail->riscv.operands, 0,
|
||||
sizeof(MI->flat_insn->detail->riscv
|
||||
.operands));
|
||||
MI->flat_insn->detail->riscv.op_count = 0;
|
||||
// re-disassemble again with no printing in order to obtain the full details
|
||||
// including the whole operands array
|
||||
printInstruction(MI, MI->address, O);
|
||||
// re-open the stream to restore the usual state
|
||||
SStream_Open(O);
|
||||
}
|
||||
} else // the instruction is not an alias
|
||||
printInstruction(McInstr, MI->address, O);
|
||||
}
|
||||
RISCV_add_groups(MI);
|
||||
RISCV_add_missing_write_access(MI);
|
||||
RISCV_compact_operands(MI);
|
||||
RISCV_set_alias_id(MI, O);
|
||||
}
|
||||
|
||||
const char *getSysRegName(unsigned reg)
|
||||
{
|
||||
const RISCV_SysReg *SysReg = RISCV_lookupSysRegByEncoding(reg);
|
||||
return SysReg->Name;
|
||||
}
|
||||
|
||||
const char *RISCV_LLVM_getRegisterName(unsigned RegNo, unsigned AltIdx)
|
||||
{
|
||||
return getRegisterName(RegNo, AltIdx);
|
||||
}
|
||||
|
||||
bool isCompressed(MCInst *MI)
|
||||
{
|
||||
MCInst unused;
|
||||
MCInst_Init(&unused, MI->csh->arch);
|
||||
return uncompressInst(&unused, MI);
|
||||
}
|
||||
Reference in New Issue
Block a user