gdb/riscv: Apply NaN boxing when writing return values into registers

When setting up function parameters we already perform NaN boxing, as
required by the RISC-V ABI, however, we don't do this when writing
values into registers as part of setting up a return value.

This commit moves the NaN boxing code into a small helper function,
and then makes use of this function when setting up function
parameters, and also when setting up return values.

This should resolve this failure:

  FAIL: gdb.base/return-nodebug.exp: float: full width of the returned result

gdb/ChangeLog:

	PR gdb/25489
	* riscv-tdep.c (riscv_arg_info::c_offset): Update comment.
	(riscv_regcache_cooked_write): New function.
	(riscv_push_dummy_call): Use new function.
	(riscv_return_value): Likewise.
This commit is contained in:
Andrew Burgess 2020-03-13 15:50:28 +00:00
parent cf2611febc
commit dd8953924b
2 changed files with 48 additions and 29 deletions

View File

@ -1,3 +1,11 @@
2020-03-25 Andrew Burgess <andrew.burgess@embecosm.com>
PR gdb/25534
* riscv-tdep.c (riscv_arg_info::c_offset): Update comment.
(riscv_regcache_cooked_write): New function.
(riscv_push_dummy_call): Use new function.
(riscv_return_value): Likewise.
2020-03-24 Simon Marchi <simon.marchi@polymtl.ca>
* fbsd-nat.c (fbsd_nat_target::follow_fork): Change bool to int.

View File

@ -1727,8 +1727,9 @@ struct riscv_arg_info
will go. */
int c_length;
/* The offset within CONTENTS for this part of the argument. Will
always be 0 for the first part. For the second part of the
/* The offset within CONTENTS for this part of the argument. This can
be non-zero even for the first part (the first field of a struct can
have a non-zero offset due to padding). For the second part of the
argument, this might be the C_LENGTH value of the first part,
however, if we are passing a structure in two registers, and there's
is padding between the first and second field, then this offset
@ -2417,6 +2418,26 @@ riscv_print_arg_location (ui_file *stream, struct gdbarch *gdbarch,
}
}
/* Wrapper around REGCACHE->cooked_write. Places the LEN bytes of DATA
into a buffer that is at least as big as the register REGNUM, padding
out the DATA with either 0x00, or 0xff. For floating point registers
0xff is used, for everyone else 0x00 is used. */
static void
riscv_regcache_cooked_write (int regnum, const gdb_byte *data, int len,
struct regcache *regcache, int flen)
{
gdb_byte tmp [sizeof (ULONGEST)];
/* FP values in FP registers must be NaN-boxed. */
if (riscv_is_fp_regno_p (regnum) && len < flen)
memset (tmp, -1, sizeof (tmp));
else
memset (tmp, 0, sizeof (tmp));
memcpy (tmp, data, len);
regcache->cooked_write (regnum, tmp);
}
/* Implement the push dummy call gdbarch callback. */
static CORE_ADDR
@ -2526,18 +2547,13 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
{
case riscv_arg_info::location::in_reg:
{
gdb_byte tmp [sizeof (ULONGEST)];
gdb_assert (info->argloc[0].c_length <= info->length);
/* FP values in FP registers must be NaN-boxed. */
if (riscv_is_fp_regno_p (info->argloc[0].loc_data.regno)
&& info->argloc[0].c_length < call_info.flen)
memset (tmp, -1, sizeof (tmp));
else
memset (tmp, 0, sizeof (tmp));
memcpy (tmp, (info->contents + info->argloc[0].c_offset),
info->argloc[0].c_length);
regcache->cooked_write (info->argloc[0].loc_data.regno, tmp);
riscv_regcache_cooked_write (info->argloc[0].loc_data.regno,
(info->contents
+ info->argloc[0].c_offset),
info->argloc[0].c_length,
regcache, call_info.flen);
second_arg_length =
(((info->argloc[0].c_length + info->argloc[0].c_offset) < info->length)
? info->argloc[1].c_length : 0);
@ -2569,19 +2585,13 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
{
case riscv_arg_info::location::in_reg:
{
gdb_byte tmp [sizeof (ULONGEST)];
gdb_assert ((riscv_is_fp_regno_p (info->argloc[1].loc_data.regno)
&& second_arg_length <= call_info.flen)
|| second_arg_length <= call_info.xlen);
/* FP values in FP registers must be NaN-boxed. */
if (riscv_is_fp_regno_p (info->argloc[1].loc_data.regno)
&& second_arg_length < call_info.flen)
memset (tmp, -1, sizeof (tmp));
else
memset (tmp, 0, sizeof (tmp));
memcpy (tmp, second_arg_data, second_arg_length);
regcache->cooked_write (info->argloc[1].loc_data.regno, tmp);
riscv_regcache_cooked_write (info->argloc[1].loc_data.regno,
second_arg_data,
second_arg_length,
regcache, call_info.flen);
}
break;
@ -2701,9 +2711,9 @@ riscv_return_value (struct gdbarch *gdbarch,
if (writebuf)
{
const gdb_byte *ptr = writebuf + info.argloc[0].c_offset;
regcache->cooked_write_part (regnum, 0,
riscv_regcache_cooked_write (regnum, ptr,
info.argloc[0].c_length,
ptr);
regcache, call_info.flen);
}
/* A return value in register can have a second part in a
@ -2730,10 +2740,11 @@ riscv_return_value (struct gdbarch *gdbarch,
if (writebuf)
{
writebuf += info.argloc[1].c_offset;
regcache->cooked_write_part (regnum, 0,
info.argloc[1].c_length,
writebuf);
const gdb_byte *ptr
= writebuf + info.argloc[1].c_offset;
riscv_regcache_cooked_write
(regnum, ptr, info.argloc[1].c_length,
regcache, call_info.flen);
}
break;