OSDN Git Service

fix bootstrap build breakage
[pf3gnuchains/gcc-fork.git] / gcc / emit-rtl.c
index 9a5533c..1a6667a 100644 (file)
@@ -97,8 +97,6 @@ rtx global_rtl[GR_MAX];
    at the beginning of each function.  */
 static GTY(()) rtx static_regno_reg_rtx[FIRST_PSEUDO_REGISTER];
 
-rtx (*gen_lowpart) (enum machine_mode mode, rtx x) = gen_lowpart_general;
-
 /* We record floating-point CONST_DOUBLEs in each floating-point mode for
    the values of 0, 1, and 2.  For the integer entries and VOIDmode, we
    record a copy of const[012]_rtx.  */
@@ -748,13 +746,96 @@ gen_reg_rtx (enum machine_mode mode)
   return val;
 }
 
-/* Generate a register with same attributes as REG,
-   but offsetted by OFFSET.  */
+/* Generate a register with same attributes as REG, but offsetted by OFFSET.
+   Do the big endian correction if needed.  */
 
 rtx
 gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno, int offset)
 {
   rtx new = gen_rtx_REG (mode, regno);
+  tree decl;
+  HOST_WIDE_INT var_size;
+
+  /* PR middle-end/14084
+     The problem appears when a variable is stored in a larger register
+     and later it is used in the original mode or some mode in between
+     or some part of variable is accessed.
+
+     On little endian machines there is no problem because
+     the REG_OFFSET of the start of the variable is the same when
+     accessed in any mode (it is 0).
+
+     However, this is not true on big endian machines.
+     The offset of the start of the variable is different when accessed
+     in different modes.
+     When we are taking a part of the REG we have to change the OFFSET
+     from offset WRT size of mode of REG to offset WRT size of variable.
+
+     If we would not do the big endian correction the resulting REG_OFFSET
+     would be larger than the size of the DECL.
+
+     Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
+
+     REG.mode  MODE  DECL size  old offset  new offset  description
+     DI        SI    4          4           0           int32 in SImode
+     DI        SI    1          4           0           char in SImode
+     DI        QI    1          7           0           char in QImode
+     DI        QI    4          5           1           1st element in QImode
+                                                        of char[4]
+     DI        HI    4          6           2           1st element in HImode
+                                                        of int16[2]
+
+     If the size of DECL is equal or greater than the size of REG
+     we can't do this correction because the register holds the
+     whole variable or a part of the variable and thus the REG_OFFSET
+     is already correct.  */
+
+  decl = REG_EXPR (reg);
+  if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
+      && decl != NULL
+      && offset > 0
+      && GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (mode)
+      && ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
+         && var_size < GET_MODE_SIZE (GET_MODE (reg))))
+    {
+      int offset_le;
+
+      /* Convert machine endian to little endian WRT size of mode of REG.  */
+      if (WORDS_BIG_ENDIAN)
+       offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
+                    / UNITS_PER_WORD) * UNITS_PER_WORD;
+      else
+       offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
+
+      if (BYTES_BIG_ENDIAN)
+       offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
+                     % UNITS_PER_WORD);
+      else
+       offset_le += offset % UNITS_PER_WORD;
+
+      if (offset_le >= var_size)
+       {
+         /* MODE is wider than the variable so the new reg will cover
+            the whole variable so the resulting OFFSET should be 0.  */
+         offset = 0;
+       }
+      else
+       {
+         /* Convert little endian to machine endian WRT size of variable.  */
+         if (WORDS_BIG_ENDIAN)
+           offset = ((var_size - 1 - offset_le)
+                     / UNITS_PER_WORD) * UNITS_PER_WORD;
+         else
+           offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
+
+         if (BYTES_BIG_ENDIAN)
+           offset += ((var_size - 1 - offset_le)
+                      % UNITS_PER_WORD);
+         else
+           offset += offset_le % UNITS_PER_WORD;
+       }
+    }
+
   REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
                                   REG_OFFSET (reg) + offset);
   return new;
