Add MPX support for i386
2013-11-20 Walfred Tedeschi <walfred.tedeschi@intel.com> * i386-linux-nat.c (GETXSTATEREGS_SUPPLIES): Add MPX registers on the range of registers to be read from xsave buffer. (i386_linux_read_description): Add case for MPX. * i386-linux-tdep.c: Include features/i386/i386-mpx-linux.c. (i386_linux_gregset_reg_offset): Add MPX registers. (i386_linux_core_read_description): Initialize also MPX. (_initialize_i386_linux_tdep): Add mpx initialization. * i386-tdep.h (gdbarch_tdep): Add fields bnd0r_regnum, bnd0_regnum, mpx_register_names. (i386_regnum): Add MPX registers. (I386_MPX_NUM_REGS): New macro. (i386_bnd_regnum_p): New function. * i386-linux-tdep.h (I386_LINUX_NUM_REGS): Set number of registers to be the number of BNDSTATUS. (tdesc_i386_mpx_linux): Add description for MPX Linux registers. * i386-tdep.c: Include features/i386/i386-mpx.c. (i386_mpx_names): Add MPX register names array. (i386_bnd_names): Add bnd pseudo register names array. (i386_bndr_regnum_p): Lookup register numbers for bnd raw registers. (i386_bndr_regnum_p): Lookup register numbers for bnd raw registers. (386_mpx_ctrl_regnum_p): Lookup register numbers for MPX control registers. (i386_bnd_type): New function. (i386_pseudo_register_type): Use i386_bnd_type for bnd pseudo register types. (i386_pseudo_register_read_into_value): Add bnd case. (i386_pseudo_register_write): Add bnd pseudo registers. (i386_register_reggroup_p): Add MPX register to the group all. (i386_validate_tdesc_p): Add MPX to the target description validation. (i386_pseudo_register_name): Add bnd pseudo registers. (i386_gdbarch_init): Add MPX for architecture initialization. (_initia_initialize_i386_tdep): Add mpx initialization. * i387-tdep.c (xsave_mpx_offset): New vector for MPX offsets on XSAVE buffer. (XSAVE_MPX_ADDR): New macro. (i387_supply_xsave): Add MPX case. (i387_collect_xsave): Add MPX case. * i387-tdep.h (I387_BND0R_REGNUM): New macro. (I387_BNDCFGU_REGNUM): New macro. (I387_NUM_MPX_REGS): New macro. (I387_NUM_BND_REGS): New macro. (I387_NUM_MPX_CTRL_REGS): New macro. (I387_MPXEND_REGNUM): New macro. * common/i386-xstate.h (I386_XSTATE_BNDREGS): New macro. (I386_XSTATE_BNDCFG): Likewise. (I386_XSTATE_MPX_MASK): Likewise. (I386_XSTATE_ALL_MASK): New macro represents flags for all states. (I386_XSTATE_BNDREGS_SIZE): New macro. (I386_XSTATE_BNDCFG_SIZE): Likewise. (I386_XSTATE_SIZE): Adapt for MPX. (I386_XSTATE_MAX_SIZE): Likewise. Change-Id: I9ddb7d49434d86fa18eb6b99515203d7c567aefd Signed-off-by: Walfred Tedeschi <walfred.tedeschi@intel.com> Conflicts: gdb/ChangeLog
This commit is contained in:
parent
ccc42043f7
commit
1dbcd68cf1
@ -1,3 +1,60 @@
|
||||
2013-11-20 Walfred Tedeschi <walfred.tedeschi@intel.com>
|
||||
|
||||
* i386-linux-nat.c (GETXSTATEREGS_SUPPLIES): Add MPX
|
||||
registers on the range of registers to be read from
|
||||
xsave buffer.
|
||||
(i386_linux_read_description): Add case for MPX.
|
||||
* i386-linux-tdep.c: Include features/i386/i386-mpx-linux.c.
|
||||
(i386_linux_gregset_reg_offset): Add MPX registers.
|
||||
(i386_linux_core_read_description): Initialize also MPX.
|
||||
(_initialize_i386_linux_tdep): Add mpx initialization.
|
||||
* i386-tdep.h (gdbarch_tdep): Add fields bnd0r_regnum, bnd0_regnum,
|
||||
mpx_register_names.
|
||||
(i386_regnum): Add MPX registers.
|
||||
(I386_MPX_NUM_REGS): New macro.
|
||||
(i386_bnd_regnum_p): New function.
|
||||
* i386-linux-tdep.h (I386_LINUX_NUM_REGS): Set
|
||||
number of registers to be the number of BNDSTATUS.
|
||||
(tdesc_i386_mpx_linux): Add description for MPX Linux registers.
|
||||
* i386-tdep.c: Include features/i386/i386-mpx.c.
|
||||
(i386_mpx_names): Add MPX register names array.
|
||||
(i386_bnd_names): Add bnd pseudo register names array.
|
||||
(i386_bndr_regnum_p): Lookup register numbers for bnd raw
|
||||
registers.
|
||||
(i386_bndr_regnum_p): Lookup register numbers for bnd raw registers.
|
||||
(386_mpx_ctrl_regnum_p): Lookup register numbers for MPX control
|
||||
registers.
|
||||
(i386_bnd_type): New function.
|
||||
(i386_pseudo_register_type): Use i386_bnd_type for bnd pseudo
|
||||
register types.
|
||||
(i386_pseudo_register_read_into_value): Add bnd case.
|
||||
(i386_pseudo_register_write): Add bnd pseudo registers.
|
||||
(i386_register_reggroup_p): Add MPX register to the group all.
|
||||
(i386_validate_tdesc_p): Add MPX to the target description
|
||||
validation.
|
||||
(i386_pseudo_register_name): Add bnd pseudo registers.
|
||||
(i386_gdbarch_init): Add MPX for architecture initialization.
|
||||
(_initia_initialize_i386_tdep): Add mpx initialization.
|
||||
* i387-tdep.c (xsave_mpx_offset): New vector for MPX offsets on
|
||||
XSAVE buffer.
|
||||
(XSAVE_MPX_ADDR): New macro.
|
||||
(i387_supply_xsave): Add MPX case.
|
||||
(i387_collect_xsave): Add MPX case.
|
||||
* i387-tdep.h (I387_BND0R_REGNUM): New macro.
|
||||
(I387_BNDCFGU_REGNUM): New macro.
|
||||
(I387_NUM_MPX_REGS): New macro.
|
||||
(I387_NUM_BND_REGS): New macro.
|
||||
(I387_NUM_MPX_CTRL_REGS): New macro.
|
||||
(I387_MPXEND_REGNUM): New macro.
|
||||
* common/i386-xstate.h (I386_XSTATE_BNDREGS): New macro.
|
||||
(I386_XSTATE_BNDCFG): Likewise.
|
||||
(I386_XSTATE_MPX_MASK): Likewise.
|
||||
(I386_XSTATE_ALL_MASK): New macro represents flags for all states.
|
||||
(I386_XSTATE_BNDREGS_SIZE): New macro.
|
||||
(I386_XSTATE_BNDCFG_SIZE): Likewise.
|
||||
(I386_XSTATE_SIZE): Adapt for MPX.
|
||||
(I386_XSTATE_MAX_SIZE): Likewise.
|
||||
|
||||
2013-11-20 Walfred Tedeschi <walfred.tedeschi@intel.com>
|
||||
|
||||
* features/i386/Makefile: Adapts for using MPX registers.
|
||||
|
@ -24,19 +24,31 @@
|
||||
#define I386_XSTATE_X87 (1ULL << 0)
|
||||
#define I386_XSTATE_SSE (1ULL << 1)
|
||||
#define I386_XSTATE_AVX (1ULL << 2)
|
||||
#define I386_XSTATE_BNDREGS (1ULL << 3)
|
||||
#define I386_XSTATE_BNDCFG (1ULL << 4)
|
||||
|
||||
/* Supported mask and size of the extended state. */
|
||||
#define I386_XSTATE_X87_MASK I386_XSTATE_X87
|
||||
#define I386_XSTATE_SSE_MASK (I386_XSTATE_X87 | I386_XSTATE_SSE)
|
||||
#define I386_XSTATE_AVX_MASK (I386_XSTATE_SSE_MASK | I386_XSTATE_AVX)
|
||||
#define I386_XSTATE_MPX_MASK (I386_XSTATE_AVX_MASK \
|
||||
| I386_XSTATE_BNDREGS \
|
||||
| I386_XSTATE_BNDCFG)
|
||||
|
||||
#define I386_XSTATE_ALL_MASK I386_XSTATE_MPX_MASK
|
||||
|
||||
#define I386_XSTATE_SSE_SIZE 576
|
||||
#define I386_XSTATE_AVX_SIZE 832
|
||||
#define I386_XSTATE_MAX_SIZE 832
|
||||
#define I386_XSTATE_BNDREGS_SIZE 1024
|
||||
#define I386_XSTATE_BNDCFG_SIZE 1088
|
||||
|
||||
#define I386_XSTATE_MAX_SIZE 1088
|
||||
|
||||
/* Get I386 XSAVE extended state size. */
|
||||
#define I386_XSTATE_SIZE(XCR0) \
|
||||
(((XCR0) & I386_XSTATE_AVX) != 0 \
|
||||
? I386_XSTATE_AVX_SIZE : I386_XSTATE_SSE_SIZE)
|
||||
#define I386_XSTATE_SIZE(XCR0) \
|
||||
(((XCR0) & I386_XSTATE_BNDCFG) != 0 ? I386_XSTATE_BNDCFG_SIZE \
|
||||
: (((XCR0) & I386_XSTATE_BNDREGS) != 0 ? I386_XSTATE_BNDCFG_SIZE \
|
||||
: (((XCR0) & I386_XSTATE_AVX_SIZE) != 0 ? I386_XSTATE_AVX_SIZE \
|
||||
: I386_XSTATE_SSE_SIZE)))
|
||||
|
||||
#endif /* I386_XSTATE_H */
|
||||
|
@ -102,7 +102,7 @@ static int have_ptrace_getregset = -1;
|
||||
(I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS)
|
||||
|
||||
#define GETXSTATEREGS_SUPPLIES(regno) \
|
||||
(I386_ST0_REGNUM <= (regno) && (regno) < I386_AVX_NUM_REGS)
|
||||
(I386_ST0_REGNUM <= (regno) && (regno) < I386_MPX_NUM_REGS)
|
||||
|
||||
/* Does the current host support the GETREGS request? */
|
||||
int have_ptrace_getregs =
|
||||
@ -1041,9 +1041,18 @@ i386_linux_read_description (struct target_ops *ops)
|
||||
}
|
||||
|
||||
/* Check the native XCR0 only if PTRACE_GETREGSET is available. */
|
||||
if (have_ptrace_getregset
|
||||
&& (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
|
||||
return tdesc_i386_avx_linux;
|
||||
if (have_ptrace_getregset)
|
||||
{
|
||||
switch ((xcr0 & I386_XSTATE_ALL_MASK))
|
||||
{
|
||||
case I386_XSTATE_MPX_MASK:
|
||||
return tdesc_i386_mpx_linux;
|
||||
case I386_XSTATE_AVX_MASK:
|
||||
return tdesc_i386_avx_linux;
|
||||
default:
|
||||
return tdesc_i386_linux;
|
||||
}
|
||||
}
|
||||
else
|
||||
return tdesc_i386_linux;
|
||||
}
|
||||
|
@ -50,6 +50,7 @@
|
||||
|
||||
#include "features/i386/i386-linux.c"
|
||||
#include "features/i386/i386-mmx-linux.c"
|
||||
#include "features/i386/i386-mpx-linux.c"
|
||||
#include "features/i386/i386-avx-linux.c"
|
||||
|
||||
/* Supported register note sections. */
|
||||
@ -569,6 +570,8 @@ int i386_linux_gregset_reg_offset[] =
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, /* MPX registers BND0 ... BND3. */
|
||||
-1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
|
||||
11 * 4 /* "orig_eax" */
|
||||
};
|
||||
|
||||
@ -642,8 +645,11 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
|
||||
{
|
||||
/* Linux/i386. */
|
||||
uint64_t xcr0 = i386_linux_core_read_xcr0 (abfd);
|
||||
switch ((xcr0 & I386_XSTATE_AVX_MASK))
|
||||
|
||||
switch ((xcr0 & I386_XSTATE_ALL_MASK))
|
||||
{
|
||||
case I386_XSTATE_MPX_MASK:
|
||||
return tdesc_i386_mpx_linux;
|
||||
case I386_XSTATE_AVX_MASK:
|
||||
return tdesc_i386_avx_linux;
|
||||
case I386_XSTATE_SSE_MASK:
|
||||
@ -979,4 +985,5 @@ _initialize_i386_linux_tdep (void)
|
||||
initialize_tdesc_i386_linux ();
|
||||
initialize_tdesc_i386_mmx_linux ();
|
||||
initialize_tdesc_i386_avx_linux ();
|
||||
initialize_tdesc_i386_mpx_linux ();
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
/* Register number for the "orig_eax" pseudo-register. If this
|
||||
pseudo-register contains a value >= 0 it is interpreted as the
|
||||
system call number that the kernel is supposed to restart. */
|
||||
#define I386_LINUX_ORIG_EAX_REGNUM I386_AVX_NUM_REGS
|
||||
#define I386_LINUX_ORIG_EAX_REGNUM I386_MPX_NUM_REGS
|
||||
|
||||
/* Total number of registers for GNU/Linux. */
|
||||
#define I386_LINUX_NUM_REGS (I386_LINUX_ORIG_EAX_REGNUM + 1)
|
||||
@ -41,6 +41,7 @@ extern uint64_t i386_linux_core_read_xcr0 (bfd *abfd);
|
||||
extern struct target_desc *tdesc_i386_linux;
|
||||
extern struct target_desc *tdesc_i386_mmx_linux;
|
||||
extern struct target_desc *tdesc_i386_avx_linux;
|
||||
extern struct target_desc *tdesc_i386_mpx_linux;
|
||||
|
||||
/* Format of XSAVE extended state is:
|
||||
struct
|
||||
@ -49,6 +50,7 @@ extern struct target_desc *tdesc_i386_avx_linux;
|
||||
sw_usable_bytes[464..511]
|
||||
xstate_hdr_bytes[512..575]
|
||||
avx_bytes[576..831]
|
||||
mpx_bytes [960..1032]
|
||||
future_state etc
|
||||
};
|
||||
|
||||
|
219
gdb/i386-tdep.c
219
gdb/i386-tdep.c
@ -57,6 +57,7 @@
|
||||
|
||||
#include "features/i386/i386.c"
|
||||
#include "features/i386/i386-avx.c"
|
||||
#include "features/i386/i386-mpx.c"
|
||||
#include "features/i386/i386-mmx.c"
|
||||
|
||||
#include "ax.h"
|
||||
@ -98,6 +99,18 @@ static const char *i386_ymmh_names[] =
|
||||
"ymm4h", "ymm5h", "ymm6h", "ymm7h",
|
||||
};
|
||||
|
||||
static const char *i386_mpx_names[] =
|
||||
{
|
||||
"bnd0raw", "bnd1raw", "bnd2raw", "bnd3raw", "bndcfgu", "bndstatus"
|
||||
};
|
||||
|
||||
/* Register names for MPX pseudo-registers. */
|
||||
|
||||
static const char *i386_bnd_names[] =
|
||||
{
|
||||
"bnd0", "bnd1", "bnd2", "bnd3"
|
||||
};
|
||||
|
||||
/* Register names for MMX pseudo-registers. */
|
||||
|
||||
static const char *i386_mmx_names[] =
|
||||
@ -202,6 +215,21 @@ i386_ymm_regnum_p (struct gdbarch *gdbarch, int regnum)
|
||||
return regnum >= 0 && regnum < tdep->num_ymm_regs;
|
||||
}
|
||||
|
||||
/* BND register? */
|
||||
|
||||
int
|
||||
i386_bnd_regnum_p (struct gdbarch *gdbarch, int regnum)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
int bnd0_regnum = tdep->bnd0_regnum;
|
||||
|
||||
if (bnd0_regnum < 0)
|
||||
return 0;
|
||||
|
||||
regnum -= bnd0_regnum;
|
||||
return regnum >= 0 && regnum < I387_NUM_BND_REGS;
|
||||
}
|
||||
|
||||
/* SSE register? */
|
||||
|
||||
int
|
||||
@ -254,6 +282,34 @@ i386_fpc_regnum_p (struct gdbarch *gdbarch, int regnum)
|
||||
&& regnum < I387_XMM0_REGNUM (tdep));
|
||||
}
|
||||
|
||||
/* BNDr (raw) register? */
|
||||
|
||||
static int
|
||||
i386_bndr_regnum_p (struct gdbarch *gdbarch, int regnum)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
|
||||
if (I387_BND0R_REGNUM (tdep) < 0)
|
||||
return 0;
|
||||
|
||||
regnum -= tdep->bnd0r_regnum;
|
||||
return regnum >= 0 && regnum < I387_NUM_BND_REGS;
|
||||
}
|
||||
|
||||
/* BND control register? */
|
||||
|
||||
static int
|
||||
i386_mpx_ctrl_regnum_p (struct gdbarch *gdbarch, int regnum)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
|
||||
if (I387_BNDCFGU_REGNUM (tdep) < 0)
|
||||
return 0;
|
||||
|
||||
regnum -= I387_BNDCFGU_REGNUM (tdep);
|
||||
return regnum >= 0 && regnum < I387_NUM_MPX_CTRL_REGS;
|
||||
}
|
||||
|
||||
/* Return the name of register REGNUM, or the empty string if it is
|
||||
an anonymous register. */
|
||||
|
||||
@ -273,6 +329,8 @@ const char *
|
||||
i386_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
if (i386_bnd_regnum_p (gdbarch, regnum))
|
||||
return i386_bnd_names[regnum - tdep->bnd0_regnum];
|
||||
if (i386_mmx_regnum_p (gdbarch, regnum))
|
||||
return i386_mmx_names[regnum - I387_MM0_REGNUM (tdep)];
|
||||
else if (i386_ymm_regnum_p (gdbarch, regnum))
|
||||
@ -2750,6 +2808,43 @@ i387_ext_type (struct gdbarch *gdbarch)
|
||||
return tdep->i387_ext_type;
|
||||
}
|
||||
|
||||
/* Construct type for pseudo BND registers. We can't use
|
||||
tdesc_find_type since a complement of one value has to be used
|
||||
to describe the upper bound. */
|
||||
|
||||
static struct type *
|
||||
i386_bnd_type (struct gdbarch *gdbarch)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
|
||||
|
||||
if (!tdep->i386_bnd_type)
|
||||
{
|
||||
struct type *t, *bound_t;
|
||||
const struct builtin_type *bt = builtin_type (gdbarch);
|
||||
|
||||
/* The type we're building is described bellow: */
|
||||
#if 0
|
||||
struct __bound128
|
||||
{
|
||||
void *lbound;
|
||||
void *ubound; /* One complement of raw ubound field. */
|
||||
};
|
||||
#endif
|
||||
|
||||
t = arch_composite_type (gdbarch,
|
||||
"__gdb_builtin_type_bound128", TYPE_CODE_STRUCT);
|
||||
|
||||
append_composite_type_field (t, "lbound", bt->builtin_data_ptr);
|
||||
append_composite_type_field (t, "ubound", bt->builtin_data_ptr);
|
||||
|
||||
TYPE_NAME (t) = "builtin_type_bound128";
|
||||
tdep->i386_bnd_type = t;
|
||||
}
|
||||
|
||||
return tdep->i386_bnd_type;
|
||||
}
|
||||
|
||||
/* Construct vector type for pseudo YMM registers. We can't use
|
||||
tdesc_find_type since YMM isn't described in target description. */
|
||||
|
||||
@ -2851,6 +2946,8 @@ i386_mmx_type (struct gdbarch *gdbarch)
|
||||
struct type *
|
||||
i386_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
|
||||
{
|
||||
if (i386_bnd_regnum_p (gdbarch, regnum))
|
||||
return i386_bnd_type (gdbarch);
|
||||
if (i386_mmx_regnum_p (gdbarch, regnum))
|
||||
return i386_mmx_type (gdbarch);
|
||||
else if (i386_ymm_regnum_p (gdbarch, regnum))
|
||||
@ -2917,8 +3014,31 @@ i386_pseudo_register_read_into_value (struct gdbarch *gdbarch,
|
||||
else
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
if (i386_bnd_regnum_p (gdbarch, regnum))
|
||||
{
|
||||
regnum -= tdep->bnd0_regnum;
|
||||
|
||||
if (i386_ymm_regnum_p (gdbarch, regnum))
|
||||
/* Extract (always little endian). Read lower 128bits. */
|
||||
status = regcache_raw_read (regcache,
|
||||
I387_BND0R_REGNUM (tdep) + regnum,
|
||||
raw_buf);
|
||||
if (status != REG_VALID)
|
||||
mark_value_bytes_unavailable (result_value, 0, 16);
|
||||
else
|
||||
{
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
|
||||
LONGEST upper, lower;
|
||||
int size = TYPE_LENGTH (builtin_type (gdbarch)->builtin_data_ptr);
|
||||
|
||||
lower = extract_unsigned_integer (raw_buf, 8, byte_order);
|
||||
upper = extract_unsigned_integer (raw_buf + 8, 8, byte_order);
|
||||
upper = ~upper;
|
||||
|
||||
memcpy (buf, &lower, size);
|
||||
memcpy (buf + size, &upper, size);
|
||||
}
|
||||
}
|
||||
else if (i386_ymm_regnum_p (gdbarch, regnum))
|
||||
{
|
||||
regnum -= tdep->ymm0_regnum;
|
||||
|
||||
@ -3011,7 +3131,34 @@ i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
|
||||
if (i386_ymm_regnum_p (gdbarch, regnum))
|
||||
if (i386_bnd_regnum_p (gdbarch, regnum))
|
||||
{
|
||||
ULONGEST upper, lower;
|
||||
int size = TYPE_LENGTH (builtin_type (gdbarch)->builtin_data_ptr);
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
|
||||
|
||||
/* New values from input value. */
|
||||
regnum -= tdep->bnd0_regnum;
|
||||
lower = extract_unsigned_integer (buf, size, byte_order);
|
||||
upper = extract_unsigned_integer (buf + size, size, byte_order);
|
||||
|
||||
/* Fetching register buffer. */
|
||||
regcache_raw_read (regcache,
|
||||
I387_BND0R_REGNUM (tdep) + regnum,
|
||||
raw_buf);
|
||||
|
||||
upper = ~upper;
|
||||
|
||||
/* Set register bits. */
|
||||
memcpy (raw_buf, &lower, 8);
|
||||
memcpy (raw_buf + 8, &upper, 8);
|
||||
|
||||
|
||||
regcache_raw_write (regcache,
|
||||
I387_BND0R_REGNUM (tdep) + regnum,
|
||||
raw_buf);
|
||||
}
|
||||
else if (i386_ymm_regnum_p (gdbarch, regnum))
|
||||
{
|
||||
regnum -= tdep->ymm0_regnum;
|
||||
|
||||
@ -3866,7 +4013,8 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
|
||||
{
|
||||
const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
int fp_regnum_p, mmx_regnum_p, xmm_regnum_p, mxcsr_regnum_p,
|
||||
ymm_regnum_p, ymmh_regnum_p;
|
||||
ymm_regnum_p, ymmh_regnum_p, bndr_regnum_p, bnd_regnum_p,
|
||||
mpx_ctrl_regnum_p;
|
||||
|
||||
/* Don't include pseudo registers, except for MMX, in any register
|
||||
groups. */
|
||||
@ -3911,13 +4059,31 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
|
||||
|| ymmh_regnum_p))
|
||||
return 0;
|
||||
|
||||
bnd_regnum_p = i386_bnd_regnum_p (gdbarch, regnum);
|
||||
if (group == all_reggroup
|
||||
&& ((bnd_regnum_p && (tdep->xcr0 & I386_XSTATE_MPX_MASK))))
|
||||
return bnd_regnum_p;
|
||||
|
||||
bndr_regnum_p = i386_bndr_regnum_p (gdbarch, regnum);
|
||||
if (group == all_reggroup
|
||||
&& ((bndr_regnum_p && (tdep->xcr0 & I386_XSTATE_MPX_MASK))))
|
||||
return 0;
|
||||
|
||||
mpx_ctrl_regnum_p = i386_mpx_ctrl_regnum_p (gdbarch, regnum);
|
||||
if (group == all_reggroup
|
||||
&& ((mpx_ctrl_regnum_p && (tdep->xcr0 & I386_XSTATE_MPX_MASK))))
|
||||
return mpx_ctrl_regnum_p;
|
||||
|
||||
if (group == general_reggroup)
|
||||
return (!fp_regnum_p
|
||||
&& !mmx_regnum_p
|
||||
&& !mxcsr_regnum_p
|
||||
&& !xmm_regnum_p
|
||||
&& !ymm_regnum_p
|
||||
&& !ymmh_regnum_p);
|
||||
&& !ymmh_regnum_p
|
||||
&& !bndr_regnum_p
|
||||
&& !bnd_regnum_p
|
||||
&& !mpx_ctrl_regnum_p);
|
||||
|
||||
return default_register_reggroup_p (gdbarch, regnum, group);
|
||||
}
|
||||
@ -7496,7 +7662,7 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
|
||||
{
|
||||
const struct target_desc *tdesc = tdep->tdesc;
|
||||
const struct tdesc_feature *feature_core;
|
||||
const struct tdesc_feature *feature_sse, *feature_avx;
|
||||
const struct tdesc_feature *feature_sse, *feature_avx, *feature_mpx;
|
||||
int i, num_regs, valid_p;
|
||||
|
||||
if (! tdesc_has_registers (tdesc))
|
||||
@ -7513,6 +7679,9 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
|
||||
/* Try AVX registers. */
|
||||
feature_avx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx");
|
||||
|
||||
/* Try MPX registers. */
|
||||
feature_mpx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.mpx");
|
||||
|
||||
valid_p = 1;
|
||||
|
||||
/* The XCR0 bits. */
|
||||
@ -7559,6 +7728,23 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
|
||||
tdep->register_names[i]);
|
||||
}
|
||||
|
||||
if (feature_mpx)
|
||||
{
|
||||
tdep->xcr0 = I386_XSTATE_MPX_MASK;
|
||||
|
||||
if (tdep->bnd0r_regnum < 0)
|
||||
{
|
||||
tdep->mpx_register_names = i386_mpx_names;
|
||||
tdep->bnd0r_regnum = I386_BND0R_REGNUM;
|
||||
tdep->bndcfgu_regnum = I386_BNDCFGU_REGNUM;
|
||||
}
|
||||
|
||||
for (i = 0; i < I387_NUM_MPX_REGS; i++)
|
||||
valid_p &= tdesc_numbered_register (feature_mpx, tdesc_data,
|
||||
I387_BND0R_REGNUM (tdep) + i,
|
||||
tdep->mpx_register_names[i]);
|
||||
}
|
||||
|
||||
return valid_p;
|
||||
}
|
||||
|
||||
@ -7572,6 +7758,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
const struct target_desc *tdesc;
|
||||
int mm0_regnum;
|
||||
int ymm0_regnum;
|
||||
int bnd0_regnum;
|
||||
int num_bnd_cooked;
|
||||
|
||||
/* If there is already a candidate, use it. */
|
||||
arches = gdbarch_list_lookup_by_info (arches, &info);
|
||||
@ -7745,8 +7933,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
|
||||
/* Even though the default ABI only includes general-purpose registers,
|
||||
floating-point registers and the SSE registers, we have to leave a
|
||||
gap for the upper AVX registers. */
|
||||
set_gdbarch_num_regs (gdbarch, I386_AVX_NUM_REGS);
|
||||
gap for the upper AVX registers and the MPX registers. */
|
||||
set_gdbarch_num_regs (gdbarch, I386_MPX_NUM_REGS);
|
||||
|
||||
/* Get the x86 target description from INFO. */
|
||||
tdesc = info.target_desc;
|
||||
@ -7767,6 +7955,10 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
tdep->num_mmx_regs = 8;
|
||||
tdep->num_ymm_regs = 0;
|
||||
|
||||
/* No MPX registers. */
|
||||
tdep->bnd0r_regnum = -1;
|
||||
tdep->bndcfgu_regnum = -1;
|
||||
|
||||
tdesc_data = tdesc_data_alloc ();
|
||||
|
||||
set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction);
|
||||
@ -7785,13 +7977,16 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
num_bnd_cooked = (tdep->bnd0r_regnum > 0 ? I387_NUM_BND_REGS : 0);
|
||||
|
||||
/* Wire in pseudo registers. Number of pseudo registers may be
|
||||
changed. */
|
||||
set_gdbarch_num_pseudo_regs (gdbarch, (tdep->num_byte_regs
|
||||
+ tdep->num_word_regs
|
||||
+ tdep->num_dword_regs
|
||||
+ tdep->num_mmx_regs
|
||||
+ tdep->num_ymm_regs));
|
||||
+ tdep->num_ymm_regs
|
||||
+ num_bnd_cooked));
|
||||
|
||||
/* Target description may be changed. */
|
||||
tdesc = tdep->tdesc;
|
||||
@ -7825,14 +8020,21 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
else
|
||||
tdep->ymm0_regnum = -1;
|
||||
|
||||
bnd0_regnum = mm0_regnum;
|
||||
if (tdep->num_mmx_regs != 0)
|
||||
{
|
||||
/* Support MMX pseudo-register if MMX hasn't been disabled. */
|
||||
tdep->mm0_regnum = mm0_regnum;
|
||||
bnd0_regnum += tdep->num_mmx_regs;
|
||||
}
|
||||
else
|
||||
tdep->mm0_regnum = -1;
|
||||
|
||||
if (tdep->bnd0r_regnum > 0)
|
||||
tdep->bnd0_regnum = bnd0_regnum;
|
||||
else
|
||||
tdep-> bnd0_regnum = -1;
|
||||
|
||||
/* Hook in the legacy prologue-based unwinders last (fallback). */
|
||||
frame_unwind_append_unwinder (gdbarch, &i386_stack_tramp_frame_unwind);
|
||||
frame_unwind_append_unwinder (gdbarch, &i386_sigtramp_frame_unwind);
|
||||
@ -7910,6 +8112,7 @@ is \"default\"."),
|
||||
initialize_tdesc_i386 ();
|
||||
initialize_tdesc_i386_mmx ();
|
||||
initialize_tdesc_i386_avx ();
|
||||
initialize_tdesc_i386_mpx ();
|
||||
|
||||
/* Tell remote stub that we support XML target description. */
|
||||
register_remote_support_xml ("i386");
|
||||
|
@ -130,6 +130,21 @@ struct gdbarch_tdep
|
||||
/* Upper YMM register names. Only used for tdesc_numbered_register. */
|
||||
const char **ymmh_register_names;
|
||||
|
||||
/* Register number for %bnd0r. Set this to -1 to indicate the absence
|
||||
bound registers. */
|
||||
int bnd0r_regnum;
|
||||
|
||||
/* Register number for pseudo register %bnd0. Set this to -1 to indicate the absence
|
||||
bound registers. */
|
||||
int bnd0_regnum;
|
||||
|
||||
/* Register number for %bndcfgu. Set this to -1 to indicate the absence
|
||||
bound control registers. */
|
||||
int bndcfgu_regnum;
|
||||
|
||||
/* MPX register names. Only used for tdesc_numbered_register. */
|
||||
const char **mpx_register_names;
|
||||
|
||||
/* Target description. */
|
||||
const struct target_desc *tdesc;
|
||||
|
||||
@ -165,6 +180,7 @@ struct gdbarch_tdep
|
||||
struct type *i386_mmx_type;
|
||||
struct type *i386_ymm_type;
|
||||
struct type *i387_ext_type;
|
||||
struct type *i386_bnd_type;
|
||||
|
||||
/* Process record/replay target. */
|
||||
/* The map for registers because the AMD64's registers order
|
||||
@ -212,7 +228,11 @@ enum i386_regnum
|
||||
I386_ST0_REGNUM, /* %st(0) */
|
||||
I386_MXCSR_REGNUM = 40, /* %mxcsr */
|
||||
I386_YMM0H_REGNUM, /* %ymm0h */
|
||||
I386_YMM7H_REGNUM = I386_YMM0H_REGNUM + 7
|
||||
I386_YMM7H_REGNUM = I386_YMM0H_REGNUM + 7,
|
||||
I386_BND0R_REGNUM,
|
||||
I386_BND3R_REGNUM = I386_BND0R_REGNUM + 3,
|
||||
I386_BNDCFGU_REGNUM,
|
||||
I386_BNDSTATUS_REGNUM
|
||||
};
|
||||
|
||||
/* Register numbers of RECORD_REGMAP. */
|
||||
@ -250,6 +270,7 @@ enum record_i386_regnum
|
||||
|
||||
#define I386_SSE_NUM_REGS (I386_MXCSR_REGNUM + 1)
|
||||
#define I386_AVX_NUM_REGS (I386_YMM7H_REGNUM + 1)
|
||||
#define I386_MPX_NUM_REGS (I386_BNDSTATUS_REGNUM + 1)
|
||||
|
||||
/* Size of the largest register. */
|
||||
#define I386_MAX_REGISTER_SIZE 16
|
||||
@ -263,6 +284,7 @@ extern int i386_word_regnum_p (struct gdbarch *gdbarch, int regnum);
|
||||
extern int i386_dword_regnum_p (struct gdbarch *gdbarch, int regnum);
|
||||
extern int i386_xmm_regnum_p (struct gdbarch *gdbarch, int regnum);
|
||||
extern int i386_ymm_regnum_p (struct gdbarch *gdbarch, int regnum);
|
||||
extern int i386_bnd_regnum_p (struct gdbarch *gdbarch, int regnum);
|
||||
|
||||
extern const char *i386_pseudo_register_name (struct gdbarch *gdbarch,
|
||||
int regnum);
|
||||
|
130
gdb/i387-tdep.c
130
gdb/i387-tdep.c
@ -763,9 +763,22 @@ static int xsave_avxh_offset[] =
|
||||
576 + 15 * 16 /* Upper 128bit of ... %ymm15 (128 bits each). */
|
||||
};
|
||||
|
||||
static int xsave_mpx_offset[] =
|
||||
{
|
||||
960 + 0 * 16, /* bnd0r...bnd3r registers. */
|
||||
960 + 1 * 16,
|
||||
960 + 2 * 16,
|
||||
960 + 3 * 16,
|
||||
1024 + 0 * 8, /* bndcfg ... bndstatus. */
|
||||
1024 + 1 * 8,
|
||||
};
|
||||
|
||||
#define XSAVE_AVXH_ADDR(tdep, xsave, regnum) \
|
||||
(xsave + xsave_avxh_offset[regnum - I387_YMM0H_REGNUM (tdep)])
|
||||
|
||||
#define XSAVE_MPX_ADDR(tdep, xsave, regnum) \
|
||||
(xsave + xsave_mpx_offset[regnum - I387_BND0R_REGNUM (tdep)])
|
||||
|
||||
/* Similar to i387_supply_fxsave, but use XSAVE extended state. */
|
||||
|
||||
void
|
||||
@ -783,7 +796,8 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
|
||||
x87 = 0x1,
|
||||
sse = 0x2,
|
||||
avxh = 0x4,
|
||||
all = x87 | sse | avxh
|
||||
mpx = 0x8,
|
||||
all = x87 | sse | avxh | mpx
|
||||
} regclass;
|
||||
|
||||
gdb_assert (regs != NULL);
|
||||
@ -795,6 +809,9 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
|
||||
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;
|
||||
@ -814,7 +831,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
|
||||
clear_bv = (~(*xstate_bv_p)) & tdep->xcr0;
|
||||
}
|
||||
else
|
||||
clear_bv = I386_XSTATE_AVX_MASK;
|
||||
clear_bv = I386_XSTATE_ALL_MASK;
|
||||
|
||||
/* With the delayed xsave mechanism, in between the program
|
||||
starting, and the program accessing the vector registers for the
|
||||
@ -838,6 +855,14 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
|
||||
XSAVE_AVXH_ADDR (tdep, regs, regnum));
|
||||
return;
|
||||
|
||||
case mpx:
|
||||
if ((clear_bv & I386_XSTATE_BNDREGS))
|
||||
regcache_raw_supply (regcache, regnum, zero);
|
||||
else
|
||||
regcache_raw_supply (regcache, regnum,
|
||||
XSAVE_MPX_ADDR (tdep, regs, regnum));
|
||||
return;
|
||||
|
||||
case sse:
|
||||
if ((clear_bv & I386_XSTATE_SSE))
|
||||
regcache_raw_supply (regcache, regnum, zero);
|
||||
@ -875,6 +900,42 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the MPX registers. */
|
||||
if ((tdep->xcr0 & I386_XSTATE_BNDREGS))
|
||||
{
|
||||
if (clear_bv & I386_XSTATE_BNDREGS)
|
||||
{
|
||||
for (i = I387_BND0R_REGNUM (tdep);
|
||||
i < I387_BNDCFGU_REGNUM (tdep); i++)
|
||||
regcache_raw_supply (regcache, i, zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = I387_BND0R_REGNUM (tdep);
|
||||
i < I387_BNDCFGU_REGNUM (tdep); i++)
|
||||
regcache_raw_supply (regcache, i,
|
||||
XSAVE_MPX_ADDR (tdep, regs, i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the MPX registers. */
|
||||
if ((tdep->xcr0 & I386_XSTATE_BNDCFG))
|
||||
{
|
||||
if (clear_bv & I386_XSTATE_BNDCFG)
|
||||
{
|
||||
for (i = I387_BNDCFGU_REGNUM (tdep);
|
||||
i < I387_MPXEND_REGNUM (tdep); i++)
|
||||
regcache_raw_supply (regcache, i, zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = I387_BNDCFGU_REGNUM (tdep);
|
||||
i < I387_MPXEND_REGNUM (tdep); i++)
|
||||
regcache_raw_supply (regcache, i,
|
||||
XSAVE_MPX_ADDR (tdep, regs, i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the XMM registers. */
|
||||
if ((tdep->xcr0 & I386_XSTATE_SSE))
|
||||
{
|
||||
@ -989,7 +1050,8 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
|
||||
x87 = 0x2 | check,
|
||||
sse = 0x4 | check,
|
||||
avxh = 0x8 | check,
|
||||
all = x87 | sse | avxh
|
||||
mpx = 0x10 | check,
|
||||
all = x87 | sse | avxh | mpx
|
||||
} regclass;
|
||||
|
||||
gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
|
||||
@ -1000,7 +1062,10 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
|
||||
else if (regnum >= I387_YMM0H_REGNUM (tdep)
|
||||
&& regnum < I387_YMMENDH_REGNUM (tdep))
|
||||
regclass = avxh;
|
||||
else if (regnum >= I387_XMM0_REGNUM(tdep)
|
||||
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)
|
||||
@ -1032,6 +1097,16 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
|
||||
/* Clear register set if its bit in xstat_bv is zero. */
|
||||
if (clear_bv)
|
||||
{
|
||||
if ((clear_bv & I386_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 & I386_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 & I386_XSTATE_AVX))
|
||||
for (i = I387_YMM0H_REGNUM (tdep);
|
||||
i < I387_YMMENDH_REGNUM (tdep); i++)
|
||||
@ -1063,6 +1138,33 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
|
||||
memcpy (p, raw, 16);
|
||||
}
|
||||
}
|
||||
/* Check if any upper MPX registers are changed. */
|
||||
if ((tdep->xcr0 & I386_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))
|
||||
{
|
||||
xstate_bv |= I386_XSTATE_BNDREGS;
|
||||
memcpy (p, raw, 16);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if any upper MPX registers are changed. */
|
||||
if ((tdep->xcr0 & I386_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))
|
||||
{
|
||||
xstate_bv |= I386_XSTATE_BNDCFG;
|
||||
memcpy (p, raw, 8);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if any SSE registers are changed. */
|
||||
if ((tdep->xcr0 & I386_XSTATE_SSE))
|
||||
@ -1113,6 +1215,25 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
|
||||
}
|
||||
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 |= I386_XSTATE_BNDREGS;
|
||||
memcpy (p, raw, 16);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = XSAVE_MPX_ADDR (tdep, regs, regnum);
|
||||
xstate_bv |= I386_XSTATE_BNDCFG;
|
||||
memcpy (p, raw, 8);
|
||||
}
|
||||
break;
|
||||
|
||||
case sse:
|
||||
/* This is an SSE register. */
|
||||
p = FXSAVE_ADDR (tdep, regs, regnum);
|
||||
@ -1154,6 +1275,7 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
|
||||
case x87:
|
||||
case sse:
|
||||
case avxh:
|
||||
case mpx:
|
||||
/* Register REGNUM has been updated. Return. */
|
||||
return;
|
||||
}
|
||||
|
@ -34,6 +34,13 @@ struct ui_file;
|
||||
#define I387_MM0_REGNUM(tdep) ((tdep)->mm0_regnum)
|
||||
#define I387_NUM_YMM_REGS(tdep) ((tdep)->num_ymm_regs)
|
||||
#define I387_YMM0H_REGNUM(tdep) ((tdep)->ymm0h_regnum)
|
||||
#define I387_BND0R_REGNUM(tdep) ((tdep)->bnd0r_regnum)
|
||||
#define I387_BNDCFGU_REGNUM(tdep) ((tdep)->bndcfgu_regnum)
|
||||
|
||||
/* Set of constants used for 32 and 64-bit. */
|
||||
#define I387_NUM_MPX_REGS 6
|
||||
#define I387_NUM_BND_REGS 4
|
||||
#define I387_NUM_MPX_CTRL_REGS 2
|
||||
|
||||
#define I387_FCTRL_REGNUM(tdep) (I387_ST0_REGNUM (tdep) + 8)
|
||||
#define I387_FSTAT_REGNUM(tdep) (I387_FCTRL_REGNUM (tdep) + 1)
|
||||
@ -48,6 +55,8 @@ struct ui_file;
|
||||
(I387_XMM0_REGNUM (tdep) + I387_NUM_XMM_REGS (tdep))
|
||||
#define I387_YMMENDH_REGNUM(tdep) \
|
||||
(I387_YMM0H_REGNUM (tdep) + I387_NUM_YMM_REGS (tdep))
|
||||
#define I387_MPXEND_REGNUM(tdep) \
|
||||
(I387_BND0R_REGNUM (tdep) + I387_NUM_MPX_REGS)
|
||||
|
||||
/* Print out the i387 floating point state. */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user