aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorTiezhu Yang <yangtiezhu@loongson.cn>2022-07-25 20:42:59 +0800
committerTiezhu Yang <yangtiezhu@loongson.cn>2022-07-26 22:17:06 +0800
commitecbff28a4457d0ebe11023fa9671d62251e7463d (patch)
tree282d7982fec7aaf8dc8b851f7838e39646e3d9a2 /gdb
parent4a75d7c5384a2c1cbaa0f341d844713d9ae475d6 (diff)
downloadgdb-ecbff28a4457d0ebe11023fa9671d62251e7463d.zip
gdb-ecbff28a4457d0ebe11023fa9671d62251e7463d.tar.gz
gdb-ecbff28a4457d0ebe11023fa9671d62251e7463d.tar.bz2
gdb: LoongArch: Handle the function return value
According to LoongArch ELF ABI specification [1], handle the function return value of various types. [1] https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_return_values Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Diffstat (limited to 'gdb')
-rw-r--r--gdb/loongarch-tdep.c233
1 files changed, 219 insertions, 14 deletions
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index 3fb7774..85a1dd7 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -1074,6 +1074,19 @@ loongarch_push_dummy_call (struct gdbarch *gdbarch,
return sp;
}
+/* Partial transfer of a cooked register. */
+
+static void
+loongarch_xfer_reg (struct regcache *regcache,
+ int regnum, int len, gdb_byte *readbuf,
+ const gdb_byte *writebuf, size_t offset)
+{
+ if (readbuf)
+ regcache->cooked_read_part (regnum, 0, len, readbuf + offset);
+ if (writebuf)
+ regcache->cooked_write_part (regnum, 0, len, writebuf + offset);
+}
+
/* Implement the return_value gdbarch method. */
static enum return_value_convention
@@ -1081,24 +1094,216 @@ loongarch_return_value (struct gdbarch *gdbarch, struct value *function,
struct type *type, struct regcache *regcache,
gdb_byte *readbuf, const gdb_byte *writebuf)
{
- int len = TYPE_LENGTH (type);
- int regnum = -1;
+ int regsize = register_size (gdbarch, 0);
+ enum type_code code = type->code ();
+ size_t len = TYPE_LENGTH (type);
+ unsigned int fixed_point_members;
+ unsigned int floating_point_members;
+ bool first_member_is_fixed_point;
+ int a0 = LOONGARCH_A0_REGNUM;
+ int a1 = LOONGARCH_A0_REGNUM + 1;
+ int f0 = LOONGARCH_FIRST_FP_REGNUM;
+ int f1 = LOONGARCH_FIRST_FP_REGNUM + 1;
- /* See if our value is returned through a register. If it is, then
- store the associated register number in REGNUM. */
- switch (type->code ())
+ if (len > 2 * regsize)
+ return RETURN_VALUE_STRUCT_CONVENTION;
+
+ switch (code)
{
- case TYPE_CODE_INT:
- regnum = LOONGARCH_A0_REGNUM;
- break;
+ case TYPE_CODE_INT:
+ case TYPE_CODE_BOOL:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_PTR:
+ {
+ /* integer or pointer type.
+ The return value is passed in a0,
+ the unsigned integer scalars are zero-extended to GRLEN bits,
+ and the signed integer scalars are sign-extended. */
+ if (writebuf)
+ {
+ gdb_byte buf[regsize];
+ if (type->is_unsigned ())
+ {
+ ULONGEST data = extract_unsigned_integer (writebuf, len, BFD_ENDIAN_LITTLE);
+ store_unsigned_integer (buf, regsize, BFD_ENDIAN_LITTLE, data);
+ }
+ else
+ {
+ LONGEST data = extract_signed_integer (writebuf, len, BFD_ENDIAN_LITTLE);
+ store_signed_integer (buf, regsize, BFD_ENDIAN_LITTLE, data);
+ }
+ loongarch_xfer_reg (regcache, a0, regsize, nullptr, buf, 0);
+ }
+ else
+ loongarch_xfer_reg (regcache, a0, len, readbuf, nullptr, 0);
+ }
+ break;
+ case TYPE_CODE_FLT:
+ /* long double type.
+ The return value is passed in a0 and a1. */
+ if (len == 2 * regsize)
+ {
+ loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a1, len - regsize, readbuf, writebuf, regsize);
+ }
+ /* float or double type.
+ The return value is passed in f0. */
+ else
+ {
+ loongarch_xfer_reg (regcache, f0, len, readbuf, writebuf, 0);
+ }
+ break;
+ case TYPE_CODE_STRUCT:
+ {
+ fixed_point_members = 0;
+ floating_point_members = 0;
+ first_member_is_fixed_point = false;
+ compute_struct_member (type,
+ &fixed_point_members,
+ &floating_point_members,
+ &first_member_is_fixed_point);
+
+ if (len > 0 && len <= regsize)
+ {
+ /* The structure has only fixed-point members. */
+ if (fixed_point_members > 0 && floating_point_members == 0)
+ {
+ /* The return value is passed in a0. */
+ loongarch_xfer_reg (regcache, a0, len, readbuf, writebuf, 0);
+ }
+ /* The structure has only floating-point members. */
+ else if (fixed_point_members == 0 && floating_point_members > 0)
+ {
+ /* The structure has one floating-point member.
+ The return value is passed in f0. */
+ if (floating_point_members == 1)
+ {
+ loongarch_xfer_reg (regcache, f0, len, readbuf, writebuf, 0);
+ }
+ /* The structure has two floating-point members.
+ The return value is passed in f0 and f1. */
+ else if (floating_point_members == 2)
+ {
+ loongarch_xfer_reg (regcache, f0, len / 2, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, f1, len / 2, readbuf, writebuf, len / 2);
+ }
+ }
+ /* The structure has both fixed-point and floating-point members. */
+ else if (fixed_point_members > 0 && floating_point_members > 0)
+ {
+ /* The structure has one float member and multiple fixed-point members.
+ The return value is passed in a0. */
+ if (floating_point_members == 1 && fixed_point_members > 1)
+ {
+ loongarch_xfer_reg (regcache, a0, len, readbuf, writebuf, 0);
+ }
+ /* The structure has one float member and one fixed-point member. */
+ else if (floating_point_members == 1 && fixed_point_members == 1)
+ {
+ /* The return value is passed in f0 and a0 if the first member is floating-point. */
+ if (first_member_is_fixed_point == false)
+ {
+ loongarch_xfer_reg (regcache, f0, regsize / 2, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a0, regsize / 2, readbuf, writebuf, regsize / 2);
+ }
+ /* The return value is passed in a0 and f0 if the first member is fixed-point. */
+ else
+ {
+ loongarch_xfer_reg (regcache, a0, regsize / 2, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, f0, regsize / 2, readbuf, writebuf, regsize / 2);
+ }
+ }
+ }
+ }
+ else if (len > regsize && len <= 2 * regsize)
+ {
+ /* The structure has only fixed-point members. */
+ if (fixed_point_members > 0 && floating_point_members == 0)
+ {
+ /* The return value is passed in a0 and a1. */
+ loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a1, len - regsize, readbuf, writebuf, regsize);
+ }
+ /* The structure has only floating-point members. */
+ else if (fixed_point_members == 0 && floating_point_members > 0)
+ {
+ /* The structure has one long double member
+ or one double member and two adjacent float members
+ or 3-4 float members.
+ The return value is passed in a0 and a1. */
+ if ((len == 16 && floating_point_members == 1)
+ || (len == 16 && floating_point_members == 3)
+ || (len == 12 && floating_point_members == 3)
+ || (len == 16 && floating_point_members == 4))
+ {
+ loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a1, len - regsize, readbuf, writebuf, regsize);
+ }
+ /* The structure has two double members
+ or one double member and one float member.
+ The return value is passed in f0 and f1. */
+ else if ((len == 16 && floating_point_members == 2)
+ || (len == 12 && floating_point_members == 2))
+ {
+ loongarch_xfer_reg (regcache, f0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, f1, len - regsize, readbuf, writebuf, regsize);
+ }
+ }
+ /* The structure has both fixed-point and floating-point members. */
+ else if (fixed_point_members > 0 && floating_point_members > 0)
+ {
+ /* The structure has one floating-point member and one fixed-point member. */
+ if (floating_point_members == 1 && fixed_point_members == 1)
+ {
+ /* The return value is passed in f0 and a0 if the first member is floating-point. */
+ if (first_member_is_fixed_point == false)
+ {
+ loongarch_xfer_reg (regcache, f0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a0, len - regsize, readbuf, writebuf, regsize);
+ }
+ /* The return value is passed in a0 and f0 if the first member is fixed-point. */
+ else
+ {
+ loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, f0, len - regsize, readbuf, writebuf, regsize);
+ }
+ }
+ else
+ {
+ /* The return value is passed in a0 and a1. */
+ loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a1, len - regsize, readbuf, writebuf, regsize);
+ }
+ }
+ }
+ }
+ break;
+ case TYPE_CODE_UNION:
+ if (len > 0 && len <= regsize)
+ {
+ /* The return value is passed in a0. */
+ loongarch_xfer_reg (regcache, a0, len, readbuf, writebuf, 0);
+ }
+ else if (len > regsize && len <= 2 * regsize)
+ {
+ /* The return value is passed in a0 and a1. */
+ loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, a1, len - regsize, readbuf, writebuf, regsize);
+ }
+ break;
+ case TYPE_CODE_COMPLEX:
+ {
+ /* The return value is passed in f0 and f1. */
+ loongarch_xfer_reg (regcache, f0, len / 2, readbuf, writebuf, 0);
+ loongarch_xfer_reg (regcache, f1, len / 2, readbuf, writebuf, len / 2);
+ }
+ break;
+ default:
+ break;
}
- /* Extract the return value from the register where it was stored. */
- if (readbuf != nullptr)
- regcache->raw_read_part (regnum, 0, len, readbuf);
- if (writebuf != nullptr)
- regcache->raw_write_part (regnum, 0, len, writebuf);
-
return RETURN_VALUE_REGISTER_CONVENTION;
}