diff options
-rw-r--r-- | gdb/ChangeLog | 17 | ||||
-rw-r--r-- | gdb/amd64-tdep.c | 22 | ||||
-rw-r--r-- | gdb/common/x86-xstate.h | 13 | ||||
-rw-r--r-- | gdb/gdbserver/ChangeLog | 10 | ||||
-rw-r--r-- | gdb/gdbserver/i387-fp.c | 210 | ||||
-rw-r--r-- | gdb/gdbserver/regcache.c | 13 | ||||
-rw-r--r-- | gdb/gdbserver/regcache.h | 3 | ||||
-rw-r--r-- | gdb/i387-tdep.c | 767 | ||||
-rw-r--r-- | gdb/i387-tdep.h | 6 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/amd64-init-x87-values.S | 31 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/amd64-init-x87-values.exp | 175 |
12 files changed, 872 insertions, 400 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 32038c59471..8314945f1c7 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 1fea26409ae..d555465c2f9 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 5a165b3f6cb..51e5c3c785a 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 74613598a28..9cd72a89f71 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 f1f7d844537..bee1af9d8c6 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 cbdf766df2c..0718b9f9a04 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 6ff13084b0f..2c0df648f6d 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 19387929c59..aca70c186f9 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 86b16f37654..b2fa75aa122 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 d6db46916e6..9fc54c0001c 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 00000000000..86043a81148 --- /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 00000000000..c17bb4e1e54 --- /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 |