[01/24] MIPS: Handle run-time reconfigurable FPR size

Message ID 1467038991-6600-1-git-send-email-bhushan.attarde@imgtec.com
State New, archived
Headers

Commit Message

Bhushan Attarde June 27, 2016, 2:49 p.m. UTC
  Many MIPS architecture processors can reconfigure the size of their
    floating-point registers at the run time.  The file comprising 32
    registers can be either 32-bit or 64-bit wide depending on whether CP0
    Status register's bit FR is zero or one, respectively.  Fortunately access
    to Status is available on all targets.

    Here's a change to handle this property.  It requires the generic register
    access code to raise the target backend's attention whenever a new
    register set has been retrieved so that it can examine the state of
    CP0.Status.FR and act accordingly.  I have added this hook to
    get_thread_regcache, the backend has then an opportunity to switch gdbarch
    as necessary and let the caller know if it did so.  If that indeed
    happened, then the register cache originally retrieved is then discarded
    and another one obtained using the newly-selected gdbarch.  This new
    register cache is not revalidated.

    This has been regression-tested successfully, using o32 and n64 multilibs
    on mips-sde-elf and mips-linux-gnu targets.  The ability to flip
    CP0.Status.FR is not however covered by any test suite, and was therefore
    tested manually.  An example session with a bare-iron MIPS64 target looks
    like this (I've stripped out some noise):

    (gdb) info all-registers
                      zero               at               v0               v1
     R0   0000000000000000 0000000000000000 0000000000000000 0000000020000000
                        a0               a1               a2               a3
     R4   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        a4               a5               a6               a7
     R8   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        t0               t1               t2               t3
     R12  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        s0               s1               s2               s3
     R16  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        s4               s5               s6               s7
     R20  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        t8               t9               k0               k1
     R24  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        gp               sp               s8               ra
     R28  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        sr               lo               hi              bad
          0000000024000000 0000000000000000 0000000000000000 0000000000000000
                     cause               pc
          0000000000000000 ffffffff80000000
     f0:  0x0000000000000000 flt: 0                 dbl: 0
     f1:  0x000000003f800000 flt: 1                 dbl: 5.2635442471208903e-315
     f2:  0x0000000000000000 flt: 0                 dbl: 0
     f3:  0x0000000040000000 flt: 2                 dbl: 5.3049894774131808e-315
     f4:  0x0000000000000000 flt: 0                 dbl: 0
     f5:  0x0000000040400000 flt: 3                 dbl: 5.325712092559326e-315
     f6:  0x0000000000000000 flt: 0                 dbl: 0
     f7:  0x0000000040800000 flt: 4                 dbl: 5.3464347077054713e-315
     f8:  0x0000000000000000 flt: 0                 dbl: 0
     f9:  0x0000000040a00000 flt: 5                 dbl: 5.3567960152785439e-315
     f10: 0x0000000000000000 flt: 0                 dbl: 0
     f11: 0x0000000040c00000 flt: 6                 dbl: 5.3671573228516165e-315
     f12: 0x0000000000000000 flt: 0                 dbl: 0
     f13: 0x0000000040e00000 flt: 7                 dbl: 5.3775186304246891e-315
     f14: 0x0000000000000000 flt: 0                 dbl: 0
     f15: 0x0000000041000000 flt: 8                 dbl: 5.3878799379977617e-315
     f16: 0x0000000000000000 flt: 0                 dbl: 0
     f17: 0x0000000041100000 flt: 9                 dbl: 5.393060591784298e-315
     f18: 0x0000000000000000 flt: 0                 dbl: 0
     f19: 0x0000000041200000 flt: 10                dbl: 5.3982412455708344e-315
     f20: 0x0000000000000000 flt: 0                 dbl: 0
     f21: 0x0000000041300000 flt: 11                dbl: 5.4034218993573707e-315
     f22: 0x0000000000000000 flt: 0                 dbl: 0
     f23: 0x0000000041400000 flt: 12                dbl: 5.408602553143907e-315
     f24: 0x0000000000000000 flt: 0                 dbl: 0
     f25: 0x0000000041500000 flt: 13                dbl: 5.4137832069304433e-315
     f26: 0x0000000000000000 flt: 0                 dbl: 0
     f27: 0x0000000041600000 flt: 14                dbl: 5.4189638607169796e-315
     f28: 0x0000000000000000 flt: 0                 dbl: 0
     f29: 0x0000000041700000 flt: 15                dbl: 5.4241445145035159e-315
     f30: 0x0000000000000000 flt: 0                 dbl: 0
     f31: 0x0000000041800000 flt: 16                dbl: 5.4293251682900522e-315
                       fsr              fir
                  00000000         00738900
    (gdb) x /i $pc
    => 0xffffffff80000000:	mtc0	v1,c0_status
    (gdb) stepi
    0xffffffff80000004 in ?? ()
    (gdb) info all-registers
                      zero               at               v0               v1
     R0   0000000000000000 0000000000000000 0000000000000000 0000000020000000
                        a0               a1               a2               a3
     R4   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        a4               a5               a6               a7
     R8   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        t0               t1               t2               t3
     R12  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        s0               s1               s2               s3
     R16  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        s4               s5               s6               s7
     R20  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        t8               t9               k0               k1
     R24  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        gp               sp               s8               ra
     R28  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        sr               lo               hi              bad
          0000000020000000 0000000000000000 0000000000000000 0000000000000000
                     cause               pc
          0000000000000000 ffffffff80000004
     f0:  0x00000000 flt: 0                 dbl: 0.0078125
     f1:  0x3f800000 flt: 1
     f2:  0x00000000 flt: 0                 dbl: 2
     f3:  0x40000000 flt: 2
     f4:  0x00000000 flt: 0                 dbl: 32
     f5:  0x40400000 flt: 3
     f6:  0x00000000 flt: 0                 dbl: 512
     f7:  0x40800000 flt: 4
     f8:  0x00000000 flt: 0                 dbl: 2048
     f9:  0x40a00000 flt: 5
     f10: 0x00000000 flt: 0                 dbl: 8192
     f11: 0x40c00000 flt: 6
     f12: 0x00000000 flt: 0                 dbl: 32768
     f13: 0x40e00000 flt: 7
     f14: 0x00000000 flt: 0                 dbl: 131072
     f15: 0x41000000 flt: 8
     f16: 0x00000000 flt: 0                 dbl: 262144
     f17: 0x41100000 flt: 9
     f18: 0x00000000 flt: 0                 dbl: 524288
     f19: 0x41200000 flt: 10
     f20: 0x00000000 flt: 0                 dbl: 1048576
     f21: 0x41300000 flt: 11
     f22: 0x00000000 flt: 0                 dbl: 2097152
     f23: 0x41400000 flt: 12
     f24: 0x00000000 flt: 0                 dbl: 4194304
     f25: 0x41500000 flt: 13
     f26: 0x00000000 flt: 0                 dbl: 8388608
     f27: 0x41600000 flt: 14
     f28: 0x00000000 flt: 0                 dbl: 16777216
     f29: 0x41700000 flt: 15
     f30: 0x00000000 flt: 0                 dbl: 33554432
     f31: 0x41800000 flt: 16
                       fsr              fir
                  00000000         00738900
    (gdb) set $sr ^= 1 << 26
    (gdb) info all-registers
                      zero               at               v0               v1
     R0   0000000000000000 0000000000000000 0000000000000000 0000000020000000
                        a0               a1               a2               a3
     R4   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        a4               a5               a6               a7
     R8   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        t0               t1               t2               t3
     R12  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        s0               s1               s2               s3
     R16  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        s4               s5               s6               s7
     R20  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        t8               t9               k0               k1
     R24  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        gp               sp               s8               ra
     R28  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                        sr               lo               hi              bad
          0000000024000000 0000000000000000 0000000000000000 0000000000000000
                     cause               pc
          0000000000000000 ffffffff80000004
     f0:  0x0000000000000000 flt: 0                 dbl: 0
     f1:  0x000000003f800000 flt: 1                 dbl: 5.2635442471208903e-315
     f2:  0x0000000000000000 flt: 0                 dbl: 0
     f3:  0x0000000040000000 flt: 2                 dbl: 5.3049894774131808e-315
     f4:  0x0000000000000000 flt: 0                 dbl: 0
     f5:  0x0000000040400000 flt: 3                 dbl: 5.325712092559326e-315
     f6:  0x0000000000000000 flt: 0                 dbl: 0
     f7:  0x0000000040800000 flt: 4                 dbl: 5.3464347077054713e-315
     f8:  0x0000000000000000 flt: 0                 dbl: 0
     f9:  0x0000000040a00000 flt: 5                 dbl: 5.3567960152785439e-315
     f10: 0x0000000000000000 flt: 0                 dbl: 0
     f11: 0x0000000040c00000 flt: 6                 dbl: 5.3671573228516165e-315
     f12: 0x0000000000000000 flt: 0                 dbl: 0
     f13: 0x0000000040e00000 flt: 7                 dbl: 5.3775186304246891e-315
     f14: 0x0000000000000000 flt: 0                 dbl: 0
     f15: 0x0000000041000000 flt: 8                 dbl: 5.3878799379977617e-315
     f16: 0x0000000000000000 flt: 0                 dbl: 0
     f17: 0x0000000041100000 flt: 9                 dbl: 5.393060591784298e-315
     f18: 0x0000000000000000 flt: 0                 dbl: 0
     f19: 0x0000000041200000 flt: 10                dbl: 5.3982412455708344e-315
     f20: 0x0000000000000000 flt: 0                 dbl: 0
     f21: 0x0000000041300000 flt: 11                dbl: 5.4034218993573707e-315
     f22: 0x0000000000000000 flt: 0                 dbl: 0
     f23: 0x0000000041400000 flt: 12                dbl: 5.408602553143907e-315
     f24: 0x0000000000000000 flt: 0                 dbl: 0
     f25: 0x0000000041500000 flt: 13                dbl: 5.4137832069304433e-315
     f26: 0x0000000000000000 flt: 0                 dbl: 0
     f27: 0x0000000041600000 flt: 14                dbl: 5.4189638607169796e-315
     f28: 0x0000000000000000 flt: 0                 dbl: 0
     f29: 0x0000000041700000 flt: 15                dbl: 5.4241445145035159e-315
     f30: 0x0000000000000000 flt: 0                 dbl: 0
     f31: 0x0000000041800000 flt: 16                dbl: 5.4293251682900522e-315
                       fsr              fir
                  00000000         00738900
    (gdb)

    This is implemented by retaining the raw register size for the FPRs at its
    native size (64-bits; this is required for remote packet offsets to work
    out correctly) and then truncating the cooked register size or not as
    required.  I have skipped `maintenance print registers' dumps here for
    brevity, the types flip between "double" and "float" as expected.

     This change supports bare-iron, Linux and IRIX targets.  For Linux and
    IRIX the width of the floating-point registers is set by the ABI of the
    program being run by the OS kernel and the kernel is responsible for
    setting CP0.Status.FR correctly; the image of Status accessible via
    ptrace(2) is read-only.  Therefore the respective backends mark the width
    as fixed and cause the run-time check to be skipped for efficiency.  I
    have verified that the Linux target does that correctly; the change for
    IRIX is the same and is expected to be all right, but I have no access to
    such a system (I will appreciate anyone verifying that).

     The change currently supports 64-bit processors only as GDB has no way to
    access upper halves of floating-point registers on 32-bit processors that
    have a 64-bit FPU (i.e. MIPS32r2 and newer processors); this is mentioned
    in the explanatory notes included with the change itself.

     The change also supports both XML and non-XML targets, but currently
    bare-iron targets for which this update has any significant meaning do not
    really support XML.  Any XML target is supposed to always provide an FPU
    description that matches the current setting of CP0.Status.FR, the new
    code verifies this is always the case and rejects the description as
    invalid otherwise.

     Generic parts require a general maintainer's approval, would whoever
    finds themselves most familiar with regcache internals please have a look
    at these bits?  Thanks.

     This addresses the CP0.Status.FR part of PR gdb/7518 (former GNATS PR
    gdb/413) and is based on work started by Nigel and David (neither at MIPS
    Technologies anymore).

    2012-06-06  Maciej W. Rozycki  <macro@codesourcery.com>
                Nigel Stephens  <nigel@mips.com>
                David Ung  <davidu@mips.com>

        PR gdb/7518

        gdb/
        * gdbarch.sh (regcache_changed): New function.
        * regcache.c (set_current_thread_ptid_arch): New function,
        factored out from...
        (get_thread_regcache): ... here.  Call gdbarch_regcache_changed
        as necessary.
        * mips-tdep.h (gdbarch_tdep): Add fp_register_size_fixed_p and
        fp_register_size members.
        (gdbarch_tdep_info): New structure.
        * mips-tdep.c (mips_register_type): Remove forward declaration.
        (mips_set_float_regsize, mips_float_regsize): New functions.
        (mips2_fp_compat): Remove function.
        (mips_register_type): Use mips_float_regsize to handle cooked
        floating-point registers.  Fold floating-point cases into common
        code.
        (mips_pseudo_register_type): Update comment on floating-point
        registers.
        (mips_read_fp_register_single): Rename raw_size local variable
        to fpsize.
        (mips_read_fp_register_double): Likewise.  Remove call to
        mips2_fp_compat.
        (mips_print_fp_register): Check the size of the register
        requested rather than $f0 and use fpsize local variable to hold
        it.  Remove call to mips2_fp_compat.
        (mips_print_float_info): Use mips_float_regsize.
        (mips_gdbarch_init): Use a proper tdep_info structure.  Match
        gdbarch against fp_register_size requested if any.  Initialize
        fp_register_size and fp_register_size_fixed_p in gdbarch target
        data.  Install mips_set_float_regsize as gdbarch regcache_changed
        routine.
        * mips-irix-tdep.c (mips_irix_init_abi): Set
        fp_register_size_fixed_p in gdbarch target data.
        * mips-linux-tdep.c (mips_linux_init_abi): Likewise.  Adjust for
        tdep_info update.
        * gdbarch.h: Regenerate.
        * gdbarch.c: Regenerate.
---
 gdb/gdbarch.c         |  32 +++++++++
 gdb/gdbarch.h         |  11 +++
 gdb/gdbarch.sh        |   5 ++
 gdb/mips-linux-tdep.c |   7 +-
 gdb/mips-tdep.c       | 194 +++++++++++++++++++++++++++++++++++---------------
 gdb/mips-tdep.h       |  16 +++++
 gdb/regcache.c        |  25 ++++++-
 7 files changed, 227 insertions(+), 63 deletions(-)
  

Comments

Maciej W. Rozycki July 25, 2016, 2:02 p.m. UTC | #1
Bhushan,

 Since this is virtually unchanged from the original submission I think it 
will be appropriate if you include a:

From: Maciej W. Rozycki <macro@codesourcery.com>

line at the beginning of the description to give a correct attribution.

>         * mips-irix-tdep.c (mips_irix_init_abi): Set
>         fp_register_size_fixed_p in gdbarch target data.

 This part is now gone along with IRIX support, so please remove it from 
the ChangeLog entry, and also make sure the rest of the ChangeLog entry is 
still consistent with the patch itself after adjustments.

> diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c
> index 8dc0566..6b9743b 100644
> --- a/gdb/mips-linux-tdep.c
> +++ b/gdb/mips-linux-tdep.c
> @@ -1737,8 +1737,9 @@ mips_linux_init_abi (struct gdbarch_info info,
>  				    mips_gdb_signal_to_target);
>  

 You've lost the removal of the `tdesc_data' local variable from the 
original change here and this variable becomes unused -- was it by 
accident?

>    tdep->syscall_next_pc = mips_linux_syscall_next_pc;
> +  tdep->fp_register_size_fixed_p = 1;
>  
> -  if (tdesc_data)
> +  if (((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data)

 Bad formatting here, you need a space after the cast.

>      {
>        const struct tdesc_feature *feature;
>  
> @@ -1753,8 +1754,8 @@ mips_linux_init_abi (struct gdbarch_info info,
>        feature = tdesc_find_feature (info.target_desc,
>  				    "org.gnu.gdb.mips.linux");
>        if (feature != NULL)
> -	tdesc_numbered_register (feature, tdesc_data, MIPS_RESTART_REGNUM,
> -				 "restart");
> +	tdesc_numbered_register (feature, (((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data),

 Likewise.  Also lines are supposed to fit in 74 columns unless there is a 
good justification to make an exception, in which case they must not 
exceed 80 columns, as per GDB formatting style (please see 
<https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards> for 
details).  So this line needs to be wrapped.

 However, I think this will best be sorted out with an auxiliary local 
`tdep_info' variable, in which case you won't need a cast at all.

> diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
> index 63c1560..ae4dc2e 100644
> --- a/gdb/mips-tdep.c
> +++ b/gdb/mips-tdep.c
> @@ -6260,20 +6310,19 @@ mips_print_fp_register (struct ui_file *file, struct frame_info *frame,
>  			int regnum)
>  {				/* Do values for FP (float) regs.  */
>    struct gdbarch *gdbarch = get_frame_arch (frame);
> +  int fpsize = register_size (gdbarch, regnum);
>    gdb_byte *raw_buffer;
>    double doub, flt1;	/* Doubles extracted from raw hex data.  */
>    int inv1, inv2;
>  
> -  raw_buffer
> -    = ((gdb_byte *)
> -       alloca (2 * register_size (gdbarch, mips_regnum (gdbarch)->fp0)));
> +  raw_buffer = alloca (2 * fpsize);

 You need to keep this cast here; have you tried building GDB with a C++ 
compiler?

 You've lost a change to `mips_print_float_info' here -- again, was it by 
accident?

> @@ -8167,6 +8216,7 @@ value_of_mips_user_reg (struct frame_info *frame, const void *baton)
>  static struct gdbarch *
>  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  {
> +  struct gdbarch_tdep_info tdep_info = { NULL };
>    struct gdbarch *gdbarch;
>    struct gdbarch_tdep *tdep;
>    int elf_flags;
> @@ -8181,6 +8231,10 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    int dspacc;
>    int dspctl;
>  
> +  /* Wire in an empty template tdep_info if one hasn't been supplied.  */
> +  if (info.tdep_info == NULL)
> +    info.tdep_info = &tdep_info;
> +
>    /* Fill in the OS dependent register numbers and names.  */
>    if (info.osabi == GDB_OSABI_IRIX)
>      {
[...]
> @@ -8311,7 +8366,15 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  	  return NULL;
>  	}
>  
> -      valid_p = 1;
> +      /* Set the floating-point register size, assuming that whoever
> +         supplied the description got the current setting right wrt
> +         CP0 Status register's bit FR if applicable.  */
> +      fpsize = tdesc_register_size (feature, mips_fprs[0]) / 8;
> +
> +      /* Only accept a description whose floating-point register size
> +         matches the requested size or if none was specified.  */
> +      valid_p = (info.tdep_info->fp_register_size == 0
> +		 || info.tdep_info->fp_register_size == fpsize);
>        for (i = 0; i < 32; i++)
>  	valid_p &= tdesc_numbered_register (feature, tdesc_data,
>  					    i + mips_regnum.fp0, mips_fprs[i]);
> @@ -8366,6 +8429,9 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  	    }
>  	}
>  
> +      /* Fix the floating-point register size found.  */
> +      info.tdep_info->fp_register_size = fpsize;
> +
>        /* It would be nice to detect an attempt to use a 64-bit ABI
>  	 when only 32-bit registers are provided.  */
>        reg_names = NULL;
> @@ -8576,6 +8642,11 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>        /* Be pedantic about which FPU is selected.  */
>        if (gdbarch_tdep (arches->gdbarch)->mips_fpu_type != fpu_type)
>  	continue;
> +      /* Ditto the requested floating-point register size if any.  */
> +      if (info.tdep_info->fp_register_size != 0
> +	  && (gdbarch_tdep (arches->gdbarch)->fp_register_size)
> +	      != info.tdep_info->fp_register_size)
> +	continue;
>  
>        if (tdesc_data != NULL)
>  	tdesc_data_cleanup (tdesc_data);
> @@ -8593,6 +8664,8 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    tdep->mips_fpu_type = fpu_type;
>    tdep->register_size_valid_p = 0;
>    tdep->register_size = 0;
> +  tdep->fp_register_size = info.tdep_info->fp_register_size;
> +  tdep->fp_register_size_fixed_p = 0;
>  
>    if (info.target_desc)
>      {
[...]
> @@ -8869,7 +8949,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    mips_register_g_packet_guesses (gdbarch);
>  
>    /* Hook in OS ABI-specific overrides, if they have been registered.  */
> -  info.tdep_info = tdesc_data;
> +  ((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data = tdesc_data;

 Missing space after the cast again, but why don't you need the same cast 
to access `info.tdep_info->fp_register_size' above?  Have you verified 
this change actually builds?

 Overall again I think it'll be best refactored to avoid these casts, e.g. 
rename the existing null `tdep_info' to `null_tdep_info', and then add a 
new `tdep_info' variable to keep a correctly typed pointer and use it 
throughout.

> diff --git a/gdb/regcache.c b/gdb/regcache.c
> index f0ba0cf..c3405b6 100644
> --- a/gdb/regcache.c
> +++ b/gdb/regcache.c
> @@ -534,16 +534,35 @@ get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
>  static ptid_t current_thread_ptid;
>  static struct gdbarch *current_thread_arch;
>  
> -struct regcache *
> -get_thread_regcache (ptid_t ptid)
> +static void
> +set_current_thread_ptid_arch (ptid_t ptid)
>  {
>    if (!current_thread_arch || !ptid_equal (current_thread_ptid, ptid))
>      {
>        current_thread_ptid = ptid;
>        current_thread_arch = target_thread_architecture (ptid);
>      }
> +}
> +
> +struct regcache *
> +get_thread_regcache (ptid_t ptid)
> +{
> +  int registers_changed_p = current_regcache == NULL;
> +  struct regcache *new_regcache;
> +
> +  set_current_thread_ptid_arch (ptid);
> +  new_regcache = get_thread_arch_regcache (ptid, current_thread_arch);
> +
> +  if (registers_changed_p
> +      && gdbarch_regcache_changed_p (current_thread_arch)
> +      && gdbarch_regcache_changed (current_thread_arch, new_regcache))
> +    {
> +      registers_changed ();
> +      set_current_thread_ptid_arch (ptid);
> +      new_regcache = get_thread_arch_regcache (ptid, current_thread_arch);
> +    }
>  
> -  return get_thread_arch_regcache (ptid, current_thread_arch);
> +  return new_regcache;
>  }
>  
>  struct regcache *

 As per <https://sourceware.org/ml/gdb-patches/2012-06/msg00291.html> this 
part requires a general maintainer's approval.

 ISTR though I hit issues with core file handling triggered by this part 
of the change, only observed after the original patch submission (and 
likely the reason I didn't just ping the patch).  So while this patch adds 
new functionality and therefore does not have to have each and every case 
handled right away from the beginning, please double-check it does not 
break things, and in particular does not cause a crash while handling a 
core file.

 Also a similar wrapper around `get_thread_arch_regcache' may be needed 
for places that call it directly rather than via `get_thread_regcache'.  
Please investigate.

  Maciej
  
Yao Qi Nov. 8, 2016, 7:46 p.m. UTC | #2
Hi Bhushan,

On Mon, Jun 27, 2016 at 3:49 PM, Bhushan Attarde
<bhushan.attarde@imgtec.com> wrote:
>     Many MIPS architecture processors can reconfigure the size of their
>     floating-point registers at the run time.  The file comprising 32
>     registers can be either 32-bit or 64-bit wide depending on whether CP0
>     Status register's bit FR is zero or one, respectively.  Fortunately access
>     to Status is available on all targets.
>
>     Here's a change to handle this property.  It requires the generic register
>     access code to raise the target backend's attention whenever a new
>     register set has been retrieved so that it can examine the state of
>     CP0.Status.FR and act accordingly.  I have added this hook to
>     get_thread_regcache, the backend has then an opportunity to switch gdbarch
>     as necessary and let the caller know if it did so.  If that indeed
>     happened, then the register cache originally retrieved is then discarded
>     and another one obtained using the newly-selected gdbarch.  This new
>     register cache is not revalidated.
>
>

>     This is implemented by retaining the raw register size for the FPRs at its
>     native size (64-bits; this is required for remote packet offsets to work
>     out correctly) and then truncating the cooked register size or not as
>     required.  I have skipped `maintenance print registers' dumps here for
>     brevity, the types flip between "double" and "float" as expected.
>

If I understand you correctly, 64-bits are still transferred in remote protocol
if FPRs are 32-bit.

>      This change supports bare-iron, Linux and IRIX targets.  For Linux and
>     IRIX the width of the floating-point registers is set by the ABI of the
>     program being run by the OS kernel and the kernel is responsible for
>     setting CP0.Status.FR correctly; the image of Status accessible via
>     ptrace(2) is read-only.  Therefore the respective backends mark the width
>     as fixed and cause the run-time check to be skipped for efficiency.  I
>     have verified that the Linux target does that correctly; the change for
>     IRIX is the same and is expected to be all right, but I have no access to
>     such a system (I will appreciate anyone verifying that).
>
>      The change currently supports 64-bit processors only as GDB has no way to
>     access upper halves of floating-point registers on 32-bit processors that
>     have a 64-bit FPU (i.e. MIPS32r2 and newer processors); this is mentioned
>     in the explanatory notes included with the change itself.
>
>      The change also supports both XML and non-XML targets, but currently
>     bare-iron targets for which this update has any significant meaning do not
>     really support XML.  Any XML target is supposed to always provide an FPU
>     description that matches the current setting of CP0.Status.FR, the new
>     code verifies this is always the case and rejects the description as
>     invalid otherwise.

How do I understand "Any XML target is supposed to always provide
an FPU description that matches the current setting of CP0.Status.FR"?
I don't see how it is done in current GDB/GDBserver.

"reconfigure FPRs" is a target description change to me, instead of a
regcache change.  I'd like GDB can deal with "target description of
process is changed in runtime" in general, because this requirement
exists in other places as well,

 - x86 kernel booting, the arch is 16-bit at the beginning, and switch to
   32-bit or 64-bit later,
   https://sourceware.org/ml/gdb/2016-11/msg00003.html
 - ARM SVE vector registers length can be changed,

There are two different ways to support handling target description
change in general.  Other thoughts are welcome too.

1)
 - Implement target hook to_thread_architecture in ARCH-linux-nat.c,
   in which we can use ptrace to read the register A which indicates
   the target description is changed or not.  In MIPS, register A is
   CP0.Status.  Create target info and return the right gdbarch.
   [In MIPS, we have one gdbarch for 32-bit FPR and one gdbarch for
   64-bit FPR]
 - Add register A to expedited registers, which is included in the stop
   reply from GDBserver.
 - Add new gdbarch method target_description_changed_p, and its
   parameter is a vector of expedited registers got in
   remote.c:process_stop_reply.  In default, it returns false.  In
   MIPS, we can tell whether target description is changed by the
   value of  and current gdbarch.
 - In remote.c:process_stop_reply, if
   gdbarch_target_description_changed_p returns true, invalidate all
   regcaches, request target description from gdbserver again, and get
   the updated target description.

2)
 - Add enum TARGET_WAITKIND_ARCH_CHANGED,
 - In ARCH-linux-nat.c, interpret/override linux_nat_wait.  If
   linux_nat_wait returns, and there is a real event of stop, call
   ptrace to get the register A value, if it is different from what we
   remember in current gdbarch, mark the event pending and return
   TARGET_WAITKIND_ARCH_CHANGED,
 - In remote, add a new stop reply, T00arch, for example,
   https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html
   If remote.c:process_stop_reply sees T00arch, return
   TARGET_WAITKIND_ARCH_CHANGED.
 - In gdb core, if TARGET_WAITKIND_ARCH_CHANGED is reported by
   target_wait, call target_find_description, to update target
   description.
 - In gdbserver, add target method target_stopped_by_changed_arch,
   and use it in remote-utils.c:prepare_resume_reply.  Get register A
   value by ptrace too, and return true if it is different from the
   knowledge in process_info.tdesc.
  
Maciej W. Rozycki Nov. 10, 2016, 12:42 p.m. UTC | #3
Yao,

 I'll chime in in Bhushan's stead a bit as I've beaten the oddities of the 
MIPS FPU to death throughout my dealings with the architecture (Bhushan, 
you're of course welcome to reply too if you have anything to add).

On Tue, 8 Nov 2016, Yao Qi wrote:

> >     This is implemented by retaining the raw register size for the FPRs at its
> >     native size (64-bits; this is required for remote packet offsets to work
> >     out correctly) and then truncating the cooked register size or not as
> >     required.  I have skipped `maintenance print registers' dumps here for
> >     brevity, the types flip between "double" and "float" as expected.
> >
> 
> If I understand you correctly, 64-bits are still transferred in remote protocol
> if FPRs are 32-bit.

 Nope.  In the FR=0 mode existing XML-described stubs present a file of 32 
32-bit FPRs; this applies to MIPS32 targets only as so far we've had an 
assumption that MIPS32 <=> FR=0 and MIPS64 <=> FR=1.  I think switching 
the view at this time would be an undesireable complication, as it would 
be yet another variant we'd have to handle forever along with the new one.

 FWIW I think the FR=0 mode should have been chosen to present the FPR 
file as 16 64-bit FPRs instead, with the cooked register view used to 
access register halves if required, but then I think a further 
complication would arise with non-XML-described stubs.

> >      The change also supports both XML and non-XML targets, but currently
> >     bare-iron targets for which this update has any significant meaning do not
> >     really support XML.  Any XML target is supposed to always provide an FPU
> >     description that matches the current setting of CP0.Status.FR, the new
> >     code verifies this is always the case and rejects the description as
> >     invalid otherwise.
> 
> How do I understand "Any XML target is supposed to always provide
> an FPU description that matches the current setting of CP0.Status.FR"?
> I don't see how it is done in current GDB/GDBserver.

 I do hope the last patch which adds `gdbserver' features en masse, which 
I didn't get to to looking through yet, has it (although I think the 
individual features should be included along with corresponding base 
target and native support instead).

> "reconfigure FPRs" is a target description change to me, instead of a
> regcache change.

 The regcache change is partly historic, because parts of the series 
(dating back to 2005) were implemented even before we had XML 
descriptions, and an FR transition is also supposed to work with 
non-XML-described MIPS64 bare-metal targets (where the FPR file is 
presented as 32 64-bit FPRs regardless of the FR setting).  I do agree a 
target description should take precedence where available, and certainly 
with Linux targets where you cannot get a MIPS64 FR=0 configuration or a 
non-XML-described MIPS32 FR=1 configuration).

 What do you propose if we don't have a target description though?  We 
can't handle MIPS32 FR=1, because stubs won't transfer the upper halves of 
FPRs, but we can handle MIPS64 FR=0, because we can simply ignore the 
irrelevant upper halves.

> There are two different ways to support handling target description
> change in general.  Other thoughts are welcome too.
> 
> 1)
>  - Implement target hook to_thread_architecture in ARCH-linux-nat.c,
>    in which we can use ptrace to read the register A which indicates
>    the target description is changed or not.  In MIPS, register A is
>    CP0.Status.  Create target info and return the right gdbarch.
>    [In MIPS, we have one gdbarch for 32-bit FPR and one gdbarch for
>    64-bit FPR]
>  - Add register A to expedited registers, which is included in the stop
>    reply from GDBserver.

 I think I'd prefer this to be the $fp_mode register if available (as 
it'll include the FRE setting as well), and the $status register 
otherwise.  Linux kernels which support the FRE setting will always make 
$fp_mode available, as it'll be a part of the same change (unless someone 
sneaks a broken patch through behind my back, as I still yet have to see 
the kernel parts of this framework, let alone review them).

>  - Add new gdbarch method target_description_changed_p, and its
>    parameter is a vector of expedited registers got in
>    remote.c:process_stop_reply.  In default, it returns false.  In
>    MIPS, we can tell whether target description is changed by the
>    value of  and current gdbarch.
>  - In remote.c:process_stop_reply, if
>    gdbarch_target_description_changed_p returns true, invalidate all
>    regcaches, request target description from gdbserver again, and get
>    the updated target description.
> 
> 2)
>  - Add enum TARGET_WAITKIND_ARCH_CHANGED,
>  - In ARCH-linux-nat.c, interpret/override linux_nat_wait.  If
>    linux_nat_wait returns, and there is a real event of stop, call
>    ptrace to get the register A value, if it is different from what we
>    remember in current gdbarch, mark the event pending and return
>    TARGET_WAITKIND_ARCH_CHANGED,
>  - In remote, add a new stop reply, T00arch, for example,
>    https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html
>    If remote.c:process_stop_reply sees T00arch, return
>    TARGET_WAITKIND_ARCH_CHANGED.
>  - In gdb core, if TARGET_WAITKIND_ARCH_CHANGED is reported by
>    target_wait, call target_find_description, to update target
>    description.
>  - In gdbserver, add target method target_stopped_by_changed_arch,
>    and use it in remote-utils.c:prepare_resume_reply.  Get register A
>    value by ptrace too, and return true if it is different from the
>    knowledge in process_info.tdesc.

 The rest looks like a good plan to me, although I think I like your #1 
proposal a bit better.

  Maciej
  
Yao Qi Nov. 11, 2016, 12:28 p.m. UTC | #4
On Thu, Nov 10, 2016 at 12:42:53PM +0000, Maciej W. Rozycki wrote:
> 
> > >     This is implemented by retaining the raw register size for the FPRs at its
> > >     native size (64-bits; this is required for remote packet offsets to work
> > >     out correctly) and then truncating the cooked register size or not as
> > >     required.  I have skipped `maintenance print registers' dumps here for
> > >     brevity, the types flip between "double" and "float" as expected.
> > >
> > 
> > If I understand you correctly, 64-bits are still transferred in remote protocol
> > if FPRs are 32-bit.
> 
>  Nope.  In the FR=0 mode existing XML-described stubs present a file of 32 
> 32-bit FPRs; this applies to MIPS32 targets only as so far we've had an 
> assumption that MIPS32 <=> FR=0 and MIPS64 <=> FR=1.  I think switching 
> the view at this time would be an undesireable complication, as it would 
> be yet another variant we'd have to handle forever along with the new one.

Yeah, there are different variants.  Let me be specific, with this patch
applied, on MIPS64, FR=0, how many bits needed for each FPR in 'g' packet?
I ask this question for this sentence "This is implemented by retaining the
raw register size for the FPRs at its native size (64-bits; this is required for
remote packet offsets to work out correctly) and then truncating the cooked
register size or not as required." in Bhushan's mail.
IIUC, with this patch applied, on MIPS64, FR=0, FPR is 32-bit, but 64-bit
space is used in 'g' packet.  No?

> 
>  FWIW I think the FR=0 mode should have been chosen to present the FPR 
> file as 16 64-bit FPRs instead, with the cooked register view used to 
> access register halves if required, but then I think a further 
> complication would arise with non-XML-described stubs.
> 
> > >      The change also supports both XML and non-XML targets, but currently
> > >     bare-iron targets for which this update has any significant meaning do not
> > >     really support XML.  Any XML target is supposed to always provide an FPU
> > >     description that matches the current setting of CP0.Status.FR, the new
> > >     code verifies this is always the case and rejects the description as
> > >     invalid otherwise.
> > 
> > How do I understand "Any XML target is supposed to always provide
> > an FPU description that matches the current setting of CP0.Status.FR"?
> > I don't see how it is done in current GDB/GDBserver.
> 
>  I do hope the last patch which adds `gdbserver' features en masse, which 
> I didn't get to to looking through yet, has it (although I think the 
> individual features should be included along with corresponding base 
> target and native support instead).

Oh, it must be this patch
https://sourceware.org/ml/gdb-patches/2016-06/msg00511.html

> 
> > "reconfigure FPRs" is a target description change to me, instead of a
> > regcache change.
> 
>  The regcache change is partly historic, because parts of the series 
> (dating back to 2005) were implemented even before we had XML 
> descriptions, and an FR transition is also supposed to work with 
> non-XML-described MIPS64 bare-metal targets (where the FPR file is 
> presented as 32 64-bit FPRs regardless of the FR setting).  I do agree a 
> target description should take precedence where available, and certainly 
> with Linux targets where you cannot get a MIPS64 FR=0 configuration or a 
> non-XML-described MIPS32 FR=1 configuration).
> 
>  What do you propose if we don't have a target description though?  We 
> can't handle MIPS32 FR=1, because stubs won't transfer the upper halves of 
> FPRs, but we can handle MIPS64 FR=0, because we can simply ignore the 
> irrelevant upper halves.

Why stubs doesn't transfer the upper bits of PFRs in case of MIPS32 FR=1?
because of keeping backward compatibility?  Does
register_remote_g_packet_guess help?

> 
> > There are two different ways to support handling target description
> > change in general.  Other thoughts are welcome too.
> > 
> > 1)
> >  - Implement target hook to_thread_architecture in ARCH-linux-nat.c,
> >    in which we can use ptrace to read the register A which indicates
> >    the target description is changed or not.  In MIPS, register A is
> >    CP0.Status.  Create target info and return the right gdbarch.
> >    [In MIPS, we have one gdbarch for 32-bit FPR and one gdbarch for
> >    64-bit FPR]
> >  - Add register A to expedited registers, which is included in the stop
> >    reply from GDBserver.
> 
>  I think I'd prefer this to be the $fp_mode register if available (as 
> it'll include the FRE setting as well), and the $status register 
> otherwise.  Linux kernels which support the FRE setting will always make 
> $fp_mode available, as it'll be a part of the same change (unless someone 
> sneaks a broken patch through behind my back, as I still yet have to see 
> the kernel parts of this framework, let alone review them).

