RFH: DWARF representation of mixed-mode registers

Richard Sandiford rdsandiford@googlemail.com
Mon Jun 9 19:09:00 GMT 2014


Sorry for the gdb/gcc crosspost, but can anyone offer any advice for
the situation Matthew describes in the message quoted below?  We have
three ABIs:

(1) traditional o32 with 32-bit FP registers.  The FP registers act as pairs
    when storing doubles.  Some architectures can only store one single-
    precision value per pair while others can store one per register.

(2) o32 with 64-bit FP registers.  Each FP register can store a single-
    or double-precision value.  Only even registers are call-saved,
    meaning that the total FP save size is the same as for (1).

(3) o32 with indeterminate FP register width ("fpcc").  This is designed to
    be individually link-compatible with (1) or (2) (but not both together,
    which would be impossible).  Among other things, doubles are restricted
    to even registers and single-precision values can only be stored in
    odd registers if they are treated as both call-clobbered and call-saved.

(1) is historical and shouldn't change.  (2) and (3) are new ABIs that
are in the late stages of being defined.

The question is: what would be the best way of representing FP registers
in the DWARF information for (2) and (3), given that (1) and (3) and
(2) and (3) could be linked together?

Thanks,
Richard

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Hi Richard,
>
> I've been working through the debug issues for O32 FPXX and O32 FP64 and have
> identified some things to resolve along with proposed fixes...
>
> What to do with debug info for double precision registers and the O32 FPXX
> ABI? The choices are:
> 1) Do what O32 FP32 does and represent 8 byte values in two DW_OP_pieces
>    referring to the relevant registers.
> 2) Do what O32 FP64 does and represent 8 byte values in a single register
> 3) Do a hybrid where 8-byte values sit in a single DW_OP_pieces.
>
> I'm tentatively swayed to (3) as it is a slightly unusual construct that means
> we should have better scope to detect it and do special handling. I've
> implemented that in the patch below but am still not sure. My next choice is (1)
> as it means there would only be special handling when debugging an O32 FP64
> program with FPXX modules rather than both O32 FP32 and O32 FP64.
>
> How to ensure unwinding will work when passing through functions with differing
> ABI variants? There isn't much room for debate here as we simply have to
> continue using the unwind table format as used in O32 FP32. This represents
> 32 32-bit floating point registers. A description of how O32 FP64 is modified to
> cope with this is on the O32 FR wiki page:
>
> https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking#8._Re-definition_of_the_-mabi.3D32_-mfp64_ABI
>
> Copied here for archive
> ===
> Changes to O32 FP64:
>
> * 6 double-precision callee-save registers ($f20 to $f30 evens). This used to be
>   12 double-precision registers.
> * GNU ABI extensions for returning _Complex float and _Complex double are
>   redefined to return the two components in $f0 and $f2 instead of $f0 and $f1.
> * The size of registers 32->63, which represent floating-point registers, in
>   dwarf unwinding tables is set to 4-byte instead of the more natural choice of
>   8-byte. This is required to ensure all ABI variants which can interlink have a
>   consistent view of unwind information. Unwind registers 32-63 are conceptually
>   defined to represent the low and high 32-bits of a double precision value in
>   even numbered registers. Register 32 is lo32($f0) and register 33 is hi32($f0).
>   The odd-numbered 64-bit floating point registers for O32 FP64 are not
>   represented in frame unwind information, this is OK because none of them are
>   callee-saved. When saving or restoring a 64-bit callee-saved register two CFI
>   directives must be emitted, one to represent the mapping for the lo32 frame
>   register and one for the hi32 frame register.
> ===
>
> I have modified the --with-fp option to be --with-fp-32 where the -32 corresponds
> to the -32 found in --with-arch-32 option. This is to support building a simple
> toolchain with one 64-bit ABI and one 32-bit ABI where the FP ABI for the 32-bit
> ABI can be set without affecting the 64-bit ABI.
>
> --with-arch-64=mips3 --with-arch-32=mips2 --with-fp-32=xx
>
> "gcc -mabi=64" then implies "-mips3 -mgp64 -mfp64"
> "gcc" then implies "-mips2 -mgp32 -mfpxx"
>
> Finally I have had to add ASM_SPECs to pass through -mhard-float, -msoft-float,
> -mdouble-float, -msingle-float. Without these building ASM sources for soft-float
> or single-float get marked as if they were hard-float double-precision with the
> new binutils .MIPS.abiflags support. This does of course mean that using an old
> GCC with a new binutils will lead to problems building soft-float or
> single-precision code.
>
> Attached is a patch that just addresses these issues rather than the whole FPXX
> patch again. I'll merge everything together and post the whole lot when things
> are resolved and more testing has been done.
>
> Regards,
> Matthew
>
> From 4fc4f1c72e1d226691d4cebf2c5fa6de809fb409 Mon Sep 17 00:00:00 2001
> From: Matthew Fortune <matthew.fortune@imgtec.com>
> Date: Sat, 31 May 2014 23:33:21 +0100
> Subject: [PATCH] fpxx feature fix
>
> ---
>  gcc/config.gcc         |    8 ++++----
>  gcc/config/mips/mips.c |   34 ++++++++++++++++++++++++++--------
>  gcc/config/mips/mips.h |   10 +++++++++-
>  gcc/dwarf2cfi.c        |    5 +++++
>  4 files changed, 44 insertions(+), 13 deletions(-)
>
> diff --git a/gcc/config.gcc b/gcc/config.gcc
> index 79268f7..dc272d8 100644
> --- a/gcc/config.gcc
> +++ b/gcc/config.gcc
> @@ -3731,7 +3731,7 @@ case "${target}" in
>  		;;
>  
>  	mips*-*-*)
> -		supported_defaults="abi arch arch_32 arch_64 float fpu nan fp tune tune_32 tune_64 divide llsc mips-plt synci"
> +		supported_defaults="abi arch arch_32 arch_64 float fpu nan fp_32 tune tune_32 tune_64 divide llsc mips-plt synci"
>  
>  		case ${with_float} in
>  		"" | soft | hard)
> @@ -3763,12 +3763,12 @@ case "${target}" in
>  			;;
>  		esac
>  
> -		case ${with_fp} in
> +		case ${with_fp_32} in
>  		"" | 32 | xx | 64)
>  			# OK
>  			;;
>  		*)
> -			echo "Unknown FP mode used in --with-fp=$with_fp" 1>&2
> +			echo "Unknown FP mode used in --with-fp-32=$with_fp_32" 1>&2
>  			exit 1
>  			;;
>  		esac
> @@ -4177,7 +4177,7 @@ case ${target} in
>  esac
>  
>  t=
> -all_defaults="abi cpu cpu_32 cpu_64 arch arch_32 arch_64 tune tune_32 tune_64 schedule float mode fpu nan fp divide llsc mips-plt synci tls"
> +all_defaults="abi cpu cpu_32 cpu_64 arch arch_32 arch_64 tune tune_32 tune_64 schedule float mode fpu nan fp_32 divide llsc mips-plt synci tls"
>  for option in $all_defaults
>  do
>  	eval "val=\$with_"`echo $option | sed s/-/_/g`
> diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
> index 5c39acd..f29fb45 100644
> --- a/gcc/config/mips/mips.c
> +++ b/gcc/config/mips/mips.c
> @@ -8735,16 +8735,30 @@ mips_dwarf_register_span (rtx reg)
>    rtx high, low;
>    enum machine_mode mode;
>  
> -  /* By default, GCC maps increasing register numbers to increasing
> -     memory locations, but paired FPRs are always little-endian,
> -     regardless of the prevailing endianness.  */
> +  /* TARGET_FLOATXX is implemented as 32-bit floating-point registers but
> +     ensures that double precision registers are treated as if they were
> +     64-bit physical registers.  The code will run correctly with 32-bit or
> +     64-bit registers which means that dwarf information cannot be precisely
> +     correct for all scenarios.  We choose to state that the 64-bit values
> +     are stored in a single 64-bit 'piece'.  This slightly unusual
> +     construct can then be interpreted as either a pair of registers if the
> +     registers are 32-bit or a single 64-bit register depending on
> +     hardware.  */
>    mode = GET_MODE (reg);
>    if (FP_REG_P (REGNO (reg))
> -      && TARGET_BIG_ENDIAN
> -      && MAX_FPRS_PER_FMT > 1
> -      && !TARGET_FLOATXX
> +      && TARGET_FLOATXX
>        && GET_MODE_SIZE (mode) > UNITS_PER_FPREG)
>      {
> +      return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (1, reg));
> +    }
> +  /* By default, GCC maps increasing register numbers to increasing
> +     memory locations, but paired FPRs are always little-endian,
> +     regardless of the prevailing endianness.  */
> +  else if (FP_REG_P (REGNO (reg))
> +	   && TARGET_BIG_ENDIAN
> +	   && MAX_FPRS_PER_FMT > 1
> +	   && GET_MODE_SIZE (mode) > UNITS_PER_FPREG)
> +    {
>        gcc_assert (GET_MODE_SIZE (mode) == UNITS_PER_HWFPVALUE);
>        high = mips_subword (reg, true);
>        low = mips_subword (reg, false);
> @@ -10572,7 +10586,9 @@ mips_for_each_saved_acc (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
>  static void
>  mips_save_reg (rtx reg, rtx mem)
>  {
> -  if (GET_MODE (reg) == DFmode && TARGET_FLOAT32)
> +  if (GET_MODE (reg) == DFmode
> +      && (!TARGET_FLOAT64
> +	  || mips_abi == ABI_32))
>      {
>        rtx x1, x2;
>  
> @@ -11497,7 +11513,9 @@ mips_restore_reg (rtx reg, rtx mem)
>       $7 instead and adjust the return insn appropriately.  */
>    if (TARGET_MIPS16 && REGNO (reg) == RETURN_ADDR_REGNUM)
>      reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
> -  else if (GET_MODE (reg) == DFmode && TARGET_FLOAT32)
> +  else if (GET_MODE (reg) == DFmode
> +	   && (!TARGET_FLOAT64
> +	       || mips_abi == ABI_32))
>      {
>        mips_add_cfa_restore (mips_subword (reg, true));
>        mips_add_cfa_restore (mips_subword (reg, false));
> diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
> index 91938d9..8a16cbc 100644
> --- a/gcc/config/mips/mips.h
> +++ b/gcc/config/mips/mips.h
> @@ -804,7 +804,7 @@ struct mips_cpu_info {
>    {"float", "%{!msoft-float:%{!mhard-float:-m%(VALUE)-float}}" }, \
>    {"fpu", "%{!msingle-float:%{!mdouble-float:-m%(VALUE)-float}}" }, \
>    {"nan", "%{!mnan=*:-mnan=%(VALUE)}" }, \
> -  {"fp", "%{!mfp*:-mfp%(VALUE)}" }, \
> +  {"fp_32", "%{" OPT_ARCH32 ":%{!mfp*:-mfp%(VALUE)}}" }, \
>    {"divide", "%{!mdivide-traps:%{!mdivide-breaks:-mdivide-%(VALUE)}}" }, \
>    {"llsc", "%{!mllsc:%{!mno-llsc:-m%(VALUE)}}" }, \
>    {"mips-plt", "%{!mplt:%{!mno-plt:-m%(VALUE)}}" }, \
> @@ -1206,6 +1206,9 @@ struct mips_cpu_info {
>  %{mabi=*} %{!mabi=*: %(asm_abi_default_spec)} \
>  %{mgp32} %{mgp64} %{march=*} %{mxgot:-xgot} \
>  %{mfp32} %{mfpxx} %{mfp64} %{mnan=*} \
> +%{mhard-float} %{msoft-float} \
> +%{mdouble-float} %{msingle-float} \
>  %{mshared} %{mno-shared} \
>  %{msym32} %{mno-sym32} \
>  %{mtune=*} \
> @@ -1323,6 +1326,11 @@ struct mips_cpu_info {
>  /* The DWARF 2 CFA column which tracks the return address.  */
>  #define DWARF_FRAME_RETURN_COLUMN RETURN_ADDR_REGNUM
>  
> +/* The mode to use to calculate the size of a DWARF 2 CFA column.  */
> +#define DWARF_REG_MODE(REGNO, MODE) \
> +  (FP_REG_P (REGNO) && mips_abi == ABI_32 && TARGET_FLOAT64 \
> +   ? SImode : (MODE))
> +
>  /* Before the prologue, RA lives in r31.  */
>  #define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (VOIDmode, RETURN_ADDR_REGNUM)
>  
> diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
> index 47fd028..16106b8 100644
> --- a/gcc/dwarf2cfi.c
> +++ b/gcc/dwarf2cfi.c
> @@ -252,6 +252,10 @@ init_return_column_size (enum machine_mode mode, rtx mem, unsigned int c)
>  		  gen_int_mode (size, mode));
>  }
>  
> +#ifndef DWARF_REG_MODE
> +#define DWARF_REG_MODE(REGNO, MODE) (MODE)
> +#endif
> +
>  /* Generate code to initialize the register size table.  */
>  
>  void
> @@ -276,6 +280,7 @@ expand_builtin_init_dwarf_reg_sizes (tree address)
>  
>  	  if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode))
>  	    save_mode = choose_hard_reg_mode (i, 1, true);
> +	  save_mode = DWARF_REG_MODE (i, save_mode);
>  	  if (dnum == DWARF_FRAME_RETURN_COLUMN)
>  	    {
>  	      if (save_mode == VOIDmode)



More information about the Gdb mailing list