gdb/record: Speeding up recording in RISC-V

I measured that removing saving mem chunks and regs to std::vector before
calling API functions speeds up stepping up to 15%, so I added this
optimization (as Guinevere Larsen <guinevere@redhat.com> recommended in
initial support). It turns out that after this, the m_record_type and
m_error_occured no longer needed, so I removed them too.

Approved-By: Guinevere Larsen <guinevere@redhat.com>
This commit is contained in:
timurgol007
2025-10-08 18:44:19 +03:00
parent 46eeb5132a
commit bef948d551

View File

@@ -4887,72 +4887,32 @@ equivalent change in the disassembler output."),
&showriscvcmdlist); &showriscvcmdlist);
} }
/* A wrapper to read register under number regnum to address addr.
Returns false if error happened and makes warning. */
static bool
try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
{
gdb_assert (regcache != nullptr);
if (regcache->raw_read (regnum, &addr)
!= register_status::REG_VALID)
{
warning (_("Can not read at address %s"), hex_string (addr));
return false;
}
return true;
}
/* Helper class to record instruction. */ /* Helper class to record instruction. */
class riscv_recorded_insn final class riscv_recorded_insn final
{ {
public:
/* Type for saved register. */ /* Type for saved register. */
using regnum_type = int; using regnum_type = int;
/* Type for saved memory. First is address, second is length. */
using memory_type = std::pair<CORE_ADDR, int>;
/* Enum class that represents which type does recording belong to. */ /* Type for memory address. */
enum class record_type using mem_addr = CORE_ADDR;
{
UNKNOWN,
ORDINARY,
/* Corner cases. */ /* Type for memory length. */
ECALL, using mem_len = int;
EBREAK,
SRET,
MRET,
};
private: /* Current regcache. */
/* Type for set of registers that need to be saved. */ struct regcache *m_regcache = nullptr;
using recorded_regs = std::vector<regnum_type>;
/* Type for set of memory records that need to be saved. */
using recorded_mems = std::vector<memory_type>;
/* Type for memory address, extracted from memory_type. */ /* Current riscv gdbarch. */
using mem_addr = decltype (std::declval<memory_type> ().first); struct riscv_gdbarch_tdep *m_gdbarch = nullptr;
/* Type for memory length, extracted from memory_type. */
using mem_len = decltype (std::declval<memory_type> ().second);
/* Record type of current instruction. */
record_type m_record_type = record_type::UNKNOWN;
/* Flag that represents was there an error in current recording. */
bool m_error_occured = false;
/* Set of registers that need to be recorded. */
recorded_regs m_regs;
/* Set of memory chunks that need to be recorded. */
recorded_mems m_mems;
/* Width in bytes of the general purpose registers for GDBARCH, /* Width in bytes of the general purpose registers for GDBARCH,
where recording is happening. */ where recording is happening. */
int m_xlen = 0; int m_xlen = 0;
/* Flag that says whether we are in baremetal mode. */
bool m_in_baremetal_mode = false;
/* Helper for decode 16-bit instruction RS1. */ /* Helper for decode 16-bit instruction RS1. */
static regnum_type static regnum_type
decode_crs1_short (ULONGEST opcode) noexcept decode_crs1_short (ULONGEST opcode) noexcept
@@ -5009,68 +4969,34 @@ private:
return (ival >> OP_SH_CSR) & OP_MASK_CSR; return (ival >> OP_SH_CSR) & OP_MASK_CSR;
} }
/* Set any record type. Always returns true. */ /* Reads register. Returns false if error happened. */
bool bool
set_record_type (record_type type) noexcept read_reg (regnum_type regnum, ULONGEST &addr) noexcept
{ {
m_record_type = type; if (m_regcache->raw_read (regnum, &addr) == register_status::REG_VALID)
return true; return true;
}
/* Set ordinary record type. Always returns true. */ warning (_("Can not read at address %s"), hex_string (addr));
bool
set_ordinary_record_type () noexcept
{
m_record_type = record_type::ORDINARY;
return true;
}
/* Set error happened. Always returns false. */
bool
set_error () noexcept
{
m_error_occured = true;
return false; return false;
} }
/* Check if current recording has an error. */ /* Save register. Returns false if error happened. */
bool
has_error () const noexcept
{
return m_error_occured;
}
/* Reads register. Sets error and returns false if error happened. */
bool
read_reg (struct regcache *regcache, regnum_type reg,
ULONGEST &addr) noexcept
{
gdb_assert (regcache != nullptr);
if (!try_read (regcache, reg, addr))
return set_error ();
return true;
}
/* Save register. Returns true or aborts if exception happened. */
bool bool
save_reg (regnum_type regnum) noexcept save_reg (regnum_type regnum) noexcept
{ {
m_regs.emplace_back (regnum); return (record_full_arch_list_add_reg (m_regcache, regnum) == 0);
return true;
} }
/* Save memory chunk. Returns true or aborts if exception happened. */ /* Save memory chunk. Returns false if error happened. */
bool bool
save_mem (mem_addr addr, mem_len len) noexcept save_mem (mem_addr addr, mem_len len) noexcept
{ {
m_mems.emplace_back (addr, len); return (record_full_arch_list_add_mem (addr, len) == 0);
return true;
} }
/* Returns true if instruction needs only saving pc. */ /* Returns true if instruction needs only saving pc. */
static bool static bool
need_save_pc (ULONGEST ival) noexcept need_save_only_pc (ULONGEST ival) noexcept
{ {
return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival) return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
|| is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival) || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
@@ -5079,19 +5005,9 @@ private:
|| is_sfence_vma_insn (ival)); || is_sfence_vma_insn (ival));
} }
/* Returns true if instruction is classified. */
bool
try_save_pc (ULONGEST ival) noexcept
{
if (!need_save_pc (ival))
return false;
return set_ordinary_record_type ();
}
/* Returns true if instruction needs only saving pc and rd. */ /* Returns true if instruction needs only saving pc and rd. */
static bool static bool
need_save_pc_rd (ULONGEST ival) noexcept need_save_rd (ULONGEST ival) noexcept
{ {
return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival) return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
|| is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival) || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
@@ -5126,21 +5042,17 @@ private:
|| is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival)); || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
} }
/* Returns true if instruction is classified. This function can set /* Returns true if instruction successfully saved rd. */
m_error_occured. */
bool bool
try_save_pc_rd (ULONGEST ival) noexcept try_save_rd (ULONGEST ival) noexcept
{ {
if (!need_save_pc_rd (ival)) return save_reg (decode_rd (ival));;
return false;
return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
} }
/* Returns true if instruction needs only saving pc and /* Returns true if instruction needs only saving pc and
floating point rd. */ floating point rd. */
static bool static bool
need_save_pc_fprd (ULONGEST ival) noexcept need_save_fprd (ULONGEST ival) noexcept
{ {
return (is_flw_insn (ival) || is_fmadd_s_insn (ival) return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
|| is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival) || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
@@ -5165,45 +5077,35 @@ private:
|| is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival)); || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
} }
/* Returns true if instruction is classified. This function can set /* Returns true if instruction successfully saved floating point rd. */
m_error_occured. */
bool bool
try_save_pc_fprd (ULONGEST ival) noexcept try_save_fprd (ULONGEST ival) noexcept
{ {
if (!need_save_pc_fprd (ival)) return save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival));
return false;
return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
|| set_ordinary_record_type ());
} }
/* Returns true if instruction needs only saving pc, rd and csr. */ /* Returns true if instruction needs only saving pc, rd and csr. */
static bool static bool
need_save_pc_rd_csr (ULONGEST ival) noexcept need_save_rd_csr (ULONGEST ival) noexcept
{ {
return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival) return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
|| is_csrrwi_insn (ival) || is_csrrsi_insn (ival) || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
|| is_csrrci_insn (ival)); || is_csrrci_insn (ival));
} }
/* Returns true if instruction is classified. This function can set /* Returns true if instruction successfully saved rd and csr. */
m_error_occured. */
bool bool
try_save_pc_rd_csr (ULONGEST ival) noexcept try_save_rd_csr (ULONGEST ival) noexcept
{ {
if (!need_save_pc_rd_csr (ival)) return (save_reg (decode_rd (ival))
return false; && save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival)));
return (!save_reg (decode_rd (ival))
|| !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
|| set_ordinary_record_type ());
} }
/* Returns the size of the memory chunk that needs to be saved if the /* Returns the size of the memory chunk that needs to be saved if the
instruction belongs to the group that needs only saving pc and memory. instruction belongs to the group that needs only saving pc and memory.
Otherwise returns 0. */ Otherwise returns 0. */
static mem_len static mem_len
need_save_pc_mem (ULONGEST ival) noexcept need_save_mem (ULONGEST ival) noexcept
{ {
if (is_sb_insn (ival)) if (is_sb_insn (ival))
return 1; return 1;
@@ -5216,28 +5118,22 @@ private:
return 0; return 0;
} }
/* Returns true if instruction is classified. This function can set /* Returns true if instruction successfully saved memory. */
m_error_occured. */
bool bool
try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept try_save_mem (ULONGEST ival, mem_len len) noexcept
{ {
gdb_assert (regcache != nullptr); mem_addr addr = 0;
ULONGEST offset = EXTRACT_STYPE_IMM (ival);
mem_addr addr = mem_addr{}; return (read_reg (decode_rs1 (ival), addr)
mem_len len = need_save_pc_mem (ival); && save_mem (addr + offset, len));
if (len <= 0)
return false;
mem_len offset = EXTRACT_STYPE_IMM (ival);
return (!read_reg (regcache, decode_rs1 (ival), addr)
|| !save_mem (addr + offset, len) || set_ordinary_record_type ());
} }
/* Returns the size of the memory chunk that needs to be saved if the /* Returns the size of the memory chunk that needs to be saved if the
instruction belongs to the group that needs only saving pc, rd and memory. instruction belongs to the group that needs only saving pc, rd and memory.
Otherwise returns 0. */ Otherwise returns 0. */
static mem_len static mem_len
need_save_pc_rd_mem (ULONGEST ival) noexcept need_save_rd_mem (ULONGEST ival) noexcept
{ {
if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival) if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
|| is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival) || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
@@ -5254,58 +5150,68 @@ private:
return 0; return 0;
} }
/* Returns true if instruction is classified. This function can set /* Returns true if instruction successfully saved rd and memory. */
m_error_occured. */
bool bool
try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept try_save_rd_mem (ULONGEST ival, mem_len len) noexcept
{ {
gdb_assert (regcache != nullptr);
mem_len len = need_save_pc_rd_mem (ival);
mem_addr addr = 0; mem_addr addr = 0;
if (len <= 0)
return false;
return (!read_reg (regcache, decode_rs1 (ival), addr) return (read_reg (decode_rs1 (ival), addr) && save_mem (addr, len)
|| !save_mem (addr, len) || !save_reg (decode_rd (ival)) && save_reg (decode_rd (ival)));
|| set_ordinary_record_type ());
} }
/* Returns true if instruction is successfully recordered. The length of /* Returns true if instruction is successfully recordered. The length of
the instruction must be equal 4 bytes. */ the instruction must be equal 4 bytes. */
bool bool
record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept record_insn_len4 (ULONGEST ival) noexcept
{ {
gdb_assert (regcache != nullptr); mem_len len = 0;
ULONGEST reg_val = 0;
if (is_ecall_insn (ival)) if (is_ecall_insn (ival))
{ {
return set_record_type (record_type::ECALL); /* We are in baremetal mode. */
if (m_in_baremetal_mode)
{
warning (_("Syscall record is not supported"));
return false;
}
/* We are in linux mode. */
return (read_reg (RISCV_A7_REGNUM, reg_val)
&& m_gdbarch->riscv_syscall_record (m_regcache, reg_val) == 0);
} }
if (is_ebreak_insn (ival)) if (is_ebreak_insn (ival))
{ return true;
return set_record_type (record_type::EBREAK);
}
if (is_sret_insn (ival)) if (is_sret_insn (ival))
{ return (save_reg (RISCV_CSR_SSTATUS_REGNUM)
return (!save_reg (RISCV_CSR_SSTATUS_REGNUM) && save_reg (RISCV_CSR_MEPC_REGNUM));
|| !save_reg (RISCV_CSR_MEPC_REGNUM)
|| set_record_type (record_type::SRET));
}
if (is_mret_insn (ival)) if (is_mret_insn (ival))
{ return (save_reg (RISCV_CSR_MSTATUS_REGNUM)
return (!save_reg (RISCV_CSR_MSTATUS_REGNUM) && save_reg (RISCV_CSR_MEPC_REGNUM));
|| !save_reg (RISCV_CSR_MEPC_REGNUM)
|| set_record_type (record_type::MRET));
}
if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival) if (need_save_only_pc (ival))
|| try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache) return true;
|| try_save_pc_rd_mem (ival, regcache))
return !has_error (); if (need_save_rd (ival))
return try_save_rd (ival);
if (need_save_fprd (ival))
return try_save_fprd (ival);
if (need_save_rd_csr (ival))
return try_save_rd_csr (ival);
len = need_save_mem (ival);
if (len > 0)
return try_save_mem (ival, len);
len = need_save_rd_mem (ival);
if (len > 0)
return try_save_rd_mem (ival, len);
warning (_("Currently this instruction with len 4(%s) is unsupported"), warning (_("Currently this instruction with len 4(%s) is unsupported"),
hex_string (ival)); hex_string (ival));
@@ -5315,105 +5221,98 @@ private:
/* Returns true if instruction is successfully recordered. The length of /* Returns true if instruction is successfully recordered. The length of
the instruction must be equal 2 bytes. */ the instruction must be equal 2 bytes. */
bool bool
record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept record_insn_len2 (ULONGEST ival) noexcept
{ {
gdb_assert (regcache != nullptr); mem_addr addr = 0;
ULONGEST offset = 0;
mem_addr addr = mem_addr{};
/* The order here is very important, because /* The order here is very important, because
opcodes of some instructions may be the same. */ opcodes of some instructions may be the same. */
if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival) if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
|| (m_xlen == 8 && is_c_ld_insn (ival))) || (m_xlen == 8 && is_c_ld_insn (ival)))
return (!save_reg (decode_crs2_short (ival)) return save_reg (decode_crs2_short (ival));
|| set_ordinary_record_type ());
if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival))) if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival)) return save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival));
|| set_ordinary_record_type ());
if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival))) if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
{ {
ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)}; offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
return (!read_reg (regcache, decode_crs1_short (ival), addr) return (read_reg (decode_crs1_short (ival), addr)
|| !save_mem (addr + offset, 8) || set_ordinary_record_type ()); && save_mem (addr + offset, 8));
} }
if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival)) if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
{ {
ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)}; offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
return (!read_reg (regcache, decode_crs1_short (ival), addr) return (read_reg (decode_crs1_short (ival), addr)
|| !save_mem (addr + offset, 4) || set_ordinary_record_type ()); && save_mem (addr + offset, 4));
} }
if (is_c_nop_insn (ival)) if (is_c_nop_insn (ival))
return set_ordinary_record_type (); return true;
if (is_c_addi_insn (ival)) if (is_c_addi_insn (ival))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (m_xlen == 4 && is_c_jal_insn (ival)) if (m_xlen == 4 && is_c_jal_insn (ival))
return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ()); return save_reg (RISCV_RA_REGNUM);
if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival)) if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (is_c_addi16sp_insn (ival)) if (is_c_addi16sp_insn (ival))
return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ()); return save_reg (RISCV_SP_REGNUM);
if (is_c_lui_insn (ival)) if (is_c_lui_insn (ival))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival) if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
|| is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival) || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
|| is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival)) || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
|| (m_xlen == 8 && is_c_addw_insn (ival))) || (m_xlen == 8 && is_c_addw_insn (ival)))
return (!save_reg (decode_crs1_short (ival)) return save_reg (decode_crs1_short (ival));
|| set_ordinary_record_type ());
if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival)) if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
return set_ordinary_record_type (); return true;
if (is_c_slli_insn (ival)) if (is_c_slli_insn (ival))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival))) if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival)) return save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival));
|| set_ordinary_record_type ());
if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival))) if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (is_c_jr_insn (ival)) if (is_c_jr_insn (ival))
return set_ordinary_record_type (); return true;
if (is_c_mv_insn (ival)) if (is_c_mv_insn (ival))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (is_c_ebreak_insn (ival)) if (is_c_ebreak_insn (ival))
{ return true;
return set_record_type (record_type::EBREAK);
}
if (is_c_jalr_insn (ival)) if (is_c_jalr_insn (ival))
return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ()); return save_reg (RISCV_RA_REGNUM);
if (is_c_add_insn (ival)) if (is_c_add_insn (ival))
return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); return save_reg (decode_crs1 (ival));
if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival))) if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
{ {
ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)}; offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
return (!read_reg (regcache, RISCV_SP_REGNUM, addr) return (read_reg (RISCV_SP_REGNUM, addr)
|| !save_mem (addr + offset, 8) || set_ordinary_record_type ()); && save_mem (addr + offset, 8));
} }
if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival))) if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
{ {
ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)}; offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
return (!read_reg (regcache, RISCV_SP_REGNUM, addr) return (read_reg (RISCV_SP_REGNUM, addr)
|| !save_mem (addr + offset, 4) || set_ordinary_record_type ()); && save_mem (addr + offset, 4));
} }
warning (_("Currently this instruction with len 2(%s) is unsupported"), warning (_("Currently this instruction with len 2(%s) is unsupported"),
@@ -5422,11 +5321,6 @@ private:
} }
public: public:
/* Iterator for registers that need to be recorded. */
using regs_iter = recorded_regs::const_iterator;
/* Iterator for memory chunks that need to be recorded. */
using mems_iter = recorded_mems::const_iterator;
/* Record instruction at address addr. Returns false if error happened. */ /* Record instruction at address addr. Returns false if error happened. */
bool bool
record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
@@ -5434,138 +5328,46 @@ public:
gdb_assert (gdbarch != nullptr); gdb_assert (gdbarch != nullptr);
gdb_assert (regcache != nullptr); gdb_assert (regcache != nullptr);
int m_length = 0; m_gdbarch = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
ULONGEST ival = 0; m_regcache = regcache;
m_xlen = riscv_isa_xlen (gdbarch); m_xlen = riscv_isa_xlen (gdbarch);
m_in_baremetal_mode = (m_gdbarch->riscv_syscall_record == nullptr);
int insn_length = 0;
ULONGEST ival = 0;
/* Since fetch_instruction can throw an exception, /* Since fetch_instruction can throw an exception,
it must be wrapped in a try-catch block. */ it must be wrapped in a try-catch block. */
try try
{ {
ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length); ival = riscv_insn::fetch_instruction (gdbarch, addr, &insn_length);
} }
catch (const gdb_exception_error &ex) catch (const gdb_exception_error &ex)
{ {
warning ("%s", ex.what ()); warning ("%s", ex.what ());
return false; return false;
} }
if (!save_reg (RISCV_PC_REGNUM)) if (!save_reg (RISCV_PC_REGNUM))
return false; return false;
if (m_length == 4) if (insn_length == 2)
return record_insn_len4 (ival, regcache); return record_insn_len2 (ival);
if (m_length == 2) if (insn_length == 4)
return record_insn_len2 (ival, regcache); return record_insn_len4 (ival);
/* 6 bytes or more. If the instruction is longer than 8 bytes, we don't /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
have full instruction bits in ival. At least, such long instructions have full instruction bits in ival. At least, such long instructions
are not defined yet, so just ignore it. */ are not defined yet, so just ignore it. */
gdb_assert (m_length > 0 && m_length % 2 == 0); gdb_assert (insn_length > 0 && insn_length % 2 == 0);
warning (_("Can not record unknown instruction (opcode = %s)"), warning (_("Can not record unknown instruction (opcode = %s)"),
hex_string (ival)); hex_string (ival));
return false; return false;
} }
/* Get record type of instruction. */
record_type
get_record_type () const noexcept
{
return m_record_type;
}
/* Returns an iterator to the beginning of the registers that need
to be saved. */
regs_iter
regs_begin () const noexcept
{
return m_regs.begin ();
}
/* Returns an iterator to the end of the registers that need
to be saved. */
regs_iter
regs_end () const noexcept
{
return m_regs.end ();
}
/* Returns an iterator to the beginning of the memory chunks that need
to be saved. */
mems_iter
mems_begin () const noexcept
{
return m_mems.begin ();
}
/* Returns an iterator to the end of the memory chunks that need
to be saved. */
mems_iter
mems_end () const noexcept
{
return m_mems.end ();
}
}; };
/* A helper function to record instruction using record API. */
static int
riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
const riscv_recorded_insn &insn)
{
gdb_assert (gdbarch != nullptr);
gdb_assert (regcache != nullptr);
riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
auto regs_begin = insn.regs_begin ();
auto regs_end = insn.regs_end ();
if (std::any_of (regs_begin,
regs_end,
[&regcache] (auto &&reg_it)
{
return record_full_arch_list_add_reg (regcache, reg_it);
}))
return -1;
auto mems_begin = insn.mems_begin ();
auto mems_end = insn.mems_end ();
if (std::any_of (mems_begin,
mems_end,
[] (auto &&mem_it)
{
return record_full_arch_list_add_mem (mem_it.first,
mem_it.second);
}))
return -1;
switch (insn.get_record_type ())
{
case riscv_recorded_insn::record_type::ORDINARY:
case riscv_recorded_insn::record_type::EBREAK:
case riscv_recorded_insn::record_type::SRET:
case riscv_recorded_insn::record_type::MRET:
break;
case riscv_recorded_insn::record_type::ECALL:
{
if (!tdep->riscv_syscall_record)
{
warning (_("Syscall record is not supported"));
return -1;
}
ULONGEST reg_val = ULONGEST{};
if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
return -1;
return tdep->riscv_syscall_record (regcache, reg_val);
}
default:
return -1;
}
return 0;
}
/* Parse the current instruction and record the values of the registers and /* Parse the current instruction and record the values of the registers and
memory that will be changed in current instruction to record_arch_list. memory that will be changed in current instruction to record_arch_list.
Return -1 if something is wrong. */ Return -1 if something is wrong. */
@@ -5581,10 +5383,8 @@ riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
if (!insn.record (gdbarch, regcache, addr)) if (!insn.record (gdbarch, regcache, addr))
return -1; return -1;
int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
if (record_full_arch_list_add_end ()) if (record_full_arch_list_add_end ())
return -1; return -1;
return ret_val; return 0;
} }