PATCH: 3/6: Add AVX support (i386 changes)

H.J. Lu
2010-03-04T18:06:58Z

Hi,

Here are i386 changes to support AVX. OK to install?

Thanks.

H.J.
----
2010-03-04 H.J. Lu <hongjiu.lu@intel.com>

* i386-darwin-tdep.c (i386_darwin_init_abi): Replace num_xmm_regs
with num_vector_regs.

* i386-linux-nat.c: Include "regset.h", "elf/common.h" and
<sys/uio.h>.
(xstate_size): New.
(xstate_size_n_of_int64): Likewise.
(fetch_xstateregs): Likewise.
(store_xstateregs): Likewise.
(i386_linux_fetch_inferior_registers): Support XSAVE extended
state.
(i386_linux_store_inferior_registers): Likewise.
(i386_linux_read_description): Check and enable AVX target
descriptions.

* i386-linux-tdep.c: Include "regset.h", "i387-tdep.h",
"i386-xstate.h" and "features/i386/i386-avx-linux.c".
(i386_linux_regset_sections): Make it global. Add
".reg-xstate".
(i386_linux_update_xstateregset): New.
(i386_linux_core_read_xcr0): Likewise.
(i386_linux_core_read_description): Check and enable AVX target
description.
(i386_linux_init_abi): Set xsave_xcr0_offset.
(_initialize_i386_linux_tdep): Call
initialize_tdesc_i386_avx_linux.

* i386-linux-tdep.h (i386_linux_core_read_xcr0): New.
(tdesc_i386_avx_linux): Likewise.
(i386_linux_regset_sections): Likewise.
(i386_linux_update_xstateregset): Likewise.
(I386_LINUX_XSAVE_XCR0_OFFSET): Likewise.

* i386-nto-tdep.c (i386nto_register_area): Replace
I387_XMM0_REGNUM with I387_VECTOR0_REGNUM.

* i386-tdep.c: Include "i386-xstate.h" and
"features/i386/i386-avx.c".
(i386_register_names): Renamed to ...
(i386_sse_register_names): This.
(i386_avx_register_names): New.
(i386_xmm_names): Likewise.
(i386_xmm_regnum_p): Likewise.
(i386_vector_regnum_p): Likewise.
(i386_supply_xstateregset): Likewise.
(i386_collect_xstateregset): Likewise.
(i386_xmm_type): Likewise.
(i386_sse_regnum_p): Removed.
(i386_mxcsr_regnum_p): Replace I387_XMM0_REGNUM with
I387_VECTOR0_REGNUM.
(i386_dbx_reg_to_regnum): Likewise.
(i386_pseudo_register_name): Support pseudo XMM registers.
(i386_pseudo_register_type): Likewise.
(i386_pseudo_register_read): Likewise.
(i386_pseudo_register_write): Likewise.
(i386_register_reggroup_p): Likewise.
(i386_regset_from_core_section): Support .reg-xstate section.
(i386_go32_init_abi): Replace num_xmm_regs with num_vector_regs.
(i386_validate_tdesc_p): Check org.gnu.gdb.i386.avx feature.
Set xcr0.
(i386_gdbarch_init): Set xstateregset to NULL. Replace
num_xmm_regs with num_vector_regs. Set num_xmm_regs. Add
num_xmm_regs to set_gdbarch_num_pseudo_regs. Call
set_gdbarch_qsupported.
(_initialize_i386_tdep): Call initialize_tdesc_i386_avx.

* i386-tdep.h (gdbarch_tdep): Add xstateregset, xmm0_regnum,
num_vector_regs, xcr0, xsave_xcr0_offset and i386_xmm_type.
(I386_MAX_REGISTER_SIZE): Changed to 32.
(i386_xmm_regnum_p): New.

* common/i386-xstate.h: New.
* config/i386/nm-linux-xstate.h: Likewise.
* config/i386/nm-linux64.h: Likewise.

* config/i386/linux64.mh (NAT_FILE): Set to nm-linux64.h.

* config/i386/nm-linux.h: Include "config/i386/nm-linux-xstate.h".

diff --git a/gdb/common/i386-xstate.h b/gdb/common/i386-xstate.h
new file mode 100644
index 0000000..8089e10
--- /dev/null
+++ b/gdb/common/i386-xstate.h
@@ -0,0 +1,53 @@
+/* Common code for i386 XSAVE extended state.
+
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ 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 I386_XSTATE_H
+#define I386_XSTATE_H 1
+
+/* The extended state feature bits. */
+#define bit_I386_XSTATE_X87 (1ULL << 0)
+#define bit_I386_XSTATE_SSE (1ULL << 1)
+#define bit_I386_XSTATE_AVX (1ULL << 2)
+
+/* Supported mask and size of the extended state. */
+#define I386_XSTATE_SSE_MASK \
+ (bit_I386_XSTATE_X87 | bit_I386_XSTATE_SSE)
+#define I386_XSTATE_AVX_MASK \
+ (I386_XSTATE_SSE_MASK | bit_I386_XSTATE_AVX)
+#define I386_XSTATE_MAX_MASK \
+ I386_XSTATE_AVX_MASK
+
+#define I386_XSTATE_SSE_MASK_STRING "0x3"
+#define I386_XSTATE_AVX_MASK_STRING "0x7"
+#define I386_XSTATE_MAX_MASK_STRING "0x7"
+
+#define I386_XSTATE_SSE_SIZE 576
+#define I386_XSTATE_AVX_SIZE 832
+#define I386_XSTATE_MAX_SIZE 832
+
+#define I386_XSTATE_SSE_SIZE_STRING "576"
+#define I386_XSTATE_AVX_SIZE_STRING "832"
+#define I386_XSTATE_MAX_SIZE_STRING "832"
+
+/* Get I386 XSAVE extended state size. */
+#define I386_XSTATE_SIZE(XCR0) \
+ (((XCR0) & bit_I386_XSTATE_AVX) != 0 \
+ ? I386_XSTATE_AVX_SIZE : I386_XSTATE_SSE_SIZE)
+
+#endif /* I386_XSTATE_H */
diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh
index 19f3be0..99a5042 100644
--- a/gdb/config/i386/linux64.mh
+++ b/gdb/config/i386/linux64.mh
@@ -2,7 +2,7 @@
NATDEPFILES= inf-ptrace.o fork-child.o \
i386-nat.o amd64-nat.o amd64-linux-nat.o linux-nat.o \
proc-service.o linux-thread-db.o linux-fork.o
-NAT_FILE= config/nm-linux.h
+NAT_FILE= nm-linux64.h