If we have $fp_mode, that is better.

> 
>  The rest looks like a good plan to me, although I think I like your #1 
> proposal a bit better.
> 

Thanks for your feedback, Maciej.  I personally like #1 better too :)
  
Maciej W. Rozycki Dec. 2, 2016, 2:30 a.m. UTC | #5
On Fri, 11 Nov 2016, Yao Qi wrote:

> > > >     This is implemented by retaining the raw register size for the FPRs at its
> > > >     native size (64-bits; this is required for remote packet offsets to work
> > > >     out correctly) and then truncating the cooked register size or not as
> > > >     required.  I have skipped `maintenance print registers' dumps here for
> > > >     brevity, the types flip between "double" and "float" as expected.
> > > >
> > > 
> > > If I understand you correctly, 64-bits are still transferred in remote protocol
> > > if FPRs are 32-bit.
> > 
> >  Nope.  In the FR=0 mode existing XML-described stubs present a file of 32 
> > 32-bit FPRs; this applies to MIPS32 targets only as so far we've had an 
> > assumption that MIPS32 <=> FR=0 and MIPS64 <=> FR=1.  I think switching 
> > the view at this time would be an undesireable complication, as it would 
> > be yet another variant we'd have to handle forever along with the new one.
> 
> Yeah, there are different variants.  Let me be specific, with this patch
> applied, on MIPS64, FR=0, how many bits needed for each FPR in 'g' packet?
> I ask this question for this sentence "This is implemented by retaining the
> raw register size for the FPRs at its native size (64-bits; this is required for
> remote packet offsets to work out correctly) and then truncating the cooked
> register size or not as required." in Bhushan's mail.
> IIUC, with this patch applied, on MIPS64, FR=0, FPR is 32-bit, but 64-bit
> space is used in 'g' packet.  No?

 Pre-XML stubs have a uniform register width so with a MIPS64 debuggee 
FPRs will be 64-bit whether FR=1 or FR=0; this patch cannot and does not 
attempt to change this, so in that case FPRs need to be explicitly 
truncated at the cooked level, which is arranged by this patch in 
`mips_register_type'.

 XML-described stubs are enforced by this patch to match `g' packet space 
with the actual FPR width, however no XML-described stub is currently 
known to support FR tracking.

 NB all this only applies to bare-metal stubs rather than our `gdbserver' 
