/* 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 */ //===-- 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 #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); }