# The dynamically loaded libthread_db needs access to symbols in the
# gdb executable.
diff --git a/gdb/config/i386/nm-linux-xstate.h b/gdb/config/i386/nm-linux-xstate.h
new file mode 100644
index 0000000..0dbf9e5
--- /dev/null
+++ b/gdb/config/i386/nm-linux-xstate.h
@@ -0,0 +1,33 @@
+/* Native XSAVE extended state support for GNU/Linux x86.
+
+ Copyright 2010 Free Software Foundation, Inc.
+
+ 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 NM_LINUX_XSTATE_H
+#define NM_LINUX_XSTATE_H
+
+#include "i386-xstate.h"
+
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET 0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET 0x4205
+#endif
+
+#endif /* NM_LINUX_XSTATE_H */
diff --git a/gdb/config/i386/nm-linux.h b/gdb/config/i386/nm-linux.h
index 10db309..fab8a0d 100644
--- a/gdb/config/i386/nm-linux.h
+++ b/gdb/config/i386/nm-linux.h
@@ -23,6 +23,7 @@
#define NM_LINUX_H

#include "config/nm-linux.h"
+#include "config/i386/nm-linux-xstate.h"

#ifdef HAVE_PTRACE_GETFPXREGS
/* Include register set support for the SSE registers. */
diff --git a/gdb/config/i386/nm-linux64.h b/gdb/config/i386/nm-linux64.h
new file mode 100644
index 0000000..75220d6
--- /dev/null
+++ b/gdb/config/i386/nm-linux64.h
@@ -0,0 +1,26 @@
+/* Native support for GNU/Linux x86-64.
+
+ Copyright 2010 Free Software Foundation, Inc.
+
+ 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 NM_LINUX64_H
+#define NM_LINUX64_H
+
+#include "config/nm-linux.h"
+#include "config/i386/nm-linux-xstate.h"
+
+#endif /* nm-linux64.h */
diff --git a/gdb/i386-darwin-tdep.c b/gdb/i386-darwin-tdep.c
index 25a5e50..5b2bc7e 100644
--- a/gdb/i386-darwin-tdep.c
+++ b/gdb/i386-darwin-tdep.c
@@ -253,7 +253,7 @@ i386_darwin_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

/* We support the SSE registers. */
- tdep->num_xmm_regs = I386_NUM_XREGS - 1;
+ tdep->num_vector_regs = I386_NUM_XREGS - 1;
set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS);

dwarf2_frame_set_signal_frame_p (gdbarch, darwin_dwarf_signal_frame_p);
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 31b9086..fa6ea20 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -23,11 +23,14 @@
#include "inferior.h"
#include "gdbcore.h"
#include "regcache.h"
+#include "regset.h"
#include "target.h"
#include "linux-nat.h"

#include "gdb_assert.h"
#include "gdb_string.h"
+#include "elf/common.h"
+#include <sys/uio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/procfs.h>
@@ -69,6 +72,16 @@

/* Defines ps_err_e, struct ps_prochandle. */
#include "gdb_proc_service.h"
+
+/* The extended state size in bytes. */
+static unsigned int xstate_size;
+
+/* The extended state size in unit of int64. We use array of int64 for
+ better alignment. */
+static unsigned int xstate_size_n_of_int64;
+
+/* Does the current host support PTRACE_GETREGSET? */
+static int have_ptrace_getregset = -1;
\f

/* The register sets used in GNU/Linux ELF core-dumps are identical to
@@ -355,6 +368,57 @@ static void store_fpregs (const struct regcache *regcache, int tid, int regno) {

/* Transfering floating-point and SSE registers to and from GDB. */