as MIPS64/FR=0 is not a supported Linux execution model.

> >  FWIW I think the FR=0 mode should have been chosen to present the FPR 
> > file as 16 64-bit FPRs instead, with the cooked register view used to 
> > access register halves if required, but then I think a further 
> > complication would arise with non-XML-described stubs.
> > 
> > > >      The change also supports both XML and non-XML targets, but currently
> > > >     bare-iron targets for which this update has any significant meaning do not
> > > >     really support XML.  Any XML target is supposed to always provide an FPU
> > > >     description that matches the current setting of CP0.Status.FR, the new
> > > >     code verifies this is always the case and rejects the description as
> > > >     invalid otherwise.
> > > 
> > > How do I understand "Any XML target is supposed to always provide
> > > an FPU description that matches the current setting of CP0.Status.FR"?
> > > I don't see how it is done in current GDB/GDBserver.
> > 
> >  I do hope the last patch which adds `gdbserver' features en masse, which 
> > I didn't get to to looking through yet, has it (although I think the 
> > individual features should be included along with corresponding base 
> > target and native support instead).
> 
> Oh, it must be this patch
> https://sourceware.org/ml/gdb-patches/2016-06/msg00511.html

 Correct.

> > > "reconfigure FPRs" is a target description change to me, instead of a
> > > regcache change.
> > 
> >  The regcache change is partly historic, because parts of the series 
> > (dating back to 2005) were implemented even before we had XML 
> > descriptions, and an FR transition is also supposed to work with 
> > non-XML-described MIPS64 bare-metal targets (where the FPR file is 
> > presented as 32 64-bit FPRs regardless of the FR setting).  I do agree a 
> > target description should take precedence where available, and certainly 
> > with Linux targets where you cannot get a MIPS64 FR=0 configuration or a 
> > non-XML-described MIPS32 FR=1 configuration).
> > 
> >  What do you propose if we don't have a target description though?  We 
> > can't handle MIPS32 FR=1, because stubs won't transfer the upper halves of 
> > FPRs, but we can handle MIPS64 FR=0, because we can simply ignore the 
> > irrelevant upper halves.
> 
> Why stubs doesn't transfer the upper bits of PFRs in case of MIPS32 FR=1?
> because of keeping backward compatibility?  Does
> register_remote_g_packet_guess help?

 Pre-XML stubs have a uniform register width matching the architecture, 
and then a fixed register layout as defined by the enum in mips-tdep.h.  
Consequently there is simply no room there for the upper halves of 
MIPS32/FR=1 FPRs, so no legacy stub ever gives access to them, because all 
registers transferred are 32-bit.  Using `register_remote_g_packet_guess' 
won't help with such missing data.

 With an XML-described stub we can of course supply whole FPRs however we 