@@ -776,7 +857,7 @@ set_reg_attrs_from_mem (rtx reg, rtx mem)
 void
 set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
 {
-  if (GET_CODE (parm_rtx) == REG)
+  if (REG_P (parm_rtx))
     set_reg_attrs_from_mem (parm_rtx, mem);
   else if (GET_CODE (parm_rtx) == PARALLEL)
     {
@@ -786,7 +867,7 @@ set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
       for (; i < XVECLEN (parm_rtx, 0); i++)
        {
          rtx x = XVECEXP (parm_rtx, 0, i);
-         if (GET_CODE (XEXP (x, 0)) == REG)
+         if (REG_P (XEXP (x, 0)))
            REG_ATTRS (XEXP (x, 0))
              = get_reg_attrs (MEM_EXPR (mem),
                               INTVAL (XEXP (x, 1)));
@@ -803,7 +884,7 @@ set_decl_rtl (tree t, rtx x)
   if (!x)
     return;
   /* For register, we maintain the reverse information too.  */
-  if (GET_CODE (x) == REG)
+  if (REG_P (x))
     REG_ATTRS (x) = get_reg_attrs (t, 0);
   else if (GET_CODE (x) == SUBREG)
     REG_ATTRS (SUBREG_REG (x))
@@ -837,7 +918,7 @@ set_decl_incoming_rtl (tree t, rtx x)
   if (!x)
     return;
   /* For register, we maintain the reverse information too.  */
-  if (GET_CODE (x) == REG)
+  if (REG_P (x))
     REG_ATTRS (x) = get_reg_attrs (t, 0);
   else if (GET_CODE (x) == SUBREG)
     REG_ATTRS (SUBREG_REG (x))
@@ -880,7 +961,7 @@ mark_user_reg (rtx reg)
       REG_USERVAR_P (XEXP (reg, 0)) = 1;
       REG_USERVAR_P (XEXP (reg, 1)) = 1;
     }
-  else if (GET_CODE (reg) == REG)
+  else if (REG_P (reg))
     REG_USERVAR_P (reg) = 1;
   else
     abort ();
@@ -929,6 +1010,17 @@ get_first_label_num (void)
 {
   return first_label_num;
 }
+
+/* If the rtx for label was created during the expansion of a nested
+   function, then first_label_num won't include this label number.
+   Fix this now so that array indicies work later.  */
+
+void
+maybe_set_first_label_num (rtx x)
+{
+  if (CODE_LABEL_NUMBER (x) < first_label_num)
+    first_label_num = CODE_LABEL_NUMBER (x);
+}
 \f
 /* Return the final regno of X, which is a SUBREG of a hard
    register.  */
@@ -942,7 +1034,7 @@ subreg_hard_regno (rtx x, int check_mode)
   /* This is where we attempt to catch illegal subregs
      created by the compiler.  */
   if (GET_CODE (x) != SUBREG
-      || GET_CODE (reg) != REG)
+      || !REG_P (reg))
     abort ();
   base_regno = REGNO (reg);
   if (base_regno >= FIRST_PSEUDO_REGISTER)
@@ -1029,7 +1121,7 @@ gen_lowpart_common (enum machine_mode mode, rtx x)
       else if (msize < xsize)
        return gen_rtx_fmt_e (GET_CODE (x), mode, XEXP (x, 0));
     }
-  else if (GET_CODE (x) == SUBREG || GET_CODE (x) == REG
+  else if (GET_CODE (x) == SUBREG || REG_P (x)
           || GET_CODE (x) == CONCAT || GET_CODE (x) == CONST_VECTOR
           || GET_CODE (x) == CONST_DOUBLE || GET_CODE (x) == CONST_INT)
     return simplify_gen_subreg (mode, x, innermode, offset);
@@ -1113,62 +1205,6 @@ gen_imagpart (enum machine_mode mode, rtx x)
     return gen_highpart (mode, x);
 }
 \f
