gdb: LoongArch: Add floating-point support
authorTiezhu Yang <yangtiezhu@loongson.cn>
Tue, 12 Jul 2022 02:33:28 +0000 (10:33 +0800)
committerTiezhu Yang <yangtiezhu@loongson.cn>
Tue, 12 Jul 2022 12:14:48 +0000 (20:14 +0800)
This commit adds floating-point support for LoongArch gdb.

Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
gdb/arch/loongarch.c
gdb/arch/loongarch.h
gdb/features/Makefile
gdb/features/loongarch/fpu.c [new file with mode: 0644]
gdb/features/loongarch/fpu.xml [new file with mode: 0644]
gdb/loongarch-linux-nat.c
gdb/loongarch-linux-tdep.c
gdb/loongarch-tdep.c
gdb/loongarch-tdep.h

index 11310c1dd933cfe9acb9dc1ff673d569bd832b48..c48c2001e96151abffed0f8e12746e45d131b50a 100644 (file)
@@ -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;
 }
 
index 5cf5498079aa8057726a49c8eb1e8ba23d05023c..799595b3e60be74c3b97d8a94e9a02ec918cb370 100644 (file)
@@ -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
   {
index 15d623c2681669bf38062255d92aaf19a70ce933..061cb2ed032e8172c421d818783c0925ad7a79e1 100644 (file)
@@ -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 (file)
index 0000000..ea3e1dd
--- /dev/null
@@ -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 (file)
index 0000000..a61057e
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.fpu">
+
+  <union id="fputype">
+    <field name="f" type="ieee_single"/>
+    <field name="d" type="ieee_double"/>
+  </union>
+
+  <reg name="f0" bitsize="64" type="fputype" group="float"/>
+  <reg name="f1" bitsize="64" type="fputype" group="float"/>
+  <reg name="f2" bitsize="64" type="fputype" group="float"/>
+  <reg name="f3" bitsize="64" type="fputype" group="float"/>
+  <reg name="f4" bitsize="64" type="fputype" group="float"/>
+  <reg name="f5" bitsize="64" type="fputype" group="float"/>
+  <reg name="f6" bitsize="64" type="fputype" group="float"/>
+  <reg name="f7" bitsize="64" type="fputype" group="float"/>
+  <reg name="f8" bitsize="64" type="fputype" group="float"/>
+  <reg name="f9" bitsize="64" type="fputype" group="float"/>
+  <reg name="f10" bitsize="64" type="fputype" group="float"/>
+  <reg name="f11" bitsize="64" type="fputype" group="float"/>
+  <reg name="f12" bitsize="64" type="fputype" group="float"/>
+  <reg name="f13" bitsize="64" type="fputype" group="float"/>
+  <reg name="f14" bitsize="64" type="fputype" group="float"/>
+  <reg name="f15" bitsize="64" type="fputype" group="float"/>
+  <reg name="f16" bitsize="64" type="fputype" group="float"/>
+  <reg name="f17" bitsize="64" type="fputype" group="float"/>
+  <reg name="f18" bitsize="64" type="fputype" group="float"/>
+  <reg name="f19" bitsize="64" type="fputype" group="float"/>
+  <reg name="f20" bitsize="64" type="fputype" group="float"/>
+  <reg name="f21" bitsize="64" type="fputype" group="float"/>
+  <reg name="f22" bitsize="64" type="fputype" group="float"/>
+  <reg name="f23" bitsize="64" type="fputype" group="float"/>
+  <reg name="f24" bitsize="64" type="fputype" group="float"/>
+  <reg name="f25" bitsize="64" type="fputype" group="float"/>
+  <reg name="f26" bitsize="64" type="fputype" group="float"/>
+  <reg name="f27" bitsize="64" type="fputype" group="float"/>
+  <reg name="f28" bitsize="64" type="fputype" group="float"/>
+  <reg name="f29" bitsize="64" type="fputype" group="float"/>
+  <reg name="f30" bitsize="64" type="fputype" group="float"/>
+  <reg name="f31" bitsize="64" type="fputype" group="float"/>
+  <reg name="fcc" bitsize="64" type="fputype" group="float"/>
+  <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
+</feature>
index 1b4a37c94fb161894f30f0262ea6d27461daaf18..3baa891519a6592d6b9d97bc4622c2d467dcf060 100644 (file)
@@ -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 = &regset, .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,
+                                         &regset, 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 = &regset, .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,
+                                            &regset, 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.  */
index 1076e9359972b1fcbc6da192adafe8440ec204ad..0a3ea80c173a2578430afc0d24620e2d0ecac1e2 100644 (file)
@@ -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
index b8f28a2b1427eaf7b6e3f4e6d1c1bc3c70fc7df4..41422f0445dc847f6f9af72f3df12e6c384ca50e 100644 (file)
@@ -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);
index b68a7892f2b9aa11cad82a82dc549153a1d091dc..e35b6cf4c7a217caaea4f99ceff710bd186c1ee1 100644 (file)
@@ -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