+/* Fetch all registers covered by the PTRACE_GETREGSET request from
+ process/thread TID and store their values in GDB's register array.
+ Return non-zero if successful, zero otherwise. */
+
+static int
+fetch_xstateregs (struct regcache *regcache, int tid)
+{
+ unsigned long long xstateregs[xstate_size_n_of_int64];
+ struct iovec iov;
+
+ if (!have_ptrace_getregset)
+ return 0;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = xstate_size;
+ if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ (int) &iov) < 0)
+ perror_with_name (_("Couldn't read extended state status"));
+
+ i387_supply_xsave (regcache, -1, xstateregs);
+ return 1;
+}
+
+/* Store all valid registers in GDB's register array covered by the
+ PTRACE_SETREGSET request into the process/thread specified by TID.
+ Return non-zero if successful, zero otherwise. */
+
+static int
+store_xstateregs (const struct regcache *regcache, int tid, int regno)
+{
+ unsigned long long xstateregs[xstate_size_n_of_int64];
+ struct iovec iov;
+
+ if (!have_ptrace_getregset)
+ return 0;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = xstate_size;
+ if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ (int) &iov) < 0)
+ perror_with_name (_("Couldn't read extended state status"));
+
+ i387_collect_xsave (regcache, regno, xstateregs, 0);
+
+ if (ptrace (PTRACE_SETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ (int) &iov) < 0)
+ perror_with_name (_("Couldn't write extended state status"));
+
+ return 1;
+}
+
#ifdef HAVE_PTRACE_GETFPXREGS

/* Fill GDB's register array with the floating-point and SSE register
@@ -489,6 +553,8 @@ i386_linux_fetch_inferior_registers (struct target_ops *ops,
return;
}

+ if (fetch_xstateregs (regcache, tid))
+ return;
if (fetch_fpxregs (regcache, tid))
return;
fetch_fpregs (regcache, tid);
@@ -503,6 +569,8 @@ i386_linux_fetch_inferior_registers (struct target_ops *ops,

if (GETFPXREGS_SUPPLIES (regno))
{
+ if (fetch_xstateregs (regcache, tid))
+ return;
if (fetch_fpxregs (regcache, tid))
return;

@@ -553,6 +621,8 @@ i386_linux_store_inferior_registers (struct target_ops *ops,
if (regno == -1)
{
store_regs (regcache, tid, regno);
+ if (store_xstateregs (regcache, tid, regno))
+ return;
if (store_fpxregs (regcache, tid, regno))
return;
store_fpregs (regcache, tid, regno);
@@ -567,6 +637,8 @@ i386_linux_store_inferior_registers (struct target_ops *ops,

if (GETFPXREGS_SUPPLIES (regno))
{
+ if (store_xstateregs (regcache, tid, regno))
+ return;
if (store_fpxregs (regcache, tid, regno))
return;

@@ -858,7 +930,49 @@ i386_linux_child_post_startup_inferior (ptid_t ptid)
static const struct target_desc *
i386_linux_read_description (struct target_ops *ops)
{
- return tdesc_i386_linux;
+ static unsigned long long xcr0;
+
+ if (have_ptrace_getregset == -1)
+ {
+ int tid;
+ unsigned long long xstateregs[(I386_XSTATE_SSE_SIZE
+ / sizeof (long long))];
+ struct iovec iov;
+
+ /* GNU/Linux LWP ID's are process ID's. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = I386_XSTATE_SSE_SIZE;
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, tid,
+ (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+ have_ptrace_getregset = 0;
+ else
+ {
+ have_ptrace_getregset = 1;
+
+ /* Get XCR0 from XSAVE extended state. */
+ xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+ / sizeof (long long))];
+
+ xstate_size = I386_XSTATE_SIZE (xcr0);
+ xstate_size_n_of_int64 = xstate_size / sizeof (long long);
+ }
+
+ i386_linux_update_xstateregset (i386_linux_regset_sections,
+ xstate_size);
+ }
+
+ /* Check the native XCR0 only if PTRACE_GETREGSET is available. */
+ if (have_ptrace_getregset
+ && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
+ return tdesc_i386_avx_linux;
+ else
+ return tdesc_i386_linux;
}

void
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index b23c109..2eb0eae 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -23,6 +23,7 @@
#include "frame.h"
#include "value.h"
#include "regcache.h"
+#include "regset.h"
#include "inferior.h"
#include "osabi.h"
#include "reggroups.h"
@@ -36,9 +37,11 @@
#include "solib-svr4.h"
#include "symtab.h"
#include "arch-utils.h"
-#include "regset.h"
#include "xml-syscall.h"

+#include "i387-tdep.h"
+#include "i386-xstate.h"
+
/* The syscall's XML filename for i386. */
#define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"

@@ -47,13 +50,15 @@
#include <stdint.h>

#include "features/i386/i386-linux.c"
+#include "features/i386/i386-avx-linux.c"

/* Supported register note sections. */
-static struct core_regset_section i386_linux_regset_sections[] =
+struct core_regset_section i386_linux_regset_sections[] =
{
{ ".reg", 144, "general-purpose" },
{ ".reg2", 108, "floating-point" },
{ ".reg-xfp", 512, "extended floating-point" },
+ { ".reg-xstate", 0, "XSAVE extended state" },
{ NULL, 0 }
};

@@ -560,6 +565,66 @@ static int i386_linux_sc_reg_offset[] =
0 * 4 /* %gs */
};

