This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

match constraints with different modes in big endian => reload ICE


The testcase in the patch below is based on
gcc.c-torture/compile/20020312-1.c, that wouldn't compile for a
big-endian target that had sizeof (int) < sizeof (long), with int as
wide as a hardware register.  The problem was that reload picked the
same hardware register for both input and output of the asm, but then
reload_cse_simplify_operands() would call constrain_operands(), that
called operands_match_p(), that would apply the WORDS_BIG_ENDIAN
adjustment to the register numbers, and decide they didn't match,
after all.  Oops.

This patch fixes the problem, arranging for reload to adjust the
register numbers too.  Also, it fixes (?) operands_match_p() such that
it uses HARD_REGNO_NREGS to compute the number of registers taken by a
mode, instead of assuming UNITS_PER_WORD applies uniformly to all
registers.

Since this patch is a do-nothing for little-endian targets, I haven't
bootstrapped it on athlon-pc-linux-gnu, but rather just tested a cross
to this big endian target I'm working on.  I could use some volunteer
help to actually bootstrap this on a fast big-endian machine.  Any
takers?

Is this obvious enough to install anyway?

Index: gcc/ChangeLog
from  Alexandre Oliva  <aoliva at redhat dot com>

	* reload.c (reload_adjust_reg_for_mode): New function.
	(subst_reloads): Call it.
	(operands_match_p): Adjust registers using HARD_REGNO_NREGS.
	* reload.h (reload_adjust_reg_for_mode): Declare.
	* reload1.c (emit_input_reload_insns, emit_output_reload_insns):
	Call it.

Index: gcc/reload.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/reload.c,v
retrieving revision 1.209
diff -u -p -r1.209 reload.c
--- gcc/reload.c 4 Feb 2003 22:47:23 -0000 1.209
+++ gcc/reload.c 22 Feb 2003 08:02:45 -0000
@@ -2137,10 +2137,10 @@ operands_match_p (x, y)
 	 (reg:SI 1) will be considered the same register.  */
       if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD
 	  && i < FIRST_PSEUDO_REGISTER)
-	i += (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD) - 1;
+	i += HARD_REGNO_NREGS (i, GET_MODE (x)) - 1;
       if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (y)) > UNITS_PER_WORD
 	  && j < FIRST_PSEUDO_REGISTER)
-	j += (GET_MODE_SIZE (GET_MODE (y)) / UNITS_PER_WORD) - 1;
+	j += HARD_REGNO_NREGS (j, GET_MODE (y)) - 1;
 
       return i == j;
     }
@@ -5954,7 +5954,7 @@ subst_reloads (insn)
 	     do the wrong thing if RELOADREG is multi-word.  RELOADREG
 	     will always be a REG here.  */
 	  if (GET_MODE (reloadreg) != r->mode && r->mode != VOIDmode)
-	    reloadreg = gen_rtx_REG (r->mode, REGNO (reloadreg));
+	    reloadreg = reload_adjust_reg_for_mode (reloadreg, r->mode);
 
 	  /* If we are putting this into a SUBREG and RELOADREG is a
 	     SUBREG, we would be making nested SUBREGs, so we have to fix
@@ -6932,6 +6932,26 @@ regno_clobbered_p (regno, insn, mode, se
     }
 
   return 0;
+}
+
+/* Find the low part, with mode MODE, of a hard regno RELOADREG.  */
+rtx
+reload_adjust_reg_for_mode (reloadreg, mode)
+     rtx reloadreg;
+     enum machine_mode mode;
+{
+  int regno;
+
+  if (GET_MODE (reloadreg) == mode)
+    return reloadreg;
+
+  regno = REGNO (reloadreg);
+
+  if (WORDS_BIG_ENDIAN)
+    regno += HARD_REGNO_NREGS (regno, GET_MODE (reloadreg))
+      - HARD_REGNO_NREGS (regno, mode);
+
+  return gen_rtx_REG (mode, regno);
 }
 
 static const char *const reload_when_needed_name[] =
Index: gcc/reload1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/reload1.c,v
retrieving revision 1.379
diff -u -p -r1.379 reload1.c
--- gcc/reload1.c 14 Feb 2003 22:23:48 -0000 1.379
+++ gcc/reload1.c 22 Feb 2003 08:02:52 -0000
@@ -6321,7 +6321,7 @@ emit_input_reload_insns (chain, rl, old,
      must always be a REG here.  */
 
   if (GET_MODE (reloadreg) != mode)
-    reloadreg = gen_rtx_REG (mode, REGNO (reloadreg));
+    reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
   while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode)
     oldequiv = SUBREG_REG (oldequiv);
   if (GET_MODE (oldequiv) != VOIDmode
@@ -6570,8 +6570,8 @@ emit_input_reload_insns (chain, rl, old,
 			oldequiv = old, real_oldequiv = real_old;
 		      else
 			second_reload_reg
-			  = gen_rtx_REG (new_mode,
-					 REGNO (second_reload_reg));
+			  = reload_adjust_reg_for_mode (second_reload_reg,
+							new_mode);
 		    }
 		}
 	    }
@@ -6693,7 +6693,7 @@ emit_output_reload_insns (chain, rl, j)
     }
 
   if (GET_MODE (reloadreg) != mode)
-    reloadreg = gen_rtx_REG (mode, REGNO (reloadreg));
+    reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
 
 #ifdef SECONDARY_OUTPUT_RELOAD_CLASS
 
@@ -6734,7 +6734,7 @@ emit_output_reload_insns (chain, rl, j)
 		= rld[secondary_reload].secondary_out_icode;
 
 	      if (GET_MODE (reloadreg) != mode)
-		reloadreg = gen_rtx_REG (mode, REGNO (reloadreg));
+		reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
 
 	      if (tertiary_icode != CODE_FOR_nothing)
 		{
Index: gcc/testsuite/ChangeLog
from  Alexandre Oliva  <aoliva at redhat dot com>

	* gcc.c-torture/execute/20030222-1.c: New test.

Index: gcc/testsuite/gcc.c-torture/execute/20030222-1.c
===================================================================
RCS file: gcc/testsuite/gcc.c-torture/execute/20030222-1.c
diff -N gcc/testsuite/gcc.c-torture/execute/20030222-1.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gcc/testsuite/gcc.c-torture/execute/20030222-1.c 22 Feb 2003 08:03:00 -0000
@@ -0,0 +1,27 @@
+/* Verify that we get the low part of the long long as an int.  We
+   used to get it wrong on big-endian machines, if register allocation
+   succeeded at all.  We use volatile to make sure the long long is
+   actually truncated to int, in case a single register is wide enough
+   for a long long.  */
+
+#include <limits.h>
+
+void
+ll_to_int (long long x, volatile int *p)
+{
+  int i;
+  asm ("" : "=r" (i) : "0" (x));
+  *p = i;
+}
+
+int val = INT_MIN + 1;
+
+int main() {
+  volatile int i;
+
+  ll_to_int ((long long)val, &i);
+  if (i != val)
+    abort ();
+  
+  exit (0);
+}
-- 
Alexandre Oliva   Enjoy Guarana', see http://www.ic.unicamp.br/~oliva/
Red Hat GCC Developer                 aoliva at {redhat dot com, gcc.gnu.org}
CS PhD student at IC-Unicamp        oliva at {lsd dot ic dot unicamp dot br, gnu.org}
Free Software Evangelist                Professional serial bug killer

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]