[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