From: Tiezhu Yang Date: Tue, 12 Jul 2022 02:33:28 +0000 (+0800) Subject: gdb: LoongArch: Add floating-point support X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=657a50227bbd835c83aadc2405e649c4b982c241;p=binutils-gdb.git gdb: LoongArch: Add floating-point support This commit adds floating-point support for LoongArch gdb. Signed-off-by: Tiezhu Yang --- diff --git a/gdb/arch/loongarch.c b/gdb/arch/loongarch.c index 11310c1dd93..c48c2001e96 100644 --- a/gdb/arch/loongarch.c +++ b/gdb/arch/loongarch.c @@ -24,6 +24,7 @@ #include "../features/loongarch/base32.c" #include "../features/loongarch/base64.c" +#include "../features/loongarch/fpu.c" #ifndef GDBSERVER #define STATIC_IN_GDB static @@ -44,6 +45,11 @@ loongarch_create_target_description (const struct loongarch_gdbarch_features fea else if (features.xlen == 8) arch_name.append ("64"); + if (features.fputype == SINGLE_FLOAT) + arch_name.append ("f"); + else if (features.fputype == DOUBLE_FLOAT) + arch_name.append ("d"); + set_tdesc_architecture (tdesc.get (), arch_name.c_str ()); long regnum = 0; @@ -54,6 +60,9 @@ loongarch_create_target_description (const struct loongarch_gdbarch_features fea else if (features.xlen == 8) regnum = create_feature_loongarch_base64 (tdesc.get (), regnum); + /* For now we only support creating single float and double float. */ + regnum = create_feature_loongarch_fpu (tdesc.get (), regnum); + return tdesc; } diff --git a/gdb/arch/loongarch.h b/gdb/arch/loongarch.h index 5cf5498079a..799595b3e60 100644 --- a/gdb/arch/loongarch.h +++ b/gdb/arch/loongarch.h @@ -23,7 +23,7 @@ #include "gdbsupport/tdesc.h" /* Register numbers of various important registers. */ -enum +enum loongarch_regnum { LOONGARCH_RA_REGNUM = 1, /* Return Address. */ LOONGARCH_SP_REGNUM = 3, /* Stack Pointer. */ @@ -36,6 +36,16 @@ enum LOONGARCH_LINUX_NUM_GREGSET = 45, /* 32 GPR, ORIG_A0, PC, BADV, RESERVED 10. */ LOONGARCH_ARG_REGNUM = 8, /* r4-r11: general-purpose argument registers. f0-f7: floating-point argument registers. */ + LOONGARCH_FIRST_FP_REGNUM = LOONGARCH_LINUX_NUM_GREGSET, + LOONGARCH_FCC_REGNUM = LOONGARCH_FIRST_FP_REGNUM + 32, + LOONGARCH_FCSR_REGNUM = LOONGARCH_FCC_REGNUM + 1, + LOONGARCH_LINUX_NUM_FPREGSET = 34, +}; + +enum loongarch_fputype +{ + SINGLE_FLOAT = 1, + DOUBLE_FLOAT = 2, }; /* The set of LoongArch architectural features that we track that impact how @@ -56,6 +66,11 @@ struct loongarch_gdbarch_features 0 value so we can spot if one of these is used uninitialised. */ int xlen = 0; + /* The type of floating-point. This is either 1 (single float) or 2 + (double float). No other value is valid. Initialise to the invalid + 0 value so we can spot if one of these is used uninitialised. */ + int fputype = 0; + /* Equality operator. */ bool operator== (const struct loongarch_gdbarch_features &rhs) const { diff --git a/gdb/features/Makefile b/gdb/features/Makefile index 15d623c2681..061cb2ed032 100644 --- a/gdb/features/Makefile +++ b/gdb/features/Makefile @@ -231,6 +231,7 @@ FEATURE_XMLFILES = aarch64-core.xml \ i386/x32-core.xml \ loongarch/base32.xml \ loongarch/base64.xml \ + loongarch/fpu.xml \ riscv/rv32e-xregs.xml \ riscv/32bit-cpu.xml \ riscv/32bit-fpu.xml \ diff --git a/gdb/features/loongarch/fpu.c b/gdb/features/loongarch/fpu.c new file mode 100644 index 00000000000..ea3e1dd9980 --- /dev/null +++ b/gdb/features/loongarch/fpu.c @@ -0,0 +1,55 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: fpu.xml */ + +#include "gdbsupport/tdesc.h" + +static int +create_feature_loongarch_fpu (struct target_desc *result, long regnum) +{ + struct tdesc_feature *feature; + + feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.fpu"); + tdesc_type_with_fields *type_with_fields; + type_with_fields = tdesc_create_union (feature, "fputype"); + tdesc_type *field_type; + field_type = tdesc_named_type (feature, "ieee_single"); + tdesc_add_field (type_with_fields, "f", field_type); + field_type = tdesc_named_type (feature, "ieee_double"); + tdesc_add_field (type_with_fields, "d", field_type); + + tdesc_create_reg (feature, "f0", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f1", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f2", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f3", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f4", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f5", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f6", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f7", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f8", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f9", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f10", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f11", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f12", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f13", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f14", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f15", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f16", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f17", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f18", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f19", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f20", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f21", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f22", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f23", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f24", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f25", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f26", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f27", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f28", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f29", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f30", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "f31", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "fcc", regnum++, 1, "float", 64, "fputype"); + tdesc_create_reg (feature, "fcsr", regnum++, 1, "float", 32, "uint32"); + return regnum; +} diff --git a/gdb/features/loongarch/fpu.xml b/gdb/features/loongarch/fpu.xml new file mode 100644 index 00000000000..a61057ec442 --- /dev/null +++ b/gdb/features/loongarch/fpu.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c index 1b4a37c94fb..3baa891519a 100644 --- a/gdb/loongarch-linux-nat.c +++ b/gdb/loongarch-linux-nat.c @@ -100,6 +100,52 @@ store_gregs_to_thread (struct regcache *regcache, int regnum, pid_t tid) } } +/* Fill GDB's register array with the fp, fcc and fcsr + register values from the current thread. */ + +static void +fetch_fpregs_from_thread (struct regcache *regcache, int regnum, pid_t tid) +{ + elf_fpregset_t regset; + + if ((regnum == -1) + || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) + perror_with_name (_("Couldn't get NT_FPREGSET registers")); + else + loongarch_fpregset.supply_regset (nullptr, regcache, regnum, + ®set, sizeof (regset)); + } +} + +/* Store to the current thread the valid fp, fcc and fcsr + register values in the GDB's register array. */ + +static void +store_fpregs_to_thread (struct regcache *regcache, int regnum, pid_t tid) +{ + elf_fpregset_t regset; + + if ((regnum == -1) + || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM)) + { + struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; + + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) + perror_with_name (_("Couldn't get NT_FPREGSET registers")); + else + { + loongarch_fpregset.collect_regset (nullptr, regcache, regnum, + ®set, sizeof (regset)); + if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) + perror_with_name (_("Couldn't set NT_FPREGSET registers")); + } + } +} + /* Implement the "fetch_registers" target_ops method. */ void @@ -109,6 +155,7 @@ loongarch_linux_nat_target::fetch_registers (struct regcache *regcache, pid_t tid = get_ptrace_pid (regcache->ptid ()); fetch_gregs_from_thread(regcache, regnum, tid); + fetch_fpregs_from_thread(regcache, regnum, tid); } /* Implement the "store_registers" target_ops method. */ @@ -120,6 +167,7 @@ loongarch_linux_nat_target::store_registers (struct regcache *regcache, pid_t tid = get_ptrace_pid (regcache->ptid ()); store_gregs_to_thread (regcache, regnum, tid); + store_fpregs_to_thread(regcache, regnum, tid); } /* Return the address in the core dump or inferior of register REGNO. */ @@ -158,12 +206,16 @@ fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregset, void supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregset) { + loongarch_fpregset.supply_regset (nullptr, regcache, -1, fpregset, + sizeof (gdb_fpregset_t)); } void fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset, int regnum) { + loongarch_fpregset.collect_regset (nullptr, regcache, regnum, fpregset, + sizeof (gdb_fpregset_t)); } /* Initialize LoongArch Linux native support. */ diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c index 1076e935997..0a3ea80c173 100644 --- a/gdb/loongarch-linux-tdep.c +++ b/gdb/loongarch-linux-tdep.c @@ -106,7 +106,7 @@ loongarch_fill_gregset (const struct regset *regset, } } -/* Register set definitions. */ +/* Define the general register regset. */ const struct regset loongarch_gregset = { @@ -115,6 +115,62 @@ const struct regset loongarch_gregset = loongarch_fill_gregset, }; +/* Unpack an elf_fpregset_t into GDB's register cache. */ +static void +loongarch_supply_fpregset (const struct regset *r, + struct regcache *regcache, int regnum, + const void *fprs, size_t len) +{ + const gdb_byte *buf = nullptr; + int fprsize = register_size (regcache->arch (), LOONGARCH_FIRST_FP_REGNUM); + + if (regnum == -1) + { + for (int i = 0; i < LOONGARCH_LINUX_NUM_FPREGSET; i++) + { + buf = (const gdb_byte *)fprs + fprsize * i; + regcache->raw_supply (LOONGARCH_FIRST_FP_REGNUM + i, (const void *)buf); + } + } + else if (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM) + { + buf = (const gdb_byte *)fprs + fprsize * (regnum - LOONGARCH_FIRST_FP_REGNUM); + regcache->raw_supply (regnum, (const void *)buf); + } +} + +/* Pack the GDB's register cache value into an elf_fpregset_t. */ +static void +loongarch_fill_fpregset (const struct regset *r, + const struct regcache *regcache, int regnum, + void *fprs, size_t len) +{ + gdb_byte *buf = nullptr; + int fprsize = register_size (regcache->arch (), LOONGARCH_FIRST_FP_REGNUM); + + if (regnum == -1) + { + for (int i = 0; i < LOONGARCH_LINUX_NUM_FPREGSET; i++) + { + buf = (gdb_byte *)fprs + fprsize * i; + regcache->raw_collect (LOONGARCH_FIRST_FP_REGNUM + i, (void *)buf); + } + } + else if (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM) + { + buf = (gdb_byte *)fprs + fprsize * (regnum - LOONGARCH_FIRST_FP_REGNUM); + regcache->raw_collect (regnum, (void *)buf); + } +} + +/* Define the FP register regset. */ +const struct regset loongarch_fpregset = +{ + nullptr, + loongarch_supply_fpregset, + loongarch_fill_fpregset, +}; + /* Implement the "init" method of struct tramp_frame. */ #define LOONGARCH_RT_SIGFRAME_UCONTEXT_OFFSET 128 @@ -152,7 +208,7 @@ static const struct tramp_frame loongarch_linux_rt_sigframe = { TRAMP_SENTINEL_INSN, ULONGEST_MAX } }, loongarch_linux_rt_sigframe_init, - NULL + nullptr }; /* Implement the "iterate_over_regset_sections" gdbarch method. */ @@ -163,10 +219,13 @@ loongarch_iterate_over_regset_sections (struct gdbarch *gdbarch, void *cb_data, const struct regcache *regcache) { - int regsize = register_size (gdbarch, 0); + int gprsize = register_size (gdbarch, 0); + int fprsize = register_size (gdbarch, LOONGARCH_FIRST_FP_REGNUM); - cb (".reg", LOONGARCH_LINUX_NUM_GREGSET * regsize, - LOONGARCH_LINUX_NUM_GREGSET * regsize, &loongarch_gregset, NULL, cb_data); + cb (".reg", LOONGARCH_LINUX_NUM_GREGSET * gprsize, + LOONGARCH_LINUX_NUM_GREGSET * gprsize, &loongarch_gregset, nullptr, cb_data); + cb (".reg2", LOONGARCH_LINUX_NUM_FPREGSET * fprsize, + LOONGARCH_LINUX_NUM_FPREGSET * fprsize, &loongarch_fpregset, nullptr, cb_data); } /* The following value is derived from __NR_rt_sigreturn in diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index b8f28a2b142..41422f0445d 100644 --- a/gdb/loongarch-tdep.c +++ b/gdb/loongarch-tdep.c @@ -462,7 +462,7 @@ pass_in_gar (struct regcache *regcache, unsigned int gar, const gdb_byte *val) static void pass_in_far (struct regcache *regcache, unsigned int far, const gdb_byte *val) { - unsigned int regnum = LOONGARCH_ARG_REGNUM - far + LOONGARCH_BADV_REGNUM + 1; + unsigned int regnum = LOONGARCH_ARG_REGNUM - far + LOONGARCH_FIRST_FP_REGNUM; regcache->cooked_write (regnum, val); } @@ -1080,6 +1080,8 @@ loongarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int regnum) { if (regnum >= 0 && regnum < 32) return regnum; + else if (regnum >= 32 && regnum < 66) + return LOONGARCH_FIRST_FP_REGNUM + regnum - 32; else return -1; } @@ -1104,6 +1106,7 @@ loongarch_features_from_bfd (const bfd *abfd) if (abfd != nullptr && bfd_get_flavour (abfd) == bfd_target_elf_flavour) { unsigned char eclass = elf_elfheader (abfd)->e_ident[EI_CLASS]; + int e_flags = elf_elfheader (abfd)->e_flags; if (eclass == ELFCLASS32) features.xlen = 4; @@ -1112,6 +1115,11 @@ loongarch_features_from_bfd (const bfd *abfd) else internal_error (__FILE__, __LINE__, _("unknown ELF header class %d"), eclass); + + if (EF_LOONGARCH_IS_SINGLE_FLOAT (e_flags)) + features.fputype = SINGLE_FLOAT; + else if (EF_LOONGARCH_IS_DOUBLE_FLOAT (e_flags)) + features.fputype = DOUBLE_FLOAT; } return features; @@ -1130,11 +1138,17 @@ loongarch_find_default_target_description (const struct gdbarch_info info) /* If the XLEN field is still 0 then we got nothing useful from INFO.BFD, maybe there was no bfd object. In this case we fall back to a minimal - useful target with no floating point, the x-register size is selected - based on the architecture from INFO. */ + useful target, the x-register size is selected based on the architecture + from INFO. */ if (features.xlen == 0) features.xlen = info.bfd_arch_info->bits_per_address == 32 ? 4 : 8; + /* If the FPUTYPE field is still 0 then we got nothing useful from INFO.BFD, + maybe there was no bfd object. In this case we fall back to a usual useful + target with double float. */ + if (features.fputype == 0) + features.fputype = DOUBLE_FLOAT; + /* Now build a target description based on the feature set. */ return loongarch_lookup_target_description (features); } @@ -1144,6 +1158,10 @@ loongarch_find_default_target_description (const struct gdbarch_info info) static struct gdbarch * loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) { + size_t regnum = 0; + struct loongarch_gdbarch_features features; + tdesc_arch_data_up tdesc_data = tdesc_data_alloc (); + loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep; const struct target_desc *tdesc = info.target_desc; /* Ensure we always have a target description. */ @@ -1155,13 +1173,6 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) if (feature_cpu == nullptr) return nullptr; - int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc"); - struct loongarch_gdbarch_features features; - features.xlen = (xlen_bitsize / 8); - - size_t regnum = 0; - tdesc_arch_data_up tdesc_data = tdesc_data_alloc (); - loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep; /* Validate the description provides the mandatory base registers and allocate their numbers. */ @@ -1175,6 +1186,22 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) if (!valid_p) return nullptr; + const struct tdesc_feature *feature_fpu + = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.fpu"); + if (feature_fpu == nullptr) + return nullptr; + + /* Validate the description provides the fpu registers and + allocate their numbers. */ + regnum = LOONGARCH_FIRST_FP_REGNUM; + for (int i = 0; i < 32; i++) + valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data.get (), regnum++, + loongarch_f_normal_name[i] + 1); + valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data.get (), regnum++, "fcc"); + valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data.get (), regnum++, "fcsr"); + if (!valid_p) + return nullptr; + /* LoongArch code is always little-endian. */ info.byte_order_for_code = BFD_ENDIAN_LITTLE; @@ -1184,11 +1211,22 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) struct loongarch_gdbarch_features abi_features = loongarch_features_from_bfd (info.abfd); - /* If the ABI_FEATURES xlen is 0 then this indicates we got no useful abi - features from the INFO object. In this case we just treat the - hardware features as defining the abi. */ + /* If the ABI_FEATURES xlen or fputype is 0 then this indicates we got + no useful abi features from the INFO object. In this case we just + treat the hardware features as defining the abi. */ if (abi_features.xlen == 0) - abi_features = features; + { + int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc"); + features.xlen = (xlen_bitsize / 8); + features.fputype = abi_features.fputype; + abi_features = features; + } + if (abi_features.fputype == 0) + { + features.xlen = abi_features.xlen; + features.fputype = DOUBLE_FLOAT; + abi_features = features; + } /* Find a candidate among the list of pre-declared architectures. */ for (arches = gdbarch_list_lookup_by_info (arches, &info); diff --git a/gdb/loongarch-tdep.h b/gdb/loongarch-tdep.h index b68a7892f2b..e35b6cf4c7a 100644 --- a/gdb/loongarch-tdep.h +++ b/gdb/loongarch-tdep.h @@ -29,6 +29,7 @@ /* Register set definitions. */ extern const struct regset loongarch_gregset; +extern const struct regset loongarch_fpregset; /* Target-dependent structure in gdbarch. */ struct loongarch_gdbarch_tdep : gdbarch_tdep