aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog17
-rw-r--r--gdb/amd64-tdep.c22
-rw-r--r--gdb/common/x86-xstate.h13
-rw-r--r--gdb/gdbserver/ChangeLog10
-rw-r--r--gdb/gdbserver/i387-fp.c210
-rw-r--r--gdb/gdbserver/regcache.c13
-rw-r--r--gdb/gdbserver/regcache.h3
-rw-r--r--gdb/i387-tdep.c767
-rw-r--r--gdb/i387-tdep.h6
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.arch/amd64-init-x87-values.S31
-rw-r--r--gdb/testsuite/gdb.arch/amd64-init-x87-values.exp175
12 files changed, 872 insertions, 400 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 32038c5..8314945 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,20 @@
+2018-05-08 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * common/x86-xstate.h (I387_FCTRL_INIT_VAL): New constant.
+ (I387_MXCSR_INIT_VAL): New constant.
+ * amd64-tdep.c (amd64_supply_xsave): Only read state from xsave
+ buffer if it was supplied by the inferior.
+ * i387-tdep.c (i387_supply_fsave): Use I387_MXCSR_INIT_VAL.
+ (i387_xsave_get_clear_bv): New function.
+ (i387_supply_xsave): Only read x87 control registers from the
+ xsave buffer if the feature is enabled, and the state will have
+ been written, otherwise, provide a suitable default.
+ (i387_collect_xsave): Pre-clear all registers in xsave buffer,
+ including x87 control registers. Update control registers if they
+ have changed from the default value, and mark features as enabled
+ as required.
+ * i387-tdep.h (i387_xsave_get_clear_bv): Declare.
+
2018-05-08 Ulrich Weigand <uweigand@de.ibm.com>
* spu-tdep.c (info_spu_event_command): Fix output formatting.
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 1fea264..d555465 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3372,13 +3372,23 @@ amd64_supply_xsave (struct regcache *regcache, int regnum,
&& gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 64)
{
const gdb_byte *regs = (const gdb_byte *) xsave;
+ static const gdb_byte zero[I386_MAX_REGISTER_SIZE] = { 0 };
+ ULONGEST clear_bv;
- if (regnum == -1 || regnum == I387_FISEG_REGNUM (tdep))
- regcache_raw_supply (regcache, I387_FISEG_REGNUM (tdep),
- regs + 12);
- if (regnum == -1 || regnum == I387_FOSEG_REGNUM (tdep))
- regcache_raw_supply (regcache, I387_FOSEG_REGNUM (tdep),
- regs + 20);
+ clear_bv = i387_xsave_get_clear_bv (gdbarch, xsave);
+
+ /* If the FISEG and FOSEG registers have not been initialised yet
+ (their CLEAR_BV bit is set) then their default values of zero will
+ have already been setup by I387_SUPPLY_XSAVE. */
+ if (!(clear_bv & X86_XSTATE_X87))
+ {
+ if (regnum == -1 || regnum == I387_FISEG_REGNUM (tdep))
+ regcache_raw_supply (regcache, I387_FISEG_REGNUM (tdep),
+ regs + 12);
+ if (regnum == -1 || regnum == I387_FOSEG_REGNUM (tdep))
+ regcache_raw_supply (regcache, I387_FOSEG_REGNUM (tdep),
+ regs + 20);
+ }
}
}
diff --git a/gdb/common/x86-xstate.h b/gdb/common/x86-xstate.h
index 5a165b3..51e5c3c 100644
--- a/gdb/common/x86-xstate.h
+++ b/gdb/common/x86-xstate.h
@@ -72,4 +72,17 @@
(HAS_MPX (XCR0) ? X86_XSTATE_BNDCFG_SIZE : \
(HAS_AVX (XCR0) ? X86_XSTATE_AVX_SIZE : X86_XSTATE_SSE_SIZE))))
+/* Initial value for fctrl register, as defined in the X86 manual, and
+ confirmed in the (Linux) kernel source. When the x87 floating point
+ feature is not enabled in an inferior we use this as the value of the
+ fcrtl register. */
+
+#define I387_FCTRL_INIT_VAL 0x037f
+
+/* Initial value for mxcsr register. When the avx and sse floating point
+ features are not enabled in an inferior we use this as the value of the
+ mxcsr register. */
+
+#define I387_MXCSR_INIT_VAL 0x1f80
+
#endif /* X86_XSTATE_H */
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 7461359..9cd72a8 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,13 @@
+2018-05-08 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * i387-fp.c (i387_cache_to_xsave): Only write x87 control
+ registers to the cache if their values have changed.
+ (i387_xsave_to_cache): Provide default values for x87 control
+ registers when these features are available, but disabled.
+ * regcache.c (supply_register_by_name_zeroed): New function.
+ * regcache.h (supply_register_by_name_zeroed): Declare new
+ function.
+
2018-05-07 Tom Tromey <tom@tromey.com>
* configure: Rebuild.
diff --git a/gdb/gdbserver/i387-fp.c b/gdb/gdbserver/i387-fp.c
index f1f7d84..bee1af9 100644
--- a/gdb/gdbserver/i387-fp.c
+++ b/gdb/gdbserver/i387-fp.c
@@ -294,17 +294,31 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
if (clear_bv)
{
if ((clear_bv & X86_XSTATE_X87))
- for (i = 0; i < 8; i++)
- memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
+ {
+ for (i = 0; i < 8; i++)
+ memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
+
+ fp->fioff = 0;
+ fp->fooff = 0;
+ fp->fctrl = I387_FCTRL_INIT_VAL;
+ fp->fstat = 0;
+ fp->ftag = 0;
+ fp->fiseg = 0;
+ fp->foseg = 0;
+ fp->fop = 0;
+ }
if ((clear_bv & X86_XSTATE_SSE))
- for (i = 0; i < num_xmm_registers; i++)
+ for (i = 0; i < num_xmm_registers; i++)
memset (((char *) &fp->xmm_space[0]) + i * 16, 0, 16);
if ((clear_bv & X86_XSTATE_AVX))
- for (i = 0; i < num_xmm_registers; i++)
+ for (i = 0; i < num_xmm_registers; i++)
memset (((char *) &fp->ymmh_space[0]) + i * 16, 0, 16);
+ if ((clear_bv & X86_XSTATE_SSE) && (clear_bv & X86_XSTATE_AVX))
+ memset (((char *) &fp->mxcsr), 0, 4);
+
if ((clear_bv & X86_XSTATE_BNDREGS))
for (i = 0; i < num_mpx_bnd_registers; i++)
memset (((char *) &fp->mpx_bnd_space[0]) + i * 16, 0, 16);
@@ -523,43 +537,93 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}
}
- /* Update the corresponding bits in xstate_bv if any SSE/AVX
- registers are changed. */
- fp->xstate_bv |= xstate_bv;
+ if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
+ {
+ collect_register_by_name (regcache, "mxcsr", raw);
+ if (memcmp (raw, &fp->mxcsr, 4) != 0)
+ {
+ if (((fp->xstate_bv | xstate_bv)
+ & (X86_XSTATE_SSE | X86_XSTATE_AVX)) == 0)
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (&fp->mxcsr, raw, 4);
+ }
+ }
- collect_register_by_name (regcache, "fioff", &fp->fioff);
- collect_register_by_name (regcache, "fooff", &fp->fooff);
- collect_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+ if (x86_xcr0 & X86_XSTATE_X87)
+ {
+ collect_register_by_name (regcache, "fioff", raw);
+ if (memcmp (raw, &fp->fioff, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (&fp->fioff, raw, 4);
+ }
- /* This one's 11 bits... */
- collect_register_by_name (regcache, "fop", &val2);
- fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
+ collect_register_by_name (regcache, "fooff", raw);
+ if (memcmp (raw, &fp->fooff, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (&fp->fooff, raw, 4);
+ }
- /* Some registers are 16-bit. */
- collect_register_by_name (regcache, "fctrl", &val);
- fp->fctrl = val;
+ /* This one's 11 bits... */
+ collect_register_by_name (regcache, "fop", &val2);
+ val2 = (val2 & 0x7FF) | (fp->fop & 0xF800);
+ if (fp->fop != val2)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fop = val2;
+ }
- collect_register_by_name (regcache, "fstat", &val);
- fp->fstat = val;
+ /* Some registers are 16-bit. */
+ collect_register_by_name (regcache, "fctrl", &val);
+ if (fp->fctrl != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fctrl = val;
+ }
- /* Convert to the simplifed tag form stored in fxsave data. */
- collect_register_by_name (regcache, "ftag", &val);
- val &= 0xFFFF;
- val2 = 0;
- for (i = 7; i >= 0; i--)
- {
- int tag = (val >> (i * 2)) & 3;
+ collect_register_by_name (regcache, "fstat", &val);
+ if (fp->fstat != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fstat = val;
+ }
- if (tag != 3)
- val2 |= (1 << i);
- }
- fp->ftag = val2;
+ /* Convert to the simplifed tag form stored in fxsave data. */
+ collect_register_by_name (regcache, "ftag", &val);
+ val &= 0xFFFF;
+ val2 = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag = (val >> (i * 2)) & 3;
- collect_register_by_name (regcache, "fiseg", &val);
- fp->fiseg = val;
+ if (tag != 3)
+ val2 |= (1 << i);
+ }
+ if (fp->ftag != val2)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->ftag = val2;
+ }
- collect_register_by_name (regcache, "foseg", &val);
- fp->foseg = val;
+ collect_register_by_name (regcache, "fiseg", &val);
+ if (fp->fiseg != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->fiseg = val;
+ }
+
+ collect_register_by_name (regcache, "foseg", &val);
+ if (fp->foseg != val)
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ fp->foseg = val;
+ }
+ }
+
+ /* Update the corresponding bits in xstate_bv if any SSE/AVX
+ registers are changed. */
+ fp->xstate_bv |= xstate_bv;
}
static int
@@ -844,39 +908,67 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}
- supply_register_by_name (regcache, "fioff", &fp->fioff);
- supply_register_by_name (regcache, "fooff", &fp->fooff);
- supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+ if ((clear_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX))
+ == (X86_XSTATE_SSE | X86_XSTATE_AVX))
+ {
+ unsigned int default_mxcsr = I387_MXCSR_INIT_VAL;
+ supply_register_by_name (regcache, "mxcsr", &default_mxcsr);
+ }
+ else
+ supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
- /* Some registers are 16-bit. */
- val = fp->fctrl & 0xFFFF;
- supply_register_by_name (regcache, "fctrl", &val);
+ if ((clear_bv & X86_XSTATE_X87) != 0)
+ {
+ supply_register_by_name_zeroed (regcache, "fioff");
+ supply_register_by_name_zeroed (regcache, "fooff");
- val = fp->fstat & 0xFFFF;
- supply_register_by_name (regcache, "fstat", &val);
+ val = I387_FCTRL_INIT_VAL;
+ supply_register_by_name (regcache, "fctrl", &val);
- /* Generate the form of ftag data that GDB expects. */
- top = (fp->fstat >> 11) & 0x7;
- val = 0;
- for (i = 7; i >= 0; i--)
- {
- int tag;
- if (fp->ftag & (1 << i))
- tag = i387_ftag (fxp, (i + 8 - top) % 8);
- else
- tag = 3;
- val |= tag << (2 * i);
+ supply_register_by_name_zeroed (regcache, "fstat");
+
+ val = 0xFFFF;
+ supply_register_by_name (regcache, "ftag", &val);
+
+ supply_register_by_name_zeroed (regcache, "fiseg");
+ supply_register_by_name_zeroed (regcache, "foseg");
+ supply_register_by_name_zeroed (regcache, "fop");
}
- supply_register_by_name (regcache, "ftag", &val);
+ else
+ {
+ supply_register_by_name (regcache, "fioff", &fp->fioff);
+ supply_register_by_name (regcache, "fooff", &fp->fooff);
- val = fp->fiseg & 0xFFFF;
- supply_register_by_name (regcache, "fiseg", &val);
+ /* Some registers are 16-bit. */
+ val = fp->fctrl & 0xFFFF;
+ supply_register_by_name (regcache, "fctrl", &val);
- val = fp->foseg & 0xFFFF;
- supply_register_by_name (regcache, "foseg", &val);
+ val = fp->fstat & 0xFFFF;
+ supply_register_by_name (regcache, "fstat", &val);
- val = (fp->fop) & 0x7FF;
- supply_register_by_name (regcache, "fop", &val);
+ /* Generate the form of ftag data that GDB expects. */
+ top = (fp->fstat >> 11) & 0x7;
+ val = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag;
+ if (fp->ftag & (1 << i))
+ tag = i387_ftag (fxp, (i + 8 - top) % 8);
+ else
+ tag = 3;
+ val |= tag << (2 * i);
+ }
+ supply_register_by_name (regcache, "ftag", &val);
+
+ val = fp->fiseg & 0xFFFF;
+ supply_register_by_name (regcache, "fiseg", &val);
+
+ val = fp->foseg & 0xFFFF;
+ supply_register_by_name (regcache, "foseg", &val);
+
+ val = (fp->fop) & 0x7FF;
+ supply_register_by_name (regcache, "fop", &val);
+ }
}
/* Default to SSE. */
diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c
index cbdf766..0718b9f 100644
--- a/gdb/gdbserver/regcache.c
+++ b/gdb/gdbserver/regcache.c
@@ -346,6 +346,19 @@ supply_register_zeroed (struct regcache *regcache, int n)
#endif
}
+#ifndef IN_PROCESS_AGENT
+
+/* Supply register called NAME with value zero to REGCACHE. */
+
+void
+supply_register_by_name_zeroed (struct regcache *regcache,
+ const char *name)
+{
+ supply_register_zeroed (regcache, find_regno (regcache->tdesc, name));
+}
+
+#endif
+
/* Supply the whole register set whose contents are stored in BUF, to
REGCACHE. If BUF is NULL, all the registers' values are recorded
as unavailable. */
diff --git a/gdb/gdbserver/regcache.h b/gdb/gdbserver/regcache.h
index 6ff1308..2c0df64 100644
--- a/gdb/gdbserver/regcache.h
+++ b/gdb/gdbserver/regcache.h
@@ -107,6 +107,9 @@ void supply_register_zeroed (struct regcache *regcache, int n);
void supply_register_by_name (struct regcache *regcache,
const char *name, const void *buf);
+void supply_register_by_name_zeroed (struct regcache *regcache,
+ const char *name);
+
void supply_regblock (struct regcache *regcache, const void *buf);
void collect_register (struct regcache *regcache, int n, void *buf);
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index 1938792..aca70c1 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -479,7 +479,7 @@ i387_supply_fsave (struct regcache *regcache, int regnum, const void *fsave)
{
gdb_byte buf[4];
- store_unsigned_integer (buf, 4, byte_order, 0x1f80);
+ store_unsigned_integer (buf, 4, byte_order, I387_MXCSR_INIT_VAL);
regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep), buf);
}
}
@@ -892,6 +892,27 @@ static int xsave_pkeys_offset[] =
#define XSAVE_PKEYS_ADDR(tdep, xsave, regnum) \
(xsave + xsave_pkeys_offset[regnum - I387_PKRU_REGNUM (tdep)])
+
+/* Extract from XSAVE a bitset of the features that are available on the
+ target, but which have not yet been enabled. */
+
+ULONGEST
+i387_xsave_get_clear_bv (struct gdbarch *gdbarch, const void *xsave)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ const gdb_byte *regs = (const gdb_byte *) xsave;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* Get `xstat_bv'. The supported bits in `xstat_bv' are 8 bytes. */
+ ULONGEST xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
+ 8, byte_order);
+
+ /* Clear part in vector registers if its bit in xstat_bv is zero. */
+ ULONGEST clear_bv = (~(xstate_bv)) & tdep->xcr0;
+
+ return clear_bv;
+}
+
/* Similar to i387_supply_fxsave, but use XSAVE extended state. */
void
@@ -899,6 +920,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
const void *xsave)
{
struct gdbarch *gdbarch = regcache->arch ();
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
const gdb_byte *regs = (const gdb_byte *) xsave;
int i;
@@ -956,20 +978,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
else
regclass = none;
- if (regclass != none)
- {
- /* Get `xstat_bv'. The supported bits in `xstat_bv' are 8 bytes. */
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- ULONGEST xstate_bv = 0;
-
- xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
- 8, byte_order);
-
- /* Clear part in vector registers if its bit in xstat_bv is zero. */
- clear_bv = (~(xstate_bv)) & tdep->xcr0;
- }
- else
- clear_bv = X86_XSTATE_ALL_MASK;
+ clear_bv = i387_xsave_get_clear_bv (gdbarch, xsave);
/* With the delayed xsave mechanism, in between the program
starting, and the program accessing the vector registers for the
@@ -1246,10 +1255,30 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
for (i = I387_FCTRL_REGNUM (tdep); i < I387_XMM0_REGNUM (tdep); i++)
if (regnum == -1 || regnum == i)
{
+ if (clear_bv & X86_XSTATE_X87)
+ {
+ if (i == I387_FCTRL_REGNUM (tdep))
+ {
+ gdb_byte buf[4];
+
+ store_unsigned_integer (buf, 4, byte_order,
+ I387_FCTRL_INIT_VAL);
+ regcache_raw_supply (regcache, i, buf);
+ }
+ else if (i == I387_FTAG_REGNUM (tdep))
+ {
+ gdb_byte buf[4];
+
+ store_unsigned_integer (buf, 4, byte_order, 0xffff);
+ regcache_raw_supply (regcache, i, buf);
+ }
+ else
+ regcache_raw_supply (regcache, i, zero);
+ }
/* Most of the FPU control registers occupy only 16 bits in
the xsave extended state. Give those a special treatment. */
- if (i != I387_FIOFF_REGNUM (tdep)
- && i != I387_FOOFF_REGNUM (tdep))
+ else if (i != I387_FIOFF_REGNUM (tdep)
+ && i != I387_FOOFF_REGNUM (tdep))
{
gdb_byte val[4];
@@ -1257,7 +1286,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
val[2] = val[3] = 0;
if (i == I387_FOP_REGNUM (tdep))
val[1] &= ((1 << 3) - 1);
- else if (i== I387_FTAG_REGNUM (tdep))
+ else if (i == I387_FTAG_REGNUM (tdep))
{
/* The fxsave area contains a simplified version of
the tag word. We have to look at the actual 80-bit
@@ -1291,13 +1320,26 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
}
regcache_raw_supply (regcache, i, val);
}
- else
+ else
regcache_raw_supply (regcache, i, FXSAVE_ADDR (tdep, regs, i));
}
if (regnum == I387_MXCSR_REGNUM (tdep) || regnum == -1)
- regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep),
- FXSAVE_MXCSR_ADDR (regs));
+ {
+ /* The MXCSR register is placed into the xsave buffer if either the
+ AVX or SSE features are enabled. */
+ if ((clear_bv & (X86_XSTATE_AVX | X86_XSTATE_SSE))
+ == (X86_XSTATE_AVX | X86_XSTATE_SSE))
+ {
+ gdb_byte buf[4];
+
+ store_unsigned_integer (buf, 4, byte_order, I387_MXCSR_INIT_VAL);
+ regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep), buf);
+ }
+ else
+ regcache_raw_supply (regcache, I387_MXCSR_REGNUM (tdep),
+ FXSAVE_MXCSR_ADDR (regs));
+ }
}
/* Similar to i387_collect_fxsave, but use XSAVE extended state. */
@@ -1307,22 +1349,24 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
void *xsave, int gcore)
{
struct gdbarch *gdbarch = regcache->arch ();
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- gdb_byte *regs = (gdb_byte *) xsave;
+ gdb_byte *p, *regs = (gdb_byte *) xsave;
+ gdb_byte raw[I386_MAX_REGISTER_SIZE];
+ ULONGEST initial_xstate_bv, clear_bv, xstate_bv = 0;
int i;
enum
{
- none = 0x0,
- check = 0x1,
- x87 = 0x2 | check,
- sse = 0x4 | check,
- avxh = 0x8 | check,
- mpx = 0x10 | check,
- avx512_k = 0x20 | check,
- avx512_zmm_h = 0x40 | check,
- avx512_ymmh_avx512 = 0x80 | check,
- avx512_xmm_avx512 = 0x100 | check,
- pkeys = 0x200 | check,
+ x87_ctrl_or_mxcsr = 0x1,
+ x87 = 0x2,
+ sse = 0x4,
+ avxh = 0x8,
+ mpx = 0x10,
+ avx512_k = 0x20,
+ avx512_zmm_h = 0x40,
+ avx512_ymmh_avx512 = 0x80,
+ avx512_xmm_avx512 = 0x100,
+ pkeys = 0x200,
all = x87 | sse | avxh | mpx | avx512_k | avx512_zmm_h
| avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys
} regclass;
@@ -1359,8 +1403,12 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
else if (regnum >= I387_ST0_REGNUM (tdep)
&& regnum < I387_FCTRL_REGNUM (tdep))
regclass = x87;
+ else if ((regnum >= I387_FCTRL_REGNUM (tdep)
+ && regnum < I387_XMM0_REGNUM (tdep))
+ || regnum == I387_MXCSR_REGNUM (tdep))
+ regclass = x87_ctrl_or_mxcsr;
else
- regclass = none;
+ internal_error (__FILE__, __LINE__, _("invalid i387 regnum %d"), regnum);
if (gcore)
{
@@ -1373,361 +1421,387 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
memcpy (XSAVE_XSTATE_BV_ADDR (regs), &tdep->xcr0, 8);
}
- if ((regclass & check))
+ /* The supported bits in `xstat_bv' are 8 bytes. */
+ initial_xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
+ 8, byte_order);
+ clear_bv = (~(initial_xstate_bv)) & tdep->xcr0;
+
+ /* The XSAVE buffer was filled lazily by the kernel. Only those
+ features that are enabled were written into the buffer, disabled
+ features left the buffer uninitialised. In order to identify if any
+ registers have changed we will be comparing the register cache
+ version to the version in the XSAVE buffer, it is important then that
+ at this point we initialise to the default values any features in
+ XSAVE that are not yet initialised.
+
+ This could be made more efficient, we know which features (from
+ REGNUM) we will be potentially updating, and could limit ourselves to
+ only clearing that feature. However, the extra complexity does not
+ seem justified at this point. */
+ if (clear_bv)
{
- gdb_byte raw[I386_MAX_REGISTER_SIZE];
- ULONGEST initial_xstate_bv, clear_bv, xstate_bv = 0;
- gdb_byte *p;
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-
- /* The supported bits in `xstat_bv' are 8 bytes. */
- initial_xstate_bv = extract_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
- 8, byte_order);
- clear_bv = (~(initial_xstate_bv)) & tdep->xcr0;
+ if ((clear_bv & X86_XSTATE_PKRU))
+ for (i = I387_PKRU_REGNUM (tdep);
+ i < I387_PKEYSEND_REGNUM (tdep); i++)
+ memset (XSAVE_PKEYS_ADDR (tdep, regs, i), 0, 4);
- /* Clear register set if its bit in xstat_bv is zero. */
- if (clear_bv)
- {
- if ((clear_bv & X86_XSTATE_PKRU))
- for (i = I387_PKRU_REGNUM (tdep);
- i < I387_PKEYSEND_REGNUM (tdep); i++)
- memset (XSAVE_PKEYS_ADDR (tdep, regs, i), 0, 4);
+ if ((clear_bv & X86_XSTATE_BNDREGS))
+ for (i = I387_BND0R_REGNUM (tdep);
+ i < I387_BNDCFGU_REGNUM (tdep); i++)
+ memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 16);
- if ((clear_bv & X86_XSTATE_BNDREGS))
- for (i = I387_BND0R_REGNUM (tdep);
- i < I387_BNDCFGU_REGNUM (tdep); i++)
- memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 16);
+ if ((clear_bv & X86_XSTATE_BNDCFG))
+ for (i = I387_BNDCFGU_REGNUM (tdep);
+ i < I387_MPXEND_REGNUM (tdep); i++)
+ memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 8);
- if ((clear_bv & X86_XSTATE_BNDCFG))
- for (i = I387_BNDCFGU_REGNUM (tdep);
- i < I387_MPXEND_REGNUM (tdep); i++)
- memset (XSAVE_MPX_ADDR (tdep, regs, i), 0, 8);
+ if ((clear_bv & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
+ for (i = I387_ZMM0H_REGNUM (tdep);
+ i < I387_ZMMENDH_REGNUM (tdep); i++)
+ memset (XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i), 0, 32);
- if ((clear_bv & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
- for (i = I387_ZMM0H_REGNUM (tdep);
- i < I387_ZMMENDH_REGNUM (tdep); i++)
- memset (XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i), 0, 32);
+ if ((clear_bv & X86_XSTATE_K))
+ for (i = I387_K0_REGNUM (tdep);
+ i < I387_KEND_REGNUM (tdep); i++)
+ memset (XSAVE_AVX512_K_ADDR (tdep, regs, i), 0, 8);
- if ((clear_bv & X86_XSTATE_K))
- for (i = I387_K0_REGNUM (tdep);
- i < I387_KEND_REGNUM (tdep); i++)
- memset (XSAVE_AVX512_K_ADDR (tdep, regs, i), 0, 8);
+ if ((clear_bv & X86_XSTATE_ZMM))
+ {
+ for (i = I387_YMM16H_REGNUM (tdep);
+ i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
+ memset (XSAVE_YMM_AVX512_ADDR (tdep, regs, i), 0, 16);
+ for (i = I387_XMM16_REGNUM (tdep);
+ i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+ memset (XSAVE_XMM_AVX512_ADDR (tdep, regs, i), 0, 16);
+ }
- if ((clear_bv & X86_XSTATE_ZMM))
- {
- for (i = I387_YMM16H_REGNUM (tdep);
- i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
- memset (XSAVE_YMM_AVX512_ADDR (tdep, regs, i), 0, 16);
- for (i = I387_XMM16_REGNUM (tdep);
- i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
- memset (XSAVE_XMM_AVX512_ADDR (tdep, regs, i), 0, 16);
- }
+ if ((clear_bv & X86_XSTATE_AVX))
+ for (i = I387_YMM0H_REGNUM (tdep);
+ i < I387_YMMENDH_REGNUM (tdep); i++)
+ memset (XSAVE_AVXH_ADDR (tdep, regs, i), 0, 16);
- if ((clear_bv & X86_XSTATE_AVX))
- for (i = I387_YMM0H_REGNUM (tdep);
- i < I387_YMMENDH_REGNUM (tdep); i++)
- memset (XSAVE_AVXH_ADDR (tdep, regs, i), 0, 16);
+ if ((clear_bv & X86_XSTATE_SSE))
+ for (i = I387_XMM0_REGNUM (tdep);
+ i < I387_MXCSR_REGNUM (tdep); i++)
+ memset (FXSAVE_ADDR (tdep, regs, i), 0, 16);
+
+ /* The mxcsr register is written into the xsave buffer if either AVX
+ or SSE is enabled, so only clear it if both of those features
+ require clearing. */
+ if ((clear_bv & (X86_XSTATE_AVX | X86_XSTATE_SSE))
+ == (X86_XSTATE_AVX | X86_XSTATE_SSE))
+ store_unsigned_integer (FXSAVE_ADDR (tdep, regs, i), 2, byte_order,
+ I387_MXCSR_INIT_VAL);
- if ((clear_bv & X86_XSTATE_SSE))
- for (i = I387_XMM0_REGNUM (tdep);
- i < I387_MXCSR_REGNUM (tdep); i++)
- memset (FXSAVE_ADDR (tdep, regs, i), 0, 16);
+ if ((clear_bv & X86_XSTATE_X87))
+ {
+ for (i = I387_ST0_REGNUM (tdep);
+ i < I387_FCTRL_REGNUM (tdep); i++)
+ memset (FXSAVE_ADDR (tdep, regs, i), 0, 10);
- if ((clear_bv & X86_XSTATE_X87))
- for (i = I387_ST0_REGNUM (tdep);
- i < I387_FCTRL_REGNUM (tdep); i++)
- memset (FXSAVE_ADDR (tdep, regs, i), 0, 10);
+ for (i = I387_FCTRL_REGNUM (tdep);
+ i < I387_XMM0_REGNUM (tdep); i++)
+ {
+ if (i == I387_FCTRL_REGNUM (tdep))
+ store_unsigned_integer (FXSAVE_ADDR (tdep, regs, i), 2,
+ byte_order, I387_FCTRL_INIT_VAL);
+ else
+ memset (FXSAVE_ADDR (tdep, regs, i), 0,
+ regcache_register_size (regcache, i));
+ }
}
+ }
- if (regclass == all)
- {
- /* Check if any PKEYS registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_PKRU))
- for (i = I387_PKRU_REGNUM (tdep);
- i < I387_PKEYSEND_REGNUM (tdep); i++)
+ if (regclass == all)
+ {
+ /* Check if any PKEYS registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_PKRU))
+ for (i = I387_PKRU_REGNUM (tdep);
+ i < I387_PKEYSEND_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_PKEYS_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 4) != 0)
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_PKEYS_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 4) != 0)
- {
- xstate_bv |= X86_XSTATE_PKRU;
- memcpy (p, raw, 4);
- }
+ xstate_bv |= X86_XSTATE_PKRU;
+ memcpy (p, raw, 4);
}
+ }
- /* Check if any ZMMH registers are changed. */
- if ((tdep->xcr0 & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
- for (i = I387_ZMM0H_REGNUM (tdep);
- i < I387_ZMMENDH_REGNUM (tdep); i++)
+ /* Check if any ZMMH registers are changed. */
+ if ((tdep->xcr0 & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
+ for (i = I387_ZMM0H_REGNUM (tdep);
+ i < I387_ZMMENDH_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 32) != 0)
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 32) != 0)
- {
- xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
- memcpy (p, raw, 32);
- }
+ xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
+ memcpy (p, raw, 32);
}
+ }
- /* Check if any K registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_K))
- for (i = I387_K0_REGNUM (tdep);
- i < I387_KEND_REGNUM (tdep); i++)
+ /* Check if any K registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_K))
+ for (i = I387_K0_REGNUM (tdep);
+ i < I387_KEND_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_AVX512_K_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 8) != 0)
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_AVX512_K_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 8) != 0)
- {
- xstate_bv |= X86_XSTATE_K;
- memcpy (p, raw, 8);
- }
+ xstate_bv |= X86_XSTATE_K;
+ memcpy (p, raw, 8);
}
+ }
- /* Check if any XMM or upper YMM registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_ZMM))
+ /* Check if any XMM or upper YMM registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_ZMM))
+ {
+ for (i = I387_YMM16H_REGNUM (tdep);
+ i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
{
- for (i = I387_YMM16H_REGNUM (tdep);
- i < I387_YMMH_AVX512_END_REGNUM (tdep); i++)
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_YMM_AVX512_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16) != 0)
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_YMM_AVX512_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 16) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 16);
- }
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 16);
}
- for (i = I387_XMM16_REGNUM (tdep);
- i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+ }
+ for (i = I387_XMM16_REGNUM (tdep);
+ i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_XMM_AVX512_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16) != 0)
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_XMM_AVX512_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 16) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 16);
- }
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 16);
}
}
+ }
- /* Check if any upper YMM registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_AVX))
- for (i = I387_YMM0H_REGNUM (tdep);
- i < I387_YMMENDH_REGNUM (tdep); i++)
+ /* Check if any upper MPX registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_BNDREGS))
+ for (i = I387_BND0R_REGNUM (tdep);
+ i < I387_BNDCFGU_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_MPX_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16))
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_AVXH_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_AVX;
- memcpy (p, raw, 16);
- }
+ xstate_bv |= X86_XSTATE_BNDREGS;
+ memcpy (p, raw, 16);
}
- /* Check if any upper MPX registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_BNDREGS))
- for (i = I387_BND0R_REGNUM (tdep);
- i < I387_BNDCFGU_REGNUM (tdep); i++)
+ }
+
+ /* Check if any upper MPX registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_BNDCFG))
+ for (i = I387_BNDCFGU_REGNUM (tdep);
+ i < I387_MPXEND_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_MPX_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 8))
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_MPX_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_BNDREGS;
- memcpy (p, raw, 16);
- }
+ xstate_bv |= X86_XSTATE_BNDCFG;
+ memcpy (p, raw, 8);
}
+ }
- /* Check if any upper MPX registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_BNDCFG))
- for (i = I387_BNDCFGU_REGNUM (tdep);
- i < I387_MPXEND_REGNUM (tdep); i++)
+ /* Check if any upper YMM registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_AVX))
+ for (i = I387_YMM0H_REGNUM (tdep);
+ i < I387_YMMENDH_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_AVXH_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16))
{
- regcache_raw_collect (regcache, i, raw);
- p = XSAVE_MPX_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 8))
- {
- xstate_bv |= X86_XSTATE_BNDCFG;
- memcpy (p, raw, 8);
- }
+ xstate_bv |= X86_XSTATE_AVX;
+ memcpy (p, raw, 16);
}
+ }
- /* Check if any SSE registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_SSE))
- for (i = I387_XMM0_REGNUM (tdep);
- i < I387_MXCSR_REGNUM (tdep); i++)
+ /* Check if any SSE registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_SSE))
+ for (i = I387_XMM0_REGNUM (tdep);
+ i < I387_MXCSR_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16))
{
- regcache_raw_collect (regcache, i, raw);
- p = FXSAVE_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_SSE;
- memcpy (p, raw, 16);
- }
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (p, raw, 16);
}
+ }
- /* Check if any X87 registers are changed. */
- if ((tdep->xcr0 & X86_XSTATE_X87))
- for (i = I387_ST0_REGNUM (tdep);
- i < I387_FCTRL_REGNUM (tdep); i++)
+ if ((tdep->xcr0 & X86_XSTATE_AVX) || (tdep->xcr0 & X86_XSTATE_SSE))
+ {
+ i = I387_MXCSR_REGNUM (tdep);
+ regcache_raw_collect (regcache, i, raw);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 4))
+ {
+ /* Now, we need to mark one of either SSE of AVX as enabled.
+ We could pick either. What we do is check to see if one
+ of the features is already enabled, if it is then we leave
+ it at that, otherwise we pick SSE. */
+ if ((xstate_bv & (X86_XSTATE_SSE | X86_XSTATE_AVX)) == 0)
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (p, raw, 4);
+ }
+ }
+
+ /* Check if any X87 registers are changed. Only the non-control
+ registers are handled here, the control registers are all handled
+ later on in this function. */
+ if ((tdep->xcr0 & X86_XSTATE_X87))
+ for (i = I387_ST0_REGNUM (tdep);
+ i < I387_FCTRL_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 10))
{
- regcache_raw_collect (regcache, i, raw);
- p = FXSAVE_ADDR (tdep, regs, i);
- if (memcmp (raw, p, 10))
- {
- xstate_bv |= X86_XSTATE_X87;
- memcpy (p, raw, 10);
- }
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, raw, 10);
}
- }
- else
- {
- /* Check if REGNUM is changed. */
- regcache_raw_collect (regcache, regnum, raw);
+ }
+ }
+ else
+ {
+ /* Check if REGNUM is changed. */
+ regcache_raw_collect (regcache, regnum, raw);
- switch (regclass)
+ switch (regclass)
+ {
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("invalid i387 regclass"));
+
+ case pkeys:
+ /* This is a PKEYS register. */
+ p = XSAVE_PKEYS_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 4) != 0)
{
- default:
- internal_error (__FILE__, __LINE__,
- _("invalid i387 regclass"));
-
- case pkeys:
- /* This is a PKEYS register. */
- p = XSAVE_PKEYS_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 4) != 0)
- {
- xstate_bv |= X86_XSTATE_PKRU;
- memcpy (p, raw, 4);
- }
- break;
-
- case avx512_zmm_h:
- /* This is a ZMM register. */
- p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 32) != 0)
- {
- xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
- memcpy (p, raw, 32);
- }
- break;
- case avx512_k:
- /* This is a AVX512 mask register. */
- p = XSAVE_AVX512_K_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 8) != 0)
- {
- xstate_bv |= X86_XSTATE_K;
- memcpy (p, raw, 8);
- }
- break;
+ xstate_bv |= X86_XSTATE_PKRU;
+ memcpy (p, raw, 4);
+ }
+ break;
- case avx512_ymmh_avx512:
- /* This is an upper YMM16-31 register. */
- p = XSAVE_YMM_AVX512_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 16) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 16);
- }
- break;
+ case avx512_zmm_h:
+ /* This is a ZMM register. */
+ p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 32) != 0)
+ {
+ xstate_bv |= (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM);
+ memcpy (p, raw, 32);
+ }
+ break;
+ case avx512_k:
+ /* This is a AVX512 mask register. */
+ p = XSAVE_AVX512_K_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 8) != 0)
+ {
+ xstate_bv |= X86_XSTATE_K;
+ memcpy (p, raw, 8);
+ }
+ break;
- case avx512_xmm_avx512:
- /* This is an upper XMM16-31 register. */
- p = XSAVE_XMM_AVX512_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 16) != 0)
- {
- xstate_bv |= X86_XSTATE_ZMM;
- memcpy (p, raw, 16);
- }
- break;
+ case avx512_ymmh_avx512:
+ /* This is an upper YMM16-31 register. */
+ p = XSAVE_YMM_AVX512_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 16) != 0)
+ {
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 16);
+ }
+ break;
- case avxh:
- /* This is an upper YMM register. */
- p = XSAVE_AVXH_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_AVX;
- memcpy (p, raw, 16);
- }
- break;
+ case avx512_xmm_avx512:
+ /* This is an upper XMM16-31 register. */
+ p = XSAVE_XMM_AVX512_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 16) != 0)
+ {
+ xstate_bv |= X86_XSTATE_ZMM;
+ memcpy (p, raw, 16);
+ }
+ break;
- case mpx:
- if (regnum < I387_BNDCFGU_REGNUM (tdep))
- {
- regcache_raw_collect (regcache, regnum, raw);
- p = XSAVE_MPX_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 16))
- {
- xstate_bv |= X86_XSTATE_BNDREGS;
- memcpy (p, raw, 16);
- }
- }
- else
- {
- p = XSAVE_MPX_ADDR (tdep, regs, regnum);
- xstate_bv |= X86_XSTATE_BNDCFG;
- memcpy (p, raw, 8);
- }
- break;
+ case avxh:
+ /* This is an upper YMM register. */
+ p = XSAVE_AVXH_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_AVX;
+ memcpy (p, raw, 16);
+ }
+ break;
- case sse:
- /* This is an SSE register. */
- p = FXSAVE_ADDR (tdep, regs, regnum);
+ case mpx:
+ if (regnum < I387_BNDCFGU_REGNUM (tdep))
+ {
+ regcache_raw_collect (regcache, regnum, raw);
+ p = XSAVE_MPX_ADDR (tdep, regs, regnum);
if (memcmp (raw, p, 16))
{
- xstate_bv |= X86_XSTATE_SSE;
+ xstate_bv |= X86_XSTATE_BNDREGS;
memcpy (p, raw, 16);
}
- break;
+ }
+ else
+ {
+ p = XSAVE_MPX_ADDR (tdep, regs, regnum);
+ xstate_bv |= X86_XSTATE_BNDCFG;
+ memcpy (p, raw, 8);
+ }
+ break;
- case x87:
- /* This is an x87 register. */
- p = FXSAVE_ADDR (tdep, regs, regnum);
- if (memcmp (raw, p, 10))
- {
- xstate_bv |= X86_XSTATE_X87;
- memcpy (p, raw, 10);
- }
- break;
+ case sse:
+ /* This is an SSE register. */
+ p = FXSAVE_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (p, raw, 16);
}
- }
+ break;
- /* Update the corresponding bits in `xstate_bv' if any SSE/AVX
- registers are changed. */
- if (xstate_bv)
- {
- /* The supported bits in `xstat_bv' are 8 bytes. */
- initial_xstate_bv |= xstate_bv;
- store_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
- 8, byte_order,
- initial_xstate_bv);
+ case x87:
+ /* This is an x87 register. */
+ p = FXSAVE_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 10))
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, raw, 10);
+ }
+ break;
- switch (regclass)
+ case x87_ctrl_or_mxcsr:
+ /* We only handle MXCSR here. All other x87 control registers
+ are handled separately below. */
+ if (regnum == I387_MXCSR_REGNUM (tdep))
{
- default:
- internal_error (__FILE__, __LINE__,
- _("invalid i387 regclass"));
-
- case all:
- break;
-
- case x87:
- case sse:
- case avxh:
- case mpx:
- case avx512_k:
- case avx512_zmm_h:
- case avx512_ymmh_avx512:
- case avx512_xmm_avx512:
- case pkeys:
- /* Register REGNUM has been updated. Return. */
- return;
+ p = FXSAVE_MXCSR_ADDR (regs);
+ if (memcmp (raw, p, 2))
+ {
+ /* We're only setting MXCSR, so check the initial state
+ to see if either of AVX or SSE are already enabled.
+ If they are then we'll attribute this changed MXCSR to
+ that feature. If neither feature is enabled, then
+ we'll attribute this change to the SSE feature. */
+ xstate_bv |= (initial_xstate_bv
+ & (X86_XSTATE_AVX | X86_XSTATE_SSE));
+ if ((xstate_bv & (X86_XSTATE_AVX | X86_XSTATE_SSE)) == 0)
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (p, raw, 2);
+ }
}
}
- else
- {
- /* Return if REGNUM isn't changed. */
- if (regclass != all)
- return;
- }
}
/* Only handle x87 control registers. */
@@ -1769,15 +1843,38 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
buf[0] |= (1 << fpreg);
}
}
- memcpy (FXSAVE_ADDR (tdep, regs, i), buf, 2);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (p, buf, 2))
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, buf, 2);
+ }
}
else
- regcache_raw_collect (regcache, i, FXSAVE_ADDR (tdep, regs, i));
+ {
+ int regsize;
+
+ regcache_raw_collect (regcache, i, raw);
+ regsize = regcache_register_size (regcache, i);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, regsize))
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, raw, regsize);
+ }
+ }
}
- if (regnum == I387_MXCSR_REGNUM (tdep) || regnum == -1)
- regcache_raw_collect (regcache, I387_MXCSR_REGNUM (tdep),
- FXSAVE_MXCSR_ADDR (regs));
+ /* Update the corresponding bits in `xstate_bv' if any
+ registers are changed. */
+ if (xstate_bv)
+ {
+ /* The supported bits in `xstat_bv' are 8 bytes. */
+ initial_xstate_bv |= xstate_bv;
+ store_unsigned_integer (XSAVE_XSTATE_BV_ADDR (regs),
+ 8, byte_order,
+ initial_xstate_bv);
+ }
}
/* Recreate the FTW (tag word) valid bits from the 80-bit FP data in
diff --git a/gdb/i387-tdep.h b/gdb/i387-tdep.h
index 86b16f3..b2fa75a 100644
--- a/gdb/i387-tdep.h
+++ b/gdb/i387-tdep.h
@@ -156,6 +156,12 @@ extern void i387_collect_fxsave (const struct regcache *regcache, int regnum,
extern void i387_collect_xsave (const struct regcache *regcache,
int regnum, void *xsave, int gcore);
+/* Extract a bitset from XSAVE indicating which features are available in
+ the inferior, but not yet initialised. */
+
+extern ULONGEST i387_xsave_get_clear_bv (struct gdbarch *gdbarch,
+ const void *xsave);
+
/* Prepare the FPU stack in REGCACHE for a function return. */
extern void i387_return_value (struct gdbarch *gdbarch,
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index d6db469..9fc54c0 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2018-05-08 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.arch/amd64-init-x87-values.S: New file.
+ * gdb.arch/amd64-init-x87-values.exp: New file.
+
2018-05-08 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.base/watchpoint-unaligned.exp: Use skip_hw_watchpoint_tests.
diff --git a/gdb/testsuite/gdb.arch/amd64-init-x87-values.S b/gdb/testsuite/gdb.arch/amd64-init-x87-values.S
new file mode 100644
index 0000000..86043a8
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-init-x87-values.S
@@ -0,0 +1,31 @@
+/* Copyright 2018 Free Software Foundation, Inc.
+
+ 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/>.
+
+ This file is part of the gdb testsuite.
+
+ Test initial values of x87 control registers, before any x87
+ instructions have been executed in the inferior. */
+
+ .global _start, main
+ .text
+_start:
+ nop
+
+main:
+ nop
+ fwait
+ nop
+ fld1
+ nop
diff --git a/gdb/testsuite/gdb.arch/amd64-init-x87-values.exp b/gdb/testsuite/gdb.arch/amd64-init-x87-values.exp
new file mode 100644
index 0000000..c17bb4e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-init-x87-values.exp
@@ -0,0 +1,175 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# 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/>.
+
+# This file is part of the gdb testsuite.
+
+# Test initial values of x87 control registers, before any x87
+# instructions have been executed in the inferior.
+
+if ![is_amd64_regs_target] then {
+ return
+}
+
+standard_testfile .S
+
+set options [list debug \
+ additional_flags=-static \
+ additional_flags=-nostartfiles]
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} $options] } {
+ return -1
+}
+
+# Start the test file, and check the x87 control registers (and
+# mxcsr), we expect the default values in all registers.
+#
+# Step forward until the x87 unit is enabled, recheck the register
+# values; they should still have the default values.
+#
+# Finally, step forward until the x87 state has changed, and then
+# recheck the register state to view the changes.
+proc_with_prefix check_x87_regs_around_init {} {
+ global binfile
+
+ clean_restart ${binfile}
+
+ # Get things started.
+ if ![runto_main] then {
+ fail "can't run to main"
+ return 0
+ }
+
+ # Check initial values of x87 control registers. The MXCSR isn't part
+ # of the x87 set, it belongs with SSE/AVX, however we test it here
+ # too, it should have its default value in both cases.
+ foreach {regname regvalue} { "fctrl" "0x37f" \
+ "fstat" "0x0" \
+ "ftag" "0xffff" \
+ "fiseg" "0x0" \
+ "fioff" "0x0" \
+ "foseg" "0x0" \
+ "fooff" "0x0" \
+ "fop" "0x0" \
+ "mxcsr" "0x1f80"} {
+ gdb_test "p/x \$${regname}" " = ${regvalue}" "check initial value of \$${regname}"
+ }
+
+ # No x87 instructions have been executed yet. Step up to FWAIT
+ # instruction. Executing this instruction will enable the x87 unit,
+ # causing the kernel to place the default values into all registers.
+ # After this GDB will no longer supply the default values itself but
+ # will instread read the values out of the xsave buffer.
+ gdb_test "stepi" "fwait" "step to FWAIT instruction"
+ gdb_test "stepi" "nop" "step past FWAIT instruction"
+
+ # The x87 unit is now enabled, but the register values should be
+ # unchanged.
+ foreach {regname regvalue} { "fctrl" "0x37f" \
+ "fstat" "0x0" \
+ "ftag" "0xffff" \
+ "fiseg" "0x0" \
+ "fioff" "0x0" \
+ "foseg" "0x0" \
+ "fooff" "0x0" \
+ "fop" "0x0" \
+ "mxcsr" "0x1f80"} {
+ gdb_test "p/x \$${regname}" " = ${regvalue}" "check post FWAIT value of \$${regname}"
+ }
+
+ # Now step to an x87 instruction that modifies some state.
+ gdb_test "stepi" "fld1" "step to FLD1 instruction"
+
+ # Grab the address of this instruction, it will appear in later
+ # results.
+ set addr [get_hexadecimal_valueof "\$pc" "0"]
+
+ # Step past the FLD1 instruction.
+ gdb_test "stepi" "nop" "step past FLD1 instruction"
+
+ # Check new values of x87 control registers (and MXCSR).
+ foreach {regname regvalue} [list "fctrl" "0x37f" \
+ "fstat" "0x3800" \
+ "ftag" "0x3fff" \
+ "fiseg" "0x0" \
+ "fioff" $addr \
+ "foseg" "0x0" \
+ "fooff" "0x0" \
+ "fop" "0x0" \
+ "mxcsr" "0x1f80" ] {
+ gdb_test "p/x \$${regname}" " = ${regvalue}" "check post FLD1 value of \$${regname}"
+ }
+}
+
+# Start the test file, all FP features will be disabled. Set a new
+# value into the MXCSR register, then step forward one instruction (a
+# nop that does not enable any FP features). Finally check that the
+# mxcsr register still has the value we set.
+proc_with_prefix check_setting_mxcsr_before_enable {} {
+ global binfile
+
+ clean_restart ${binfile}
+
+ if ![runto_main] then {
+ fail "can't run to main"
+ return 0
+ }
+
+ gdb_test_no_output "set \$mxcsr=0x9f80" "set a new value for MXCSR"
+ gdb_test "stepi" "fwait" "step forward one instruction for mxcsr test"
+ gdb_test "p/x \$mxcsr" " = 0x9f80" "check new value of MXCSR is still in place"
+}
+
+# Start the test file, all FP features will be disabled. Set new
+# values into the x87 control-registers, then step forward one
+# instruction (a nop that does not enable any FP features). Finally
+# check that all the x87 control-registers still have the values we
+# set.
+proc_with_prefix check_setting_x87_regs_before_enable {} {
+ global binfile
+
+ clean_restart ${binfile}
+
+ if ![runto_main] then {
+ fail "can't run to main"
+ return 0
+ }
+
+ foreach {regname regvalue} [list "fctrl" "0x37f" \
+ "fstat" "0x3800" \
+ "ftag" "0x7777" \
+ "fiseg" "0x12" \
+ "fioff" "0x2418" \
+ "foseg" "0x24" \
+ "fooff" "0x36" \
+ "fop" "0x100" ] {
+ gdb_test_no_output "set \$$regname=$regvalue" "set a new value for $regname"
+ }
+
+ gdb_test "stepi" "fwait" "step forward one instruction for x87 test"
+
+ foreach {regname regvalue} [list "fctrl" "0x37f" \
+ "fstat" "0x3800" \
+ "ftag" "0x7777" \
+ "fiseg" "0x12" \
+ "fioff" "0x2418" \
+ "foseg" "0x24" \
+ "fooff" "0x36" \
+ "fop" "0x100" ] {
+ gdb_test "p/x \$$regname" "= $regvalue" "check new value of $regname"
+ }
+}
+
+check_x87_regs_around_init
+check_setting_mxcsr_before_enable
+check_setting_x87_regs_before_enable