Blame SOURCES/gcc34-pr14084.patch

6fdc0f
2004-05-27  Josef Zlomek  <zlomekj@suse.cz>
6fdc0f
6fdc0f
	PR middle-end/14084
6fdc0f
	* emit-rtl.c (gen_rtx_REG_offset): Adjust the offset according
6fdc0f
	to size of decl.
6fdc0f
6fdc0f
--- gcc/emit-rtl.c	25 May 2004 12:04:15 -0000	1.391
6fdc0f
+++ gcc/emit-rtl.c	27 May 2004 04:28:12 -0000	1.392
6fdc0f
@@ -746,13 +746,96 @@ gen_reg_rtx (enum machine_mode mode)
6fdc0f
   return val;
6fdc0f
 }
6fdc0f
 
6fdc0f
-/* Generate a register with same attributes as REG,
6fdc0f
-   but offsetted by OFFSET.  */
6fdc0f
+/* Generate a register with same attributes as REG, but offsetted by OFFSET.
6fdc0f
+   Do the big endian correction if needed.  */
6fdc0f
 
6fdc0f
 rtx
6fdc0f
 gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno, int offset)
6fdc0f
 {
6fdc0f
   rtx new = gen_rtx_REG (mode, regno);
6fdc0f
+  tree decl;
6fdc0f
+  HOST_WIDE_INT var_size;
6fdc0f
+
6fdc0f
+  /* PR middle-end/14084
6fdc0f
+     The problem appears when a variable is stored in a larger register
6fdc0f
+     and later it is used in the original mode or some mode in between
6fdc0f
+     or some part of variable is accessed.
6fdc0f
+
6fdc0f
+     On little endian machines there is no problem because
6fdc0f
+     the REG_OFFSET of the start of the variable is the same when
6fdc0f
+     accessed in any mode (it is 0).
6fdc0f
+
6fdc0f
+     However, this is not true on big endian machines.
6fdc0f
+     The offset of the start of the variable is different when accessed
6fdc0f
+     in different modes.
6fdc0f
+     When we are taking a part of the REG we have to change the OFFSET
6fdc0f
+     from offset WRT size of mode of REG to offset WRT size of variable.
6fdc0f
+
6fdc0f
+     If we would not do the big endian correction the resulting REG_OFFSET
6fdc0f
+     would be larger than the size of the DECL.
6fdc0f
+
6fdc0f
+     Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
6fdc0f
+
6fdc0f
+     REG.mode  MODE  DECL size  old offset  new offset  description
6fdc0f
+     DI        SI    4          4           0           int32 in SImode
6fdc0f
+     DI        SI    1          4           0           char in SImode
6fdc0f
+     DI        QI    1          7           0           char in QImode
6fdc0f
+     DI        QI    4          5           1           1st element in QImode
6fdc0f
+                                                        of char[4]
6fdc0f
+     DI        HI    4          6           2           1st element in HImode
6fdc0f
+                                                        of int16[2]
6fdc0f
+
6fdc0f
+     If the size of DECL is equal or greater than the size of REG
6fdc0f
+     we can't do this correction because the register holds the
6fdc0f
+     whole variable or a part of the variable and thus the REG_OFFSET
6fdc0f
+     is already correct.  */
6fdc0f
+
6fdc0f
+  decl = REG_EXPR (reg);
6fdc0f
+  if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
6fdc0f
+      && decl != NULL
6fdc0f
+      && offset > 0
6fdc0f
+      && GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (mode)
6fdc0f
+      && ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
6fdc0f
+	  && var_size < GET_MODE_SIZE (GET_MODE (reg))))
6fdc0f
+    {
6fdc0f
+      int offset_le;
6fdc0f
+
6fdc0f
+      /* Convert machine endian to little endian WRT size of mode of REG.  */
6fdc0f
+      if (WORDS_BIG_ENDIAN)
6fdc0f
+	offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
6fdc0f
+		     / UNITS_PER_WORD) * UNITS_PER_WORD;
6fdc0f
+      else
6fdc0f
+	offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
6fdc0f
+
6fdc0f
+      if (BYTES_BIG_ENDIAN)
6fdc0f
+	offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
6fdc0f
+		      % UNITS_PER_WORD);
6fdc0f
+      else
6fdc0f
+	offset_le += offset % UNITS_PER_WORD;
6fdc0f
+
6fdc0f
+      if (offset_le >= var_size)
6fdc0f
+	{
6fdc0f
+	  /* MODE is wider than the variable so the new reg will cover
6fdc0f
+	     the whole variable so the resulting OFFSET should be 0.  */
6fdc0f
+	  offset = 0;
6fdc0f
+	}
6fdc0f
+      else
6fdc0f
+	{
6fdc0f
+	  /* Convert little endian to machine endian WRT size of variable.  */
6fdc0f
+	  if (WORDS_BIG_ENDIAN)
6fdc0f
+	    offset = ((var_size - 1 - offset_le)
6fdc0f
+		      / UNITS_PER_WORD) * UNITS_PER_WORD;
6fdc0f
+	  else
6fdc0f
+	    offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
6fdc0f
+
6fdc0f
+	  if (BYTES_BIG_ENDIAN)
6fdc0f
+	    offset += ((var_size - 1 - offset_le)
6fdc0f
+		       % UNITS_PER_WORD);
6fdc0f
+	  else
6fdc0f
+	    offset += offset_le % UNITS_PER_WORD;
6fdc0f
+	}
6fdc0f
+    }
6fdc0f
+
6fdc0f
   REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
6fdc0f
 				   REG_OFFSET (reg) + offset);
6fdc0f
   return new;