[PATCH] RISC-V: Add software single step support for cm.popret[z]

Kito Cheng kito.cheng@gmail.com
Thu May 15 09:18:35 GMT 2025
this should be send to gdb-patches@sourceware.org rather than
binutils@sourceware.org

On Thu, May 15, 2025 at 5:17 PM Songhe Zhu <zhusonghe@eswincomputing.com> wrote:
>
> 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