From c131fcee79bd9082762a1699a657949771fa245f Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Wed, 7 Apr 2010 18:43:45 +0000 Subject: [PATCH] Support i386 AVX. 2010-04-07 H.J. Lu * i386-linux-nat.c: Include "regset.h", "elf/common.h", and "i386-xstate.h". (PTRACE_GETREGSET): New. (PTRACE_SETREGSET): Likewise. (fetch_xstateregs): Likewise. (store_xstateregs): Likewise. (GETXSTATEREGS_SUPPLIES): Likewise. (regmap): Include 8 upper YMM registers. (i386_linux_fetch_inferior_registers): Support XSAVE extended state. (i386_linux_store_inferior_registers): Likewise. (i386_linux_read_description): Check and enable AVX target descriptions. * i386-linux-tdep.c: Include "regset.h", "i387-tdep.h", "i386-xstate.h" and "features/i386/i386-avx-linux.c". (i386_linux_regset_sections): Add ".reg-xstate". (i386_linux_gregset_reg_offset): Include 8 upper YMM registers. (i386_linux_core_read_xcr0): New. (i386_linux_core_read_description): Check and enable AVX target description. (i386_linux_init_abi): Set xsave_xcr0_offset. (_initialize_i386_linux_tdep): Call initialize_tdesc_i386_avx_linux. * i386-linux-tdep.h (I386_LINUX_ORIG_EAX_REGNUM): Replace I386_SSE_NUM_REGS with I386_AVX_NUM_REGS. (i386_linux_core_read_xcr0): New. (tdesc_i386_avx_linux): Likewise. (I386_LINUX_XSAVE_XCR0_OFFSET): Likewise. * i386-tdep.c: Include "i386-xstate.h" and "features/i386/i386-avx.c". (i386_ymm_names): New. (i386_ymmh_names): Likewise. (i386_ymmh_regnum_p): Likewise. (i386_ymm_regnum_p): Likewise. (i386_xmm_regnum_p): Likewise. (i386_register_name): Likewise. (i386_ymm_type): Likewise. (i386_supply_xstateregset): Likewise. (i386_collect_xstateregset): Likewise. (i386_sse_regnum_p): Removed. (i386_pseudo_register_name): Support pseudo YMM registers. (i386_pseudo_register_type): Likewise. (i386_pseudo_register_read): Likewise. (i386_pseudo_register_write): Likewise. (i386_dbx_reg_to_regnum): Return %ymmN register number for %xmmN if AVX is available. (i386_regset_from_core_section): Support .reg-xstate section. (i386_register_reggroup_p): Supper upper YMM and YMM registers. (i386_process_record): Replace i386_sse_regnum_p with i386_xmm_regnum_p. (i386_validate_tdesc_p): Support org.gnu.gdb.i386.avx feature. Set ymmh_register_names, num_ymm_regs, ymm0h_regnum and xcr0. (i386_gdbarch_init): Set xstateregset. Set xsave_xcr0_offset. Call set_gdbarch_register_name. Replace I386_SSE_NUM_REGS with I386_AVX_NUM_REGS. Set ymmh_register_names, ymm0h_regnum and num_ymm_regs. Add num_ymm_regs to set_gdbarch_num_pseudo_regs. Set ymm0_regnum. (_initialize_i386_tdep): Call initialize_tdesc_i386_avx. * i386-tdep.h (gdbarch_tdep): Add xstateregset, ymm0_regnum, xcr0, xsave_xcr0_offset, ymm0h_regnum, ymmh_register_names and i386_ymm_type. (i386_regnum): Add I386_YMM0H_REGNUM, and I386_YMM7H_REGNUM. (I386_AVX_NUM_REGS): New. (i386_xmm_regnum_p): Likewise. (i386_ymm_regnum_p): Likewise. (i386_ymmh_regnum_p): Likewise. * common/i386-xstate.h: New. --- gdb/ChangeLog | 75 +++++++++ gdb/common/i386-xstate.h | 41 +++++ gdb/i386-linux-nat.c | 125 +++++++++++++- gdb/i386-linux-tdep.c | 55 ++++++- gdb/i386-linux-tdep.h | 28 +++- gdb/i386-tdep.c | 341 +++++++++++++++++++++++++++++++++------ gdb/i386-tdep.h | 34 +++- 7 files changed, 646 insertions(+), 53 deletions(-) create mode 100644 gdb/common/i386-xstate.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index a74b14c7826..fb2830f93c8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,78 @@ +2010-04-07 H.J. Lu + + * i386-linux-nat.c: Include "regset.h", "elf/common.h", + and "i386-xstate.h". + (PTRACE_GETREGSET): New. + (PTRACE_SETREGSET): Likewise. + (fetch_xstateregs): Likewise. + (store_xstateregs): Likewise. + (GETXSTATEREGS_SUPPLIES): Likewise. + (regmap): Include 8 upper YMM registers. + (i386_linux_fetch_inferior_registers): Support XSAVE extended + state. + (i386_linux_store_inferior_registers): Likewise. + (i386_linux_read_description): Check and enable AVX target + descriptions. + + * i386-linux-tdep.c: Include "regset.h", "i387-tdep.h", + "i386-xstate.h" and "features/i386/i386-avx-linux.c". + (i386_linux_regset_sections): Add ".reg-xstate". + (i386_linux_gregset_reg_offset): Include 8 upper YMM registers. + (i386_linux_core_read_xcr0): New. + (i386_linux_core_read_description): Check and enable AVX target + description. + (i386_linux_init_abi): Set xsave_xcr0_offset. + (_initialize_i386_linux_tdep): Call + initialize_tdesc_i386_avx_linux. + + * i386-linux-tdep.h (I386_LINUX_ORIG_EAX_REGNUM): Replace + I386_SSE_NUM_REGS with I386_AVX_NUM_REGS. + (i386_linux_core_read_xcr0): New. + (tdesc_i386_avx_linux): Likewise. + (I386_LINUX_XSAVE_XCR0_OFFSET): Likewise. + + * i386-tdep.c: Include "i386-xstate.h" and + "features/i386/i386-avx.c". + (i386_ymm_names): New. + (i386_ymmh_names): Likewise. + (i386_ymmh_regnum_p): Likewise. + (i386_ymm_regnum_p): Likewise. + (i386_xmm_regnum_p): Likewise. + (i386_register_name): Likewise. + (i386_ymm_type): Likewise. + (i386_supply_xstateregset): Likewise. + (i386_collect_xstateregset): Likewise. + (i386_sse_regnum_p): Removed. + (i386_pseudo_register_name): Support pseudo YMM registers. + (i386_pseudo_register_type): Likewise. + (i386_pseudo_register_read): Likewise. + (i386_pseudo_register_write): Likewise. + (i386_dbx_reg_to_regnum): Return %ymmN register number for + %xmmN if AVX is available. + (i386_regset_from_core_section): Support .reg-xstate section. + (i386_register_reggroup_p): Supper upper YMM and YMM registers. + (i386_process_record): Replace i386_sse_regnum_p with + i386_xmm_regnum_p. + (i386_validate_tdesc_p): Support org.gnu.gdb.i386.avx feature. + Set ymmh_register_names, num_ymm_regs, ymm0h_regnum and xcr0. + (i386_gdbarch_init): Set xstateregset. Set xsave_xcr0_offset. + Call set_gdbarch_register_name. Replace I386_SSE_NUM_REGS with + I386_AVX_NUM_REGS. Set ymmh_register_names, ymm0h_regnum and + num_ymm_regs. Add num_ymm_regs to set_gdbarch_num_pseudo_regs. + Set ymm0_regnum. + (_initialize_i386_tdep): Call initialize_tdesc_i386_avx. + + * i386-tdep.h (gdbarch_tdep): Add xstateregset, ymm0_regnum, + xcr0, xsave_xcr0_offset, ymm0h_regnum, ymmh_register_names and + i386_ymm_type. + (i386_regnum): Add I386_YMM0H_REGNUM, and I386_YMM7H_REGNUM. + (I386_AVX_NUM_REGS): New. + (i386_xmm_regnum_p): Likewise. + (i386_ymm_regnum_p): Likewise. + (i386_ymmh_regnum_p): Likewise. + + * common/i386-xstate.h: New. + 2010-04-07 H.J. Lu * config/djgpp/fnchange.lst: Add x86 AVX XML files. diff --git a/gdb/common/i386-xstate.h b/gdb/common/i386-xstate.h new file mode 100644 index 00000000000..5e160157203 --- /dev/null +++ b/gdb/common/i386-xstate.h @@ -0,0 +1,41 @@ +/* Common code for i386 XSAVE extended state. + + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef I386_XSTATE_H +#define I386_XSTATE_H 1 + +/* The extended state feature bits. */ +#define I386_XSTATE_X87 (1ULL << 0) +#define I386_XSTATE_SSE (1ULL << 1) +#define I386_XSTATE_AVX (1ULL << 2) + +/* Supported mask and size of the extended state. */ +#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_SSE_SIZE 576 +#define I386_XSTATE_AVX_SIZE 832 +#define I386_XSTATE_MAX_SIZE 832 + +/* Get I386 XSAVE extended state size. */ +#define I386_XSTATE_SIZE(XCR0) \ + (((XCR0) & I386_XSTATE_AVX) != 0 \ + ? I386_XSTATE_AVX_SIZE : I386_XSTATE_SSE_SIZE) + +#endif /* I386_XSTATE_H */ diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c index 31b9086f465..a251907d502 100644 --- a/gdb/i386-linux-nat.c +++ b/gdb/i386-linux-nat.c @@ -23,11 +23,14 @@ #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "regset.h" #include "target.h" #include "linux-nat.h" #include "gdb_assert.h" #include "gdb_string.h" +#include "elf/common.h" +#include #include #include #include @@ -69,6 +72,19 @@ /* Defines ps_err_e, struct ps_prochandle. */ #include "gdb_proc_service.h" + +#include "i386-xstate.h" + +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif + +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif + +/* Does the current host support PTRACE_GETREGSET? */ +static int have_ptrace_getregset = -1; /* The register sets used in GNU/Linux ELF core-dumps are identical to @@ -98,6 +114,8 @@ static int regmap[] = -1, -1, -1, -1, /* xmm0, xmm1, xmm2, xmm3 */ -1, -1, -1, -1, /* xmm4, xmm5, xmm6, xmm6 */ -1, /* mxcsr */ + -1, -1, -1, -1, /* ymm0h, ymm1h, ymm2h, ymm3h */ + -1, -1, -1, -1, /* ymm4h, ymm5h, ymm6h, ymm6h */ ORIG_EAX }; @@ -110,6 +128,9 @@ static int regmap[] = #define GETFPXREGS_SUPPLIES(regno) \ (I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS) +#define GETXSTATEREGS_SUPPLIES(regno) \ + (I386_ST0_REGNUM <= (regno) && (regno) < I386_AVX_NUM_REGS) + /* Does the current host support the GETREGS request? */ int have_ptrace_getregs = #ifdef HAVE_PTRACE_GETREGS @@ -355,6 +376,57 @@ static void store_fpregs (const struct regcache *regcache, int tid, int regno) { /* Transfering floating-point and SSE registers to and from GDB. */ +/* Fetch all registers covered by the PTRACE_GETREGSET request from + process/thread TID and store their values in GDB's register array. + Return non-zero if successful, zero otherwise. */ + +static int +fetch_xstateregs (struct regcache *regcache, int tid) +{ + char xstateregs[I386_XSTATE_MAX_SIZE]; + struct iovec iov; + + if (!have_ptrace_getregset) + return 0; + + iov.iov_base = xstateregs; + iov.iov_len = sizeof(xstateregs); + if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE, + &iov) < 0) + perror_with_name (_("Couldn't read extended state status")); + + i387_supply_xsave (regcache, -1, xstateregs); + return 1; +} + +/* Store all valid registers in GDB's register array covered by the + PTRACE_SETREGSET request into the process/thread specified by TID. + Return non-zero if successful, zero otherwise. */ + +static int +store_xstateregs (const struct regcache *regcache, int tid, int regno) +{ + char xstateregs[I386_XSTATE_MAX_SIZE]; + struct iovec iov; + + if (!have_ptrace_getregset) + return 0; + + iov.iov_base = xstateregs; + iov.iov_len = sizeof(xstateregs); + if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE, + &iov) < 0) + perror_with_name (_("Couldn't read extended state status")); + + i387_collect_xsave (regcache, regno, xstateregs, 0); + + if (ptrace (PTRACE_SETREGSET, tid, (unsigned int) NT_X86_XSTATE, + (int) &iov) < 0) + perror_with_name (_("Couldn't write extended state status")); + + return 1; +} + #ifdef HAVE_PTRACE_GETFPXREGS /* Fill GDB's register array with the floating-point and SSE register @@ -489,6 +561,8 @@ i386_linux_fetch_inferior_registers (struct target_ops *ops, return; } + if (fetch_xstateregs (regcache, tid)) + return; if (fetch_fpxregs (regcache, tid)) return; fetch_fpregs (regcache, tid); @@ -501,6 +575,12 @@ i386_linux_fetch_inferior_registers (struct target_ops *ops, return; } + if (GETXSTATEREGS_SUPPLIES (regno)) + { + if (fetch_xstateregs (regcache, tid)) + return; + } + if (GETFPXREGS_SUPPLIES (regno)) { if (fetch_fpxregs (regcache, tid)) @@ -553,6 +633,8 @@ i386_linux_store_inferior_registers (struct target_ops *ops, if (regno == -1) { store_regs (regcache, tid, regno); + if (store_xstateregs (regcache, tid, regno)) + return; if (store_fpxregs (regcache, tid, regno)) return; store_fpregs (regcache, tid, regno); @@ -565,6 +647,12 @@ i386_linux_store_inferior_registers (struct target_ops *ops, return; } + if (GETXSTATEREGS_SUPPLIES (regno)) + { + if (store_xstateregs (regcache, tid, regno)) + return; + } + if (GETFPXREGS_SUPPLIES (regno)) { if (store_fpxregs (regcache, tid, regno)) @@ -858,7 +946,42 @@ i386_linux_child_post_startup_inferior (ptid_t ptid) static const struct target_desc * i386_linux_read_description (struct target_ops *ops) { - return tdesc_i386_linux; + static uint64_t xcr0; + + if (have_ptrace_getregset == -1) + { + int tid; + uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))]; + struct iovec iov; + + /* GNU/Linux LWP ID's are process ID's. */ + tid = TIDGET (inferior_ptid); + if (tid == 0) + tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + + iov.iov_base = xstateregs; + iov.iov_len = sizeof (xstateregs); + + /* Check if PTRACE_GETREGSET works. */ + if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE, + &iov) < 0) + have_ptrace_getregset = 0; + else + { + have_ptrace_getregset = 1; + + /* Get XCR0 from XSAVE extended state. */ + xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET + / sizeof (long long))]; + } + } + + /* Check the native XCR0 only if PTRACE_GETREGSET is available. */ + if (have_ptrace_getregset + && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK) + return tdesc_i386_avx_linux; + else + return tdesc_i386_linux; } void diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index b23c109cc23..34a19249ce6 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -23,6 +23,7 @@ #include "frame.h" #include "value.h" #include "regcache.h" +#include "regset.h" #include "inferior.h" #include "osabi.h" #include "reggroups.h" @@ -36,9 +37,11 @@ #include "solib-svr4.h" #include "symtab.h" #include "arch-utils.h" -#include "regset.h" #include "xml-syscall.h" +#include "i387-tdep.h" +#include "i386-xstate.h" + /* The syscall's XML filename for i386. */ #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml" @@ -47,6 +50,7 @@ #include #include "features/i386/i386-linux.c" +#include "features/i386/i386-avx-linux.c" /* Supported register note sections. */ static struct core_regset_section i386_linux_regset_sections[] = @@ -54,6 +58,7 @@ static struct core_regset_section i386_linux_regset_sections[] = { ".reg", 144, "general-purpose" }, { ".reg2", 108, "floating-point" }, { ".reg-xfp", 512, "extended floating-point" }, + { ".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state" }, { NULL, 0 } }; @@ -533,6 +538,7 @@ static 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, -1, -1, -1, -1, 11 * 4 /* "orig_eax" */ }; @@ -560,6 +566,43 @@ static int i386_linux_sc_reg_offset[] = 0 * 4 /* %gs */ }; +/* Get XSAVE extended state xcr0 from core dump. */ + +uint64_t +i386_linux_core_read_xcr0 (struct gdbarch *gdbarch, + struct target_ops *target, bfd *abfd) +{ + asection *xstate = bfd_get_section_by_name (abfd, ".reg-xstate"); + uint64_t xcr0; + + if (xstate) + { + size_t size = bfd_section_size (abfd, xstate); + + /* Check extended state size. */ + if (size < I386_XSTATE_AVX_SIZE) + xcr0 = I386_XSTATE_SSE_MASK; + else + { + char contents[8]; + + if (! bfd_get_section_contents (abfd, xstate, contents, + I386_LINUX_XSAVE_XCR0_OFFSET, + 8)) + { + warning (_("Couldn't read `xcr0' bytes from `.reg-xstate' section in core file.")); + return 0; + } + + xcr0 = bfd_get_64 (abfd, contents); + } + } + else + xcr0 = I386_XSTATE_SSE_MASK; + + return xcr0; +} + /* Get Linux/x86 target description from core dump. */ static const struct target_desc * @@ -568,12 +611,17 @@ i386_linux_core_read_description (struct gdbarch *gdbarch, bfd *abfd) { asection *section = bfd_get_section_by_name (abfd, ".reg2"); + uint64_t xcr0; if (section == NULL) return NULL; /* Linux/i386. */ - return tdesc_i386_linux; + xcr0 = i386_linux_core_read_xcr0 (gdbarch, target, abfd); + if ((xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK) + return tdesc_i386_avx_linux; + else + return tdesc_i386_linux; } static void @@ -623,6 +671,8 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_reg_offset = i386_linux_sc_reg_offset; tdep->sc_num_regs = ARRAY_SIZE (i386_linux_sc_reg_offset); + tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_OFFSET; + set_gdbarch_process_record (gdbarch, i386_process_record); set_gdbarch_process_record_signal (gdbarch, i386_linux_record_signal); @@ -840,4 +890,5 @@ _initialize_i386_linux_tdep (void) /* Initialize the Linux target description */ initialize_tdesc_i386_linux (); + initialize_tdesc_i386_avx_linux (); } diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h index 11f7295dfa3..eaeb63cefa2 100644 --- a/gdb/i386-linux-tdep.h +++ b/gdb/i386-linux-tdep.h @@ -30,12 +30,38 @@ /* 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_SSE_NUM_REGS +#define I386_LINUX_ORIG_EAX_REGNUM I386_AVX_NUM_REGS /* Total number of registers for GNU/Linux. */ #define I386_LINUX_NUM_REGS (I386_LINUX_ORIG_EAX_REGNUM + 1) +/* Get XSAVE extended state xcr0 from core dump. */ +extern uint64_t i386_linux_core_read_xcr0 + (struct gdbarch *gdbarch, struct target_ops *target, bfd *abfd); + /* Linux target description. */ extern struct target_desc *tdesc_i386_linux; +extern struct target_desc *tdesc_i386_avx_linux; + +/* Format of XSAVE extended state is: + struct + { + fxsave_bytes[0..463] + sw_usable_bytes[464..511] + xstate_hdr_bytes[512..575] + avx_bytes[576..831] + future_state etc + }; + + Same memory layout will be used for the coredump NT_X86_XSTATE + representing the XSAVE extended state registers. + + The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled + extended state mask, which is the same as the extended control register + 0 (the XFEATURE_ENABLED_MASK register), XCR0. We can use this mask + together with the mask saved in the xstate_hdr_bytes to determine what + states the processor/OS supports and what state, used or initialized, + the process/thread is in. */ +#define I386_LINUX_XSAVE_XCR0_OFFSET 464 #endif /* i386-linux-tdep.h */ diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 703d003a8c7..ce658cdb6a7 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -51,11 +51,13 @@ #include "i386-tdep.h" #include "i387-tdep.h" +#include "i386-xstate.h" #include "record.h" #include #include "features/i386/i386.c" +#include "features/i386/i386-avx.c" /* Register names. */ @@ -74,6 +76,18 @@ static const char *i386_register_names[] = "mxcsr" }; +static const char *i386_ymm_names[] = +{ + "ymm0", "ymm1", "ymm2", "ymm3", + "ymm4", "ymm5", "ymm6", "ymm7", +}; + +static const char *i386_ymmh_names[] = +{ + "ymm0h", "ymm1h", "ymm2h", "ymm3h", + "ymm4h", "ymm5h", "ymm6h", "ymm7h", +}; + /* Register names for MMX pseudo-registers. */ static const char *i386_mmx_names[] = @@ -150,18 +164,47 @@ i386_dword_regnum_p (struct gdbarch *gdbarch, int regnum) return regnum >= 0 && regnum < tdep->num_dword_regs; } +int +i386_ymmh_regnum_p (struct gdbarch *gdbarch, int regnum) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int ymm0h_regnum = tdep->ymm0h_regnum; + + if (ymm0h_regnum < 0) + return 0; + + regnum -= ymm0h_regnum; + return regnum >= 0 && regnum < tdep->num_ymm_regs; +} + +/* AVX register? */ + +int +i386_ymm_regnum_p (struct gdbarch *gdbarch, int regnum) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int ymm0_regnum = tdep->ymm0_regnum; + + if (ymm0_regnum < 0) + return 0; + + regnum -= ymm0_regnum; + return regnum >= 0 && regnum < tdep->num_ymm_regs; +} + /* SSE register? */ -static int -i386_sse_regnum_p (struct gdbarch *gdbarch, int regnum) +int +i386_xmm_regnum_p (struct gdbarch *gdbarch, int regnum) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int num_xmm_regs = I387_NUM_XMM_REGS (tdep); - if (I387_NUM_XMM_REGS (tdep) == 0) + if (num_xmm_regs == 0) return 0; - return (I387_XMM0_REGNUM (tdep) <= regnum - && regnum < I387_MXCSR_REGNUM (tdep)); + regnum -= I387_XMM0_REGNUM (tdep); + return regnum >= 0 && regnum < num_xmm_regs; } static int @@ -201,6 +244,19 @@ i386_fpc_regnum_p (struct gdbarch *gdbarch, int regnum) && regnum < I387_XMM0_REGNUM (tdep)); } +/* Return the name of register REGNUM, or the empty string if it is + an anonymous register. */ + +static const char * +i386_register_name (struct gdbarch *gdbarch, int regnum) +{ + /* Hide the upper YMM registers. */ + if (i386_ymmh_regnum_p (gdbarch, regnum)) + return ""; + + return tdesc_register_name (gdbarch, regnum); +} + /* Return the name of register REGNUM. */ const char * @@ -209,6 +265,8 @@ i386_pseudo_register_name (struct gdbarch *gdbarch, int regnum) struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); if (i386_mmx_regnum_p (gdbarch, regnum)) return i386_mmx_names[regnum - I387_MM0_REGNUM (tdep)]; + else if (i386_ymm_regnum_p (gdbarch, regnum)) + return i386_ymm_names[regnum - tdep->ymm0_regnum]; else if (i386_byte_regnum_p (gdbarch, regnum)) return i386_byte_names[regnum - tdep->al_regnum]; else if (i386_word_regnum_p (gdbarch, regnum)) @@ -246,7 +304,13 @@ i386_dbx_reg_to_regnum (struct gdbarch *gdbarch, int reg) else if (reg >= 21 && reg <= 28) { /* SSE registers. */ - return reg - 21 + I387_XMM0_REGNUM (tdep); + int ymm0_regnum = tdep->ymm0_regnum; + + if (ymm0_regnum >= 0 + && i386_xmm_regnum_p (gdbarch, reg)) + return reg - 21 + ymm0_regnum; + else + return reg - 21 + I387_XMM0_REGNUM (tdep); } else if (reg >= 29 && reg <= 36) { @@ -2184,6 +2248,59 @@ i387_ext_type (struct gdbarch *gdbarch) return tdep->i387_ext_type; } +/* Construct vector type for pseudo YMM registers. We can't use + tdesc_find_type since YMM isn't described in target description. */ + +static struct type * +i386_ymm_type (struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (!tdep->i386_ymm_type) + { + const struct builtin_type *bt = builtin_type (gdbarch); + + /* The type we're building is this: */ +#if 0 + union __gdb_builtin_type_vec256i + { + int128_t uint128[2]; + int64_t v2_int64[4]; + int32_t v4_int32[8]; + int16_t v8_int16[16]; + int8_t v16_int8[32]; + double v2_double[4]; + float v4_float[8]; + }; +#endif + + struct type *t; + + t = arch_composite_type (gdbarch, + "__gdb_builtin_type_vec256i", TYPE_CODE_UNION); + append_composite_type_field (t, "v8_float", + init_vector_type (bt->builtin_float, 8)); + append_composite_type_field (t, "v4_double", + init_vector_type (bt->builtin_double, 4)); + append_composite_type_field (t, "v32_int8", + init_vector_type (bt->builtin_int8, 32)); + append_composite_type_field (t, "v16_int16", + init_vector_type (bt->builtin_int16, 16)); + append_composite_type_field (t, "v8_int32", + init_vector_type (bt->builtin_int32, 8)); + append_composite_type_field (t, "v4_int64", + init_vector_type (bt->builtin_int64, 4)); + append_composite_type_field (t, "v2_int128", + init_vector_type (bt->builtin_int128, 2)); + + TYPE_VECTOR (t) = 1; + TYPE_NAME (t) = "builtin_type_vec128i"; + tdep->i386_ymm_type = t; + } + + return tdep->i386_ymm_type; +} + /* Construct vector type for MMX registers. */ static struct type * i386_mmx_type (struct gdbarch *gdbarch) @@ -2234,6 +2351,8 @@ i386_pseudo_register_type (struct gdbarch *gdbarch, int regnum) { if (i386_mmx_regnum_p (gdbarch, regnum)) return i386_mmx_type (gdbarch); + else if (i386_ymm_regnum_p (gdbarch, regnum)) + return i386_ymm_type (gdbarch); else { const struct builtin_type *bt = builtin_type (gdbarch); @@ -2285,7 +2404,22 @@ i386_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - if (i386_word_regnum_p (gdbarch, regnum)) + if (i386_ymm_regnum_p (gdbarch, regnum)) + { + regnum -= tdep->ymm0_regnum; + + /* Extract (always little endian). Read lower 128bits. */ + regcache_raw_read (regcache, + I387_XMM0_REGNUM (tdep) + regnum, + raw_buf); + memcpy (buf, raw_buf, 16); + /* Read upper 128bits. */ + regcache_raw_read (regcache, + tdep->ymm0h_regnum + regnum, + raw_buf); + memcpy (buf + 16, raw_buf, 16); + } + else if (i386_word_regnum_p (gdbarch, regnum)) { int gpnum = regnum - tdep->ax_regnum; @@ -2334,7 +2468,20 @@ i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - if (i386_word_regnum_p (gdbarch, regnum)) + if (i386_ymm_regnum_p (gdbarch, regnum)) + { + regnum -= tdep->ymm0_regnum; + + /* ... Write lower 128bits. */ + regcache_raw_write (regcache, + I387_XMM0_REGNUM (tdep) + regnum, + buf); + /* ... Write upper 128bits. */ + regcache_raw_write (regcache, + tdep->ymm0h_regnum + regnum, + buf + 16); + } + else if (i386_word_regnum_p (gdbarch, regnum)) { int gpnum = regnum - tdep->ax_regnum; @@ -2581,6 +2728,28 @@ i386_collect_fpregset (const struct regset *regset, i387_collect_fsave (regcache, regnum, fpregs); } +/* Similar to i386_supply_fpregset, but use XSAVE extended state. */ + +static void +i386_supply_xstateregset (const struct regset *regset, + struct regcache *regcache, int regnum, + const void *xstateregs, size_t len) +{ + const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch); + i387_supply_xsave (regcache, regnum, xstateregs); +} + +/* Similar to i386_collect_fpregset , but use XSAVE extended state. */ + +static void +i386_collect_xstateregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *xstateregs, size_t len) +{ + const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch); + i387_collect_xsave (regcache, regnum, xstateregs, 1); +} + /* Return the appropriate register set for the core section identified by SECT_NAME and SECT_SIZE. */ @@ -2608,6 +2777,16 @@ i386_regset_from_core_section (struct gdbarch *gdbarch, return tdep->fpregset; } + if (strcmp (sect_name, ".reg-xstate") == 0) + { + if (tdep->xstateregset == NULL) + tdep->xstateregset = regset_alloc (gdbarch, + i386_supply_xstateregset, + i386_collect_xstateregset); + + return tdep->xstateregset; + } + return NULL; } @@ -2801,46 +2980,60 @@ int i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum, struct reggroup *group) { - int sse_regnum_p, fp_regnum_p, mmx_regnum_p, byte_regnum_p, - word_regnum_p, dword_regnum_p; + const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int fp_regnum_p, mmx_regnum_p, xmm_regnum_p, mxcsr_regnum_p, + ymm_regnum_p, ymmh_regnum_p; /* Don't include pseudo registers, except for MMX, in any register groups. */ - byte_regnum_p = i386_byte_regnum_p (gdbarch, regnum); - if (byte_regnum_p) + if (i386_byte_regnum_p (gdbarch, regnum)) return 0; - word_regnum_p = i386_word_regnum_p (gdbarch, regnum); - if (word_regnum_p) + if (i386_word_regnum_p (gdbarch, regnum)) return 0; - dword_regnum_p = i386_dword_regnum_p (gdbarch, regnum); - if (dword_regnum_p) + if (i386_dword_regnum_p (gdbarch, regnum)) return 0; mmx_regnum_p = i386_mmx_regnum_p (gdbarch, regnum); if (group == i386_mmx_reggroup) return mmx_regnum_p; - sse_regnum_p = (i386_sse_regnum_p (gdbarch, regnum) - || i386_mxcsr_regnum_p (gdbarch, regnum)); + xmm_regnum_p = i386_xmm_regnum_p (gdbarch, regnum); + mxcsr_regnum_p = i386_mxcsr_regnum_p (gdbarch, regnum); if (group == i386_sse_reggroup) - return sse_regnum_p; + return xmm_regnum_p || mxcsr_regnum_p; + + ymm_regnum_p = i386_ymm_regnum_p (gdbarch, regnum); if (group == vector_reggroup) - return mmx_regnum_p || sse_regnum_p; + return (mmx_regnum_p + || ymm_regnum_p + || mxcsr_regnum_p + || (xmm_regnum_p + && ((tdep->xcr0 & I386_XSTATE_AVX_MASK) + == I386_XSTATE_SSE_MASK))); fp_regnum_p = (i386_fp_regnum_p (gdbarch, regnum) || i386_fpc_regnum_p (gdbarch, regnum)); if (group == float_reggroup) return fp_regnum_p; + /* For "info reg all", don't include upper YMM registers nor XMM + registers when AVX is supported. */ + ymmh_regnum_p = i386_ymmh_regnum_p (gdbarch, regnum); + if (group == all_reggroup + && ((xmm_regnum_p + && (tdep->xcr0 & I386_XSTATE_AVX)) + || ymmh_regnum_p)) + return 0; + if (group == general_reggroup) return (!fp_regnum_p && !mmx_regnum_p - && !sse_regnum_p - && !byte_regnum_p - && !word_regnum_p - && !dword_regnum_p); + && !mxcsr_regnum_p + && !xmm_regnum_p + && !ymm_regnum_p + && !ymmh_regnum_p); return default_register_reggroup_p (gdbarch, regnum, group); } @@ -5665,7 +5858,7 @@ no_support_3dnow_data: record_arch_list_add_reg (ir.regcache, i); for (i = I387_XMM0_REGNUM (tdep); - i386_sse_regnum_p (gdbarch, i); i++) + i386_xmm_regnum_p (gdbarch, i); i++) record_arch_list_add_reg (ir.regcache, i); if (i386_mxcsr_regnum_p (gdbarch, I387_MXCSR_REGNUM(tdep))) @@ -6065,7 +6258,7 @@ reswitch_prefix_add: if (i386_record_modrm (&ir)) return -1; ir.reg |= rex_r; - if (!i386_sse_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.reg)) + if (!i386_xmm_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.reg)) goto no_support; record_arch_list_add_reg (ir.regcache, I387_XMM0_REGNUM (tdep) + ir.reg); @@ -6097,7 +6290,7 @@ reswitch_prefix_add: || opcode == 0x0f17 || opcode == 0x660f17) goto no_support; ir.rm |= ir.rex_b; - if (!i386_sse_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.rm)) + if (!i386_xmm_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.rm)) goto no_support; record_arch_list_add_reg (ir.regcache, I387_XMM0_REGNUM (tdep) + ir.rm); @@ -6275,7 +6468,7 @@ reswitch_prefix_add: if (i386_record_modrm (&ir)) return -1; ir.rm |= ir.rex_b; - if (!i386_sse_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.rm)) + if (!i386_xmm_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.rm)) goto no_support; record_arch_list_add_reg (ir.regcache, I387_XMM0_REGNUM (tdep) + ir.rm); @@ -6329,7 +6522,7 @@ reswitch_prefix_add: if (ir.mod == 3) { ir.rm |= ir.rex_b; - if (!i386_sse_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.rm)) + if (!i386_xmm_regnum_p (gdbarch, I387_XMM0_REGNUM (tdep) + ir.rm)) goto no_support; record_arch_list_add_reg (ir.regcache, I387_XMM0_REGNUM (tdep) + ir.rm); @@ -6449,7 +6642,8 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep, struct tdesc_arch_data *tdesc_data) { const struct target_desc *tdesc = tdep->tdesc; - const struct tdesc_feature *feature_core, *feature_vector; + const struct tdesc_feature *feature_core; + const struct tdesc_feature *feature_sse, *feature_avx; int i, num_regs, valid_p; if (! tdesc_has_registers (tdesc)) @@ -6459,13 +6653,37 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep, feature_core = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.core"); /* Get SSE registers. */ - feature_vector = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse"); + feature_sse = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse"); - if (feature_core == NULL || feature_vector == NULL) + if (feature_core == NULL || feature_sse == NULL) return 0; + /* Try AVX registers. */ + feature_avx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx"); + valid_p = 1; + /* The XCR0 bits. */ + if (feature_avx) + { + tdep->xcr0 = I386_XSTATE_AVX_MASK; + + /* It may have been set by OSABI initialization function. */ + if (tdep->num_ymm_regs == 0) + { + tdep->ymmh_register_names = i386_ymmh_names; + tdep->num_ymm_regs = 8; + tdep->ymm0h_regnum = I386_YMM0H_REGNUM; + } + + for (i = 0; i < tdep->num_ymm_regs; i++) + valid_p &= tdesc_numbered_register (feature_avx, tdesc_data, + tdep->ymm0h_regnum + i, + tdep->ymmh_register_names[i]); + } + else + tdep->xcr0 = I386_XSTATE_SSE_MASK; + num_regs = tdep->num_core_regs; for (i = 0; i < num_regs; i++) valid_p &= tdesc_numbered_register (feature_core, tdesc_data, i, @@ -6474,7 +6692,7 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep, /* Need to include %mxcsr, so add one. */ num_regs += tdep->num_xmm_regs + 1; for (; i < num_regs; i++) - valid_p &= tdesc_numbered_register (feature_vector, tdesc_data, i, + valid_p &= tdesc_numbered_register (feature_sse, tdesc_data, i, tdep->register_names[i]); return valid_p; @@ -6489,6 +6707,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) struct tdesc_arch_data *tdesc_data; const struct target_desc *tdesc; int mm0_regnum; + int ymm0_regnum; /* If there is already a candidate, use it. */ arches = gdbarch_list_lookup_by_info (arches, &info); @@ -6509,6 +6728,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->fpregset = NULL; tdep->sizeof_fpregset = I387_SIZEOF_FSAVE; + tdep->xstateregset = NULL; + /* The default settings include the FPU registers, the MMX registers and the SSE registers. This can be overridden for a specific ABI by adjusting the members `st0_regnum', `mm0_regnum' and @@ -6538,6 +6759,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->sc_pc_offset = -1; tdep->sc_sp_offset = -1; + tdep->xsave_xcr0_offset = -1; + tdep->record_regmap = i386_record_regmap; /* The format used for `long double' on almost all i386 targets is @@ -6654,9 +6877,14 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_tdesc_pseudo_register_type (gdbarch, i386_pseudo_register_type); set_tdesc_pseudo_register_name (gdbarch, i386_pseudo_register_name); - /* The default ABI includes general-purpose registers, - floating-point registers, and the SSE registers. */ - set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS); + /* Override the normal target description method to make the AVX + upper halves anonymous. */ + set_gdbarch_register_name (gdbarch, i386_register_name); + + /* 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); /* Get the x86 target description from INFO. */ tdesc = info.target_desc; @@ -6667,10 +6895,15 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->num_core_regs = I386_NUM_GREGS + I387_NUM_REGS; tdep->register_names = i386_register_names; + /* No upper YMM registers. */ + tdep->ymmh_register_names = NULL; + tdep->ymm0h_regnum = -1; + tdep->num_byte_regs = 8; tdep->num_word_regs = 8; tdep->num_dword_regs = 0; tdep->num_mmx_regs = 8; + tdep->num_ymm_regs = 0; tdesc_data = tdesc_data_alloc (); @@ -6678,24 +6911,25 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) info.tdep_info = (void *) tdesc_data; gdbarch_init_osabi (info, gdbarch); + if (!i386_validate_tdesc_p (tdep, tdesc_data)) + { + tdesc_data_cleanup (tdesc_data); + xfree (tdep); + gdbarch_free (gdbarch); + return NULL; + } + /* 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_mmx_regs + + tdep->num_ymm_regs)); /* Target description may be changed. */ tdesc = tdep->tdesc; - if (!i386_validate_tdesc_p (tdep, tdesc_data)) - { - tdesc_data_cleanup (tdesc_data); - xfree (tdep); - gdbarch_free (gdbarch); - return NULL; - } - tdesc_use_registers (gdbarch, tdesc, tdesc_data); /* Override gdbarch_register_reggroup_p set in tdesc_use_registers. */ @@ -6705,16 +6939,26 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->al_regnum = gdbarch_num_regs (gdbarch); tdep->ax_regnum = tdep->al_regnum + tdep->num_byte_regs; - mm0_regnum = tdep->ax_regnum + tdep->num_word_regs; + ymm0_regnum = tdep->ax_regnum + tdep->num_word_regs; if (tdep->num_dword_regs) { /* Support dword pseudo-registesr if it hasn't been disabled, */ - tdep->eax_regnum = mm0_regnum; - mm0_regnum = tdep->eax_regnum + tdep->num_dword_regs; + tdep->eax_regnum = ymm0_regnum; + ymm0_regnum += tdep->num_dword_regs; } else tdep->eax_regnum = -1; + mm0_regnum = ymm0_regnum; + if (tdep->num_ymm_regs) + { + /* Support YMM pseudo-registesr if it is available, */ + tdep->ymm0_regnum = ymm0_regnum; + mm0_regnum += tdep->num_ymm_regs; + } + else + tdep->ymm0_regnum = -1; + if (tdep->num_mmx_regs != 0) { /* Support MMX pseudo-registesr if MMX hasn't been disabled, */ @@ -6797,6 +7041,7 @@ is \"default\"."), /* Initialize the standard target descriptions. */ initialize_tdesc_i386 (); + initialize_tdesc_i386_avx (); /* Tell remote stub that we support XML target description. */ register_remote_support_xml ("i386"); diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index 72c634e6b7d..6520d67ad05 100644 --- a/gdb/i386-tdep.h +++ b/gdb/i386-tdep.h @@ -109,6 +109,9 @@ struct gdbarch_tdep struct regset *fpregset; size_t sizeof_fpregset; + /* XSAVE extended state. */ + struct regset *xstateregset; + /* Register number for %st(0). The register numbers for the other registers follow from this one. Set this to -1 to indicate the absence of an FPU. */ @@ -121,6 +124,13 @@ struct gdbarch_tdep of MMX support. */ int mm0_regnum; + /* Number of pseudo YMM registers. */ + int num_ymm_regs; + + /* Register number for %ymm0. Set this to -1 to indicate the absence + of pseudo YMM register support. */ + int ymm0_regnum; + /* Number of byte registers. */ int num_byte_regs; @@ -146,9 +156,24 @@ struct gdbarch_tdep /* Number of SSE registers. */ int num_xmm_regs; + /* Bits of the extended control register 0 (the XFEATURE_ENABLED_MASK + register), excluding the x87 bit, which are supported by this GDB. + */ + uint64_t xcr0; + + /* Offset of XCR0 in XSAVE extended state. */ + int xsave_xcr0_offset; + /* Register names. */ const char **register_names; + /* Register number for %ymm0h. Set this to -1 to indicate the absence + of upper YMM register support. */ + int ymm0h_regnum; + + /* Upper YMM register names. Only used for tdesc_numbered_register. */ + const char **ymmh_register_names; + /* Target description. */ const struct target_desc *tdesc; @@ -182,6 +207,7 @@ struct gdbarch_tdep /* ISA-specific data types. */ struct type *i386_mmx_type; + struct type *i386_ymm_type; struct type *i387_ext_type; /* Process record/replay target. */ @@ -228,7 +254,9 @@ enum i386_regnum I386_FS_REGNUM, /* %fs */ I386_GS_REGNUM, /* %gs */ I386_ST0_REGNUM, /* %st(0) */ - I386_MXCSR_REGNUM = 40 /* %mxcsr */ + I386_MXCSR_REGNUM = 40, /* %mxcsr */ + I386_YMM0H_REGNUM, /* %ymm0h */ + I386_YMM7H_REGNUM = I386_YMM0H_REGNUM + 7 }; /* Register numbers of RECORD_REGMAP. */ @@ -265,6 +293,7 @@ enum record_i386_regnum #define I386_NUM_XREGS 9 #define I386_SSE_NUM_REGS (I386_MXCSR_REGNUM + 1) +#define I386_AVX_NUM_REGS (I386_YMM7H_REGNUM + 1) /* Size of the largest register. */ #define I386_MAX_REGISTER_SIZE 16 @@ -276,6 +305,9 @@ extern struct type *i387_ext_type (struct gdbarch *gdbarch); extern int i386_byte_regnum_p (struct gdbarch *gdbarch, int regnum); extern int i386_word_regnum_p (struct gdbarch *gdbarch, int regnum); extern int i386_dword_regnum_p (struct gdbarch *gdbarch, int regnum); +extern int i386_xmm_regnum_p (struct gdbarch *gdbarch, int regnum); +extern int i386_ymm_regnum_p (struct gdbarch *gdbarch, int regnum); +extern int i386_ymmh_regnum_p (struct gdbarch *gdbarch, int regnum); extern const char *i386_pseudo_register_name (struct gdbarch *gdbarch, int regnum); -- 2.30.2