wish, although I think there's still value in keeping the MIPS32/FR=0 
packet format the same (i.e. as defined by mips-tdep.h) so that 
communication continues working with legacy pre-XML GDB clients.  Of 
course once we switch to MIPS32/FR=1, then communication will break, but 
pre-XML GDB clients don't support the cooked MIPS32/FR=1 register format 
anyway.

 Did I answer your questions?

  Maciej
  

Patch

diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 4143744..0673fc1 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -199,6 +199,7 @@  struct gdbarch
   gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum;
   gdbarch_register_name_ftype *register_name;
   gdbarch_register_type_ftype *register_type;
+  gdbarch_regcache_changed_ftype *regcache_changed;
   gdbarch_dummy_id_ftype *dummy_id;
   int deprecated_fp_regnum;
   gdbarch_push_dummy_call_ftype *push_dummy_call;
@@ -543,6 +544,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   if (gdbarch->register_name == 0)
     fprintf_unfiltered (log, "\n\tregister_name");
   /* Skip verify of register_type, has predicate.  */
+  /* Skip verify of regcache_changed, has predicate.  */
   /* Skip verify of dummy_id, has predicate.  */
   /* Skip verify of deprecated_fp_regnum, invalid_p == 0 */
   /* Skip verify of push_dummy_call, has predicate.  */