+/* Update XSAVE extended state register note section. */
+
+void
+i386_linux_update_xstateregset
+ (struct core_regset_section *regset_sections, unsigned int xstate_size)
+{
+ int i;
+
+ /* Update the XSAVE extended state register note section for "gcore".
+ Disable it if its size is 0. */
+ for (i = 0; regset_sections[i].sect_name != NULL; i++)
+ if (strcmp (regset_sections[i].sect_name, ".reg-xstate") == 0)
+ {
+ if (xstate_size)
+ regset_sections[i].size = xstate_size;
+ else
+ regset_sections[i].sect_name = NULL;
+ break;
+ }
+}
+
+/* Get XSAVE extended state xcr0 from core dump. */
+
+unsigned long long
+i386_linux_core_read_xcr0 (struct gdbarch *gdbarch,
+ struct target_ops *target, bfd *abfd)
+{
+ asection *xstate = bfd_get_section_by_name (abfd, ".reg-xstate");
+ unsigned long long xcr0;
+
+ if (xstate)
+ {
+ size_t size = bfd_section_size (abfd, xstate);
+
+ gdb_assert (size >= I386_XSTATE_SSE_SIZE);
+
+ /* Check extended state size. */
+ if (size < I386_XSTATE_AVX_SIZE)
+ xcr0 = I386_XSTATE_SSE_MASK;
+ else
+ {
+ char contents[8];
+
+ if (! bfd_get_section_contents (abfd, xstate, contents,
+ (file_ptr) I386_LINUX_XSAVE_XCR0_OFFSET,
+ 8))
+ {
+ warning (_("Couldn't read `xcr0' bytes from `.reg-xstate' section in core file."));
+ return 0;
+ }
+
+ xcr0 = bfd_get_64 (abfd, contents);
+ }
+ }
+ else
+ xcr0 = I386_XSTATE_SSE_MASK;
+
+ return xcr0;
+}
+
/* Get Linux/x86 target description from core dump. */

static const struct target_desc *
@@ -568,12 +633,17 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
bfd *abfd)
{
asection *section = bfd_get_section_by_name (abfd, ".reg2");
+ unsigned long long xcr0;

if (section == NULL)
return NULL;

/* Linux/i386. */
- return tdesc_i386_linux;
+ xcr0 = i386_linux_core_read_xcr0 (gdbarch, target, abfd);
+ if ((xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
+ return tdesc_i386_avx_linux;
+ else
+ return tdesc_i386_linux;
}

static void
@@ -623,6 +693,8 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->sc_reg_offset = i386_linux_sc_reg_offset;
tdep->sc_num_regs = ARRAY_SIZE (i386_linux_sc_reg_offset);

+ tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_OFFSET;
+
set_gdbarch_process_record (gdbarch, i386_process_record);
set_gdbarch_process_record_signal (gdbarch, i386_linux_record_signal);

@@ -840,4 +912,5 @@ _initialize_i386_linux_tdep (void)

/* Initialize the Linux target description */
initialize_tdesc_i386_linux ();
+ initialize_tdesc_i386_avx_linux ();
}
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 11f7295..d29f0f8 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -35,7 +35,40 @@
/* Total number of registers for GNU/Linux. */
#define I386_LINUX_NUM_REGS (I386_LINUX_ORIG_EAX_REGNUM + 1)

+/* Get XSAVE extended state xcr0 from core dump. */
+extern unsigned long long i386_linux_core_read_xcr0
+ (struct gdbarch *gdbarch, struct target_ops *target, bfd *abfd);
+
/* Linux target description. */
extern struct target_desc *tdesc_i386_linux;
+extern struct target_desc *tdesc_i386_avx_linux;
+
+/* Supported register note sections. */
+extern struct core_regset_section i386_linux_regset_sections[];
+
+/* Update XSAVE extended state register note section. */
+extern void i386_linux_update_xstateregset
+ (struct core_regset_section *regset_sections, unsigned int xstate_size);
+
+/* Format of XSAVE extended state is:
+ struct
+ {
+ fxsave_bytes[0..463]
+ sw_usable_bytes[464..511]
+ xstate_hdr_bytes[512..575]
+ avx_bytes[576..831]
+ future_state etc
+ };
+
+ Same memory layout will be used for the coredump NT_X86_XSTATE
+ representing the XSAVE extended state registers.
+
+ The first 8 bytes of the sw_usable_bytes[464..467] is set to OS enabled
+ enabled state mask, which is same as the 64bit mask returned by the
+ xgetbv's XCR0). We can use this mask as well as the mask saved in the
+ xstate_hdr bytes to interpret what states the processor/OS supports and
+ what state is in, used/initialized conditions, for the particular
+ process/thread. */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464

#endif /* i386-linux-tdep.h */
diff --git a/gdb/i386-nto-tdep.c b/gdb/i386-nto-tdep.c
index 09c55e2..cbf24cf 100644
--- a/gdb/i386-nto-tdep.c
+++ b/gdb/i386-nto-tdep.c
@@ -158,7 +158,7 @@ i386nto_register_area (struct gdbarch *gdbarch,
&& regno <= I387_FOP_REGNUM (tdep));
int st_reg = (regno >= I387_ST0_REGNUM (tdep)
&& regno < I387_ST0_REGNUM (tdep) + 8);
- int xmm_reg = (regno >= I387_XMM0_REGNUM (tdep)
+ int xmm_reg = (regno >= I387_VECTOR0_REGNUM (tdep)
&& regno < I387_MXCSR_REGNUM (tdep));

if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
@@ -194,7 +194,7 @@ i386nto_register_area (struct gdbarch *gdbarch,
/* XMM registers. */
regsize = 16;
off_adjust = 160;
- regno_base = I387_XMM0_REGNUM (tdep);
+ regno_base = I387_VECTOR0_REGNUM (tdep);
}
else if (regno == I387_MXCSR_REGNUM (tdep))
{
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 05afa56..7959c40 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -50,15 +50,17 @@

#include "i386-tdep.h"
#include "i387-tdep.h"
+#include "i386-xstate.h"

#include "record.h"
#include <stdint.h>

#include "features/i386/i386.c"
+#include "features/i386/i386-avx.c"

/* Register names. */

-static const char *i386_register_names[] =
+static const char *i386_sse_register_names[] =
{
"eax", "ecx", "edx", "ebx",
"esp", "ebp", "esi", "edi",
@@ -73,6 +75,21 @@ static const char *i386_register_names[] =
"mxcsr"
};

+static const char *i386_avx_register_names[] =
+{
+ "eax", "ecx", "edx", "ebx",
+ "esp", "ebp", "esi", "edi",
+ "eip", "eflags", "cs", "ss",
+ "ds", "es", "fs", "gs",
+ "st0", "st1", "st2", "st3",
+ "st4", "st5", "st6", "st7",
+ "fctrl", "fstat", "ftag", "fiseg",
+ "fioff", "foseg", "fooff", "fop",
+ "ymm0", "ymm1", "ymm2", "ymm3",
+ "ymm4", "ymm5", "ymm6", "ymm7",
+ "mxcsr"
+};
+
/* Register names for MMX pseudo-registers. */

static const char *i386_mmx_names[] =
@@ -97,6 +114,13 @@ static const char *i386_word_names[] =
"sp", "bp", "si", "di"
};

+/* Register names for XMMM pseudo-registers. */
+
+static const char *i386_xmm_names[] =
+{
+ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
+};
+
/* MMX register? */

static int
@@ -149,18 +173,32 @@ i386_dword_regnum_p (struct gdbarch *gdbarch, int regnum)
return regnum >= 0 && regnum < tdep->num_dword_regs;
}

-/* SSE register? */
+int
+i386_xmm_regnum_p (struct gdbarch *gdbarch, int regnum)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int xmm0_regnum = tdep->xmm0_regnum;
+
+ if (xmm0_regnum < 0)
+ return 0;
+
+ regnum -= xmm0_regnum;
+ return regnum >= 0 && regnum < tdep->num_xmm_regs;
+}
+
+/* Vector register? */

