gdb/riscv: better support for fflags and frm registers
authorAndrew Burgess <aburgess@redhat.com>
Thu, 11 Aug 2022 17:38:53 +0000 (18:38 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Wed, 31 Aug 2022 15:07:05 +0000 (16:07 +0100)
commit4749b84b51bcaa59acfb8bda5d9e134432528cf9
tree8c25668a8b8fc03c8281646cbdf702cdfdfffe80
parentb49d7aa74466fb73529ebf0e6c907c2142505610
gdb/riscv: better support for fflags and frm registers

First, some background on the RISC-V registers fflags, frm, and fcsr.

These three registers all relate to the floating-point status and
control mechanism on RISC-V.  The fcsr is the floatint-point control
status register, and consists of two parts, the flags (bits 0 to 4)
and the rounding-mode (bits 5 to 7).

The fcsr register is just one of many control/status registers (or
CSRs) available on RISC-V.  The fflags and frm registers are also
CSRs.  These CSRs are aliases for the relevant parts of the fcsr
register.  So fflags is an alias for bits 0 to 4 of fcsr, and frm is
an alias for bits 5 to 7 of fcsr.

This means that a user can change the floating-point rounding mode
either, by writing a complete new value into fcsr, or by writing just
the rounding mode into frm.

How this impacts on GDB is like this: a target description could,
legitimately include all three registers, fcsr, fflags, and frm.  The
QEMU target currently does this, and this makes sense.  The target is
emulating the complete system, and has all three CSRs available, so
why not tell GDB about this.

In contrast, the RISC-V native Linux target only has access to the
fcsr.  This is because the ptrace data structure that the kernel uses
for reading and writing floating point state only contains a copy of
the fcsr, after all, this one field really contains both the fflags
and frm fields, so why carry around duplicate data.

So, we might expect that the target description for the RISC-V native
Linux GDB would only contain the fcsr register.  Unfortunately, this
is not the case.  The RISC-V native Linux target uses GDB's builtin
target descriptions by calling riscv_lookup_target_description, this
will then add an fpu feature from gdb/features/riscv, either
32bit-fpu.xml or 64bit-fpu.xml.  The problem, is that these features
include an entry for fcsr, fflags, and frm.  This means that GDB
expects the target to handle reading and writing these registers.  And
the RISC-V native Linux target currently doesn't.

In riscv_linux_nat_target::store_registers and
riscv_linux_nat_target::fetch_registers only the fcsr register is
handled, this means that, for RISC-V native Linux, the fflags and frm
registers always show up as <unavailable> - they are present in the
target description, but the target doesn't know how to access the
registers.

A final complication relating to these floating pointer CSRs is which
target description feature the registers appear in.

These registers are CSRs, so it would seem sensible that these
registers should appear in the CSR target description feature.

However, when I first added RISC-V target description support, I was
using a RISC-V simulator that didn't support any CSRs other than the
floating point related ones.  This simulator bundled all the float
related CSRs into the fpu target feature.  This didn't feel completely
unreasonable to me, and so I had GDB check for these registers in
either target feature.

In this commit I make some changes relating to how GDB handles the
three floating point CSR:

1. Remove fflags and frm from 32bit-fpu.xml and 64bit-fpu.xml.  This
means that the default RISC-V target description (which RISC-V native
FreeBSD), and the target descriptions created for RISC-V native Linux,
will not include these registers.  There's nothing stopping some other
target (e.g. QEMU) from continuing to include all three of these CSRs,
the code in riscv-tdep.c continues to check for all three of these
registers, and will handle them correctly if they are present.

2. If a target supplied fcsr, but does not supply fflags and/or frm,
then RISC-V GDB will now create two pseudo registers in order to
emulate the two missing CSRs.  These new pseudo-registers do the
obvious thing of just reading and writing the fcsr register.

3. With the new pseudo-registers we can no longer make use of the GDB
register numbers RISCV_CSR_FFLAGS_REGNUM and RISCV_CSR_FRM_REGNUM.
These will be the numbers used if the target supplies the registers in
its target description, but, if GDB falls back to using
pseudo-registers, then new, unique numbers will be used.  To handle
this I've added riscv_gdbarch_tdep::fflags_regnum and
riscv_gdbarch_tdep::frm_regnum, I've then updated the RISC-V code to
compare against these fields.

When adding the pseudo-register support, it is important that the
pseudo-register numbers are calculated after the call to
tdesc_use_registers.  This is because we don't know the total number
of physical registers until after this call, and the psuedo-register
numbers must follow on from the real (target supplied) registers.

I've updated some tests to include more testing of the fflags and frm
registers, as well as adding a new test.
14 files changed:
gdb/arch/riscv.h
gdb/features/riscv/32bit-fpu.c
gdb/features/riscv/32bit-fpu.xml
gdb/features/riscv/64bit-fpu.c
gdb/features/riscv/64bit-fpu.xml
gdb/riscv-tdep.c
gdb/riscv-tdep.h
gdb/testsuite/gdb.arch/riscv-info-fcsr.exp
gdb/testsuite/gdb.arch/riscv-tdesc-fcsr-32.xml [new file with mode: 0644]
gdb/testsuite/gdb.arch/riscv-tdesc-fcsr-64.xml [new file with mode: 0644]
gdb/testsuite/gdb.arch/riscv-tdesc-loading-05.xml [new file with mode: 0644]
gdb/testsuite/gdb.arch/riscv-tdesc-loading-06.xml [new file with mode: 0644]
gdb/testsuite/gdb.arch/riscv-tdesc-regs.exp
gdb/testsuite/gdb.base/float.exp