Blame SOURCES/gcc34-pr14084.patch

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