static int
-i386_sse_regnum_p (struct gdbarch *gdbarch, int regnum)
+i386_vector_regnum_p (struct gdbarch *gdbarch, int regnum)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int num_vector_regs = I387_NUM_VECTOR_REGS (tdep);

- if (I387_NUM_XMM_REGS (tdep) == 0)
+ if (num_vector_regs == 0)
return 0;

- return (I387_XMM0_REGNUM (tdep) <= regnum
- && regnum < I387_MXCSR_REGNUM (tdep));
+ regnum -= I387_VECTOR0_REGNUM (tdep);
+ return regnum >= 0 && regnum < num_vector_regs;
}

static int
@@ -168,7 +206,7 @@ i386_mxcsr_regnum_p (struct gdbarch *gdbarch, int regnum)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- if (I387_NUM_XMM_REGS (tdep) == 0)
+ if (I387_NUM_VECTOR_REGS (tdep) == 0)
return 0;

return (regnum == I387_MXCSR_REGNUM (tdep));
@@ -197,7 +235,7 @@ i386_fpc_regnum_p (struct gdbarch *gdbarch, int regnum)
return 0;

return (I387_FCTRL_REGNUM (tdep) <= regnum
- && regnum < I387_XMM0_REGNUM (tdep));
+ && regnum < I387_VECTOR0_REGNUM (tdep));
}

/* Return the name of register REGNUM. */
@@ -208,6 +246,8 @@ i386_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (i386_mmx_regnum_p (gdbarch, regnum))
return i386_mmx_names[regnum - I387_MM0_REGNUM (tdep)];
+ else if (i386_xmm_regnum_p (gdbarch, regnum))
+ return i386_xmm_names[regnum - tdep->xmm0_regnum];
else if (i386_byte_regnum_p (gdbarch, regnum))
return i386_byte_names[regnum - tdep->al_regnum];
else if (i386_word_regnum_p (gdbarch, regnum))
@@ -244,8 +284,8 @@ i386_dbx_reg_to_regnum (struct gdbarch *gdbarch, int reg)
}
else if (reg >= 21 && reg <= 28)
{
- /* SSE registers. */
- return reg - 21 + I387_XMM0_REGNUM (tdep);
+ /* Vector registers. */
+ return reg - 21 + I387_VECTOR0_REGNUM (tdep);
}
else if (reg >= 29 && reg <= 36)
{
@@ -2183,6 +2223,58 @@ i387_ext_type (struct gdbarch *gdbarch)
return tdep->i387_ext_type;
}

