[PATCH] RISC-V: Add software single step support for cm.popret[z]
Songhe Zhu
zhusonghe@eswincomputing.com
Thu May 15 09:15:42 GMT 2025
From: zhusonghe <zhusonghe@eswincomputing.com>
The Linux GDB build configures gdbarch_software_single_step_p() = 1,
indicating that software single-stepping is enabled while hardware
single-stepping is disabled. According to the RISC-V Zc Specification v1.0.4-3,
instructions like cm.popret[z] require precise control flow handling.
When single-stepping through these instructions, the Program Counter (PC)
must be explicitly updated to maintain correct execution flow.
Passing riscv-gnu-toolchain regressions.
Signed-off-by: Songhe Zhu <zhusonghe@eswincomputing.com>
Co-Authored by: Fei Gao <gaofei@eswincomputing.com>
gdb/ChangeLog:
* riscv-tdep.c (ROUND_UP): New.
(zcmp_base_adj): Compute stack_adj_base.
(zcmp_rlist_regcounts): Through reg_list, compute reg counts.
(class riscv_insn): Add support for cm.popret[z] opcodes.
(riscv_insn::decode): Ditto.
(riscv_next_pc): Add support cm.popret[z] function.
---
gdb/riscv-tdep.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 8998a297315..fa9fef44723 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -66,6 +66,9 @@
/* The biggest alignment that the target supports. */
#define BIGGEST_ALIGNMENT 16
+/* This macro rounds x up to the y boundary. */
+#define ROUND_UP(x,y) (((x) + (y) - 1) & ~((y) - 1))
+
/* Define a series of is_XXX_insn functions to check if the value INSN
is an instance of instruction XXX. */
#define DECLARE_INSN(INSN_NAME, INSN_MATCH, INSN_MASK) \
@@ -774,6 +777,20 @@ show_riscv_debug_variable (struct ui_file *file, int from_tty,
c->name, value);
}
+
+static int
+zcmp_base_adj (int word_bytes, int regcounts)
+{
+ return ROUND_UP(word_bytes * regcounts, 16);
+}
+
+ /* According to zcmp rlist field caculate register counts. */
+static int
+zcmp_rlist_regcounts (int rlist)
+{
+ return (rlist == 15 ? rlist - 2 : rlist - 3);
+}
+
/* See riscv-tdep.h. */
int
@@ -1593,6 +1610,8 @@ class riscv_insn
BGE,
BLTU,
BGEU,
+ CM_POPRET,
+ CM_POPRETZ,
/* These are needed for stepping over atomic sequences. */
SLTI,
SLTIU,
@@ -1691,6 +1710,12 @@ class riscv_insn
return ((opcode >> offset) & 0x7) + 8;
}
+ /* Extract 4 bit rlist field at OFFSET from instruction OPCODE. */
+ int decode_rlist (unsigned long opcode, int offset)
+ {
+ return (opcode >> offset) & 0xF;
+ }
+
/* Helper for DECODE, decode 32-bit R-type instruction. */
void decode_r_type_insn (enum opcode opcode, ULONGEST ival)
{
@@ -1822,6 +1847,13 @@ class riscv_insn
m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
}
+ void decode_zcmp_type_insn (enum opcode opcode, ULONGEST ival)
+ {
+ m_opcode = opcode;
+ m_rs1 = decode_rlist (ival, OP_SH_REG_LIST);
+ m_imm.s = EXTRACT_ZCMP_SPIMM (ival);
+ }
+
/* The length of the instruction in bytes. Should be 2 or 4. */
int m_length;
@@ -2091,6 +2123,10 @@ riscv_insn::decode (struct gdbarch *gdbarch, CORE_ADDR pc)
decode_ci_type_insn (LD, ival, RISCV_SP_REGNUM);
else if (is_c_lwsp_insn (ival))
decode_ci_type_insn (LW, ival, RISCV_SP_REGNUM);
+ else if (is_cm_popret_insn (ival))
+ decode_zcmp_type_insn (CM_POPRET, ival);
+ else if (is_cm_popretz_insn (ival))
+ decode_zcmp_type_insn (CM_POPRETZ, ival);
else
/* None of the other fields of INSN are valid in this case. */
m_opcode = OTHER;
@@ -4523,6 +4559,24 @@ riscv_next_pc (struct regcache *regcache, CORE_ADDR pc)
if (tdep->syscall_next_pc != nullptr)
next_pc = tdep->syscall_next_pc (get_current_frame ());
}
+ else if ((insn.opcode () == riscv_insn::CM_POPRET)
+ || (insn.opcode () == riscv_insn::CM_POPRETZ))
+ {
+ LONGEST sp;
+ gdb_byte buf[8];
+ int isa_xlen = riscv_isa_xlen (gdbarch);
+ int regcounts = zcmp_rlist_regcounts (insn.rs1 ());
+ int stack_adj_base = zcmp_base_adj (isa_xlen, regcounts);
+ int stack_adj = stack_adj_base + insn.imm_signed ();
+ regcache->cooked_read (RISCV_SP_REGNUM, &sp);
+ enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch);
+ CORE_ADDR ra_addr = sp + stack_adj - isa_xlen * regcounts;
+ int status = target_read_memory (ra_addr, buf, isa_xlen);
+ if (status)
+ memory_error (TARGET_XFER_E_IO, ra_addr);
+ ULONGEST ra_value = extract_unsigned_integer (buf, isa_xlen, byte_order);
+ next_pc = (ra_value) & ~(CORE_ADDR) 0x1;
+ }
return next_pc;
}
--
2.17.1
More information about the Binutils
mailing list