@@ -1225,6 +1227,12 @@  gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: record_special_symbol = <%s>\n",
                       host_address_to_string (gdbarch->record_special_symbol));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_regcache_changed_p() = %d\n",
+                      gdbarch_regcache_changed_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: regcache_changed = <%s>\n",
+                      host_address_to_string (gdbarch->regcache_changed));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: register_name = <%s>\n",
                       host_address_to_string (gdbarch->register_name));
   fprintf_unfiltered (file,
@@ -2171,6 +2179,30 @@  set_gdbarch_register_type (struct gdbarch *gdbarch,
 }
 
 int
+gdbarch_regcache_changed_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->regcache_changed != NULL;
+}
+
+int
+gdbarch_regcache_changed (struct gdbarch *gdbarch, struct regcache *regcache)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->regcache_changed != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_regcache_changed called\n");
+  return gdbarch->regcache_changed (gdbarch, regcache);
+}
+
+void
+set_gdbarch_regcache_changed (struct gdbarch *gdbarch,
+                              gdbarch_regcache_changed_ftype regcache_changed)
+{
+  gdbarch->regcache_changed = regcache_changed;
+}
+
+int
 gdbarch_dummy_id_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 3fadcd1..22b02a3 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -356,6 +356,17 @@  typedef struct type * (gdbarch_register_type_ftype) (struct gdbarch *gdbarch, in
 extern struct type * gdbarch_register_type (struct gdbarch *gdbarch, int reg_nr);
 extern void set_gdbarch_register_type (struct gdbarch *gdbarch, gdbarch_register_type_ftype *register_type);
 
+/* Notify the architecture that the registers have changed and we now have
+   a new regcache to examine.  Return one if a new architecture has been
+   selected that changed the layout of the regcache and it has to be
+   discarded and a new one initialised, zero otherwise. */
+
+extern int gdbarch_regcache_changed_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_regcache_changed_ftype) (struct gdbarch *gdbarch, struct regcache *regcache);
+extern int gdbarch_regcache_changed (struct gdbarch *gdbarch, struct regcache *regcache);
+extern void set_gdbarch_regcache_changed (struct gdbarch *gdbarch, gdbarch_regcache_changed_ftype *regcache_changed);
+
 extern int gdbarch_dummy_id_p (struct gdbarch *gdbarch);
 
 typedef struct frame_id (gdbarch_dummy_id_ftype) (struct gdbarch *gdbarch, struct frame_info *this_frame);
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 4ac6b90..2c09371 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -469,6 +469,11 @@  m:const char *:register_name:int regnr:regnr::0
 # the register cache should call this function directly; others should
 # use "register_type".
 M:struct type *:register_type:int reg_nr:reg_nr