-/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a value,
-   return an rtx (MEM, SUBREG, or CONST_INT) that refers to the
-   least-significant part of X.
-   MODE specifies how big a part of X to return;
-   it usually should not be larger than a word.
-   If X is a MEM whose address is a QUEUED, the value may be so also.  */
-
-rtx
-gen_lowpart_general (enum machine_mode mode, rtx x)
-{
-  rtx result = gen_lowpart_common (mode, x);
-
-  if (result)
-    return result;
-  else if (GET_CODE (x) == REG)
-    {
-      /* Must be a hard reg that's not valid in MODE.  */
-      result = gen_lowpart_common (mode, copy_to_reg (x));
-      if (result == 0)
-       abort ();
-      return result;
-    }
-  else if (GET_CODE (x) == MEM)
-    {
-      /* The only additional case we can do is MEM.  */
-      int offset = 0;
-
-      /* The following exposes the use of "x" to CSE.  */
-      if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD
-         && SCALAR_INT_MODE_P (GET_MODE (x))
-         && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                   GET_MODE_BITSIZE (GET_MODE (x)))
-         && ! no_new_pseudos)
-       return gen_lowpart (mode, force_reg (GET_MODE (x), x));
-
-      if (WORDS_BIG_ENDIAN)
-       offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
-                 - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
-
-      if (BYTES_BIG_ENDIAN)
-       /* Adjust the address so that the address-after-the-data
-          is unchanged.  */
-       offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
-                  - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
-
-      return adjust_address (x, mode, offset);
-    }
-  else if (GET_CODE (x) == ADDRESSOF)
-    return gen_lowpart (mode, force_reg (GET_MODE (x), x));
-  else
-    abort ();
-}
-
-/* Like `gen_lowpart', but refer to the most significant part.
-   This is used to access the imaginary part of a complex number.  */
-
 rtx
 gen_highpart (enum machine_mode mode, rtx x)
 {
@@ -1351,7 +1387,7 @@ operand_subword_force (rtx op, unsigned int offset, enum machine_mode mode)
     {
       /* If this is a register which can not be accessed by words, copy it
         to a pseudo register.  */
-      if (GET_CODE (op) == REG)
+      if (REG_P (op))
        op = copy_to_reg (op);
       else
        op = force_reg (mode, op);
@@ -2387,8 +2423,8 @@ copy_most_rtx (rtx orig, rtx may_share)
   RTX_FLAG (copy, in_struct) = RTX_FLAG (orig, in_struct);
   RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil);
   RTX_FLAG (copy, unchanging) = RTX_FLAG (orig, unchanging);
-  RTX_FLAG (copy, integrated) = RTX_FLAG (orig, integrated);
   RTX_FLAG (copy, frame_related) = RTX_FLAG (orig, frame_related);
+  RTX_FLAG (copy, return_val) = RTX_FLAG (orig, return_val);
 
   format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
 
@@ -2748,9 +2784,9 @@ make_safe_from (rtx x, rtx other)
  done:
   if ((GET_CODE (other) == MEM
        && ! CONSTANT_P (x)
-       && GET_CODE (x) != REG
+       && !REG_P (x)
        && GET_CODE (x) != SUBREG)
-      || (GET_CODE (other) == REG
+      || (REG_P (other)
          && (REGNO (other) < FIRST_PSEUDO_REGISTER
              || reg_mentioned_p (other, x))))
     {
@@ -2960,8 +2996,7 @@ next_real_insn (rtx insn)
   while (insn)
     {
       insn = NEXT_INSN (insn);
-      if (insn == 0 || GET_CODE (insn) == INSN
-         || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+      if (insn == 0 || INSN_P (insn))
        break;
     }
 
@@ -2978,8 +3013,7 @@ prev_real_insn (rtx insn)
   while (insn)
     {
       insn = PREV_INSN (insn);
-      if (insn == 0 || GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
-         || GET_CODE (insn) == JUMP_INSN)
+      if (insn == 0 || INSN_P (insn))
        break;
     }
 
@@ -3075,6 +3109,21 @@ prev_label (rtx insn)
 
   return insn;
 }
+
+/* Return the last label to mark the same position as LABEL.  Return null
+   if LABEL itself is null.  */
+
+rtx
+skip_consecutive_labels (rtx label)
+{
+  rtx insn;
+
+  for (insn = label; insn != 0 && !INSN_P (insn); insn = NEXT_INSN (insn))
+    if (LABEL_P (insn))
+      label = insn;
+
+  return label;
+}
 \f
 #ifdef HAVE_cc0
 /* INSN uses CC0 and is being moved into a delay slot.  Set up REG_CC_SETTER