summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2022-11-17 16:09:00 -0800
committerJohn Baldwin <jhb@FreeBSD.org>2022-11-17 16:09:00 -0800
commitf7eb074720f52900ef0718e2f3ca3b6ecdcfea52 (patch)
treea8ce3a8f1a04018af4c660d58a875b4727b64248
parent92dec34206195149627768faaf5100351ecd0bbb (diff)
downloadbinutils-gdb-users/jhb/cheri-gdb-12-branch.tar.gz
RISC-V: Handle capability return values.users/jhb/cheri-gdb-12-branch
Add simple handling of capability function arguments (also used for return values) and update riscv_return_value to propagate tags.
-rw-r--r--gdb/riscv-tdep.c65
1 files changed, 62 insertions, 3 deletions
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 404cd72bb1c..701cfa9bc7e 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -2411,6 +2411,7 @@ struct riscv_call_info
{
xlen = riscv_abi_xlen (gdbarch);
flen = riscv_abi_flen (gdbarch);
+ clen = riscv_abi_clen (gdbarch);
/* Reduce the number of integer argument registers when using the
embedded abi (i.e. rv32e). */
@@ -2438,6 +2439,7 @@ struct riscv_call_info
are just the results of calling RISCV_ABI_XLEN and RISCV_ABI_FLEN. */
int xlen;
int flen;
+ int clen;
};
/* Return the number of registers available for use as parameters in the
@@ -2464,12 +2466,12 @@ riscv_arg_regs_available (struct riscv_arg_reg *reg)
static bool
riscv_assign_reg_location (struct riscv_arg_info::location *loc,
struct riscv_arg_reg *reg,
- int length, int offset)
+ int length, int offset, int base_regnum = 0)
{
if (reg->next_regnum <= reg->last_regnum)
{
loc->loc_type = riscv_arg_info::location::in_reg;
- loc->loc_data.regno = reg->next_regnum;
+ loc->loc_data.regno = reg->next_regnum + base_regnum;
reg->next_regnum++;
loc->c_length = length;
loc->c_offset = offset;
@@ -2571,6 +2573,44 @@ riscv_call_arg_scalar_int (struct riscv_arg_info *ainfo,
}
}
+static void
+riscv_call_arg_scalar_capabiliy (struct riscv_arg_info *ainfo,
+ struct riscv_call_info *cinfo)
+{
+ if (ainfo->length > cinfo->clen)
+ {
+ /* Argument is going to be passed by reference. */
+ ainfo->argloc[0].loc_type
+ = riscv_arg_info::location::by_ref;
+ cinfo->memory.ref_offset
+ = align_up (cinfo->memory.ref_offset, ainfo->align);
+ ainfo->argloc[0].loc_data.offset = cinfo->memory.ref_offset;
+ cinfo->memory.ref_offset += ainfo->length;
+ ainfo->argloc[0].c_length = ainfo->length;
+
+ /* The second location for this argument is given over to holding the
+ address of the by-reference data. Pass 0 for the offset as this
+ is not part of the actual argument value. */
+ if (!riscv_assign_reg_location (&ainfo->argloc[1],
+ &cinfo->int_regs,
+ cinfo->clen, 0,
+ RISCV_CA0_REGNUM - RISCV_A0_REGNUM))
+ riscv_assign_stack_location (&ainfo->argloc[1],
+ &cinfo->memory, cinfo->clen,
+ cinfo->clen);
+ }
+ else
+ {
+ int align = std::max (ainfo->align, cinfo->clen);
+
+ if (!riscv_assign_reg_location(&ainfo->argloc[0],
+ &cinfo->int_regs, cinfo->clen, 0,
+ RISCV_CA0_REGNUM - RISCV_A0_REGNUM))
+ riscv_assign_stack_location (&ainfo->argloc[1],
+ &cinfo->memory, cinfo->clen, align);
+ }
+}
+
/* Like RISCV_CALL_ARG_SCALAR_INT, except the argument described by AINFO
is being passed with the floating point ABI. */
@@ -2908,12 +2948,21 @@ riscv_arg_location (struct gdbarch *gdbarch,
switch (ainfo->type->code ())
{
+ case TYPE_CODE_CAPABILITY:
+ riscv_call_arg_scalar_capabiliy (ainfo, cinfo);
+ break;
+ case TYPE_CODE_PTR:
+ if (ainfo->length == cinfo->clen)
+ {
+ riscv_call_arg_scalar_capabiliy (ainfo, cinfo);
+ break;
+ }
+ /* Fall through */
case TYPE_CODE_INT:
case TYPE_CODE_BOOL:
case TYPE_CODE_CHAR:
case TYPE_CODE_RANGE:
case TYPE_CODE_ENUM:
- case TYPE_CODE_PTR:
case TYPE_CODE_FIXED_POINT:
if (ainfo->length <= cinfo->xlen)
{
@@ -3340,6 +3389,9 @@ riscv_return_value (struct gdbarch *gdbarch,
regcache->cooked_read_part (regnum, 0,
info.argloc[0].c_length,
ptr);
+ if (value_tagged (value))
+ set_value_tag (value,
+ regcache->raw_collect_tag (regnum));
}
if (writebuf)
@@ -3348,6 +3400,13 @@ riscv_return_value (struct gdbarch *gdbarch,
riscv_regcache_cooked_write (regnum, ptr,
info.argloc[0].c_length,
regcache, call_info.flen);
+ if (register_has_tag (gdbarch, regnum))
+ {
+ if (value_tagged (value))
+ regcache->raw_supply_tag (regnum, value_tag (value));
+ else
+ regcache->raw_supply_tag (regnum, false);
+ }
}
/* A return value in register can have a second part in a