diff options
-rw-r--r-- | gdb/ChangeLog | 16 | ||||
-rw-r--r-- | gdb/Makefile.in | 4 | ||||
-rw-r--r-- | gdb/NEWS | 4 | ||||
-rw-r--r-- | gdb/configure.tgt | 5 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 23 | ||||
-rw-r--r-- | gdb/features/Makefile | 1 | ||||
-rw-r--r-- | gdb/features/nds32-core.xml | 44 | ||||
-rw-r--r-- | gdb/features/nds32-fpu.xml | 42 | ||||
-rw-r--r-- | gdb/features/nds32-system.xml | 14 | ||||
-rw-r--r-- | gdb/features/nds32.c | 92 | ||||
-rw-r--r-- | gdb/features/nds32.xml | 14 | ||||
-rw-r--r-- | gdb/nds32-tdep.c | 2184 | ||||
-rw-r--r-- | gdb/nds32-tdep.h | 54 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/float.exp | 9 | ||||
-rw-r--r-- | gdb/testsuite/gdb.xml/tdesc-regs.exp | 3 |
17 files changed, 2518 insertions, 1 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 9e57431..faa9e9f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,19 @@ +2016-06-17 Yan-Ting Lin <currygt52@gmail.com> + + * Makefile.in (ALL_TARGET_OBS): Add nds32-tdep.o. + (HFILES_NO_SRCDIR): Add nds32-tdep.h. + (ALLDEPFILES): Add nds32-tdep.c. + * NEWS: Mention new NDS32 port. + * configure.tgt: Add NDS32. + * nds32-tdep.c: New file. + * nds32-tdep.h: New file. + * features/Makefile (XMLTOC): Add nds32.xml. + * features/nds32-core.xml: New file. + * features/nds32-fpu.xml: New file. + * features/nds32-system.xml: New file. + * features/nds32.c: New file (generated). + * features/nds32.xml: New file. + 2016-06-14 John Baldwin <jhb@FreeBSD.org> * v850-tdep.c (v850_use_struct_convention): Trim type length checks. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 02eb57f..16d5f27 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -691,6 +691,7 @@ ALL_TARGET_OBS = \ moxie-tdep.o \ msp430-tdep.o \ mt-tdep.o \ + nds32-tdep.o \ nios2-tdep.o nios2-linux-tdep.o \ nto-tdep.o \ ppc-linux-tdep.o ppcfbsd-tdep.o ppcnbsd-tdep.o ppcobsd-tdep.o \ @@ -967,7 +968,7 @@ amd64-darwin-tdep.h charset-list.h \ config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \ dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \ i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \ -ft32-tdep.h \ +ft32-tdep.h nds32-tdep.h \ osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \ python/python-internal.h python/python.h ravenscar-thread.h record.h \ record-full.h solib-aix.h \ @@ -1726,6 +1727,7 @@ ALLDEPFILES = \ mipsnbsd-nat.c mipsnbsd-tdep.c \ mips64obsd-nat.c mips64obsd-tdep.c \ msp430-tdep.c \ + nds32-tdep.c \ nios2-tdep.c nios2-linux-tdep.c \ nbsd-nat.c nbsd-tdep.c obsd-nat.c obsd-tdep.c \ posix-hdep.c common/posix-strerror.c \ @@ -86,6 +86,10 @@ maint selftest =record-started,thread-group="i1",method="btrace",format="bts" +* New targets + +Andes NDS32 nds32*-*-elf + *** Changes in GDB 7.11 * GDB now supports debugging kernel-based threads on FreeBSD. diff --git a/gdb/configure.tgt b/gdb/configure.tgt index cd8e05d..7f1aac3 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -394,6 +394,11 @@ mt-*-*) gdb_target_obs="mt-tdep.o" ;; +nds32*-*-elf) + # Target: AndesTech NDS32 core + gdb_target_obs="nds32-tdep.o" + ;; + nios2*-*-linux*) # Target: Altera Nios II running Linux gdb_target_obs="nios2-tdep.o nios2-linux-tdep.o solib-svr4.o \ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index a01d545..1af71e5 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2016-06-17 Yan-Ting Lin <currygt52@gmail.com> + + * gdb.texinfo (Standard Target Features): Document NDS32 features. + (NDS32 Features): New Section. + 2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de> * gdb.texinfo (Examining Memory): Document negative repeat diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 6e2b493..795a70b 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -40817,6 +40817,7 @@ registers using the capitalization used in the description. * MicroBlaze Features:: * MIPS Features:: * M68K Features:: +* NDS32 Features:: * Nios II Features:: * PowerPC Features:: * S/390 and System z Features:: @@ -41025,6 +41026,28 @@ This feature is optional. If present, it should contain registers @samp{fpiaddr}. @end table +@node NDS32 Features +@subsection NDS32 Features +@cindex target descriptions, NDS32 features + +The @samp{org.gnu.gdb.nds32.core} feature is required for NDS32 +targets. It should contain at least registers @samp{r0} through +@samp{r10}, @samp{r15}, @samp{fp}, @samp{gp}, @samp{lp}, @samp{sp}, +and @samp{pc}. + +The @samp{org.gnu.gdb.nds32.fpu} feature is optional. If present, +it should contain 64-bit double-precision floating-point registers +@samp{fd0} through @emph{fdN}, which should be @samp{fd3}, @samp{fd7}, +@samp{fd15}, or @samp{fd31} based on the FPU configuration implemented. + +@emph{Note:} The first sixteen 64-bit double-precision floating-point +registers are overlapped with the thirty-two 32-bit single-precision +floating-point registers. The 32-bit single-precision registers, if +not being listed explicitly, will be synthesized from halves of the +overlapping 64-bit double-precision registers. Listing 32-bit +single-precision registers explicitly is deprecated, and the +support to it could be totally removed some day. + @node Nios II Features @subsection Nios II Features @cindex target descriptions, Nios II features diff --git a/gdb/features/Makefile b/gdb/features/Makefile index e5c5154..809c811 100644 --- a/gdb/features/Makefile +++ b/gdb/features/Makefile @@ -189,6 +189,7 @@ XMLTOC = \ mips-linux.xml \ mips64-dsp-linux.xml \ mips64-linux.xml \ + nds32.xml \ nios2-linux.xml \ nios2.xml \ rs6000/powerpc-32.xml \ diff --git a/gdb/features/nds32-core.xml b/gdb/features/nds32-core.xml new file mode 100644 index 0000000..9ea1640 --- /dev/null +++ b/gdb/features/nds32-core.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2013-2016 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.nds32.core"> + <reg name="r0" bitsize="32" regnum="0"/> + <reg name="r1" bitsize="32"/> + <reg name="r2" bitsize="32"/> + <reg name="r3" bitsize="32"/> + <reg name="r4" bitsize="32"/> + <reg name="r5" bitsize="32"/> + <reg name="r6" bitsize="32"/> + <reg name="r7" bitsize="32"/> + <reg name="r8" bitsize="32"/> + <reg name="r9" bitsize="32"/> + <reg name="r10" bitsize="32"/> + <reg name="r11" bitsize="32"/> + <reg name="r12" bitsize="32"/> + <reg name="r13" bitsize="32"/> + <reg name="r14" bitsize="32"/> + <reg name="r15" bitsize="32"/> + <reg name="r16" bitsize="32"/> + <reg name="r17" bitsize="32"/> + <reg name="r18" bitsize="32"/> + <reg name="r19" bitsize="32"/> + <reg name="r20" bitsize="32"/> + <reg name="r21" bitsize="32"/> + <reg name="r22" bitsize="32"/> + <reg name="r23" bitsize="32"/> + <reg name="r24" bitsize="32"/> + <reg name="r25" bitsize="32"/> + <reg name="r26" bitsize="32"/> + <reg name="r27" bitsize="32"/> + <reg name="fp" bitsize="32" type="data_ptr"/> + <reg name="gp" bitsize="32" type="data_ptr"/> + <reg name="lp" bitsize="32" type="code_ptr"/> + <reg name="sp" bitsize="32" type="data_ptr"/> + + <reg name="pc" bitsize="32" type="code_ptr"/> +</feature> diff --git a/gdb/features/nds32-fpu.xml b/gdb/features/nds32-fpu.xml new file mode 100644 index 0000000..b01a4c0 --- /dev/null +++ b/gdb/features/nds32-fpu.xml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2013-2016 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.nds32.fpu"> + <reg name="fd0" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd1" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd2" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd3" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd4" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd5" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd6" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd7" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd8" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd9" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd10" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd11" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd12" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd13" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd14" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd15" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd16" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd17" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd18" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd19" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd20" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd21" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd22" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd23" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd24" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd25" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd26" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd27" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd28" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd29" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd30" bitsize="64" type="ieee_double" group="float"/> + <reg name="fd31" bitsize="64" type="ieee_double" group="float"/> +</feature> diff --git a/gdb/features/nds32-system.xml b/gdb/features/nds32-system.xml new file mode 100644 index 0000000..c2ea8ac --- /dev/null +++ b/gdb/features/nds32-system.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2013-2016 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.nds32.system"> + <reg name="ir0" bitsize="32"/> + + <reg name="itb" bitsize="32"/> + <reg name="ifc_lp" bitsize="32"/> +</feature> diff --git a/gdb/features/nds32.c b/gdb/features/nds32.c new file mode 100644 index 0000000..21f63f5 --- /dev/null +++ b/gdb/features/nds32.c @@ -0,0 +1,92 @@ +/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro: + Original: nds32.xml */ + +#include "defs.h" +#include "osabi.h" +#include "target-descriptions.h" + +struct target_desc *tdesc_nds32; +static void +initialize_tdesc_nds32 (void) +{ + struct target_desc *result = allocate_target_description (); + struct tdesc_feature *feature; + + set_tdesc_architecture (result, bfd_scan_arch ("n1h")); + + feature = tdesc_create_feature (result, "org.gnu.gdb.nds32.core"); + tdesc_create_reg (feature, "r0", 0, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r1", 1, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r2", 2, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r3", 3, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r4", 4, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r5", 5, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r6", 6, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r7", 7, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r8", 8, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r9", 9, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r10", 10, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r11", 11, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r12", 12, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r13", 13, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r14", 14, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r15", 15, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r16", 16, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r17", 17, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r18", 18, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r19", 19, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r20", 20, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r21", 21, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r22", 22, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r23", 23, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r24", 24, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r25", 25, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r26", 26, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "r27", 27, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "fp", 28, 1, NULL, 32, "data_ptr"); + tdesc_create_reg (feature, "gp", 29, 1, NULL, 32, "data_ptr"); + tdesc_create_reg (feature, "lp", 30, 1, NULL, 32, "code_ptr"); + tdesc_create_reg (feature, "sp", 31, 1, NULL, 32, "data_ptr"); + tdesc_create_reg (feature, "pc", 32, 1, NULL, 32, "code_ptr"); + + feature = tdesc_create_feature (result, "org.gnu.gdb.nds32.fpu"); + tdesc_create_reg (feature, "fd0", 33, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd1", 34, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd2", 35, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd3", 36, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd4", 37, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd5", 38, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd6", 39, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd7", 40, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd8", 41, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd9", 42, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd10", 43, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd11", 44, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd12", 45, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd13", 46, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd14", 47, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd15", 48, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd16", 49, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd17", 50, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd18", 51, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd19", 52, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd20", 53, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd21", 54, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd22", 55, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd23", 56, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd24", 57, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd25", 58, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd26", 59, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd27", 60, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd28", 61, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd29", 62, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd30", 63, 1, "float", 64, "ieee_double"); + tdesc_create_reg (feature, "fd31", 64, 1, "float", 64, "ieee_double"); + + feature = tdesc_create_feature (result, "org.gnu.gdb.nds32.system"); + tdesc_create_reg (feature, "ir0", 65, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "itb", 66, 1, NULL, 32, "int"); + tdesc_create_reg (feature, "ifc_lp", 67, 1, NULL, 32, "int"); + + tdesc_nds32 = result; +} diff --git a/gdb/features/nds32.xml b/gdb/features/nds32.xml new file mode 100644 index 0000000..4819317 --- /dev/null +++ b/gdb/features/nds32.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2013-2016 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 target SYSTEM "gdb-target.dtd"> +<target> + <architecture>nds32</architecture> + <xi:include href="nds32-core.xml"/> + <xi:include href="nds32-fpu.xml"/> + <xi:include href="nds32-system.xml"/> +</target> diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c new file mode 100644 index 0000000..de72656 --- /dev/null +++ b/gdb/nds32-tdep.c @@ -0,0 +1,2184 @@ +/* Target-dependent code for the NDS32 architecture, for GDB. + + Copyright (C) 2013-2016 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "frame.h" +#include "frame-unwind.h" +#include "frame-base.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "gdbcore.h" +#include "value.h" +#include "reggroups.h" +#include "inferior.h" +#include "osabi.h" +#include "arch-utils.h" +#include "regcache.h" +#include "dis-asm.h" +#include "user-regs.h" +#include "elf-bfd.h" +#include "dwarf2-frame.h" +#include "remote.h" +#include "target-descriptions.h" + +#include "nds32-tdep.h" +#include "elf/nds32.h" +#include "opcode/nds32.h" +#include "features/nds32.c" + +/* Simple macros for instruction analysis. */ +#define CHOP_BITS(insn, n) (insn & ~__MASK (n)) +#define N32_LSMW_ENABLE4(insn) (((insn) >> 6) & 0xf) +#define N32_SMW_ADM \ + N32_TYPE4 (LSMW, 0, 0, 0, 1, (N32_LSMW_ADM << 2) | N32_LSMW_LSMW) +#define N32_LMW_BIM \ + N32_TYPE4 (LSMW, 0, 0, 0, 0, (N32_LSMW_BIM << 2) | N32_LSMW_LSMW) +#define N32_FLDI_SP \ + N32_TYPE2 (LDC, 0, REG_SP, 0) + +extern void _initialize_nds32_tdep (void); + +/* Use an invalid address value as 'not available' marker. */ +enum { REG_UNAVAIL = (CORE_ADDR) -1 }; + +/* Use an impossible value as invalid offset. */ +enum { INVALID_OFFSET = (CORE_ADDR) -1 }; + +/* Instruction groups for NDS32 epilogue analysis. */ +enum +{ + /* Instructions used everywhere, not only in epilogue. */ + INSN_NORMAL, + /* Instructions used to reset sp for local vars, arguments, etc. */ + INSN_RESET_SP, + /* Instructions used to recover saved regs and to recover padding. */ + INSN_RECOVER, + /* Instructions used to return to the caller. */ + INSN_RETURN, + /* Instructions used to recover saved regs and to return to the caller. */ + INSN_RECOVER_RETURN, +}; + +static const char *const nds32_register_names[] = +{ + /* 32 GPRs. */ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "fp", "gp", "lp", "sp", + /* PC. */ + "pc", +}; + +static const char *const nds32_fdr_register_names[] = +{ + "fd0", "fd1", "fd2", "fd3", "fd4", "fd5", "fd6", "fd7", + "fd8", "fd9", "fd10", "fd11", "fd12", "fd13", "fd14", "fd15", + "fd16", "fd17", "fd18", "fd19", "fd20", "fd21", "fd22", "fd23", + "fd24", "fd25", "fd26", "fd27", "fd28", "fd29", "fd30", "fd31" +}; + +static const char *const nds32_fsr_register_names[] = +{ + "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", + "fs8", "fs9", "fs10", "fs11", "fs12", "fs13", "fs14", "fs15", + "fs16", "fs17", "fs18", "fs19", "fs20", "fs21", "fs22", "fs23", + "fs24", "fs25", "fs26", "fs27", "fs28", "fs29", "fs30", "fs31" +}; + +/* The number of registers for four FPU configuration options. */ +const int num_fdr_map[] = { 4, 8, 16, 32 }; +const int num_fsr_map[] = { 8, 16, 32, 32 }; + +/* Aliases for registers. */ +static const struct +{ + const char *name; + const char *alias; +} nds32_register_aliases[] = +{ + {"r15", "ta"}, + {"r26", "p0"}, + {"r27", "p1"}, + {"fp", "r28"}, + {"gp", "r29"}, + {"lp", "r30"}, + {"sp", "r31"}, + + {"cr0", "cpu_ver"}, + {"cr1", "icm_cfg"}, + {"cr2", "dcm_cfg"}, + {"cr3", "mmu_cfg"}, + {"cr4", "msc_cfg"}, + {"cr5", "core_id"}, + {"cr6", "fucop_exist"}, + {"cr7", "msc_cfg2"}, + + {"ir0", "psw"}, + {"ir1", "ipsw"}, + {"ir2", "p_psw"}, + {"ir3", "ivb"}, + {"ir4", "eva"}, + {"ir5", "p_eva"}, + {"ir6", "itype"}, + {"ir7", "p_itype"}, + {"ir8", "merr"}, + {"ir9", "ipc"}, + {"ir10", "p_ipc"}, + {"ir11", "oipc"}, + {"ir12", "p_p0"}, + {"ir13", "p_p1"}, + {"ir14", "int_mask"}, + {"ir15", "int_pend"}, + {"ir16", "sp_usr"}, + {"ir17", "sp_priv"}, + {"ir18", "int_pri"}, + {"ir19", "int_ctrl"}, + {"ir20", "sp_usr1"}, + {"ir21", "sp_priv1"}, + {"ir22", "sp_usr2"}, + {"ir23", "sp_priv2"}, + {"ir24", "sp_usr3"}, + {"ir25", "sp_priv3"}, + {"ir26", "int_mask2"}, + {"ir27", "int_pend2"}, + {"ir28", "int_pri2"}, + {"ir29", "int_trigger"}, + + {"mr0", "mmu_ctl"}, + {"mr1", "l1_pptb"}, + {"mr2", "tlb_vpn"}, + {"mr3", "tlb_data"}, + {"mr4", "tlb_misc"}, + {"mr5", "vlpt_idx"}, + {"mr6", "ilmb"}, + {"mr7", "dlmb"}, + {"mr8", "cache_ctl"}, + {"mr9", "hsmp_saddr"}, + {"mr10", "hsmp_eaddr"}, + {"mr11", "bg_region"}, + + {"dr0", "bpc0"}, + {"dr1", "bpc1"}, + {"dr2", "bpc2"}, + {"dr3", "bpc3"}, + {"dr4", "bpc4"}, + {"dr5", "bpc5"}, + {"dr6", "bpc6"}, + {"dr7", "bpc7"}, + {"dr8", "bpa0"}, + {"dr9", "bpa1"}, + {"dr10", "bpa2"}, + {"dr11", "bpa3"}, + {"dr12", "bpa4"}, + {"dr13", "bpa5"}, + {"dr14", "bpa6"}, + {"dr15", "bpa7"}, + {"dr16", "bpam0"}, + {"dr17", "bpam1"}, + {"dr18", "bpam2"}, + {"dr19", "bpam3"}, + {"dr20", "bpam4"}, + {"dr21", "bpam5"}, + {"dr22", "bpam6"}, + {"dr23", "bpam7"}, + {"dr24", "bpv0"}, + {"dr25", "bpv1"}, + {"dr26", "bpv2"}, + {"dr27", "bpv3"}, + {"dr28", "bpv4"}, + {"dr29", "bpv5"}, + {"dr30", "bpv6"}, + {"dr31", "bpv7"}, + {"dr32", "bpcid0"}, + {"dr33", "bpcid1"}, + {"dr34", "bpcid2"}, + {"dr35", "bpcid3"}, + {"dr36", "bpcid4"}, + {"dr37", "bpcid5"}, + {"dr38", "bpcid6"}, + {"dr39", "bpcid7"}, + {"dr40", "edm_cfg"}, + {"dr41", "edmsw"}, + {"dr42", "edm_ctl"}, + {"dr43", "edm_dtr"}, + {"dr44", "bpmtc"}, + {"dr45", "dimbr"}, + {"dr46", "tecr0"}, + {"dr47", "tecr1"}, + + {"hspr0", "hsp_ctl"}, + {"hspr1", "sp_bound"}, + {"hspr2", "sp_bound_priv"}, + + {"pfr0", "pfmc0"}, + {"pfr1", "pfmc1"}, + {"pfr2", "pfmc2"}, + {"pfr3", "pfm_ctl"}, + {"pfr4", "pft_ctl"}, + + {"dmar0", "dma_cfg"}, + {"dmar1", "dma_gcsw"}, + {"dmar2", "dma_chnsel"}, + {"dmar3", "dma_act"}, + {"dmar4", "dma_setup"}, + {"dmar5", "dma_isaddr"}, + {"dmar6", "dma_esaddr"}, + {"dmar7", "dma_tcnt"}, + {"dmar8", "dma_status"}, + {"dmar9", "dma_2dset"}, + {"dmar10", "dma_2dsctl"}, + {"dmar11", "dma_rcnt"}, + {"dmar12", "dma_hstatus"}, + + {"racr0", "prusr_acc_ctl"}, + {"fucpr", "fucop_ctl"}, + + {"idr0", "sdz_ctl"}, + {"idr1", "misc_ctl"}, + {"idr2", "ecc_misc"}, + + {"secur0", "sfcr"}, + {"secur1", "sign"}, + {"secur2", "isign"}, + {"secur3", "p_isign"}, +}; + +/* Value of a register alias. BATON is the regnum of the corresponding + register. */ + +static struct value * +value_of_nds32_reg (struct frame_info *frame, const void *baton) +{ + return value_of_register ((int) (intptr_t) baton, frame); +} + +/* Implement the "frame_align" gdbarch method. */ + +static CORE_ADDR +nds32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) +{ + /* 8-byte aligned. */ + return align_down (sp, 8); +} + +/* Implement the "breakpoint_from_pc" gdbarch method. + + Use the program counter to determine the contents and size of a + breakpoint instruction. Return a pointer to a string of bytes that + encode a breakpoint instruction, store the length of the string in + *LENPTR and optionally adjust *PCPTR to point to the correct memory + location for inserting the breakpoint. */ + +static const gdb_byte * +nds32_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, + int *lenptr) +{ + /* The same insn machine code is used for little-endian and big-endian. */ + static const gdb_byte break_insn[] = { 0xEA, 0x00 }; + + *lenptr = sizeof (break_insn); + return break_insn; +} + +/* Implement the "dwarf2_reg_to_regnum" gdbarch method. */ + +static int +nds32_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + const int FSR = 38; + const int FDR = FSR + 32; + + if (num >= 0 && num < 32) + { + /* General-purpose registers (R0 - R31). */ + return num; + } + else if (num >= FSR && num < FSR + 32) + { + /* Single precision floating-point registers (FS0 - FS31). */ + return num - FSR + tdep->fs0_regnum; + } + else if (num >= FDR && num < FDR + 32) + { + /* Double precision floating-point registers (FD0 - FD31). */ + return num - FDR + NDS32_FD0_REGNUM; + } + + /* No match, return a inaccessible register number. */ + return -1; +} + +/* NDS32 register groups. */ +static struct reggroup *nds32_cr_reggroup; +static struct reggroup *nds32_ir_reggroup; +static struct reggroup *nds32_mr_reggroup; +static struct reggroup *nds32_dr_reggroup; +static struct reggroup *nds32_pfr_reggroup; +static struct reggroup *nds32_hspr_reggroup; +static struct reggroup *nds32_dmar_reggroup; +static struct reggroup *nds32_racr_reggroup; +static struct reggroup *nds32_idr_reggroup; +static struct reggroup *nds32_secur_reggroup; + +static void +nds32_init_reggroups (void) +{ + nds32_cr_reggroup = reggroup_new ("cr", USER_REGGROUP); + nds32_ir_reggroup = reggroup_new ("ir", USER_REGGROUP); + nds32_mr_reggroup = reggroup_new ("mr", USER_REGGROUP); + nds32_dr_reggroup = reggroup_new ("dr", USER_REGGROUP); + nds32_pfr_reggroup = reggroup_new ("pfr", USER_REGGROUP); + nds32_hspr_reggroup = reggroup_new ("hspr", USER_REGGROUP); + nds32_dmar_reggroup = reggroup_new ("dmar", USER_REGGROUP); + nds32_racr_reggroup = reggroup_new ("racr", USER_REGGROUP); + nds32_idr_reggroup = reggroup_new ("idr", USER_REGGROUP); + nds32_secur_reggroup = reggroup_new ("secur", USER_REGGROUP); +} + +static void +nds32_add_reggroups (struct gdbarch *gdbarch) +{ + /* Add pre-defined register groups. */ + reggroup_add (gdbarch, general_reggroup); + reggroup_add (gdbarch, float_reggroup); + reggroup_add (gdbarch, system_reggroup); + reggroup_add (gdbarch, all_reggroup); + reggroup_add (gdbarch, save_reggroup); + reggroup_add (gdbarch, restore_reggroup); + + /* Add NDS32 register groups. */ + reggroup_add (gdbarch, nds32_cr_reggroup); + reggroup_add (gdbarch, nds32_ir_reggroup); + reggroup_add (gdbarch, nds32_mr_reggroup); + reggroup_add (gdbarch, nds32_dr_reggroup); + reggroup_add (gdbarch, nds32_pfr_reggroup); + reggroup_add (gdbarch, nds32_hspr_reggroup); + reggroup_add (gdbarch, nds32_dmar_reggroup); + reggroup_add (gdbarch, nds32_racr_reggroup); + reggroup_add (gdbarch, nds32_idr_reggroup); + reggroup_add (gdbarch, nds32_secur_reggroup); +} + +/* Implement the "register_reggroup_p" gdbarch method. */ + +static int +nds32_register_reggroup_p (struct gdbarch *gdbarch, int regnum, + struct reggroup *reggroup) +{ + const char *reg_name; + const char *group_name; + int ret; + + if (reggroup == all_reggroup) + return 1; + + /* General reggroup contains only GPRs and PC. */ + if (reggroup == general_reggroup) + return regnum <= NDS32_PC_REGNUM; + + if (reggroup == float_reggroup || reggroup == save_reggroup + || reggroup == restore_reggroup) + { + ret = tdesc_register_in_reggroup_p (gdbarch, regnum, reggroup); + if (ret != -1) + return ret; + + return default_register_reggroup_p (gdbarch, regnum, reggroup); + } + + if (reggroup == system_reggroup) + return (regnum > NDS32_PC_REGNUM) + && !nds32_register_reggroup_p (gdbarch, regnum, float_reggroup); + + /* The NDS32 reggroup contains registers whose name is prefixed + by reggroup name. */ + reg_name = gdbarch_register_name (gdbarch, regnum); + group_name = reggroup_name (reggroup); + return !strncmp (reg_name, group_name, strlen (group_name)); +} + +/* Implement the "pseudo_register_type" tdesc_arch_data method. */ + +static struct type * +nds32_pseudo_register_type (struct gdbarch *gdbarch, int regnum) +{ + regnum -= gdbarch_num_regs (gdbarch); + + /* Currently, only FSRs could be defined as pseudo registers. */ + if (regnum < gdbarch_num_pseudo_regs (gdbarch)) + return arch_float_type (gdbarch, -1, "builtin_type_ieee_single", + floatformats_ieee_single); + + warning (_("Unknown nds32 pseudo register %d."), regnum); + return NULL; +} + +/* Implement the "pseudo_register_name" tdesc_arch_data method. */ + +static const char * +nds32_pseudo_register_name (struct gdbarch *gdbarch, int regnum) +{ + regnum -= gdbarch_num_regs (gdbarch); + + /* Currently, only FSRs could be defined as pseudo registers. */ + if (regnum < gdbarch_num_pseudo_regs (gdbarch)) + return nds32_fsr_register_names[regnum]; + + warning (_("Unknown nds32 pseudo register %d."), regnum); + return NULL; +} + +/* Implement the "pseudo_register_read" gdbarch method. */ + +static enum register_status +nds32_pseudo_register_read (struct gdbarch *gdbarch, + struct regcache *regcache, int regnum, + gdb_byte *buf) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + gdb_byte reg_buf[8]; + int offset, fdr_regnum; + enum register_status status = REG_UNKNOWN; + + /* Sanity check. */ + if (tdep->fpu_freg == -1 || tdep->use_pseudo_fsrs == 0) + return status; + + regnum -= gdbarch_num_regs (gdbarch); + + /* Currently, only FSRs could be defined as pseudo registers. */ + if (regnum < gdbarch_num_pseudo_regs (gdbarch)) + { + /* fs0 is always the most significant half of fd0. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + offset = (regnum & 1) ? 4 : 0; + else + offset = (regnum & 1) ? 0 : 4; + + fdr_regnum = NDS32_FD0_REGNUM + (regnum >> 1); + status = regcache_raw_read (regcache, fdr_regnum, reg_buf); + if (status == REG_VALID) + memcpy (buf, reg_buf + offset, 4); + } + + return status; +} + +/* Implement the "pseudo_register_write" gdbarch method. */ + +static void +nds32_pseudo_register_write (struct gdbarch *gdbarch, + struct regcache *regcache, int regnum, + const gdb_byte *buf) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + gdb_byte reg_buf[8]; + int offset, fdr_regnum; + + /* Sanity check. */ + if (tdep->fpu_freg == -1 || tdep->use_pseudo_fsrs == 0) + return; + + regnum -= gdbarch_num_regs (gdbarch); + + /* Currently, only FSRs could be defined as pseudo registers. */ + if (regnum < gdbarch_num_pseudo_regs (gdbarch)) + { + /* fs0 is always the most significant half of fd0. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + offset = (regnum & 1) ? 4 : 0; + else + offset = (regnum & 1) ? 0 : 4; + + fdr_regnum = NDS32_FD0_REGNUM + (regnum >> 1); + regcache_raw_read (regcache, fdr_regnum, reg_buf); + memcpy (reg_buf + offset, buf, 4); + regcache_raw_write (regcache, fdr_regnum, reg_buf); + } +} + +/* Helper function for NDS32 ABI. Return true if FPRs can be used + to pass function arguments and return value. */ + +static int +nds32_abi_use_fpr (int elf_abi) +{ + return elf_abi == E_NDS_ABI_V2FP_PLUS; +} + +/* Helper function for NDS32 ABI. Return true if GPRs and stack + can be used together to pass an argument. */ + +static int +nds32_abi_split (int elf_abi) +{ + return elf_abi == E_NDS_ABI_AABI; +} + +#define NDS32_NUM_SAVED_REGS (NDS32_LP_REGNUM + 1) + +struct nds32_frame_cache +{ + /* The previous frame's inner most stack address. Used as this + frame ID's stack_addr. */ + CORE_ADDR prev_sp; + + /* The frame's base, optionally used by the high-level debug info. */ + CORE_ADDR base; + + /* During prologue analysis, keep how far the SP and FP have been offset + from the start of the stack frame (as defined by the previous frame's + stack pointer). + During epilogue analysis, keep how far the SP has been offset from the + current stack pointer. */ + CORE_ADDR sp_offset; + CORE_ADDR fp_offset; + + /* The address of the first instruction in this function. */ + CORE_ADDR pc; + + /* Saved registers. */ + CORE_ADDR saved_regs[NDS32_NUM_SAVED_REGS]; +}; + +/* Allocate and initialize a frame cache. */ + +static struct nds32_frame_cache * +nds32_alloc_frame_cache (void) +{ + struct nds32_frame_cache *cache; + int i; + + cache = FRAME_OBSTACK_ZALLOC (struct nds32_frame_cache); + + /* Initialize fp_offset to check if FP is set in prologue. */ + cache->fp_offset = INVALID_OFFSET; + + /* Saved registers. We initialize these to -1 since zero is a valid + offset. */ + for (i = 0; i < NDS32_NUM_SAVED_REGS; i++) + cache->saved_regs[i] = REG_UNAVAIL; + + return cache; +} + +/* Helper function for instructions used to push multiple words. */ + +static void +nds32_push_multiple_words (struct nds32_frame_cache *cache, int rb, int re, + int enable4) +{ + CORE_ADDR sp_offset = cache->sp_offset; + int i; + + /* Check LP, GP, FP in enable4. */ + for (i = 1; i <= 3; i++) + { + if ((enable4 >> i) & 0x1) + { + sp_offset += 4; + cache->saved_regs[NDS32_SP_REGNUM - i] = sp_offset; + } + } + + /* Skip case where re == rb == sp. */ + if ((rb < REG_FP) && (re < REG_FP)) + { + for (i = re; i >= rb; i--) + { + sp_offset += 4; + cache->saved_regs[i] = sp_offset; + } + } + + /* For sp, update the offset. */ + cache->sp_offset = sp_offset; +} + +/* Analyze the instructions within the given address range. If CACHE + is non-NULL, fill it in. Return the first address beyond the given + address range. If CACHE is NULL, return the first address not + recognized as a prologue instruction. */ + +static CORE_ADDR +nds32_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, + CORE_ADDR limit_pc, struct nds32_frame_cache *cache) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi); + /* Current scanning status. */ + int in_prologue_bb = 0; + int val_ta = 0; + uint32_t insn, insn_len; + + for (; pc < limit_pc; pc += insn_len) + { + insn = read_memory_unsigned_integer (pc, 4, BFD_ENDIAN_BIG); + + if ((insn & 0x80000000) == 0) + { + /* 32-bit instruction */ + insn_len = 4; + + if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_SP, 0)) + { + /* addi $sp, $sp, imm15s */ + int imm15s = N32_IMM15S (insn); + + if (imm15s < 0) + { + if (cache != NULL) + cache->sp_offset += -imm15s; + + in_prologue_bb = 1; + continue; + } + } + else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_FP, REG_SP, 0)) + { + /* addi $fp, $sp, imm15s */ + int imm15s = N32_IMM15S (insn); + + if (imm15s > 0) + { + if (cache != NULL) + cache->fp_offset = cache->sp_offset - imm15s; + + in_prologue_bb = 1; + continue; + } + } + else if ((insn & ~(__MASK (19) << 6)) == N32_SMW_ADM + && N32_RA5 (insn) == REG_SP) + { + /* smw.adm Rb, [$sp], Re, enable4 */ + if (cache != NULL) + nds32_push_multiple_words (cache, N32_RT5 (insn), + N32_RB5 (insn), + N32_LSMW_ENABLE4 (insn)); + in_prologue_bb = 1; + continue; + } + else if (insn == N32_ALU1 (ADD, REG_SP, REG_SP, REG_TA) + || insn == N32_ALU1 (ADD, REG_SP, REG_TA, REG_SP)) + { + /* add $sp, $sp, $ta */ + /* add $sp, $ta, $sp */ + if (val_ta < 0) + { + if (cache != NULL) + cache->sp_offset += -val_ta; + + in_prologue_bb = 1; + continue; + } + } + else if (CHOP_BITS (insn, 20) == N32_TYPE1 (MOVI, REG_TA, 0)) + { + /* movi $ta, imm20s */ + if (cache != NULL) + val_ta = N32_IMM20S (insn); + + continue; + } + else if (CHOP_BITS (insn, 20) == N32_TYPE1 (SETHI, REG_TA, 0)) + { + /* sethi $ta, imm20u */ + if (cache != NULL) + val_ta = N32_IMM20U (insn) << 12; + + continue; + } + else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ORI, REG_TA, REG_TA, 0)) + { + /* ori $ta, $ta, imm15u */ + if (cache != NULL) + val_ta |= N32_IMM15U (insn); + + continue; + } + else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_TA, REG_TA, 0)) + { + /* addi $ta, $ta, imm15s */ + if (cache != NULL) + val_ta += N32_IMM15S (insn); + + continue; + } + if (insn == N32_ALU1 (ADD, REG_GP, REG_TA, REG_GP) + || insn == N32_ALU1 (ADD, REG_GP, REG_GP, REG_TA)) + { + /* add $gp, $ta, $gp */ + /* add $gp, $gp, $ta */ + in_prologue_bb = 1; + continue; + } + else if (CHOP_BITS (insn, 20) == N32_TYPE1 (MOVI, REG_GP, 0)) + { + /* movi $gp, imm20s */ + in_prologue_bb = 1; + continue; + } + else if (CHOP_BITS (insn, 20) == N32_TYPE1 (SETHI, REG_GP, 0)) + { + /* sethi $gp, imm20u */ + in_prologue_bb = 1; + continue; + } + else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ORI, REG_GP, REG_GP, 0)) + { + /* ori $gp, $gp, imm15u */ + in_prologue_bb = 1; + continue; + } + else + { + /* Jump/Branch insns never appear in prologue basic block. + The loop can be escaped early when these insns are met. */ + if (in_prologue_bb == 1) + { + int op = N32_OP6 (insn); + + if (op == N32_OP6_JI + || op == N32_OP6_JREG + || op == N32_OP6_BR1 + || op == N32_OP6_BR2 + || op == N32_OP6_BR3) + break; + } + } + + if (abi_use_fpr && N32_OP6 (insn) == N32_OP6_SDC + && __GF (insn, 12, 3) == 0) + { + /* For FPU insns, CP (bit [13:14]) should be CP0, and only + normal form (bit [12] == 0) is used. */ + + /* fsdi FDt, [$sp + (imm12s << 2)] */ + if (N32_RA5 (insn) == REG_SP) + continue; + } + + /* The optimizer might shove anything into the prologue, if + we build up cache (cache != NULL) from analyzing prologue, + we just skip what we don't recognize and analyze further to + make cache as complete as possible. However, if we skip + prologue, we'll stop immediately on unrecognized + instruction. */ + if (cache == NULL) + break; + } + else + { + /* 16-bit instruction */ + insn_len = 2; + + insn >>= 16; + + if (CHOP_BITS (insn, 10) == N16_TYPE10 (ADDI10S, 0)) + { + /* addi10s.sp */ + int imm10s = N16_IMM10S (insn); + + if (imm10s < 0) + { + if (cache != NULL) + cache->sp_offset += -imm10s; + + in_prologue_bb = 1; + continue; + } + } + else if (__GF (insn, 7, 8) == N16_T25_PUSH25) + { + /* push25 */ + if (cache != NULL) + { + int imm8u = (insn & 0x1f) << 3; + int re = (insn >> 5) & 0x3; + const int reg_map[] = { 6, 8, 10, 14 }; + + /* Operation 1 -- smw.adm R6, [$sp], Re, #0xe */ + nds32_push_multiple_words (cache, 6, reg_map[re], 0xe); + + /* Operation 2 -- sp = sp - (imm5u << 3) */ + cache->sp_offset += imm8u; + } + + in_prologue_bb = 1; + continue; + } + else if (insn == N16_TYPE5 (ADD5PC, REG_GP)) + { + /* add5.pc $gp */ + in_prologue_bb = 1; + continue; + } + else if (CHOP_BITS (insn, 5) == N16_TYPE55 (MOVI55, REG_GP, 0)) + { + /* movi55 $gp, imm5s */ + in_prologue_bb = 1; + continue; + } + else + { + /* Jump/Branch insns never appear in prologue basic block. + The loop can be escaped early when these insns are met. */ + if (in_prologue_bb == 1) + { + uint32_t insn5 = CHOP_BITS (insn, 5); + uint32_t insn8 = CHOP_BITS (insn, 8); + uint32_t insn38 = CHOP_BITS (insn, 11); + + if (insn5 == N16_TYPE5 (JR5, 0) + || insn5 == N16_TYPE5 (JRAL5, 0) + || insn5 == N16_TYPE5 (RET5, 0) + || insn8 == N16_TYPE8 (J8, 0) + || insn8 == N16_TYPE8 (BEQZS8, 0) + || insn8 == N16_TYPE8 (BNEZS8, 0) + || insn38 == N16_TYPE38 (BEQZ38, 0, 0) + || insn38 == N16_TYPE38 (BNEZ38, 0, 0) + || insn38 == N16_TYPE38 (BEQS38, 0, 0) + || insn38 == N16_TYPE38 (BNES38, 0, 0)) + break; + } + } + + /* The optimizer might shove anything into the prologue, if + we build up cache (cache != NULL) from analyzing prologue, + we just skip what we don't recognize and analyze further to + make cache as complete as possible. However, if we skip + prologue, we'll stop immediately on unrecognized + instruction. */ + if (cache == NULL) + break; + } + } + + return pc; +} + +/* Implement the "skip_prologue" gdbarch method. + + Find the end of function prologue. */ + +static CORE_ADDR +nds32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + CORE_ADDR func_addr, limit_pc; + + /* See if we can determine the end of the prologue via the symbol table. + If so, then return either PC, or the PC after the prologue, whichever + is greater. */ + if (find_pc_partial_function (pc, NULL, &func_addr, NULL)) + { + CORE_ADDR post_prologue_pc + = skip_prologue_using_sal (gdbarch, func_addr); + if (post_prologue_pc != 0) + return max (pc, post_prologue_pc); + } + + /* Can't determine prologue from the symbol table, need to examine + instructions. */ + + /* Find an upper limit on the function prologue using the debug + information. If the debug information could not be used to provide + that bound, then use an arbitrary large number as the upper bound. */ + limit_pc = skip_prologue_using_sal (gdbarch, pc); + if (limit_pc == 0) + limit_pc = pc + 128; /* Magic. */ + + /* Find the end of prologue. */ + return nds32_analyze_prologue (gdbarch, pc, limit_pc, NULL); +} + +/* Allocate and fill in *THIS_CACHE with information about the prologue of + *THIS_FRAME. Do not do this if *THIS_CACHE was already allocated. Return + a pointer to the current nds32_frame_cache in *THIS_CACHE. */ + +static struct nds32_frame_cache * +nds32_frame_cache (struct frame_info *this_frame, void **this_cache) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + struct nds32_frame_cache *cache; + CORE_ADDR current_pc; + ULONGEST prev_sp; + ULONGEST this_base; + int i; + + if (*this_cache) + return (struct nds32_frame_cache *) *this_cache; + + cache = nds32_alloc_frame_cache (); + *this_cache = cache; + + cache->pc = get_frame_func (this_frame); + current_pc = get_frame_pc (this_frame); + nds32_analyze_prologue (gdbarch, cache->pc, current_pc, cache); + + /* Compute the previous frame's stack pointer (which is also the + frame's ID's stack address), and this frame's base pointer. */ + if (cache->fp_offset != INVALID_OFFSET) + { + /* FP is set in prologue, so it can be used to calculate other info. */ + this_base = get_frame_register_unsigned (this_frame, NDS32_FP_REGNUM); + prev_sp = this_base + cache->fp_offset; + } + else + { + this_base = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM); + prev_sp = this_base + cache->sp_offset; + } + + cache->prev_sp = prev_sp; + cache->base = this_base; + + /* Adjust all the saved registers such that they contain addresses + instead of offsets. */ + for (i = 0; i < NDS32_NUM_SAVED_REGS; i++) + if (cache->saved_regs[i] != REG_UNAVAIL) + cache->saved_regs[i] = cache->prev_sp - cache->saved_regs[i]; + + return cache; +} + +/* Implement the "this_id" frame_unwind method. + + Our frame ID for a normal frame is the current function's starting + PC and the caller's SP when we were called. */ + +static void +nds32_frame_this_id (struct frame_info *this_frame, + void **this_cache, struct frame_id *this_id) +{ + struct nds32_frame_cache *cache = nds32_frame_cache (this_frame, this_cache); + + /* This marks the outermost frame. */ + if (cache->prev_sp == 0) + return; + + *this_id = frame_id_build (cache->prev_sp, cache->pc); +} + +/* Implement the "prev_register" frame_unwind method. */ + +static struct value * +nds32_frame_prev_register (struct frame_info *this_frame, void **this_cache, + int regnum) +{ + struct nds32_frame_cache *cache = nds32_frame_cache (this_frame, this_cache); + + if (regnum == NDS32_SP_REGNUM) + return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp); + + /* The PC of the previous frame is stored in the LP register of + the current frame. */ + if (regnum == NDS32_PC_REGNUM) + regnum = NDS32_LP_REGNUM; + + if (regnum < NDS32_NUM_SAVED_REGS && cache->saved_regs[regnum] != REG_UNAVAIL) + return frame_unwind_got_memory (this_frame, regnum, + cache->saved_regs[regnum]); + + return frame_unwind_got_register (this_frame, regnum, regnum); +} + +static const struct frame_unwind nds32_frame_unwind = +{ + NORMAL_FRAME, + default_frame_unwind_stop_reason, + nds32_frame_this_id, + nds32_frame_prev_register, + NULL, + default_frame_sniffer, +}; + +/* Return the frame base address of *THIS_FRAME. */ + +static CORE_ADDR +nds32_frame_base_address (struct frame_info *this_frame, void **this_cache) +{ + struct nds32_frame_cache *cache = nds32_frame_cache (this_frame, this_cache); + + return cache->base; +} + +static const struct frame_base nds32_frame_base = +{ + &nds32_frame_unwind, + nds32_frame_base_address, + nds32_frame_base_address, + nds32_frame_base_address +}; + +/* Helper function for instructions used to pop multiple words. */ + +static void +nds32_pop_multiple_words (struct nds32_frame_cache *cache, int rb, int re, + int enable4) +{ + CORE_ADDR sp_offset = cache->sp_offset; + int i; + + /* Skip case where re == rb == sp. */ + if ((rb < REG_FP) && (re < REG_FP)) + { + for (i = rb; i <= re; i++) + { + cache->saved_regs[i] = sp_offset; + sp_offset += 4; + } + } + + /* Check FP, GP, LP in enable4. */ + for (i = 3; i >= 1; i--) + { + if ((enable4 >> i) & 0x1) + { + cache->saved_regs[NDS32_SP_REGNUM - i] = sp_offset; + sp_offset += 4; + } + } + + /* For sp, update the offset. */ + cache->sp_offset = sp_offset; +} + +/* The instruction sequences in NDS32 epilogue are + + INSN_RESET_SP (optional) + (If exists, this must be the first instruction in epilogue + and the stack has not been destroyed.). + INSN_RECOVER (optional). + INSN_RETURN/INSN_RECOVER_RETURN (required). */ + +/* Helper function for analyzing the given 32-bit INSN. If CACHE is non-NULL, + the necessary information will be recorded. */ + +static inline int +nds32_analyze_epilogue_insn32 (int abi_use_fpr, uint32_t insn, + struct nds32_frame_cache *cache) +{ + if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_SP, 0) + && N32_IMM15S (insn) > 0) + /* addi $sp, $sp, imm15s */ + return INSN_RESET_SP; + else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_FP, 0) + && N32_IMM15S (insn) < 0) + /* addi $sp, $fp, imm15s */ + return INSN_RESET_SP; + else if ((insn & ~(__MASK (19) << 6)) == N32_LMW_BIM + && N32_RA5 (insn) == REG_SP) + { + /* lmw.bim Rb, [$sp], Re, enable4 */ + if (cache != NULL) + nds32_pop_multiple_words (cache, N32_RT5 (insn), + N32_RB5 (insn), N32_LSMW_ENABLE4 (insn)); + + return INSN_RECOVER; + } + else if (insn == N32_JREG (JR, 0, REG_LP, 0, 1)) + /* ret $lp */ + return INSN_RETURN; + else if (insn == N32_ALU1 (ADD, REG_SP, REG_SP, REG_TA) + || insn == N32_ALU1 (ADD, REG_SP, REG_TA, REG_SP)) + /* add $sp, $sp, $ta */ + /* add $sp, $ta, $sp */ + return INSN_RESET_SP; + else if (abi_use_fpr + && (insn & ~(__MASK (5) << 20 | __MASK (13))) == N32_FLDI_SP) + { + if (__GF (insn, 12, 1) == 0) + /* fldi FDt, [$sp + (imm12s << 2)] */ + return INSN_RECOVER; + else + { + /* fldi.bi FDt, [$sp], (imm12s << 2) */ + int offset = N32_IMM12S (insn) << 2; + + if (offset == 8 || offset == 12) + { + if (cache != NULL) + cache->sp_offset += offset; + + return INSN_RECOVER; + } + } + } + + return INSN_NORMAL; +} + +/* Helper function for analyzing the given 16-bit INSN. If CACHE is non-NULL, + the necessary information will be recorded. */ + +static inline int +nds32_analyze_epilogue_insn16 (uint32_t insn, struct nds32_frame_cache *cache) +{ + if (insn == N16_TYPE5 (RET5, REG_LP)) + /* ret5 $lp */ + return INSN_RETURN; + else if (CHOP_BITS (insn, 10) == N16_TYPE10 (ADDI10S, 0)) + { + /* addi10s.sp */ + int imm10s = N16_IMM10S (insn); + + if (imm10s > 0) + { + if (cache != NULL) + cache->sp_offset += imm10s; + + return INSN_RECOVER; + } + } + else if (__GF (insn, 7, 8) == N16_T25_POP25) + { + /* pop25 */ + if (cache != NULL) + { + int imm8u = (insn & 0x1f) << 3; + int re = (insn >> 5) & 0x3; + const int reg_map[] = { 6, 8, 10, 14 }; + + /* Operation 1 -- sp = sp + (imm5u << 3) */ + cache->sp_offset += imm8u; + + /* Operation 2 -- lmw.bim R6, [$sp], Re, #0xe */ + nds32_pop_multiple_words (cache, 6, reg_map[re], 0xe); + } + + /* Operation 3 -- ret $lp */ + return INSN_RECOVER_RETURN; + } + + return INSN_NORMAL; +} + +/* Analyze a reasonable amount of instructions from the given PC to find + the instruction used to return to the caller. Return 1 if the 'return' + instruction could be found, 0 otherwise. + + If CACHE is non-NULL, fill it in. */ + +static int +nds32_analyze_epilogue (struct gdbarch *gdbarch, CORE_ADDR pc, + struct nds32_frame_cache *cache) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi); + CORE_ADDR limit_pc; + uint32_t insn, insn_len; + int insn_type = INSN_NORMAL; + + if (abi_use_fpr) + limit_pc = pc + 48; + else + limit_pc = pc + 16; + + for (; pc < limit_pc; pc += insn_len) + { + insn = read_memory_unsigned_integer (pc, 4, BFD_ENDIAN_BIG); + + if ((insn & 0x80000000) == 0) + { + /* 32-bit instruction */ + insn_len = 4; + + insn_type = nds32_analyze_epilogue_insn32 (abi_use_fpr, insn, cache); + if (insn_type == INSN_RETURN) + return 1; + else if (insn_type == INSN_RECOVER) + continue; + } + else + { + /* 16-bit instruction */ + insn_len = 2; + + insn >>= 16; + insn_type = nds32_analyze_epilogue_insn16 (insn, cache); + if (insn_type == INSN_RETURN || insn_type == INSN_RECOVER_RETURN) + return 1; + else if (insn_type == INSN_RECOVER) + continue; + } + + /* Stop the scan if this is an unexpected instruction. */ + break; + } + + return 0; +} + +/* Implement the "stack_frame_destroyed_p" gdbarch method. */ + +static int +nds32_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi); + int insn_type = INSN_NORMAL; + int ret_found = 0; + uint32_t insn; + + insn = read_memory_unsigned_integer (addr, 4, BFD_ENDIAN_BIG); + + if ((insn & 0x80000000) == 0) + { + /* 32-bit instruction */ + + insn_type = nds32_analyze_epilogue_insn32 (abi_use_fpr, insn, NULL); + } + else + { + /* 16-bit instruction */ + + insn >>= 16; + insn_type = nds32_analyze_epilogue_insn16 (insn, NULL); + } + + if (insn_type == INSN_NORMAL || insn_type == INSN_RESET_SP) + return 0; + + /* Search the required 'return' instruction within the following reasonable + instructions. */ + ret_found = nds32_analyze_epilogue (gdbarch, addr, NULL); + if (ret_found == 0) + return 0; + + /* Scan backwards to make sure that the last instruction has adjusted + stack. Both a 16-bit and a 32-bit instruction will be tried. This is + just a heuristic, so the false positives will be acceptable. */ + insn = read_memory_unsigned_integer (addr - 2, 4, BFD_ENDIAN_BIG); + + /* Only 16-bit instructions are possible at addr - 2. */ + if ((insn & 0x80000000) != 0) + { + /* This may be a 16-bit instruction or part of a 32-bit instruction. */ + + insn_type = nds32_analyze_epilogue_insn16 (insn >> 16, NULL); + if (insn_type == INSN_RECOVER) + return 1; + } + + insn = read_memory_unsigned_integer (addr - 4, 4, BFD_ENDIAN_BIG); + + /* If this is a 16-bit instruction at addr - 4, then there must be another + 16-bit instruction at addr - 2, so only 32-bit instructions need to + be analyzed here. */ + if ((insn & 0x80000000) == 0) + { + /* This may be a 32-bit instruction or part of a 32-bit instruction. */ + + insn_type = nds32_analyze_epilogue_insn32 (abi_use_fpr, insn, NULL); + if (insn_type == INSN_RECOVER || insn_type == INSN_RESET_SP) + return 1; + } + + return 0; +} + +/* Implement the "sniffer" frame_unwind method. */ + +static int +nds32_epilogue_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, void **this_cache) +{ + if (frame_relative_level (this_frame) == 0) + return nds32_stack_frame_destroyed_p (get_frame_arch (this_frame), + get_frame_pc (this_frame)); + else + return 0; +} + +/* Allocate and fill in *THIS_CACHE with information needed to unwind + *THIS_FRAME within epilogue. Do not do this if *THIS_CACHE was already + allocated. Return a pointer to the current nds32_frame_cache in + *THIS_CACHE. */ + +static struct nds32_frame_cache * +nds32_epilogue_frame_cache (struct frame_info *this_frame, void **this_cache) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + struct nds32_frame_cache *cache; + CORE_ADDR current_pc, current_sp; + int i; + + if (*this_cache) + return (struct nds32_frame_cache *) *this_cache; + + cache = nds32_alloc_frame_cache (); + *this_cache = cache; + + cache->pc = get_frame_func (this_frame); + current_pc = get_frame_pc (this_frame); + nds32_analyze_epilogue (gdbarch, current_pc, cache); + + current_sp = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM); + cache->prev_sp = current_sp + cache->sp_offset; + + /* Adjust all the saved registers such that they contain addresses + instead of offsets. */ + for (i = 0; i < NDS32_NUM_SAVED_REGS; i++) + if (cache->saved_regs[i] != REG_UNAVAIL) + cache->saved_regs[i] = current_sp + cache->saved_regs[i]; + + return cache; +} + +/* Implement the "this_id" frame_unwind method. */ + +static void +nds32_epilogue_frame_this_id (struct frame_info *this_frame, + void **this_cache, struct frame_id *this_id) +{ + struct nds32_frame_cache *cache + = nds32_epilogue_frame_cache (this_frame, this_cache); + + /* This marks the outermost frame. */ + if (cache->prev_sp == 0) + return; + + *this_id = frame_id_build (cache->prev_sp, cache->pc); +} + +/* Implement the "prev_register" frame_unwind method. */ + +static struct value * +nds32_epilogue_frame_prev_register (struct frame_info *this_frame, + void **this_cache, int regnum) +{ + struct nds32_frame_cache *cache + = nds32_epilogue_frame_cache (this_frame, this_cache); + + if (regnum == NDS32_SP_REGNUM) + return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp); + + /* The PC of the previous frame is stored in the LP register of + the current frame. */ + if (regnum == NDS32_PC_REGNUM) + regnum = NDS32_LP_REGNUM; + + if (regnum < NDS32_NUM_SAVED_REGS && cache->saved_regs[regnum] != REG_UNAVAIL) + return frame_unwind_got_memory (this_frame, regnum, + cache->saved_regs[regnum]); + + return frame_unwind_got_register (this_frame, regnum, regnum); +} + +static const struct frame_unwind nds32_epilogue_frame_unwind = +{ + NORMAL_FRAME, + default_frame_unwind_stop_reason, + nds32_epilogue_frame_this_id, + nds32_epilogue_frame_prev_register, + NULL, + nds32_epilogue_frame_sniffer +}; + +/* Implement the "dummy_id" gdbarch method. */ + +static struct frame_id +nds32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + CORE_ADDR sp = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM); + + return frame_id_build (sp, get_frame_pc (this_frame)); +} + +/* Implement the "unwind_pc" gdbarch method. */ + +static CORE_ADDR +nds32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_unwind_register_unsigned (next_frame, NDS32_PC_REGNUM); +} + +/* Implement the "unwind_sp" gdbarch method. */ + +static CORE_ADDR +nds32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_unwind_register_unsigned (next_frame, NDS32_SP_REGNUM); +} + +/* Floating type and struct type that has only one floating type member + can pass value using FPU registers (when FPU ABI is used). */ + +static int +nds32_check_calling_use_fpr (struct type *type) +{ + struct type *t; + enum type_code typecode; + + t = type; + while (1) + { + t = check_typedef (t); + typecode = TYPE_CODE (t); + if (typecode != TYPE_CODE_STRUCT) + break; + else if (TYPE_NFIELDS (t) != 1) + return 0; + else + t = TYPE_FIELD_TYPE (t, 0); + } + + return typecode == TYPE_CODE_FLT; +} + +/* Return the alignment (in bytes) of the given type. */ + +static int +nds32_type_align (struct type *type) +{ + int n; + int align; + int falign; + + type = check_typedef (type); + switch (TYPE_CODE (type)) + { + default: + /* Should never happen. */ + internal_error (__FILE__, __LINE__, _("unknown type alignment")); + return 4; + + case TYPE_CODE_PTR: + case TYPE_CODE_ENUM: + case TYPE_CODE_INT: + case TYPE_CODE_FLT: + case TYPE_CODE_SET: + case TYPE_CODE_RANGE: + case TYPE_CODE_REF: + case TYPE_CODE_CHAR: + case TYPE_CODE_BOOL: + return TYPE_LENGTH (type); + + case TYPE_CODE_ARRAY: + case TYPE_CODE_COMPLEX: + return nds32_type_align (TYPE_TARGET_TYPE (type)); + + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + align = 1; + for (n = 0; n < TYPE_NFIELDS (type); n++) + { + falign = nds32_type_align (TYPE_FIELD_TYPE (type, n)); + if (falign > align) + align = falign; + } + return align; + } +} + +/* Implement the "push_dummy_call" gdbarch method. */ + +static CORE_ADDR +nds32_push_dummy_call (struct gdbarch *gdbarch, struct value *function, + struct regcache *regcache, CORE_ADDR bp_addr, + int nargs, struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + const int REND = 6; /* End for register offset. */ + int goff = 0; /* Current gpr offset for argument. */ + int foff = 0; /* Current fpr offset for argument. */ + int soff = 0; /* Current stack offset for argument. */ + int i; + ULONGEST regval; + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + struct type *func_type = value_type (function); + int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi); + int abi_split = nds32_abi_split (tdep->elf_abi); + + /* Set the return address. For the NDS32, the return breakpoint is + always at BP_ADDR. */ + regcache_cooked_write_unsigned (regcache, NDS32_LP_REGNUM, bp_addr); + + /* If STRUCT_RETURN is true, then the struct return address (in + STRUCT_ADDR) will consume the first argument-passing register. + Both adjust the register count and store that value. */ + if (struct_return) + { + regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, struct_addr); + goff++; + } + + /* Now make sure there's space on the stack */ + for (i = 0; i < nargs; i++) + { + struct type *type = value_type (args[i]); + int align = nds32_type_align (type); + + /* If align is zero, it may be an empty struct. + Just ignore the argument of empty struct. */ + if (align == 0) + continue; + + sp -= TYPE_LENGTH (type); + sp = align_down (sp, align); + } + + /* Stack must be 8-byte aligned. */ + sp = align_down (sp, 8); + + soff = 0; + for (i = 0; i < nargs; i++) + { + const gdb_byte *val; + int align, len; + struct type *type; + int calling_use_fpr; + int use_fpr = 0; + + type = value_type (args[i]); + calling_use_fpr = nds32_check_calling_use_fpr (type); + len = TYPE_LENGTH (type); + align = nds32_type_align (type); + val = value_contents (args[i]); + + /* The size of a composite type larger than 4 bytes will be rounded + up to the nearest multiple of 4. */ + if (len > 4) + len = align_up (len, 4); + + /* Variadic functions are handled differently between AABI and ABI2FP+. + + For AABI, the caller pushes arguments in registers, callee stores + unnamed arguments in stack, and then va_arg fetch arguments in stack. + Therefore, we don't have to handle variadic functions specially. + + For ABI2FP+, the caller pushes only named arguments in registers + and pushes all unnamed arguments in stack. */ + + if (abi_use_fpr && TYPE_VARARGS (func_type) + && i >= TYPE_NFIELDS (func_type)) + goto use_stack; + + /* Try to use FPRs to pass arguments only when + 1. The program is built using toolchain with FPU support. + 2. The type of this argument can use FPR to pass value. */ + use_fpr = abi_use_fpr && calling_use_fpr; + + if (use_fpr) + { + if (tdep->fpu_freg == -1) + goto error_no_fpr; + + /* Adjust alignment. */ + if ((align >> 2) > 0) + foff = align_up (foff, align >> 2); + + if (foff < REND) + { + switch (len) + { + case 4: + regcache_cooked_write (regcache, + tdep->fs0_regnum + foff, val); + foff++; + break; + case 8: + regcache_cooked_write (regcache, + NDS32_FD0_REGNUM + (foff >> 1), val); + foff += 2; + break; + default: + /* Long double? */ + internal_error (__FILE__, __LINE__, + "Do not know how to handle %d-byte double.\n", + len); + break; + } + continue; + } + } + else + { + /* + When passing arguments using GPRs, + + * A composite type not larger than 4 bytes is passed in $rN. + The format is as if the value is loaded with load instruction + of corresponding size (e.g., LB, LH, LW). + + For example, + + r0 + 31 0 + LITTLE: [x x b a] + BIG: [x x a b] + + * Otherwise, a composite type is passed in consecutive registers. + The size is rounded up to the nearest multiple of 4. + The successive registers hold the parts of the argument as if + were loaded using lmw instructions. + + For example, + + r0 r1 + 31 0 31 0 + LITTLE: [d c b a] [x x x e] + BIG: [a b c d] [e x x x] + */ + + /* Adjust alignment. */ + if ((align >> 2) > 0) + goff = align_up (goff, align >> 2); + + if (len <= (REND - goff) * 4) + { + /* This argument can be passed wholly via GPRs. */ + while (len > 0) + { + regval = extract_unsigned_integer (val, (len > 4) ? 4 : len, + byte_order); + regcache_cooked_write_unsigned (regcache, + NDS32_R0_REGNUM + goff, + regval); + len -= 4; + val += 4; + goff++; + } + continue; + } + else if (abi_split) + { + /* Some parts of this argument can be passed via GPRs. */ + while (goff < REND) + { + regval = extract_unsigned_integer (val, (len > 4) ? 4 : len, + byte_order); + regcache_cooked_write_unsigned (regcache, + NDS32_R0_REGNUM + goff, + regval); + len -= 4; + val += 4; + goff++; + } + } + } + +use_stack: + /* + When pushing (split parts of) an argument into stack, + + * A composite type not larger than 4 bytes is copied to different + base address. + In little-endian, the first byte of this argument is aligned + at the low address of the next free word. + In big-endian, the last byte of this argument is aligned + at the high address of the next free word. + + For example, + + sp [ - ] [ c ] hi + [ c ] [ b ] + [ b ] [ a ] + [ a ] [ - ] lo + LITTLE BIG + */ + + /* Adjust alignment. */ + soff = align_up (soff, align); + + while (len > 0) + { + int rlen = (len > 4) ? 4 : len; + + if (byte_order == BFD_ENDIAN_BIG) + write_memory (sp + soff + 4 - rlen, val, rlen); + else + write_memory (sp + soff, val, rlen); + + len -= 4; + val += 4; + soff += 4; + } + } + + /* Finally, update the SP register. */ + regcache_cooked_write_unsigned (regcache, NDS32_SP_REGNUM, sp); + + return sp; + +error_no_fpr: + /* If use_fpr, but no floating-point register exists, + then it is an error. */ + error (_("Fail to call. FPU registers are required.")); +} + +/* Read, for architecture GDBARCH, a function return value of TYPE + from REGCACHE, and copy that into VALBUF. */ + +static void +nds32_extract_return_value (struct gdbarch *gdbarch, struct type *type, + struct regcache *regcache, gdb_byte *valbuf) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi); + int calling_use_fpr; + int len; + + calling_use_fpr = nds32_check_calling_use_fpr (type); + len = TYPE_LENGTH (type); + + if (abi_use_fpr && calling_use_fpr) + { + if (len == 4) + regcache_cooked_read (regcache, tdep->fs0_regnum, valbuf); + else if (len == 8) + regcache_cooked_read (regcache, NDS32_FD0_REGNUM, valbuf); + else + internal_error (__FILE__, __LINE__, + _("Cannot extract return value of %d bytes " + "long floating-point."), len); + } + else + { + /* + When returning result, + + * A composite type not larger than 4 bytes is returned in $r0. + The format is as if the result is loaded with load instruction + of corresponding size (e.g., LB, LH, LW). + + For example, + + r0 + 31 0 + LITTLE: [x x b a] + BIG: [x x a b] + + * Otherwise, a composite type not larger than 8 bytes is returned + in $r0 and $r1. + In little-endian, the first word is loaded in $r0. + In big-endian, the last word is loaded in $r1. + + For example, + + r0 r1 + 31 0 31 0 + LITTLE: [d c b a] [x x x e] + BIG: [x x x a] [b c d e] + */ + + ULONGEST tmp; + + if (len < 4) + { + /* By using store_unsigned_integer we avoid having to do + anything special for small big-endian values. */ + regcache_cooked_read_unsigned (regcache, NDS32_R0_REGNUM, &tmp); + store_unsigned_integer (valbuf, len, byte_order, tmp); + } + else if (len == 4) + { + regcache_cooked_read (regcache, NDS32_R0_REGNUM, valbuf); + } + else if (len < 8) + { + int len1, len2; + + len1 = byte_order == BFD_ENDIAN_BIG ? len - 4 : 4; + len2 = len - len1; + + regcache_cooked_read_unsigned (regcache, NDS32_R0_REGNUM, &tmp); + store_unsigned_integer (valbuf, len1, byte_order, tmp); + + regcache_cooked_read_unsigned (regcache, NDS32_R0_REGNUM + 1, &tmp); + store_unsigned_integer (valbuf + len1, len2, byte_order, tmp); + } + else + { + regcache_cooked_read (regcache, NDS32_R0_REGNUM, valbuf); + regcache_cooked_read (regcache, NDS32_R0_REGNUM + 1, valbuf + 4); + } + } +} + +/* Write, for architecture GDBARCH, a function return value of TYPE + from VALBUF into REGCACHE. */ + +static void +nds32_store_return_value (struct gdbarch *gdbarch, struct type *type, + struct regcache *regcache, const gdb_byte *valbuf) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi); + int calling_use_fpr; + int len; + + calling_use_fpr = nds32_check_calling_use_fpr (type); + len = TYPE_LENGTH (type); + + if (abi_use_fpr && calling_use_fpr) + { + if (len == 4) + regcache_cooked_write (regcache, tdep->fs0_regnum, valbuf); + else if (len == 8) + regcache_cooked_write (regcache, NDS32_FD0_REGNUM, valbuf); + else + internal_error (__FILE__, __LINE__, + _("Cannot store return value of %d bytes " + "long floating-point."), len); + } + else + { + ULONGEST regval; + + if (len < 4) + { + regval = extract_unsigned_integer (valbuf, len, byte_order); + regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, regval); + } + else if (len == 4) + { + regcache_cooked_write (regcache, NDS32_R0_REGNUM, valbuf); + } + else if (len < 8) + { + int len1, len2; + + len1 = byte_order == BFD_ENDIAN_BIG ? len - 4 : 4; + len2 = len - len1; + + regval = extract_unsigned_integer (valbuf, len1, byte_order); + regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, regval); + + regval = extract_unsigned_integer (valbuf + len1, len2, byte_order); + regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM + 1, + regval); + } + else + { + regcache_cooked_write (regcache, NDS32_R0_REGNUM, valbuf); + regcache_cooked_write (regcache, NDS32_R0_REGNUM + 1, valbuf + 4); + } + } +} + +/* Implement the "return_value" gdbarch method. + + Determine, for architecture GDBARCH, how a return value of TYPE + should be returned. If it is supposed to be returned in registers, + and READBUF is non-zero, read the appropriate value from REGCACHE, + and copy it into READBUF. If WRITEBUF is non-zero, write the value + from WRITEBUF into REGCACHE. */ + +static enum return_value_convention +nds32_return_value (struct gdbarch *gdbarch, struct value *func_type, + struct type *type, struct regcache *regcache, + gdb_byte *readbuf, const gdb_byte *writebuf) +{ + if (TYPE_LENGTH (type) > 8) + { + return RETURN_VALUE_STRUCT_CONVENTION; + } + else + { + if (readbuf != NULL) + nds32_extract_return_value (gdbarch, type, regcache, readbuf); + if (writebuf != NULL) + nds32_store_return_value (gdbarch, type, regcache, writebuf); + + return RETURN_VALUE_REGISTER_CONVENTION; + } +} + +/* Implement the "get_longjmp_target" gdbarch method. */ + +static int +nds32_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) +{ + gdb_byte buf[4]; + CORE_ADDR jb_addr; + struct gdbarch *gdbarch = get_frame_arch (frame); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + jb_addr = get_frame_register_unsigned (frame, NDS32_R0_REGNUM); + + if (target_read_memory (jb_addr + 11 * 4, buf, 4)) + return 0; + + *pc = extract_unsigned_integer (buf, 4, byte_order); + return 1; +} + +/* Validate the given TDESC, and fixed-number some registers in it. + Return 0 if the given TDESC does not contain the required feature + or not contain required registers. */ + +static int +nds32_validate_tdesc_p (const struct target_desc *tdesc, + struct tdesc_arch_data *tdesc_data, + int *fpu_freg, int *use_pseudo_fsrs) +{ + const struct tdesc_feature *feature; + int i, valid_p; + + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.nds32.core"); + if (feature == NULL) + return 0; + + valid_p = 1; + /* Validate and fixed-number R0-R10. */ + for (i = NDS32_R0_REGNUM; i <= NDS32_R0_REGNUM + 10; i++) + valid_p &= tdesc_numbered_register (feature, tdesc_data, i, + nds32_register_names[i]); + + /* Validate R15. */ + valid_p &= tdesc_unnumbered_register (feature, + nds32_register_names[NDS32_TA_REGNUM]); + + /* Validate and fixed-number FP, GP, LP, SP, PC. */ + for (i = NDS32_FP_REGNUM; i <= NDS32_PC_REGNUM; i++) + valid_p &= tdesc_numbered_register (feature, tdesc_data, i, + nds32_register_names[i]); + + if (!valid_p) + return 0; + + /* Fixed-number R11-R27. */ + for (i = NDS32_R0_REGNUM + 11; i <= NDS32_R0_REGNUM + 27; i++) + tdesc_numbered_register (feature, tdesc_data, i, nds32_register_names[i]); + + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.nds32.fpu"); + if (feature != NULL) + { + int num_fdr_regs, num_fsr_regs, fs0_regnum, num_listed_fsr; + int freg = -1; + + /* Guess FPU configuration via listed registers. */ + if (tdesc_unnumbered_register (feature, "fd31")) + freg = 3; + else if (tdesc_unnumbered_register (feature, "fd15")) + freg = 2; + else if (tdesc_unnumbered_register (feature, "fd7")) + freg = 1; + else if (tdesc_unnumbered_register (feature, "fd3")) + freg = 0; + + if (freg == -1) + /* Required FDR is not found. */ + return 0; + else + *fpu_freg = freg; + + /* Validate and fixed-number required FDRs. */ + num_fdr_regs = num_fdr_map[freg]; + for (i = 0; i < num_fdr_regs; i++) + valid_p &= tdesc_numbered_register (feature, tdesc_data, + NDS32_FD0_REGNUM + i, + nds32_fdr_register_names[i]); + if (!valid_p) + return 0; + + /* Count the number of listed FSRs, and fixed-number them if present. */ + num_fsr_regs = num_fsr_map[freg]; + fs0_regnum = NDS32_FD0_REGNUM + num_fdr_regs; + num_listed_fsr = 0; + for (i = 0; i < num_fsr_regs; i++) + num_listed_fsr += tdesc_numbered_register (feature, tdesc_data, + fs0_regnum + i, + nds32_fsr_register_names[i]); + + if (num_listed_fsr == 0) + /* No required FSRs are listed explicitly, make them pseudo registers + of FDRs. */ + *use_pseudo_fsrs = 1; + else if (num_listed_fsr == num_fsr_regs) + /* All required FSRs are listed explicitly. */ + *use_pseudo_fsrs = 0; + else + /* Some required FSRs are missing. */ + return 0; + } + + return 1; +} + +/* Initialize the current architecture based on INFO. If possible, + re-use an architecture from ARCHES, which is a list of + architectures already created during this debugging session. + + Called e.g. at program startup, when reading a core file, and when + reading a binary file. */ + +static struct gdbarch * +nds32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch *gdbarch; + struct gdbarch_tdep *tdep; + struct gdbarch_list *best_arch; + struct tdesc_arch_data *tdesc_data = NULL; + const struct target_desc *tdesc = info.target_desc; + int elf_abi = E_NDS_ABI_AABI; + int fpu_freg = -1; + int use_pseudo_fsrs = 0; + int i, num_regs, maxregs; + + /* Extract the elf_flags if available. */ + if (info.abfd && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) + elf_abi = elf_elfheader (info.abfd)->e_flags & EF_NDS_ABI; + + /* If there is already a candidate, use it. */ + for (best_arch = gdbarch_list_lookup_by_info (arches, &info); + best_arch != NULL; + best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info)) + { + struct gdbarch_tdep *idep = gdbarch_tdep (best_arch->gdbarch); + + if (idep->elf_abi != elf_abi) + continue; + + /* Found a match. */ + break; + } + + if (best_arch != NULL) + return best_arch->gdbarch; + + if (!tdesc_has_registers (tdesc)) + tdesc = tdesc_nds32; + + tdesc_data = tdesc_data_alloc (); + + if (!nds32_validate_tdesc_p (tdesc, tdesc_data, &fpu_freg, &use_pseudo_fsrs)) + { + tdesc_data_cleanup (tdesc_data); + return NULL; + } + + /* Allocate space for the new architecture. */ + tdep = XCNEW (struct gdbarch_tdep); + tdep->fpu_freg = fpu_freg; + tdep->use_pseudo_fsrs = use_pseudo_fsrs; + tdep->fs0_regnum = -1; + tdep->elf_abi = elf_abi; + + gdbarch = gdbarch_alloc (&info, tdep); + + if (fpu_freg == -1) + num_regs = NDS32_NUM_REGS; + else if (use_pseudo_fsrs == 1) + { + set_gdbarch_pseudo_register_read (gdbarch, nds32_pseudo_register_read); + set_gdbarch_pseudo_register_write (gdbarch, nds32_pseudo_register_write); + set_tdesc_pseudo_register_name (gdbarch, nds32_pseudo_register_name); + set_tdesc_pseudo_register_type (gdbarch, nds32_pseudo_register_type); + set_gdbarch_num_pseudo_regs (gdbarch, num_fsr_map[fpu_freg]); + + num_regs = NDS32_NUM_REGS + num_fdr_map[fpu_freg]; + } + else + num_regs = NDS32_NUM_REGS + num_fdr_map[fpu_freg] + num_fsr_map[fpu_freg]; + + set_gdbarch_num_regs (gdbarch, num_regs); + tdesc_use_registers (gdbarch, tdesc, tdesc_data); + + /* Cache the register number of fs0. */ + if (fpu_freg != -1) + tdep->fs0_regnum = user_reg_map_name_to_regnum (gdbarch, "fs0", -1); + + /* Add NDS32 register aliases. To avoid search in user register name space, + user_reg_map_name_to_regnum is not used. */ + maxregs = (gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch)); + for (i = 0; i < ARRAY_SIZE (nds32_register_aliases); i++) + { + int regnum, j; + + regnum = -1; + /* Search register name space. */ + for (j = 0; j < maxregs; j++) + { + const char *regname = gdbarch_register_name (gdbarch, j); + + if (regname != NULL + && strcmp (regname, nds32_register_aliases[i].name) == 0) + { + regnum = j; + break; + } + } + + /* Try next alias entry if the given name can not be found in register + name space. */ + if (regnum == -1) + continue; + + user_reg_add (gdbarch, nds32_register_aliases[i].alias, + value_of_nds32_reg, (const void *) (intptr_t) regnum); + } + + nds32_add_reggroups (gdbarch); + + /* Hook in ABI-specific overrides, if they have been registered. */ + info.tdep_info = (void *) tdesc_data; + gdbarch_init_osabi (info, gdbarch); + + /* Override tdesc_register callbacks for system registers. */ + set_gdbarch_register_reggroup_p (gdbarch, nds32_register_reggroup_p); + + set_gdbarch_sp_regnum (gdbarch, NDS32_SP_REGNUM); + set_gdbarch_pc_regnum (gdbarch, NDS32_PC_REGNUM); + set_gdbarch_unwind_sp (gdbarch, nds32_unwind_sp); + set_gdbarch_unwind_pc (gdbarch, nds32_unwind_pc); + set_gdbarch_stack_frame_destroyed_p (gdbarch, nds32_stack_frame_destroyed_p); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, nds32_dwarf2_reg_to_regnum); + + set_gdbarch_push_dummy_call (gdbarch, nds32_push_dummy_call); + set_gdbarch_return_value (gdbarch, nds32_return_value); + set_gdbarch_dummy_id (gdbarch, nds32_dummy_id); + + set_gdbarch_skip_prologue (gdbarch, nds32_skip_prologue); + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + set_gdbarch_breakpoint_from_pc (gdbarch, nds32_breakpoint_from_pc); + + set_gdbarch_frame_align (gdbarch, nds32_frame_align); + frame_base_set_default (gdbarch, &nds32_frame_base); + + set_gdbarch_print_insn (gdbarch, print_insn_nds32); + + /* Handle longjmp. */ + set_gdbarch_get_longjmp_target (gdbarch, nds32_get_longjmp_target); + + /* The order of appending is the order it check frame. */ + dwarf2_append_unwinders (gdbarch); + frame_unwind_append_unwinder (gdbarch, &nds32_epilogue_frame_unwind); + frame_unwind_append_unwinder (gdbarch, &nds32_frame_unwind); + + return gdbarch; +} + +void +_initialize_nds32_tdep (void) +{ + /* Initialize gdbarch. */ + register_gdbarch_init (bfd_arch_nds32, nds32_gdbarch_init); + + initialize_tdesc_nds32 (); + nds32_init_reggroups (); +} diff --git a/gdb/nds32-tdep.h b/gdb/nds32-tdep.h new file mode 100644 index 0000000..78ad7b2 --- /dev/null +++ b/gdb/nds32-tdep.h @@ -0,0 +1,54 @@ +/* Target-dependent code for the NDS32 architecture, for GDB. + + Copyright (C) 2013-2016 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef NDS32_TDEP_H +#define NDS32_TDEP_H + +enum nds32_regnum +{ + /* General purpose registers. */ + NDS32_R0_REGNUM = 0, + NDS32_R5_REGNUM = 5, + NDS32_TA_REGNUM = 15, /* Temporary register. */ + NDS32_FP_REGNUM = 28, /* Frame pointer. */ + NDS32_GP_REGNUM = 29, /* Global pointer. */ + NDS32_LP_REGNUM = 30, /* Link pointer. */ + NDS32_SP_REGNUM = 31, /* Stack pointer. */ + + NDS32_PC_REGNUM = 32, /* Program counter. */ + + NDS32_NUM_REGS, + + /* The first double precision floating-point register. */ + NDS32_FD0_REGNUM = NDS32_NUM_REGS, +}; + +struct gdbarch_tdep +{ + /* The guessed FPU configuration. */ + int fpu_freg; + /* FSRs are defined as pseudo registers. */ + int use_pseudo_fsrs; + /* Cached regnum of the first FSR (FS0). */ + int fs0_regnum; + /* ELF ABI info. */ + int elf_abi; +}; +#endif /* NDS32_TDEP_H */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ba7dd39..3dad273 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2016-06-17 Yan-Ting Lin <currygt52@gmail.com> + + * gdb.base/float.exp: Add target check for nds32*-*-*. + * gdb.xml/tdesc-regs.exp: Set core-regs for nds32*-*-*. + 2016-06-13 Andrew Burgess <andrew.burgess@embecosm.com> * gdb.base/call-ar-st.exp: Report unsupported rather than xfail diff --git a/gdb/testsuite/gdb.base/float.exp b/gdb/testsuite/gdb.base/float.exp index 939f07f..bc3e230 100644 --- a/gdb/testsuite/gdb.base/float.exp +++ b/gdb/testsuite/gdb.base/float.exp @@ -76,6 +76,15 @@ if { [is_aarch64_target] } then { pass "info float (with FPU)" } } +} elseif [istarget "nds32*-*-*"] then { + gdb_test_multiple "info float" "info_float" { + -re "fd0.*fd3.*$gdb_prompt $" { + pass "info float (with FPU)" + } + -re "No floating.point info available for this processor.*" { + pass "info float (without FPU)" + } + } } elseif [istarget "powerpc*-*-*"] then { gdb_test_multiple "info float" "info_float" { -re "f0.*f1.*f31.*fpscr.*$gdb_prompt $" { diff --git a/gdb/testsuite/gdb.xml/tdesc-regs.exp b/gdb/testsuite/gdb.xml/tdesc-regs.exp index 48204cd..c197e28 100644 --- a/gdb/testsuite/gdb.xml/tdesc-regs.exp +++ b/gdb/testsuite/gdb.xml/tdesc-regs.exp @@ -39,6 +39,9 @@ switch -glob -- [istarget] { "mips*-*-*" { set core-regs {mips-cpu.xml mips-cp0.xml mips-fpu.xml mips-dsp.xml} } + "nds32*-*-*" { + set core-regs {nds32-core.xml} + } "nios2-*-*" { set core-regs {nios2-cpu.xml} } |