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.
/* When true this target is RV32E. */
bool embedded = false;
+ /* Track if the target description has an fcsr, fflags, and frm
+ registers. Some targets provide all these in their target
+ descriptions, while some only offer fcsr, while others don't even
+ offer that register. If a target provides fcsr but not fflags and/or
+ frm, then we can emulate these registers as pseudo registers. */
+ bool has_fcsr_reg = false;
+ bool has_fflags_reg = false;
+ bool has_frm_reg = false;
+
/* Equality operator. */
bool operator== (const struct riscv_gdbarch_features &rhs) const
{
return (xlen == rhs.xlen && flen == rhs.flen
- && embedded == rhs.embedded && vlen == rhs.vlen);
+ && embedded == rhs.embedded && vlen == rhs.vlen
+ && has_fflags_reg == rhs.has_fflags_reg
+ && has_frm_reg == rhs.has_frm_reg
+ && has_fcsr_reg == rhs.has_fcsr_reg);
}
/* Inequality operator. */
std::size_t hash () const noexcept
{
std::size_t val = ((embedded ? 1 : 0) << 10
+ | (has_fflags_reg ? 1 : 0) << 11
+ | (has_frm_reg ? 1 : 0) << 12
+ | (has_fcsr_reg ? 1 : 0) << 13
| (xlen & 0x1f) << 5
| (flen & 0x1f) << 0
- | (vlen & 0xfff) << 11);
+ | (vlen & 0xfff) << 14);
return val;
}
};
tdesc_create_reg (feature, "ft9", regnum++, 1, NULL, 32, "ieee_single");
tdesc_create_reg (feature, "ft10", regnum++, 1, NULL, 32, "ieee_single");
tdesc_create_reg (feature, "ft11", regnum++, 1, NULL, 32, "ieee_single");
- regnum = 66;
- tdesc_create_reg (feature, "fflags", regnum++, 1, NULL, 32, "int");
- tdesc_create_reg (feature, "frm", regnum++, 1, NULL, 32, "int");
+ regnum = 68;
tdesc_create_reg (feature, "fcsr", regnum++, 1, NULL, 32, "int");
return regnum;
}
<reg name="ft10" bitsize="32" type="ieee_single"/>
<reg name="ft11" bitsize="32" type="ieee_single"/>
- <reg name="fflags" bitsize="32" type="int" regnum="66"/>
- <reg name="frm" bitsize="32" type="int" regnum="67"/>
<reg name="fcsr" bitsize="32" type="int" regnum="68"/>
</feature>
tdesc_create_reg (feature, "ft9", regnum++, 1, NULL, 64, "riscv_double");
tdesc_create_reg (feature, "ft10", regnum++, 1, NULL, 64, "riscv_double");
tdesc_create_reg (feature, "ft11", regnum++, 1, NULL, 64, "riscv_double");
- regnum = 66;
- tdesc_create_reg (feature, "fflags", regnum++, 1, NULL, 32, "int");
- tdesc_create_reg (feature, "frm", regnum++, 1, NULL, 32, "int");
+ regnum = 68;
tdesc_create_reg (feature, "fcsr", regnum++, 1, NULL, 32, "int");
return regnum;
}
<reg name="ft10" bitsize="64" type="riscv_double"/>
<reg name="ft11" bitsize="64" type="riscv_double"/>
- <reg name="fflags" bitsize="32" type="int" regnum="66"/>
- <reg name="frm" bitsize="32" type="int" regnum="67"/>
<reg name="fcsr" bitsize="32" type="int" regnum="68"/>
</feature>
return name;
}
+/* Implement gdbarch_pseudo_register_read. Read pseudo-register REGNUM
+ from REGCACHE and place the register value into BUF. BUF is sized
+ based on the type of register REGNUM, all of BUF should be written too,
+ the result should be sign or zero extended as appropriate. */
+
+static enum register_status
+riscv_pseudo_register_read (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int regnum, gdb_byte *buf)
+{
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+
+ if (regnum == tdep->fflags_regnum || regnum == tdep->frm_regnum)
+ {
+ /* Clear BUF. */
+ memset (buf, 0, register_size (gdbarch, regnum));
+
+ /* Read the first byte of the fcsr register, this contains both frm
+ and fflags. */
+ enum register_status status
+ = regcache->raw_read_part (RISCV_CSR_FCSR_REGNUM, 0, 1, buf);
+
+ if (status != REG_VALID)
+ return status;
+
+ /* Extract the appropriate parts. */
+ if (regnum == tdep->fflags_regnum)
+ buf[0] &= 0x1f;
+ else if (regnum == tdep->frm_regnum)
+ buf[0] = (buf[0] >> 5) & 0x7;
+
+ return REG_VALID;
+ }
+
+ return REG_UNKNOWN;
+}
+
+/* Implement gdbarch_pseudo_register_write. Write the contents of BUF into
+ pseudo-register REGNUM in REGCACHE. BUF is sized based on the type of
+ register REGNUM. */
+
+static void
+riscv_pseudo_register_write (struct gdbarch *gdbarch,
+ struct regcache *regcache, int regnum,
+ const gdb_byte *buf)
+{
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+
+ if (regnum == tdep->fflags_regnum || regnum == tdep->frm_regnum)
+ {
+ int fcsr_regnum = RISCV_CSR_FCSR_REGNUM;
+ gdb_byte raw_buf[register_size (gdbarch, fcsr_regnum)];
+
+ regcache->raw_read (fcsr_regnum, raw_buf);
+
+ if (regnum == tdep->fflags_regnum)
+ raw_buf[0] = (raw_buf[0] & ~0x1f) | (buf[0] & 0x1f);
+ else if (regnum == tdep->frm_regnum)
+ raw_buf[0] = (raw_buf[0] & ~(0x7 << 5)) | ((buf[0] & 0x7) << 5);
+
+ regcache->raw_write (fcsr_regnum, raw_buf);
+ }
+ else
+ gdb_assert_not_reached ("unknown pseudo register %d", regnum);
+}
+
/* Implement the cannot_store_register gdbarch method. The zero register
(x0) is read-only on RISC-V. */
else
{
struct value_print_options opts;
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
/* Print the register in hex. */
get_formatted_print_options (&opts, 'x');
}
}
else if (regnum == RISCV_CSR_FCSR_REGNUM
- || regnum == RISCV_CSR_FFLAGS_REGNUM
- || regnum == RISCV_CSR_FRM_REGNUM)
+ || regnum == tdep->fflags_regnum
+ || regnum == tdep->frm_regnum)
{
- LONGEST d;
-
- d = value_as_long (val);
+ LONGEST d = value_as_long (val);
gdb_printf (file, "\t");
- if (regnum != RISCV_CSR_FRM_REGNUM)
+ if (regnum != tdep->frm_regnum)
gdb_printf (file,
"NV:%d DZ:%d OF:%d UF:%d NX:%d",
(int) ((d >> 4) & 0x1),
(int) ((d >> 1) & 0x1),
(int) ((d >> 0) & 0x1));
- if (regnum != RISCV_CSR_FFLAGS_REGNUM)
+ if (regnum != tdep->fflags_regnum)
{
static const char * const sfrm[] =
{
riscv_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
const struct reggroup *reggroup)
{
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+
/* Used by 'info registers' and 'info registers <groupname>'. */
if (gdbarch_register_name (gdbarch, regnum) == NULL
|| gdbarch_register_name (gdbarch, regnum)[0] == '\0')
return 0;
- if (regnum > RISCV_LAST_REGNUM)
+ if (regnum > RISCV_LAST_REGNUM && regnum < gdbarch_num_regs (gdbarch))
{
/* Any extra registers from the CSR tdesc_feature (identified in
riscv_tdesc_unknown_reg) are removed from the save/restore groups
else if (reggroup == float_reggroup)
return (riscv_is_fp_regno_p (regnum)
|| regnum == RISCV_CSR_FCSR_REGNUM
- || regnum == RISCV_CSR_FFLAGS_REGNUM
- || regnum == RISCV_CSR_FRM_REGNUM);
+ || regnum == tdep->fflags_regnum
+ || regnum == tdep->frm_regnum);
else if (reggroup == general_reggroup)
return regnum < RISCV_FIRST_FP_REGNUM;
else if (reggroup == restore_reggroup || reggroup == save_reggroup)
if (riscv_has_fp_regs (gdbarch))
return (regnum <= RISCV_LAST_FP_REGNUM
|| regnum == RISCV_CSR_FCSR_REGNUM
- || regnum == RISCV_CSR_FFLAGS_REGNUM
- || regnum == RISCV_CSR_FRM_REGNUM);
+ || regnum == tdep->fflags_regnum
+ || regnum == tdep->frm_regnum);
else
return regnum < RISCV_FIRST_FP_REGNUM;
}
return 0;
}
+/* Return the name for pseudo-register REGNUM for GDBARCH. */
+
+static const char *
+riscv_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
+{
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+
+ if (regnum == tdep->fflags_regnum)
+ return "fflags";
+ else if (regnum == tdep->frm_regnum)
+ return "frm";
+ else
+ gdb_assert_not_reached ("unknown pseudo register number %d", regnum);
+}
+
+/* Return the type for pseudo-register REGNUM for GDBARCH. */
+
+static struct type *
+riscv_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
+{
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+
+ if (regnum == tdep->fflags_regnum || regnum == tdep->frm_regnum)
+ return builtin_type (gdbarch)->builtin_int32;
+ else
+ gdb_assert_not_reached ("unknown pseudo register number %d", regnum);
+}
+
+/* Return true (non-zero) if pseudo-register REGNUM from GDBARCH is a
+ member of REGGROUP, otherwise return false (zero). */
+
+static int
+riscv_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+ const struct reggroup *reggroup)
+{
+ /* The standard function will also work for pseudo-registers. */
+ return riscv_register_reggroup_p (gdbarch, regnum, reggroup);
+}
+
/* Implement the print_registers_info gdbarch method. This is used by
'info registers' and 'info all-registers'. */
return NULL;
}
+ if (tdesc_found_register (tdesc_data.get (), RISCV_CSR_FFLAGS_REGNUM))
+ features.has_fflags_reg = true;
+ if (tdesc_found_register (tdesc_data.get (), RISCV_CSR_FRM_REGNUM))
+ features.has_frm_reg = true;
+ if (tdesc_found_register (tdesc_data.get (), RISCV_CSR_FCSR_REGNUM))
+ features.has_fcsr_reg = true;
+
/* Have a look at what the supplied (if any) bfd object requires of the
target, then check that this matches with what the target is
providing. */
just a little easier. */
set_gdbarch_num_regs (gdbarch, RISCV_LAST_REGNUM + 1);
- /* We don't have to provide the count of 0 here (its the default) but
- include this line to make it explicit that, right now, we don't have
- any pseudo registers on RISC-V. */
- set_gdbarch_num_pseudo_regs (gdbarch, 0);
-
/* Some specific register numbers GDB likes to know about. */
set_gdbarch_sp_regnum (gdbarch, RISCV_SP_REGNUM);
set_gdbarch_pc_regnum (gdbarch, RISCV_PC_REGNUM);
set_gdbarch_print_registers_info (gdbarch, riscv_print_registers_info);
+ set_tdesc_pseudo_register_name (gdbarch, riscv_pseudo_register_name);
+ set_tdesc_pseudo_register_type (gdbarch, riscv_pseudo_register_type);
+ set_tdesc_pseudo_register_reggroup_p (gdbarch,
+ riscv_pseudo_register_reggroup_p);
+ set_gdbarch_pseudo_register_read (gdbarch, riscv_pseudo_register_read);
+ set_gdbarch_pseudo_register_write (gdbarch, riscv_pseudo_register_write);
+
/* Finalise the target description registers. */
tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data),
riscv_tdesc_unknown_reg);
+ /* Calculate the number of pseudo registers we need. The fflags and frm
+ registers are sub-fields of the fcsr CSR register (csr3). However,
+ these registers can also be accessed directly as separate CSR
+ registers (fflags is csr1, and frm is csr2). And so, some targets
+ might choose to offer direct access to all three registers in the
+ target description, while other targets might choose to only offer
+ access to fcsr.
+
+ As we scan the target description we spot which of fcsr, fflags, and
+ frm are available. If fcsr is available but either of fflags and/or
+ frm are not available, then we add pseudo-registers to provide the
+ missing functionality.
+
+ This has to be done after the call to tdesc_use_registers as we don't
+ know the final register number until after that call, and the pseudo
+ register numbers need to be after the physical registers. */
+ int num_pseudo_regs = 0;
+ int next_pseudo_regnum = gdbarch_num_regs (gdbarch);
+
+ if (features.has_fflags_reg)
+ tdep->fflags_regnum = RISCV_CSR_FFLAGS_REGNUM;
+ else if (features.has_fcsr_reg)
+ {
+ tdep->fflags_regnum = next_pseudo_regnum;
+ pending_aliases.emplace_back ("csr1", (void *) &tdep->fflags_regnum);
+ next_pseudo_regnum++;
+ num_pseudo_regs++;
+ }
+
+ if (features.has_frm_reg)
+ tdep->frm_regnum = RISCV_CSR_FRM_REGNUM;
+ else if (features.has_fcsr_reg)
+ {
+ tdep->frm_regnum = next_pseudo_regnum;
+ pending_aliases.emplace_back ("csr2", (void *) &tdep->frm_regnum);
+ next_pseudo_regnum++;
+ num_pseudo_regs++;
+ }
+
+ set_gdbarch_num_pseudo_regs (gdbarch, num_pseudo_regs);
+
/* Override the register type callback setup by the target description
mechanism. This allows us to provide special type for floating point
registers. */
if (regnum == -1 || regnum == RISCV_ZERO_REGNUM)
regcache->raw_supply_zeroed (RISCV_ZERO_REGNUM);
- if (regnum == -1 || regnum == RISCV_CSR_FFLAGS_REGNUM
- || regnum == RISCV_CSR_FRM_REGNUM)
+ struct gdbarch *gdbarch = regcache->arch ();
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+
+ if (regnum == -1
+ || regnum == tdep->fflags_regnum
+ || regnum == tdep->frm_regnum)
{
int fcsr_regnum = RISCV_CSR_FCSR_REGNUM;
registers. */
if (regcache->get_register_status (fcsr_regnum) == REG_VALID)
{
+ /* If we have an fcsr register then we should have fflags and frm
+ too, either provided by the target, or provided as a pseudo
+ register by GDB. */
+ gdb_assert (tdep->fflags_regnum >= 0);
+ gdb_assert (tdep->frm_regnum >= 0);
+
ULONGEST fcsr_val;
regcache->raw_read (fcsr_regnum, &fcsr_val);
ULONGEST fflags_val = fcsr_val & 0x1f;
ULONGEST frm_val = (fcsr_val >> 5) & 0x7;
- /* And supply these if needed. */
- if (regnum == -1 || regnum == RISCV_CSR_FFLAGS_REGNUM)
- regcache->raw_supply_integer (RISCV_CSR_FFLAGS_REGNUM,
+ /* And supply these if needed. We can only supply real
+ registers, so don't try to supply fflags or frm if they are
+ implemented as pseudo-registers. */
+ if ((regnum == -1 || regnum == tdep->fflags_regnum)
+ && tdep->fflags_regnum < gdbarch_num_regs (gdbarch))
+ regcache->raw_supply_integer (tdep->fflags_regnum,
(gdb_byte *) &fflags_val,
sizeof (fflags_val),
/* is_signed */ false);
- if (regnum == -1 || regnum == RISCV_CSR_FRM_REGNUM)
- regcache->raw_supply_integer (RISCV_CSR_FRM_REGNUM,
+ if ((regnum == -1 || regnum == tdep->frm_regnum)
+ && tdep->frm_regnum < gdbarch_num_regs (gdbarch))
+ regcache->raw_supply_integer (tdep->frm_regnum,
(gdb_byte *)&frm_val,
sizeof (fflags_val),
/* is_signed */ false);
/* ISA-specific data types. */
struct type *riscv_fpreg_d_type = nullptr;
+ /* The location of these registers, set to -2 by default so we don't
+ match against -1 which is frequently used to mean "all registers",
+ e.g. in the regcache supply/collect code. */
+ int fflags_regnum = -2;
+ int frm_regnum = -2;
+
/* Use for tracking unknown CSRs in the target description.
UNKNOWN_CSRS_FIRST_REGNUM is the number assigned to the first unknown
CSR. All other unknown CSRs will be assigned sequential numbers after
exp_continue
}
- -re "^(fflags\|frm)\\s+<unavailable>\r\n" {
- # Currently, on some targets (e.g. RISC-V native Linux) the
- # fflags and frm registers show as being available, but are
- # unreadable, the result is these registers report
- # themselves as <unavailable>. So long as fcsr is readable
- # (which is checked below), then for now we accept this.
- set reg_name $expect_out(1,string)
- incr reg_counts($reg_name)
- exp_continue
- }
-
-re "^(frm)\\s+${frm_value}\\s+${frm_pattern}\r\n" {
set reg_name $expect_out(1,string)
incr reg_counts($reg_name)
with_test_prefix "set through fcsr" {
check_fcsr $fflags_value $frm_value $frm_string
}
+
+ # Reset fcsr register back to zero.
+ gdb_test_no_output "set \$fcsr = 0x0" \
+ "reset fcsr back to 0x0"
+ gdb_test "p/x \$fcsr" " = 0x0"
+
+ # Now set fcsr value through fflags and frm.
+ gdb_test_no_output "set \$fflags = ${fflags_value}"
+ gdb_test_no_output "set \$frm = ${frm_value}"
+
+ with_test_prefix "set through fflags and frm" {
+ check_fcsr $fflags_value $frm_value $frm_string
+ }
}
}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>riscv</architecture>
+ <feature name="org.gnu.gdb.riscv.cpu">
+ <reg name="zero" bitsize="32" type="int"/>
+ <reg name="ra" bitsize="32" type="code_ptr"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="gp" bitsize="32" type="data_ptr"/>
+ <reg name="tp" bitsize="32" type="data_ptr"/>
+ <reg name="t0" bitsize="32" type="int"/>
+ <reg name="t1" bitsize="32" type="int"/>
+ <reg name="t2" bitsize="32" type="int"/>
+ <reg name="fp" bitsize="32" type="data_ptr"/>
+ <reg name="s1" bitsize="32" type="int"/>
+ <reg name="a0" bitsize="32" type="int"/>
+ <reg name="a1" bitsize="32" type="int"/>
+ <reg name="a2" bitsize="32" type="int"/>
+ <reg name="a3" bitsize="32" type="int"/>
+ <reg name="a4" bitsize="32" type="int"/>
+ <reg name="a5" bitsize="32" type="int"/>
+ <reg name="a6" bitsize="32" type="int"/>
+ <reg name="a7" bitsize="32" type="int"/>
+ <reg name="s2" bitsize="32" type="int"/>
+ <reg name="s3" bitsize="32" type="int"/>
+ <reg name="s4" bitsize="32" type="int"/>
+ <reg name="s5" bitsize="32" type="int"/>
+ <reg name="s6" bitsize="32" type="int"/>
+ <reg name="s7" bitsize="32" type="int"/>
+ <reg name="s8" bitsize="32" type="int"/>
+ <reg name="s9" bitsize="32" type="int"/>
+ <reg name="s10" bitsize="32" type="int"/>
+ <reg name="s11" bitsize="32" type="int"/>
+ <reg name="t3" bitsize="32" type="int"/>
+ <reg name="t4" bitsize="32" type="int"/>
+ <reg name="t5" bitsize="32" type="int"/>
+ <reg name="t6" bitsize="32" type="int"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+ </feature>
+ <feature name="org.gnu.gdb.riscv.fpu">
+ <reg name="ft0" bitsize="32" type="float"/>
+ <reg name="ft1" bitsize="32" type="float"/>
+ <reg name="ft2" bitsize="32" type="float"/>
+ <reg name="ft3" bitsize="32" type="float"/>
+ <reg name="ft4" bitsize="32" type="float"/>
+ <reg name="ft5" bitsize="32" type="float"/>
+ <reg name="ft6" bitsize="32" type="float"/>
+ <reg name="ft7" bitsize="32" type="float"/>
+ <reg name="fs0" bitsize="32" type="float"/>
+ <reg name="fs1" bitsize="32" type="float"/>
+ <reg name="fa0" bitsize="32" type="float"/>
+ <reg name="fa1" bitsize="32" type="float"/>
+ <reg name="fa2" bitsize="32" type="float"/>
+ <reg name="fa3" bitsize="32" type="float"/>
+ <reg name="fa4" bitsize="32" type="float"/>
+ <reg name="fa5" bitsize="32" type="float"/>
+ <reg name="fa6" bitsize="32" type="float"/>
+ <reg name="fa7" bitsize="32" type="float"/>
+ <reg name="fs2" bitsize="32" type="float"/>
+ <reg name="fs3" bitsize="32" type="float"/>
+ <reg name="fs4" bitsize="32" type="float"/>
+ <reg name="fs5" bitsize="32" type="float"/>
+ <reg name="fs6" bitsize="32" type="float"/>
+ <reg name="fs7" bitsize="32" type="float"/>
+ <reg name="fs8" bitsize="32" type="float"/>
+ <reg name="fs9" bitsize="32" type="float"/>
+ <reg name="fs10" bitsize="32" type="float"/>
+ <reg name="fs11" bitsize="32" type="float"/>
+ <reg name="ft8" bitsize="32" type="float"/>
+ <reg name="ft9" bitsize="32" type="float"/>
+ <reg name="ft10" bitsize="32" type="float"/>
+ <reg name="ft11" bitsize="32" type="float"/>
+ <reg name="fcsr" bitsize="32" type="int"/>
+ </feature>
+</target>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>riscv</architecture>
+ <feature name="org.gnu.gdb.riscv.cpu">
+ <reg name="zero" bitsize="64" type="int"/>
+ <reg name="ra" bitsize="64" type="code_ptr"/>
+ <reg name="sp" bitsize="64" type="data_ptr"/>
+ <reg name="gp" bitsize="64" type="data_ptr"/>
+ <reg name="tp" bitsize="64" type="data_ptr"/>
+ <reg name="t0" bitsize="64" type="int"/>
+ <reg name="t1" bitsize="64" type="int"/>
+ <reg name="t2" bitsize="64" type="int"/>
+ <reg name="fp" bitsize="64" type="data_ptr"/>
+ <reg name="s1" bitsize="64" type="int"/>
+ <reg name="a0" bitsize="64" type="int"/>
+ <reg name="a1" bitsize="64" type="int"/>
+ <reg name="a2" bitsize="64" type="int"/>
+ <reg name="a3" bitsize="64" type="int"/>
+ <reg name="a4" bitsize="64" type="int"/>
+ <reg name="a5" bitsize="64" type="int"/>
+ <reg name="a6" bitsize="64" type="int"/>
+ <reg name="a7" bitsize="64" type="int"/>
+ <reg name="s2" bitsize="64" type="int"/>
+ <reg name="s3" bitsize="64" type="int"/>
+ <reg name="s4" bitsize="64" type="int"/>
+ <reg name="s5" bitsize="64" type="int"/>
+ <reg name="s6" bitsize="64" type="int"/>
+ <reg name="s7" bitsize="64" type="int"/>
+ <reg name="s8" bitsize="64" type="int"/>
+ <reg name="s9" bitsize="64" type="int"/>
+ <reg name="s10" bitsize="64" type="int"/>
+ <reg name="s11" bitsize="64" type="int"/>
+ <reg name="t3" bitsize="64" type="int"/>
+ <reg name="t4" bitsize="64" type="int"/>
+ <reg name="t5" bitsize="64" type="int"/>
+ <reg name="t6" bitsize="64" type="int"/>
+ <reg name="pc" bitsize="64" type="code_ptr"/>
+ </feature>
+ <feature name="org.gnu.gdb.riscv.fpu">
+ <union id="riscv_double">
+ <field name="float" type="ieee_single"/>
+ <field name="double" type="ieee_double"/>
+ </union>
+ <reg name="ft0" bitsize="64" type="riscv_double"/>
+ <reg name="ft1" bitsize="64" type="riscv_double"/>
+ <reg name="ft2" bitsize="64" type="riscv_double"/>
+ <reg name="ft3" bitsize="64" type="riscv_double"/>
+ <reg name="ft4" bitsize="64" type="riscv_double"/>
+ <reg name="ft5" bitsize="64" type="riscv_double"/>
+ <reg name="ft6" bitsize="64" type="riscv_double"/>
+ <reg name="ft7" bitsize="64" type="riscv_double"/>
+ <reg name="fs0" bitsize="64" type="riscv_double"/>
+ <reg name="fs1" bitsize="64" type="riscv_double"/>
+ <reg name="fa0" bitsize="64" type="riscv_double"/>
+ <reg name="fa1" bitsize="64" type="riscv_double"/>
+ <reg name="fa2" bitsize="64" type="riscv_double"/>
+ <reg name="fa3" bitsize="64" type="riscv_double"/>
+ <reg name="fa4" bitsize="64" type="riscv_double"/>
+ <reg name="fa5" bitsize="64" type="riscv_double"/>
+ <reg name="fa6" bitsize="64" type="riscv_double"/>
+ <reg name="fa7" bitsize="64" type="riscv_double"/>
+ <reg name="fs2" bitsize="64" type="riscv_double"/>
+ <reg name="fs3" bitsize="64" type="riscv_double"/>
+ <reg name="fs4" bitsize="64" type="riscv_double"/>
+ <reg name="fs5" bitsize="64" type="riscv_double"/>
+ <reg name="fs6" bitsize="64" type="riscv_double"/>
+ <reg name="fs7" bitsize="64" type="riscv_double"/>
+ <reg name="fs8" bitsize="64" type="riscv_double"/>
+ <reg name="fs9" bitsize="64" type="riscv_double"/>
+ <reg name="fs10" bitsize="64" type="riscv_double"/>
+ <reg name="fs11" bitsize="64" type="riscv_double"/>
+ <reg name="ft8" bitsize="64" type="riscv_double"/>
+ <reg name="ft9" bitsize="64" type="riscv_double"/>
+ <reg name="ft10" bitsize="64" type="riscv_double"/>
+ <reg name="ft11" bitsize="64" type="riscv_double"/>
+ <reg name="fcsr" bitsize="32" type="int"/>
+ </feature>
+</target>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>riscv</architecture>
+ <feature name="org.gnu.gdb.riscv.cpu">
+ <reg name="zero" bitsize="32" type="int"/>
+ <reg name="ra" bitsize="32" type="code_ptr"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="gp" bitsize="32" type="data_ptr"/>
+ <reg name="tp" bitsize="32" type="data_ptr"/>
+ <reg name="t0" bitsize="32" type="int"/>
+ <reg name="t1" bitsize="32" type="int"/>
+ <reg name="t2" bitsize="32" type="int"/>
+ <reg name="fp" bitsize="32" type="data_ptr"/>
+ <reg name="s1" bitsize="32" type="int"/>
+ <reg name="a0" bitsize="32" type="int"/>
+ <reg name="a1" bitsize="32" type="int"/>
+ <reg name="a2" bitsize="32" type="int"/>
+ <reg name="a3" bitsize="32" type="int"/>
+ <reg name="a4" bitsize="32" type="int"/>
+ <reg name="a5" bitsize="32" type="int"/>
+ <reg name="a6" bitsize="32" type="int"/>
+ <reg name="a7" bitsize="32" type="int"/>
+ <reg name="s2" bitsize="32" type="int"/>
+ <reg name="s3" bitsize="32" type="int"/>
+ <reg name="s4" bitsize="32" type="int"/>
+ <reg name="s5" bitsize="32" type="int"/>
+ <reg name="s6" bitsize="32" type="int"/>
+ <reg name="s7" bitsize="32" type="int"/>
+ <reg name="s8" bitsize="32" type="int"/>
+ <reg name="s9" bitsize="32" type="int"/>
+ <reg name="s10" bitsize="32" type="int"/>
+ <reg name="s11" bitsize="32" type="int"/>
+ <reg name="t3" bitsize="32" type="int"/>
+ <reg name="t4" bitsize="32" type="int"/>
+ <reg name="t5" bitsize="32" type="int"/>
+ <reg name="t6" bitsize="32" type="int"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+ </feature>
+ <feature name="org.gnu.gdb.riscv.fpu">
+ <reg name="ft0" bitsize="32" type="float"/>
+ <reg name="ft1" bitsize="32" type="float"/>
+ <reg name="ft2" bitsize="32" type="float"/>
+ <reg name="ft3" bitsize="32" type="float"/>
+ <reg name="ft4" bitsize="32" type="float"/>
+ <reg name="ft5" bitsize="32" type="float"/>
+ <reg name="ft6" bitsize="32" type="float"/>
+ <reg name="ft7" bitsize="32" type="float"/>
+ <reg name="fs0" bitsize="32" type="float"/>
+ <reg name="fs1" bitsize="32" type="float"/>
+ <reg name="fa0" bitsize="32" type="float"/>
+ <reg name="fa1" bitsize="32" type="float"/>
+ <reg name="fa2" bitsize="32" type="float"/>
+ <reg name="fa3" bitsize="32" type="float"/>
+ <reg name="fa4" bitsize="32" type="float"/>
+ <reg name="fa5" bitsize="32" type="float"/>
+ <reg name="fa6" bitsize="32" type="float"/>
+ <reg name="fa7" bitsize="32" type="float"/>
+ <reg name="fs2" bitsize="32" type="float"/>
+ <reg name="fs3" bitsize="32" type="float"/>
+ <reg name="fs4" bitsize="32" type="float"/>
+ <reg name="fs5" bitsize="32" type="float"/>
+ <reg name="fs6" bitsize="32" type="float"/>
+ <reg name="fs7" bitsize="32" type="float"/>
+ <reg name="fs8" bitsize="32" type="float"/>
+ <reg name="fs9" bitsize="32" type="float"/>
+ <reg name="fs10" bitsize="32" type="float"/>
+ <reg name="fs11" bitsize="32" type="float"/>
+ <reg name="ft8" bitsize="32" type="float"/>
+ <reg name="ft9" bitsize="32" type="float"/>
+ <reg name="ft10" bitsize="32" type="float"/>
+ <reg name="ft11" bitsize="32" type="float"/>
+ </feature>
+ <feature name="org.gnu.gdb.riscv.csr">
+ <reg name="fcsr" bitsize="32" type="int"/>
+ </feature>
+</target>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>riscv</architecture>
+ <feature name="org.gnu.gdb.riscv.cpu">
+ <reg name="zero" bitsize="32" type="int"/>
+ <reg name="ra" bitsize="32" type="code_ptr"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="gp" bitsize="32" type="data_ptr"/>
+ <reg name="tp" bitsize="32" type="data_ptr"/>
+ <reg name="t0" bitsize="32" type="int"/>
+ <reg name="t1" bitsize="32" type="int"/>
+ <reg name="t2" bitsize="32" type="int"/>
+ <reg name="fp" bitsize="32" type="data_ptr"/>
+ <reg name="s1" bitsize="32" type="int"/>
+ <reg name="a0" bitsize="32" type="int"/>
+ <reg name="a1" bitsize="32" type="int"/>
+ <reg name="a2" bitsize="32" type="int"/>
+ <reg name="a3" bitsize="32" type="int"/>
+ <reg name="a4" bitsize="32" type="int"/>
+ <reg name="a5" bitsize="32" type="int"/>
+ <reg name="a6" bitsize="32" type="int"/>
+ <reg name="a7" bitsize="32" type="int"/>
+ <reg name="s2" bitsize="32" type="int"/>
+ <reg name="s3" bitsize="32" type="int"/>
+ <reg name="s4" bitsize="32" type="int"/>
+ <reg name="s5" bitsize="32" type="int"/>
+ <reg name="s6" bitsize="32" type="int"/>
+ <reg name="s7" bitsize="32" type="int"/>
+ <reg name="s8" bitsize="32" type="int"/>
+ <reg name="s9" bitsize="32" type="int"/>
+ <reg name="s10" bitsize="32" type="int"/>
+ <reg name="s11" bitsize="32" type="int"/>
+ <reg name="t3" bitsize="32" type="int"/>
+ <reg name="t4" bitsize="32" type="int"/>
+ <reg name="t5" bitsize="32" type="int"/>
+ <reg name="t6" bitsize="32" type="int"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+ </feature>
+ <feature name="org.gnu.gdb.riscv.fpu">
+ <reg name="ft0" bitsize="32" type="float"/>
+ <reg name="ft1" bitsize="32" type="float"/>
+ <reg name="ft2" bitsize="32" type="float"/>
+ <reg name="ft3" bitsize="32" type="float"/>
+ <reg name="ft4" bitsize="32" type="float"/>
+ <reg name="ft5" bitsize="32" type="float"/>
+ <reg name="ft6" bitsize="32" type="float"/>
+ <reg name="ft7" bitsize="32" type="float"/>
+ <reg name="fs0" bitsize="32" type="float"/>
+ <reg name="fs1" bitsize="32" type="float"/>
+ <reg name="fa0" bitsize="32" type="float"/>
+ <reg name="fa1" bitsize="32" type="float"/>
+ <reg name="fa2" bitsize="32" type="float"/>
+ <reg name="fa3" bitsize="32" type="float"/>
+ <reg name="fa4" bitsize="32" type="float"/>
+ <reg name="fa5" bitsize="32" type="float"/>
+ <reg name="fa6" bitsize="32" type="float"/>
+ <reg name="fa7" bitsize="32" type="float"/>
+ <reg name="fs2" bitsize="32" type="float"/>
+ <reg name="fs3" bitsize="32" type="float"/>
+ <reg name="fs4" bitsize="32" type="float"/>
+ <reg name="fs5" bitsize="32" type="float"/>
+ <reg name="fs6" bitsize="32" type="float"/>
+ <reg name="fs7" bitsize="32" type="float"/>
+ <reg name="fs8" bitsize="32" type="float"/>
+ <reg name="fs9" bitsize="32" type="float"/>
+ <reg name="fs10" bitsize="32" type="float"/>
+ <reg name="fs11" bitsize="32" type="float"/>
+ <reg name="ft8" bitsize="32" type="float"/>
+ <reg name="ft9" bitsize="32" type="float"/>
+ <reg name="ft10" bitsize="32" type="float"/>
+ <reg name="ft11" bitsize="32" type="float"/>
+ <reg name="fcsr" bitsize="32" type="int"/>
+ </feature>
+</target>
}
array unset reg_counts
}
+
+# Next load a target description that contains fcsr, but not fflags or
+# frm. Then check that GDB provides an fflags and frm registers using
+# the pseudo-register mechanism.
+if { $xlen == 4 } {
+ set xml_tdesc "riscv-tdesc-fcsr-32.xml"
+} else {
+ set xml_tdesc "riscv-tdesc-fcsr-64.xml"
+}
+set xml_tdesc "${srcdir}/${subdir}/${xml_tdesc}"
+
+# Maybe copy the target over if we're remote testing.
+if {[is_remote host]} {
+ set remote_file [remote_download host $xml_tdesc]
+} else {
+ set remote_file $xml_tdesc
+}
+
+gdb_test_no_output "set tdesc filename $remote_file" \
+ "load the target description that lacks fflags and frm"
+
+foreach reg {fflags frm} {
+ gdb_test_multiple "info registers $reg" "" {
+ -re "^info registers $reg\r\n" {
+ exp_continue
+ }
+
+ -wrap -re "^Invalid register `$reg`" {
+ fail $gdb_test_name
+ }
+
+ -wrap -re "^$reg\\s+\[^\r\n\]+" {
+ pass $gdb_test_name
+ }
+ }
+}
} elseif [istarget "sparc*-*-*"] then {
gdb_test "info float" "f0.*f1.*f31.*d0.*d30.*" "info float"
} elseif [istarget "riscv*-*-*"] then {
- # RISC-V may or may not have an FPU
+ # RISC-V may or may not have an FPU. Additionally, the order of
+ # fcsr relative to fflags and frm can change depending on whether
+ # the fflags and frm registers are implemented as real registers
+ # (supplied in the target description) or pseudo-registers
+ # (supplied by GDB as a view into fcsr).
gdb_test_multiple "info float" "info float" {
-re "ft0.*ft1.*ft11.*fflags.*frm.*fcsr.*$gdb_prompt $" {
pass "info float (with FPU)"
}
+ -re "ft0.*ft1.*ft11.*fcsr.*fflags.*frm.*$gdb_prompt $" {
+ pass "info float (with FPU)"
+ }
-re "No floating.point info available for this processor.*$gdb_prompt $" {
pass "info float (without FPU)"
}