+# Notify the architecture that the registers have changed and we now have
+# a new regcache to examine.  Return one if a new architecture has been
+# selected that changed the layout of the regcache and it has to be
+# discarded and a new one initialised, zero otherwise.
+M:int:regcache_changed:struct regcache *regcache:regcache
 
 M:struct frame_id:dummy_id:struct frame_info *this_frame:this_frame
 # Implement DUMMY_ID and PUSH_DUMMY_CALL, then delete
diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c
index 8dc0566..6b9743b 100644
--- a/gdb/mips-linux-tdep.c
+++ b/gdb/mips-linux-tdep.c
@@ -1737,8 +1737,9 @@  mips_linux_init_abi (struct gdbarch_info info,
 				    mips_gdb_signal_to_target);
 
   tdep->syscall_next_pc = mips_linux_syscall_next_pc;
+  tdep->fp_register_size_fixed_p = 1;
 
-  if (tdesc_data)
+  if (((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data)
     {
       const struct tdesc_feature *feature;
 
@@ -1753,8 +1754,8 @@  mips_linux_init_abi (struct gdbarch_info info,
       feature = tdesc_find_feature (info.target_desc,
 				    "org.gnu.gdb.mips.linux");
       if (feature != NULL)
-	tdesc_numbered_register (feature, tdesc_data, MIPS_RESTART_REGNUM,
-				 "restart");
+	tdesc_numbered_register (feature, (((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data),
+				 MIPS_RESTART_REGNUM, "restart");
     }
 }
 
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 63c1560..ae4dc2e 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -58,8 +58,6 @@ 
 
 static const struct objfile_data *mips_pdr_data;
 
-static struct type *mips_register_type (struct gdbarch *gdbarch, int regnum);
-
 static int mips32_instruction_has_delay_slot (struct gdbarch *gdbarch,
 					      ULONGEST inst);
 static int micromips_instruction_has_delay_slot (ULONGEST insn, int mustbe32);
@@ -274,6 +272,77 @@  mips_abi_regsize (struct gdbarch *gdbarch)
     }
 }
 
+/* Determine the current floating-point register size and update our
+   architecture data accordingly.  Return one if the size has changed
+   and a new architecture has been selected, zero otherwise.
+
+   For MIPS1, MIPS2 and MIPS32 rev. 1 processors the size is hardwired
+   to 32 bits.  For MIPS3 and other 64-bit processors up to the MIPS64
+   rev. 1 ISA the size is determined by the CP0 Status register's bit
+   FR.  If this bit is 1, then the size is 64 bits.  If it is 0, then
+   the FPU operates in the compatibility mode and the size is 32 bits.
+
+   From MIPS32 and MIPS64 rev. 2 ISAs up the size is implementation
+   specific and reported by the CP1 FIR register's bit F64.  If this
+   bit is 0, then the size is hardwired to 32 bits.  If this bit is 1,
+   then the size is determined by the CP0 Status register's bit FR as
+   described above.  Unfortunately we may not have access to the CP0
+   registers needed to determine whether the ISA implemented is MIPS32
+   or MIPS64 rev. 2 or higher.
+
+   We currently cannot handle the 64-bit floating-point register size
+   on MIPS32 rev. 2 and higher ISA processors though as no target
+   provides access to upper halves of such registers.  Therefore we
+   hardcode the size to 32 bits for any 32-bit processors.
+
+   As the CP0 Status register's bit FR cannot be modified on processors
+   that do not implement an FPU, backends for operating systems that
+   can emulate the FPU in software should set the size according to the
+   ABI in use and then set fp_register_size_fixed_p to 1 to prevent
+   further updates.  */
+
+static int
+mips_set_float_regsize (struct gdbarch *gdbarch, struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  struct gdbarch_tdep_info tdep_info = { NULL };
+  struct gdbarch_info info;
+  int fpsize;
+
+  if (tdep->fp_register_size_fixed_p)
+    return 0;
+
+  fpsize = mips_isa_regsize (gdbarch);
+  if (fpsize == 8)
+    {
+      enum register_status status;
+      ULONGEST sr;
+
+      status = regcache_raw_read_unsigned (regcache, MIPS_PS_REGNUM, &sr);
+      if (status == REG_VALID)
+	fpsize = (sr & ST0_FR) ? 8 : 4;
+    }
+
+  if (fpsize == tdep->fp_register_size)
+    return 0;
+
+  /* Need a new gdbarch, go get one.  */
+  gdbarch_info_init (&info);
+  info.tdep_info = &tdep_info;
+  info.tdep_info->fp_register_size = fpsize;
+  gdbarch_update_p (info);
+
+  return 1;
+}
+
+/* Return the currently configured floating-point register size.  */
+
+static int
+mips_float_regsize (struct gdbarch *gdbarch)
+{
+  return gdbarch_tdep (gdbarch)->fp_register_size;
+}
+
 /* MIPS16/microMIPS function addresses are odd (bit 0 is set).  Here
    are some functions to handle addresses associated with compressed
    code including but not limited to testing, setting, or clearing
@@ -504,32 +573,6 @@  mips_xfer_register (struct gdbarch *gdbarch, struct regcache *regcache,
     fprintf_unfiltered (gdb_stdlog, "\n");
 }
 
-/* Determine if a MIPS3 or later cpu is operating in MIPS{1,2} FPU
-   compatiblity mode.  A return value of 1 means that we have
-   physical 64-bit registers, but should treat them as 32-bit registers.  */
-
-static int
-mips2_fp_compat (struct frame_info *frame)
-{
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  /* MIPS1 and MIPS2 have only 32 bit FPRs, and the FR bit is not
-     meaningful.  */
-  if (register_size (gdbarch, mips_regnum (gdbarch)->fp0) == 4)
-    return 0;
-
-#if 0
-  /* FIXME drow 2002-03-10: This is disabled until we can do it consistently,
-     in all the places we deal with FP registers.  PR gdb/413.  */
-  /* Otherwise check the FR bit in the status register - it controls
-     the FP compatiblity mode.  If it is clear we are in compatibility
-     mode.  */
-  if ((get_frame_register_unsigned (frame, MIPS_PS_REGNUM) & ST0_FR) == 0)
-    return 1;
-#endif
-
-  return 0;
-}
-
 #define VM_MIN_ADDRESS (CORE_ADDR)0x400000
 
 static CORE_ADDR heuristic_proc_start (struct gdbarch *, CORE_ADDR);
@@ -1005,31 +1048,36 @@  static struct type *
 mips_register_type (struct gdbarch *gdbarch, int regnum)
 {
   gdb_assert (regnum >= 0 && regnum < 2 * gdbarch_num_regs (gdbarch));
-  if (mips_float_register_p (gdbarch, regnum))
-    {
-      /* The floating-point registers raw, or cooked, always match
-         mips_isa_regsize(), and also map 1:1, byte for byte.  */
-      if (mips_isa_regsize (gdbarch) == 4)
-	return builtin_type (gdbarch)->builtin_float;
-      else
-	return builtin_type (gdbarch)->builtin_double;
-    }
-  else if (regnum < gdbarch_num_regs (gdbarch))
+  if (regnum < gdbarch_num_regs (gdbarch))
     {
       /* The raw or ISA registers.  These are all sized according to
 	 the ISA regsize.  */
-      if (mips_isa_regsize (gdbarch) == 4)
-	return builtin_type (gdbarch)->builtin_int32;
+      int regsize = mips_isa_regsize (gdbarch);
+
+      if (mips_float_register_p (gdbarch, regnum))
+	return (regsize == 4
+		? builtin_type (gdbarch)->builtin_float
+		: builtin_type (gdbarch)->builtin_double);
       else
-	return builtin_type (gdbarch)->builtin_int64;
+	return (regsize == 4
+		? builtin_type (gdbarch)->builtin_int32
+		: builtin_type (gdbarch)->builtin_int64);
     }
   else
     {
-      int rawnum = regnum - gdbarch_num_regs (gdbarch);
-
       /* The cooked or ABI registers.  These are sized according to
 	 the ABI (with a few complications).  */
-      if (rawnum == mips_regnum (gdbarch)->fp_control_status
+      int rawnum = regnum - gdbarch_num_regs (gdbarch);
+
+      /* Floating-point registers of most 64-bit and some 32-bit MIPS
+         processors can be reconfigured dynamically at the run time as
+         either 64-bit or 32-bit via the CP0 Status register's FR bit.
+         Use the current setting for cooked registers.  */
+      if (mips_float_register_p (gdbarch, regnum))
+	return (mips_float_regsize (gdbarch) == 4
+		? builtin_type (gdbarch)->builtin_float
+		: builtin_type (gdbarch)->builtin_double);
+      else if (rawnum == mips_regnum (gdbarch)->fp_control_status
 	  || rawnum == mips_regnum (gdbarch)->fp_implementation_revision)
 	return builtin_type (gdbarch)->builtin_int32;
       else if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
@@ -1074,8 +1122,10 @@  mips_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
     return rawtype;
 
   if (mips_float_register_p (gdbarch, rawnum))
-    /* Present the floating point registers however the hardware did;
-       do not try to convert between FPU layouts.  */
+    /* Present the floating point registers however the hardware did; do
+       not try to convert between FPU layouts.  A target description is
+       expected to have taken the CP0 Status register's FR bit into account
+       as necessary, this has been already verified in mips_gdbarch_init.  */
     return rawtype;
 
   /* Use pointer types for registers if we can.  For n32 we can not,
@@ -6187,13 +6237,13 @@  mips_read_fp_register_single (struct frame_info *frame, int regno,
 			      gdb_byte *rare_buffer)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
-  int raw_size = register_size (gdbarch, regno);
-  gdb_byte *raw_buffer = (gdb_byte *) alloca (raw_size);
+  int fpsize = register_size (gdbarch, regno);
+  gdb_byte *raw_buffer = alloca (fpsize);
 
   if (!deprecated_frame_register_read (frame, regno, raw_buffer))
     error (_("can't read register %d (%s)"),
 	   regno, gdbarch_register_name (gdbarch, regno));
-  if (raw_size == 8)
+  if (fpsize == 8)
     {
       /* We have a 64-bit value for this register.  Find the low-order
          32 bits.  */
@@ -6221,9 +6271,9 @@  mips_read_fp_register_double (struct frame_info *frame, int regno,
 			      gdb_byte *rare_buffer)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
-  int raw_size = register_size (gdbarch, regno);
+  int fpsize = register_size (gdbarch, regno);
 
-  if (raw_size == 8 && !mips2_fp_compat (frame))
+  if (fpsize == 8)
     {
       /* We have a 64-bit value for this register, and we should use
          all 64 bits.  */
@@ -6260,20 +6310,19 @@  mips_print_fp_register (struct ui_file *file, struct frame_info *frame,
 			int regnum)
 {				/* Do values for FP (float) regs.  */
   struct gdbarch *gdbarch = get_frame_arch (frame);
+  int fpsize = register_size (gdbarch, regnum);
   gdb_byte *raw_buffer;
   double doub, flt1;	/* Doubles extracted from raw hex data.  */
   int inv1, inv2;
 
-  raw_buffer
-    = ((gdb_byte *)
-       alloca (2 * register_size (gdbarch, mips_regnum (gdbarch)->fp0)));
+  raw_buffer = alloca (2 * fpsize);
 
   fprintf_filtered (file, "%s:", gdbarch_register_name (gdbarch, regnum));
   fprintf_filtered (file, "%*s",
 		    4 - (int) strlen (gdbarch_register_name (gdbarch, regnum)),
 		    "");
 
-  if (register_size (gdbarch, regnum) == 4 || mips2_fp_compat (frame))
+  if (fpsize == 4)
     {
       struct value_print_options opts;
 
@@ -8167,6 +8216,7 @@  value_of_mips_user_reg (struct frame_info *frame, const void *baton)
 static struct gdbarch *
 mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 {
+  struct gdbarch_tdep_info tdep_info = { NULL };
   struct gdbarch *gdbarch;
   struct gdbarch_tdep *tdep;
   int elf_flags;
@@ -8181,6 +8231,10 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   int dspacc;
   int dspctl;
 
+  /* Wire in an empty template tdep_info if one hasn't been supplied.  */
+  if (info.tdep_info == NULL)
+    info.tdep_info = &tdep_info;
+
   /* Fill in the OS dependent register numbers and names.  */
   if (info.osabi == GDB_OSABI_IRIX)
     {
@@ -8252,6 +8306,7 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
       const struct tdesc_feature *feature;
       int valid_p;
+      int fpsize;
 
       feature = tdesc_find_feature (info.target_desc,
 				    "org.gnu.gdb.mips.cpu");
@@ -8311,7 +8366,15 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	  return NULL;
 	}
 
-      valid_p = 1;
+      /* Set the floating-point register size, assuming that whoever
+         supplied the description got the current setting right wrt
+         CP0 Status register's bit FR if applicable.  */
+      fpsize = tdesc_register_size (feature, mips_fprs[0]) / 8;
+
+      /* Only accept a description whose floating-point register size
+         matches the requested size or if none was specified.  */
+      valid_p = (info.tdep_info->fp_register_size == 0
+		 || info.tdep_info->fp_register_size == fpsize);
       for (i = 0; i < 32; i++)
 	valid_p &= tdesc_numbered_register (feature, tdesc_data,
 					    i + mips_regnum.fp0, mips_fprs[i]);
@@ -8366,6 +8429,9 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	    }
 	}
 
+      /* Fix the floating-point register size found.  */
+      info.tdep_info->fp_register_size = fpsize;
+
       /* It would be nice to detect an attempt to use a 64-bit ABI
 	 when only 32-bit registers are provided.  */
       reg_names = NULL;
@@ -8576,6 +8642,11 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       /* Be pedantic about which FPU is selected.  */
       if (gdbarch_tdep (arches->gdbarch)->mips_fpu_type != fpu_type)
 	continue;
+      /* Ditto the requested floating-point register size if any.  */
+      if (info.tdep_info->fp_register_size != 0
+	  && (gdbarch_tdep (arches->gdbarch)->fp_register_size)
+	      != info.tdep_info->fp_register_size)
+	continue;
 
       if (tdesc_data != NULL)
 	tdesc_data_cleanup (tdesc_data);
@@ -8593,6 +8664,8 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->mips_fpu_type = fpu_type;
   tdep->register_size_valid_p = 0;
   tdep->register_size = 0;
+  tdep->fp_register_size = info.tdep_info->fp_register_size;
+  tdep->fp_register_size_fixed_p = 0;
 
   if (info.target_desc)
     {
@@ -8609,6 +8682,12 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	}
     }
 
+  /* If we haven't figured out the size of floating-point registers
+     by now yet, then assume it is the same as for general-purpose
+     registers.  */
+  if (tdep->fp_register_size == 0)
+    tdep->fp_register_size = mips_isa_regsize (gdbarch);
+
   /* Initially set everything according to the default ABI/ISA.  */
   set_gdbarch_short_bit (gdbarch, 16);
   set_gdbarch_int_bit (gdbarch, 32);
@@ -8830,6 +8909,7 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_integer_to_address (gdbarch, mips_integer_to_address);
 
   set_gdbarch_register_type (gdbarch, mips_register_type);
+  set_gdbarch_regcache_changed (gdbarch, mips_set_float_regsize);
 
   set_gdbarch_print_registers_info (gdbarch, mips_print_registers_info);
 
@@ -8869,7 +8949,7 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   mips_register_g_packet_guesses (gdbarch);
 
   /* Hook in OS ABI-specific overrides, if they have been registered.  */
-  info.tdep_info = tdesc_data;
+  ((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data = tdesc_data;
   gdbarch_init_osabi (info, gdbarch);
 
   /* The hook may have adjusted num_regs, fetch the final value and
diff --git a/gdb/mips-tdep.h b/gdb/mips-tdep.h
index 2e4d194..6c1d9ca 100644
--- a/gdb/mips-tdep.h
+++ b/gdb/mips-tdep.h
@@ -113,11 +113,27 @@  struct gdbarch_tdep
   int register_size_valid_p;
   int register_size;
 
+  /* The size of floating-point registers determined at the run time.
+     This corresponds to CP0 Status register's bit FR setting if
+     implemented unless fixed_p is set.  */
+  int fp_register_size_fixed_p;
+  int fp_register_size;
+
   /* Return the expected next PC if FRAME is stopped at a syscall
      instruction.  */
   CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
 };
 
+/* MIPS specific per-architecture initialization information.  */
+struct gdbarch_tdep_info
+{
+  /* Target description data.  */
+  struct tdesc_arch_data *tdesc_data;
+
+  /* The size of floating-point registers determined at the run time.  */
+  int fp_register_size;
+};
+
 /* Register numbers of various important registers.  */
 
 enum
diff --git a/gdb/regcache.c b/gdb/regcache.c
index f0ba0cf..c3405b6 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -534,16 +534,35 @@  get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
 static ptid_t current_thread_ptid;
 static struct gdbarch *current_thread_arch;
 
-struct regcache *
-get_thread_regcache (ptid_t ptid)
+static void
+set_current_thread_ptid_arch (ptid_t ptid)
 {
   if (!current_thread_arch || !ptid_equal (current_thread_ptid, ptid))
     {
       current_thread_ptid = ptid;
       current_thread_arch = target_thread_architecture (ptid);
     }
+}
+
+struct regcache *
+get_thread_regcache (ptid_t ptid)
+{
+  int registers_changed_p = current_regcache == NULL;
+  struct regcache *new_regcache;
+
+  set_current_thread_ptid_arch (ptid);
+  new_regcache = get_thread_arch_regcache (ptid, current_thread_arch);
+
+  if (registers_changed_p
+      && gdbarch_regcache_changed_p (current_thread_arch)
+      && gdbarch_regcache_changed (current_thread_arch, new_regcache))
+    {
+      registers_changed ();
+      set_current_thread_ptid_arch (ptid);
+      new_regcache = get_thread_arch_regcache (ptid, current_thread_arch);
+    }
 
-  return get_thread_arch_regcache (ptid, current_thread_arch);
+  return new_regcache;
 }
 
 struct regcache *