diff mbox

[PATCH+1,MIPS] Implement O32 FPXX ABI (GCC)

Message ID 6D39441BF12EF246A7ABCE6654B0235354C034@LEMAIL01.le.imgtec.org
State New
Headers show

Commit Message

Matthew Fortune June 6, 2014, 1:30 p.m. UTC
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
diff mbox

Patch

===
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)