+/* Construct vector type for pseudo XMM registers. We can't use
+ tdesc_find_type since XMM isn't described in target description. */
+
+static struct type *
+i386_xmm_type (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (!tdep->i386_xmm_type)
+ {
+ const struct builtin_type *bt = builtin_type (gdbarch);
+
+ /* The type we're building is this: */
+#if 0
+ union __gdb_builtin_type_vec128i
+ {
+ int128_t uint128;
+ int64_t v2_int64[2];
+ int32_t v4_int32[4];
+ int16_t v8_int16[8];
+ int8_t v16_int8[16];
+ double v2_double[2];
+ float v4_float[4];
+ };
+#endif
+
+ struct type *t;
+
+ t = arch_composite_type (gdbarch,
+ "__gdb_builtin_type_vec128i", TYPE_CODE_UNION);
+ append_composite_type_field (t, "v4_float",
+ init_vector_type (bt->builtin_float, 4));
+ append_composite_type_field (t, "v2_double",
+ init_vector_type (bt->builtin_double, 2));
+ append_composite_type_field (t, "v16_int8",
+ init_vector_type (bt->builtin_int8, 16));
+ append_composite_type_field (t, "v8_int16",
+ init_vector_type (bt->builtin_int16, 8));
+ append_composite_type_field (t, "v4_int32",
+ init_vector_type (bt->builtin_int32, 4));
+ append_composite_type_field (t, "v2_int64",
+ init_vector_type (bt->builtin_int64, 2));
+ append_composite_type_field (t, "uint128", bt->builtin_int128);
+
+ TYPE_VECTOR (t) = 1;
+ TYPE_NAME (t) = "builtin_type_vec128i";
+ tdep->i386_xmm_type = t;
+ }
+
+ return tdep->i386_xmm_type;
+}
+
/* Construct vector type for MMX registers. */
static struct type *
i386_mmx_type (struct gdbarch *gdbarch)
@@ -2233,6 +2325,8 @@ i386_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
{
if (i386_mmx_regnum_p (gdbarch, regnum))
return i386_mmx_type (gdbarch);
+ else if (i386_xmm_regnum_p (gdbarch, regnum))
+ return i386_xmm_type (gdbarch);
else
{
const struct builtin_type *bt = builtin_type (gdbarch);
@@ -2284,7 +2378,16 @@ i386_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- if (i386_word_regnum_p (gdbarch, regnum))
+ if (i386_xmm_regnum_p (gdbarch, regnum))
+ {
+ int vecnum = (I387_VECTOR0_REGNUM (tdep)
+ + regnum - tdep->xmm0_regnum);
+
+ /* Extract (always little endian). */
+ regcache_raw_read (regcache, vecnum, raw_buf);
+ memcpy (buf, raw_buf, 16);
+ }
+ else if (i386_word_regnum_p (gdbarch, regnum))
{
int gpnum = regnum - tdep->ax_regnum;

@@ -2333,7 +2436,19 @@ i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- if (i386_word_regnum_p (gdbarch, regnum))
+ if (i386_xmm_regnum_p (gdbarch, regnum))
+ {
+ int vecnum = (I387_VECTOR0_REGNUM (tdep)
+ + regnum - tdep->xmm0_regnum);
+
+ /* Read ... */
+ regcache_raw_read (regcache, vecnum, raw_buf);
+ /* ... Modify ... (always little endian). */
+ memcpy (raw_buf, buf, 16);
+ /* ... Write. */
+ regcache_raw_write (regcache, vecnum, raw_buf);
+ }
+ else if (i386_word_regnum_p (gdbarch, regnum))
{
int gpnum = regnum - tdep->ax_regnum;

@@ -2580,6 +2695,28 @@ i386_collect_fpregset (const struct regset *regset,
i387_collect_fsave (regcache, regnum, fpregs);
}

+/* Similar to i386_supply_fpregset, but use XSAVE extended state. */
+
+static void
+i386_supply_xstateregset (const struct regset *regset,
+ struct regcache *regcache, int regnum,
+ const void *xstateregs, size_t len)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+ i387_supply_xsave (regcache, regnum, xstateregs);
+}
+
+/* Similar to i386_collect_fpregset , but use XSAVE extended state. */
+
+static void
+i386_collect_xstateregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *xstateregs, size_t len)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+ i387_collect_xsave (regcache, regnum, xstateregs, 1);
+}
+
/* Return the appropriate register set for the core section identified
by SECT_NAME and SECT_SIZE. */

@@ -2607,6 +2744,16 @@ i386_regset_from_core_section (struct gdbarch *gdbarch,
return tdep->fpregset;
}

+ if (strcmp (sect_name, ".reg-xstate") == 0)
+ {
+ if (tdep->xstateregset == NULL)
+ tdep->xstateregset = regset_alloc (gdbarch,
+ i386_supply_xstateregset,
+ i386_collect_xstateregset);
+
+ return tdep->xstateregset;
+ }
+
return NULL;
}
\f
@@ -2757,7 +2904,7 @@ i386_go32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->jb_pc_offset = 36;

/* DJGPP does not support the SSE registers. */
- tdep->num_xmm_regs = 0;
+ tdep->num_vector_regs = 0;
set_gdbarch_num_regs (gdbarch, I386_NUM_GREGS + I387_NUM_REGS);

/* Native compiler is GCC, which uses the SVR4 register numbering
@@ -2800,8 +2947,9 @@ int
i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
struct reggroup *group)
{
- int sse_regnum_p, fp_regnum_p, mmx_regnum_p, byte_regnum_p,
- word_regnum_p, dword_regnum_p;
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int vector_regnum_p, fp_regnum_p, mmx_regnum_p, byte_regnum_p,
+ word_regnum_p, dword_regnum_p, xmm_regnum_p;

/* Don't include pseudo registers, except for MMX, in any register
groups. */
@@ -2821,12 +2969,20 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
if (group == i386_mmx_reggroup)
return mmx_regnum_p;

