+ struct gdbarch *gdbarch = regcache->arch ();
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ gdb_byte *p, *regs = (gdb_byte *) xsave;
+ gdb_byte raw[I386_MAX_REGISTER_SIZE];
+ ULONGEST initial_xstate_bv, clear_bv, xstate_bv = 0;
+ unsigned int i;
+ /* See the comment in i387_supply_xsave(). */
+ unsigned int zmm_endlo_regnum = I387_ZMM0H_REGNUM (tdep)
+ + std::min (tdep->num_zmm_regs, 16);
+ enum
+ {
+ 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;
+
+ gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
+ gdb_assert (tdep->num_xmm_regs > 0);
+
+ if (regnum == -1)
+ regclass = all;
+ else if (regnum >= I387_PKRU_REGNUM (tdep)
+ && regnum < I387_PKEYSEND_REGNUM (tdep))
+ regclass = pkeys;
+ else if (regnum >= I387_ZMM0H_REGNUM (tdep)
+ && regnum < I387_ZMMENDH_REGNUM (tdep))
+ regclass = avx512_zmm_h;
+ else if (regnum >= I387_K0_REGNUM (tdep)
+ && regnum < I387_KEND_REGNUM (tdep))
+ regclass = avx512_k;
+ else if (regnum >= I387_YMM16H_REGNUM (tdep)
+ && regnum < I387_YMMH_AVX512_END_REGNUM (tdep))
+ regclass = avx512_ymmh_avx512;
+ else if (regnum >= I387_XMM16_REGNUM (tdep)
+ && regnum < I387_XMM_AVX512_END_REGNUM (tdep))
+ regclass = avx512_xmm_avx512;
+ else if (regnum >= I387_YMM0H_REGNUM (tdep)
+ && regnum < I387_YMMENDH_REGNUM (tdep))
+ regclass = avxh;
+ else if (regnum >= I387_BND0R_REGNUM (tdep)
+ && regnum < I387_MPXEND_REGNUM (tdep))
+ regclass = mpx;
+ else if (regnum >= I387_XMM0_REGNUM (tdep)
+ && regnum < I387_MXCSR_REGNUM (tdep))
+ regclass = sse;
+ 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
+ internal_error (__FILE__, __LINE__, _("invalid i387 regnum %d"), regnum);
+
+ if (gcore)
+ {
+ /* Clear XSAVE extended state. */
+ memset (regs, 0, X86_XSTATE_SIZE (tdep->xcr0));
+
+ /* Update XCR0 and `xstate_bv' with XCR0 for gcore. */
+ if (tdep->xsave_xcr0_offset != -1)
+ memcpy (regs + tdep->xsave_xcr0_offset, &tdep->xcr0, 8);
+ memcpy (XSAVE_XSTATE_BV_ADDR (regs), &tdep->xcr0, 8);
+ }
+
+ /* 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)
+ {
+ 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_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))
+ for (i = I387_ZMM0H_REGNUM (tdep); i < zmm_endlo_regnum; 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_ZMM))
+ {
+ for (i = zmm_endlo_regnum; i < I387_ZMMENDH_REGNUM (tdep); i++)
+ memset (XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, i), 0, 32);
+ 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_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_MXCSR_ADDR (regs), 2, byte_order,
+ I387_MXCSR_INIT_VAL);
+
+ 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++)
+ {
+ regcache->raw_collect (i, raw);
+ p = XSAVE_PKEYS_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 4) != 0)
+ {
+ 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++)
+ {
+ regcache->raw_collect (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);
+ }
+ }
+
+ /* 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 (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);
+ }
+ }
+
+ /* 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++)
+ {
+ regcache->raw_collect (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);
+ }
+ }
+ for (i = I387_XMM16_REGNUM (tdep);
+ i < I387_XMM_AVX512_END_REGNUM (tdep); i++)
+ {
+ regcache->raw_collect (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);
+ }
+ }
+ }
+
+ /* 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 (i, raw);
+ p = XSAVE_MPX_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_BNDREGS;
+ memcpy (p, raw, 16);
+ }
+ }
+
+ /* 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 (i, raw);
+ p = XSAVE_MPX_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 8))
+ {
+ xstate_bv |= X86_XSTATE_BNDCFG;
+ memcpy (p, raw, 8);
+ }
+ }
+
+ /* 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 (i, raw);
+ p = XSAVE_AVXH_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16))
+ {
+ 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++)
+ {
+ regcache->raw_collect (i, raw);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= X86_XSTATE_SSE;
+ memcpy (p, raw, 16);
+ }
+ }
+
+ if ((tdep->xcr0 & X86_XSTATE_AVX) || (tdep->xcr0 & X86_XSTATE_SSE))
+ {
+ i = I387_MXCSR_REGNUM (tdep);
+ regcache->raw_collect (i, raw);
+ p = FXSAVE_MXCSR_ADDR (regs);
+ 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 (i, raw);
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 10))
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, raw, 10);
+ }
+ }
+ }
+ else
+ {
+ /* Check if REGNUM is changed. */
+ regcache->raw_collect (regnum, raw);
+
+ 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)
+ {
+ 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;
+
+ 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_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 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 mpx:
+ if (regnum < I387_BNDCFGU_REGNUM (tdep))
+ {
+ regcache->raw_collect (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 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;
+
+ 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 x87_ctrl_or_mxcsr:
+ /* We only handle MXCSR here. All other x87 control registers
+ are handled separately below. */
+ if (regnum == I387_MXCSR_REGNUM (tdep))
+ {
+ 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);
+ }
+ }
+ }
+ }
+
+ /* Only handle x87 control registers. */
+ for (i = I387_FCTRL_REGNUM (tdep); i < I387_XMM0_REGNUM (tdep); i++)
+ if (regnum == -1 || regnum == i)
+ {
+ /* 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))
+ {
+ gdb_byte buf[4];
+
+ regcache->raw_collect (i, buf);
+
+ if (i == I387_FOP_REGNUM (tdep))
+ {
+ /* The opcode occupies only 11 bits. Make sure we
+ don't touch the other bits. */
+ buf[1] &= ((1 << 3) - 1);
+ buf[1] |= ((FXSAVE_ADDR (tdep, regs, i))[1] & ~((1 << 3) - 1));
+ }
+ else if (i == I387_FTAG_REGNUM (tdep))
+ {
+ /* Converting back is much easier. */
+
+ unsigned short ftag;
+ int fpreg;
+
+ ftag = (buf[1] << 8) | buf[0];
+ buf[0] = 0;
+ buf[1] = 0;
+
+ for (fpreg = 7; fpreg >= 0; fpreg--)
+ {
+ int tag = (ftag >> (fpreg * 2)) & 3;
+
+ if (tag != 3)
+ buf[0] |= (1 << fpreg);
+ }
+ }
+ p = FXSAVE_ADDR (tdep, regs, i);
+ if (memcmp (p, buf, 2))
+ {
+ xstate_bv |= X86_XSTATE_X87;
+ memcpy (p, buf, 2);
+ }
+ }
+ else
+ {
+ int regsize;
+
+ regcache->raw_collect (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);
+ }
+ }
+ }
+
+ /* 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);
+ }