diff options
author | Tiezhu Yang <yangtiezhu@loongson.cn> | 2022-07-25 20:42:59 +0800 |
---|---|---|
committer | Tiezhu Yang <yangtiezhu@loongson.cn> | 2022-07-26 22:17:06 +0800 |
commit | ecbff28a4457d0ebe11023fa9671d62251e7463d (patch) | |
tree | 282d7982fec7aaf8dc8b851f7838e39646e3d9a2 /gdb | |
parent | 4a75d7c5384a2c1cbaa0f341d844713d9ae475d6 (diff) | |
download | gdb-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.c | 233 |
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; } |