- sse_regnum_p = (i386_sse_regnum_p (gdbarch, regnum)
- || i386_mxcsr_regnum_p (gdbarch, regnum));
- if (group == i386_sse_reggroup)
- return sse_regnum_p;
+ vector_regnum_p = (i386_vector_regnum_p (gdbarch, regnum)
+ || i386_mxcsr_regnum_p (gdbarch, regnum));
if (group == vector_reggroup)
- return mmx_regnum_p || sse_regnum_p;
+ return mmx_regnum_p || vector_regnum_p;
+
+ xmm_regnum_p = i386_xmm_regnum_p (gdbarch, regnum);
+ if (group == i386_sse_reggroup)
+ return (xmm_regnum_p
+ || (vector_regnum_p
+ && ((tdep->xcr0 & I386_XSTATE_AVX_MASK)
+ == I386_XSTATE_SSE_MASK)));
+
+ if (xmm_regnum_p)
+ return 0;

fp_regnum_p = (i386_fp_regnum_p (gdbarch, regnum)
|| i386_fpc_regnum_p (gdbarch, regnum));
@@ -2835,8 +2991,9 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,

if (group == general_reggroup)
return (!fp_regnum_p
+ && !vector_regnum_p
&& !mmx_regnum_p
- && !sse_regnum_p
+ && !xmm_regnum_p
&& !byte_regnum_p
&& !word_regnum_p
&& !dword_regnum_p);
@@ -5651,6 +5808,7 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
const struct target_desc *tdesc = tdep->tdesc;
const struct tdesc_feature *feature_core, *feature_vector;
int i, num_regs, valid_p;
+ unsigned long long xcr0;

if (! tdesc_has_registers (tdesc))
return 0;
@@ -5658,8 +5816,16 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
/* Get core registers. */
feature_core = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.core");

- /* Get SSE registers. */
+ /* Try SSE registers first. */
feature_vector = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse");
+ if (feature_vector)
+ xcr0 = I386_XSTATE_SSE_MASK;
+ else
+ {
+ /* Try AVX registers. */
+ feature_vector = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx");
+ xcr0 = I386_XSTATE_AVX_MASK;
+ }

if (feature_core == NULL || feature_vector == NULL)
return 0;
@@ -5672,11 +5838,14 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
tdep->register_names[i]);

/* Need to include %mxcsr, so add one. */
- num_regs += tdep->num_xmm_regs + 1;
+ num_regs += tdep->num_vector_regs + 1;
for (; i < num_regs; i++)
valid_p &= tdesc_numbered_register (feature_vector, tdesc_data, i,
tdep->register_names[i]);

+ /* The XCR0 bits. */
+ tdep->xcr0 = xcr0;
+
return valid_p;
}

@@ -5689,6 +5858,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
struct tdesc_arch_data *tdesc_data;
const struct target_desc *tdesc;
int mm0_regnum;
+ int xmm0_regnum;

/* If there is already a candidate, use it. */
arches = gdbarch_list_lookup_by_info (arches, &info);
@@ -5709,10 +5879,12 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->fpregset = NULL;
tdep->sizeof_fpregset = I387_SIZEOF_FSAVE;

+ tdep->xstateregset = NULL;
+
/* The default settings include the FPU registers, the MMX registers
and the SSE registers. This can be overridden for a specific ABI
by adjusting the members `st0_regnum', `mm0_regnum' and
- `num_xmm_regs' of `struct gdbarch_tdep', otherwise the registers
+ `num_vector_regs' of `struct gdbarch_tdep', otherwise the registers
will show up in the output of "info all-registers". Ideally we
should try to autodetect whether they are available, such that we
can prevent "info all-registers" from displaying registers that
@@ -5726,7 +5898,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->st0_regnum = I386_ST0_REGNUM;

/* I386_NUM_XREGS includes %mxcsr, so substract one. */
- tdep->num_xmm_regs = I386_NUM_XREGS - 1;
+ tdep->num_vector_regs = I386_NUM_XREGS - 1;

tdep->jb_pc_offset = -1;
tdep->struct_return = pcc_struct_return;
@@ -5738,6 +5910,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->sc_pc_offset = -1;
tdep->sc_sp_offset = -1;

+ tdep->xsave_xcr0_offset = -1;
+
tdep->record_regmap = i386_record_regmap;

/* The format used for `long double' on almost all i386 targets is
@@ -5865,7 +6039,17 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->tdesc = tdesc;

tdep->num_core_regs = I386_NUM_GREGS + I387_NUM_REGS;
- tdep->register_names = i386_register_names;
+
+ if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse") != NULL)
+ {
+ tdep->register_names = i386_sse_register_names;
+ tdep->num_xmm_regs = 0;
+ }
+ else
+ {
+ tdep->register_names = i386_avx_register_names;
+ tdep->num_xmm_regs = 8;
+ }

tdep->num_byte_regs = 8;
tdep->num_word_regs = 8;
@@ -5883,7 +6067,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_num_pseudo_regs (gdbarch, (tdep->num_byte_regs
+ tdep->num_word_regs
+ tdep->num_dword_regs
- + tdep->num_mmx_regs));
+ + tdep->num_mmx_regs
+ + tdep->num_xmm_regs));

/* Target description may be changed. */
tdesc = tdep->tdesc;
@@ -5905,16 +6090,30 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->al_regnum = gdbarch_num_regs (gdbarch);
tdep->ax_regnum = tdep->al_regnum + tdep->num_byte_regs;

