aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYan-Ting Lin <currygt52@gmail.com>2016-06-17 15:25:08 +0800
committerpatrick <patrick@andestech.com>2016-06-17 16:58:05 +0800
commita28d8e5037333291991f7b0036b273e8ca1ffc34 (patch)
tree73724d3d633b6b0bfb733ebb1aa5bee0f35231eb
parent96074adc6a780c7984645e3e42434be368974737 (diff)
downloadgdb-a28d8e5037333291991f7b0036b273e8ca1ffc34.zip
gdb-a28d8e5037333291991f7b0036b273e8ca1ffc34.tar.gz
gdb-a28d8e5037333291991f7b0036b273e8ca1ffc34.tar.bz2
gdb: new AndesTech NDS32 port
gdb/ChangeLog: * 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. gdb/doc/ChangeLog: * gdb.texinfo (Standard Target Features): Document NDS32 features. (NDS32 Features): New Section. gdb/testsuite/ChangeLog: * gdb.base/float.exp: Add target check for nds32*-*-*. * gdb.xml/tdesc-regs.exp: Set core-regs for nds32*-*-*.
-rw-r--r--gdb/ChangeLog16
-rw-r--r--gdb/Makefile.in4
-rw-r--r--gdb/NEWS4
-rw-r--r--gdb/configure.tgt5
-rw-r--r--gdb/doc/ChangeLog5
-rw-r--r--gdb/doc/gdb.texinfo23
-rw-r--r--gdb/features/Makefile1
-rw-r--r--gdb/features/nds32-core.xml44
-rw-r--r--gdb/features/nds32-fpu.xml42
-rw-r--r--gdb/features/nds32-system.xml14
-rw-r--r--gdb/features/nds32.c92
-rw-r--r--gdb/features/nds32.xml14
-rw-r--r--gdb/nds32-tdep.c2184
-rw-r--r--gdb/nds32-tdep.h54
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.base/float.exp9
-rw-r--r--gdb/testsuite/gdb.xml/tdesc-regs.exp3
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 \
diff --git a/gdb/NEWS b/gdb/NEWS
index a3852ca..340a751 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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}
}