- mm0_regnum = tdep->ax_regnum + tdep->num_word_regs;
+ xmm0_regnum = tdep->ax_regnum + tdep->num_word_regs;
if (tdep->num_dword_regs)
{
/* Support dword pseudo-registesr if it hasn't been disabled, */
- tdep->eax_regnum = mm0_regnum;
- mm0_regnum = tdep->eax_regnum + tdep->num_dword_regs;
+ tdep->num_dword_regs = tdep->num_dword_regs;
+ tdep->eax_regnum = xmm0_regnum;
+ xmm0_regnum = tdep->eax_regnum + tdep->num_dword_regs;
}
else
tdep->eax_regnum = -1;

+ if (tdep->num_xmm_regs)
+ {
+ /* Support XMM pseudo-registesr if it is available, */
+ tdep->num_xmm_regs = tdep->num_xmm_regs;
+ tdep->xmm0_regnum = xmm0_regnum;
+ mm0_regnum = tdep->xmm0_regnum + tdep->num_xmm_regs;
+ }
+ else
+ {
+ tdep->xmm0_regnum = -1;
+ mm0_regnum = xmm0_regnum;
+ }
+
if (tdep->num_mmx_regs != 0)
{
/* Support MMX pseudo-registesr if MMX hasn't been disabled, */
@@ -5940,6 +6139,12 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_fast_tracepoint_valid_at (gdbarch,
i386_fast_tracepoint_valid_at);

+ /* Specify XSAVE extended state support with the largest extended
+ state size and XCR0. */
+ set_gdbarch_qsupported (gdbarch,
+ "x86:xstate=" I386_XSTATE_MAX_SIZE_STRING
+ ":xcr0=" I386_XSTATE_MAX_MASK_STRING);
+
return gdbarch;
}

@@ -5997,4 +6202,5 @@ is \"default\"."),

/* Initialize the standard target descriptions. */
initialize_tdesc_i386 ();
+ initialize_tdesc_i386_avx ();
}
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 72c634e..2746fb4 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -42,7 +42,7 @@ struct regcache;
determines the register number at which the FPU data registers
start. The number of FPU data and control registers is the same
for both architectures. The number of SSE registers however,
- differs and is determined by the num_xmm_regs member of `struct
+ differs and is determined by the num_vector_regs member of `struct
gdbarch_tdep'. */

/* Convention for returning structures. */
@@ -109,6 +109,9 @@ struct gdbarch_tdep
struct regset *fpregset;
size_t sizeof_fpregset;

+ /* XSAVE extended state. */
+ struct regset *xstateregset;
+
/* Register number for %st(0). The register numbers for the other
registers follow from this one. Set this to -1 to indicate the
absence of an FPU. */
@@ -121,6 +124,13 @@ struct gdbarch_tdep
of MMX support. */
int mm0_regnum;

+ /* Number of pseudo XMM registers. */
+ int num_xmm_regs;
+
+ /* Register number for %xmm0. Set this to -1 to indicate the absence
+ of pseudo XMM register support. */
+ int xmm0_regnum;
+
/* Number of byte registers. */
int num_byte_regs;

@@ -143,8 +153,16 @@ struct gdbarch_tdep
/* Number of core registers. */
int num_core_regs;

- /* Number of SSE registers. */
- int num_xmm_regs;
+ /* Number of vector registers. */
+ int num_vector_regs;
+
+ /* Bits of the extended control register 0 (the XFEATURE_ENABLED_MASK
+ register), excluding the x87 bit, which are supported by this gdb.
+ */
+ unsigned long long xcr0;
+
+ /* Offset of XCR0 in XSAVE extended state. */
+ int xsave_xcr0_offset;

/* Register names. */
const char **register_names;
@@ -182,6 +200,7 @@ struct gdbarch_tdep

/* ISA-specific data types. */
struct type *i386_mmx_type;
+ struct type *i386_xmm_type;
struct type *i387_ext_type;

/* Process record/replay target. */
@@ -267,7 +286,7 @@ enum record_i386_regnum
#define I386_SSE_NUM_REGS (I386_MXCSR_REGNUM + 1)

/* Size of the largest register. */
-#define I386_MAX_REGISTER_SIZE 16
+#define I386_MAX_REGISTER_SIZE 32

/* Types for i386-specific registers. */
extern struct type *i387_ext_type (struct gdbarch *gdbarch);
@@ -276,6 +295,7 @@ extern struct type *i387_ext_type (struct gdbarch *gdbarch);
extern int i386_byte_regnum_p (struct gdbarch *gdbarch, int regnum);
extern int i386_word_regnum_p (struct gdbarch *gdbarch, int regnum);
extern int i386_dword_regnum_p (struct gdbarch *gdbarch, int regnum);
+extern int i386_xmm_regnum_p (struct gdbarch *gdbarch, int regnum);

extern const char *i386_pseudo_register_name (struct gdbarch *gdbarch,
int regnum);