OSDN Git Service

* reload.c (find_reloads_address_1): Handle PLUS expressions resulting
[pf3gnuchains/gcc-fork.git] / gcc / reload1.c
index e644322..c469fb0 100644 (file)
@@ -1,7 +1,7 @@
 /* Reload pseudo regs into hard regs for insns that require hard regs.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-   Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -888,16 +888,8 @@ reload (rtx first, int global)
     {
       int something_changed;
       int did_spill;
-
       HOST_WIDE_INT starting_frame_size;
 
-      /* Round size of stack frame to stack_alignment_needed.  This must be done
-        here because the stack size may be a part of the offset computation
-        for register elimination, and there might have been new stack slots
-        created in the last iteration of this loop.  */
-      if (cfun->stack_alignment_needed)
-        assign_stack_local (BLKmode, 0, cfun->stack_alignment_needed);
-
       starting_frame_size = get_frame_size ();
 
       set_initial_elim_offsets ();
@@ -964,6 +956,20 @@ reload (rtx first, int global)
       /* If we allocated another stack slot, redo elimination bookkeeping.  */
       if (starting_frame_size != get_frame_size ())
        continue;
+      if (starting_frame_size && cfun->stack_alignment_needed)
+       {
+         /* If we have a stack frame, we must align it now.  The
+            stack size may be a part of the offset computation for
+            register elimination.  So if this changes the stack size,
+            then repeat the elimination bookkeeping.  We don't
+            realign when there is no stack, as that will cause a
+            stack frame when none is needed should
+            STARTING_FRAME_OFFSET not be already aligned to
+            STACK_BOUNDARY.  */
+         assign_stack_local (BLKmode, 0, cfun->stack_alignment_needed);
+         if (starting_frame_size != get_frame_size ())
+           continue;
+       }
 
       if (caller_save_needed)
        {
@@ -2542,6 +2548,30 @@ eliminate_regs_1 (rtx x, enum machine_mode mem_mode, rtx insn,
     case POST_INC:
     case PRE_DEC:
     case POST_DEC:
+      /* We do not support elimination of a register that is modified.
+        elimination_effects has already make sure that this does not
+        happen.  */
+      return x;
+
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      /* We do not support elimination of a register that is modified.
+        elimination_effects has already make sure that this does not
+        happen.  The only remaining case we need to consider here is
+        that the increment value may be an eliminable register.  */
+      if (GET_CODE (XEXP (x, 1)) == PLUS
+         && XEXP (XEXP (x, 1), 0) == XEXP (x, 0))
+       {
+         rtx new = eliminate_regs_1 (XEXP (XEXP (x, 1), 1), mem_mode,
+                                     insn, true);
+
+         if (new != XEXP (XEXP (x, 1), 1))
+           return gen_rtx_fmt_ee (code, GET_MODE (x), XEXP (x, 0),
+                                  gen_rtx_PLUS (GET_MODE (x),
+                                                XEXP (x, 0), new));
+       }
+      return x;
+
     case STRICT_LOW_PART:
     case NEG:          case NOT:
     case SIGN_EXTEND:  case ZERO_EXTEND:
@@ -2737,6 +2767,14 @@ elimination_effects (rtx x, enum machine_mode mem_mode)
     case POST_DEC:
     case POST_MODIFY:
     case PRE_MODIFY:
+      /* If we modify the source of an elimination rule, disable it.  */
+      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+       if (ep->from_rtx == XEXP (x, 0))
+         ep->can_eliminate = 0;
+
+      /* If we modify the target of an elimination rule by adding a constant,
+        update its offset.  If we modify the target in any other way, we'll
+        have to disable the rule as well.  */
       for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
        if (ep->to_rtx == XEXP (x, 0))
          {
@@ -2751,11 +2789,15 @@ elimination_effects (rtx x, enum machine_mode mem_mode)
              ep->offset += size;
            else if (code == PRE_INC || code == POST_INC)
              ep->offset -= size;
-           else if ((code == PRE_MODIFY || code == POST_MODIFY)
-                    && GET_CODE (XEXP (x, 1)) == PLUS
-                    && XEXP (x, 0) == XEXP (XEXP (x, 1), 0)
-                    && CONSTANT_P (XEXP (XEXP (x, 1), 1)))
-             ep->offset -= INTVAL (XEXP (XEXP (x, 1), 1));
+           else if (code == PRE_MODIFY || code == POST_MODIFY)
+             {
+               if (GET_CODE (XEXP (x, 1)) == PLUS
+                   && XEXP (x, 0) == XEXP (XEXP (x, 1), 0)
+                   && CONST_INT_P (XEXP (XEXP (x, 1), 1)))
+                 ep->offset -= INTVAL (XEXP (XEXP (x, 1), 1));
+               else
+                 ep->can_eliminate = 0;
+             }
          }
 
       /* These two aren't unary operators.  */
@@ -3094,39 +3136,20 @@ eliminate_regs_in_insn (rtx insn, int replace)
          {
            rtx to_rtx = ep->to_rtx;
            offset += ep->offset;
+           offset = trunc_int_for_mode (offset, GET_MODE (reg));
 
            if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG)
              to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)),
                                    to_rtx);
-           if (offset == 0)
-             {
-               int num_clobbers;
-               /* We assume here that if we need a PARALLEL with
-                  CLOBBERs for this assignment, we can do with the
-                  MATCH_SCRATCHes that add_clobbers allocates.
-                  There's not much we can do if that doesn't work.  */
-               PATTERN (insn) = gen_rtx_SET (VOIDmode,
-                                             SET_DEST (old_set),
-                                             to_rtx);
-               num_clobbers = 0;
-               INSN_CODE (insn) = recog (PATTERN (insn), insn, &num_clobbers);
-               if (num_clobbers)
-                 {
-                   rtvec vec = rtvec_alloc (num_clobbers + 1);
-
-                   vec->elem[0] = PATTERN (insn);
-                   PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, vec);
-                   add_clobbers (PATTERN (insn), INSN_CODE (insn));
-                 }
-               gcc_assert (INSN_CODE (insn) >= 0);
-             }
            /* If we have a nonzero offset, and the source is already
               a simple REG, the following transformation would
               increase the cost of the insn by replacing a simple REG
               with (plus (reg sp) CST).  So try only when we already
               had a PLUS before.  */
-           else if (plus_src)
+           if (offset == 0 || plus_src)
              {
+               rtx new_src = plus_constant (to_rtx, offset);
+
                new_body = old_body;
                if (! replace)
                  {
@@ -3137,8 +3160,20 @@ eliminate_regs_in_insn (rtx insn, int replace)
                PATTERN (insn) = new_body;
                old_set = single_set (insn);
 
-               XEXP (SET_SRC (old_set), 0) = to_rtx;
-               XEXP (SET_SRC (old_set), 1) = GEN_INT (offset);
+               /* First see if this insn remains valid when we make the
+                  change.  If not, try to replace the whole pattern with
+                  a simple set (this may help if the original insn was a
+                  PARALLEL that was only recognized as single_set due to 
+                  REG_UNUSED notes).  If this isn't valid either, keep
+                  the INSN_CODE the same and let reload fix it up.  */
+               if (!validate_change (insn, &SET_SRC (old_set), new_src, 0))
+                 {
+                   rtx new_pat = gen_rtx_SET (VOIDmode,
+                                              SET_DEST (old_set), new_src);
+
+                   if (!validate_change (insn, &PATTERN (insn), new_pat, 0))
+                     SET_SRC (old_set) = new_src;
+                 }
              }
            else
              break;
@@ -4794,6 +4829,51 @@ reload_reg_reaches_end_p (unsigned int regno, int opnum, enum reload_type type)
     }
 }
 \f
+
+/*  Returns whether R1 and R2 are uniquely chained: the value of one
+    is used by the other, and that value is not used by any other
+    reload for this insn.  This is used to partially undo the decision
+    made in find_reloads when in the case of multiple
+    RELOAD_FOR_OPERAND_ADDRESS reloads it converts all
+    RELOAD_FOR_OPADDR_ADDR reloads into RELOAD_FOR_OPERAND_ADDRESS
+    reloads.  This code tries to avoid the conflict created by that
+    change.  It might be cleaner to explicitly keep track of which
+    RELOAD_FOR_OPADDR_ADDR reload is associated with which
+    RELOAD_FOR_OPERAND_ADDRESS reload, rather than to try to detect
+    this after the fact. */
+static bool
+reloads_unique_chain_p (int r1, int r2)
+{
+  int i;
+
+  /* We only check input reloads.  */
+  if (! rld[r1].in || ! rld[r2].in)
+    return false;
+
+  /* Avoid anything with output reloads.  */
+  if (rld[r1].out || rld[r2].out)
+    return false;
+
+  /* "chained" means one reload is a component of the other reload,
+     not the same as the other reload.  */
+  if (rld[r1].opnum != rld[r2].opnum
+      || rtx_equal_p (rld[r1].in, rld[r2].in)
+      || rld[r1].optional || rld[r2].optional
+      || ! (reg_mentioned_p (rld[r1].in, rld[r2].in)
+           || reg_mentioned_p (rld[r2].in, rld[r1].in)))
+    return false;
+
+  for (i = 0; i < n_reloads; i ++)
+    /* Look for input reloads that aren't our two */
+    if (i != r1 && i != r2 && rld[i].in)
+      {
+       /* If our reload is mentioned at all, it isn't a simple chain.  */
+       if (reg_mentioned_p (rld[r1].in, rld[i].in))
+         return false;
+      }
+  return true;
+}
+
 /* Return 1 if the reloads denoted by R1 and R2 cannot share a register.
    Return 0 otherwise.
 
@@ -4842,7 +4922,8 @@ reloads_conflict (int r1, int r2)
 
     case RELOAD_FOR_OPERAND_ADDRESS:
       return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN
-             || r2_type == RELOAD_FOR_OPERAND_ADDRESS);
+             || (r2_type == RELOAD_FOR_OPERAND_ADDRESS
+                 && !reloads_unique_chain_p (r1, r2)));
 
     case RELOAD_FOR_OPADDR_ADDR:
       return (r2_type == RELOAD_FOR_INPUT
@@ -5576,10 +5657,11 @@ choose_reload_regs (struct insn_chain *chain)
              else if (GET_CODE (rld[r].in_reg) == SUBREG
                       && REG_P (SUBREG_REG (rld[r].in_reg)))
                {
-                 byte = SUBREG_BYTE (rld[r].in_reg);
                  regno = REGNO (SUBREG_REG (rld[r].in_reg));
                  if (regno < FIRST_PSEUDO_REGISTER)
                    regno = subreg_regno (rld[r].in_reg);
+                 else
+                   byte = SUBREG_BYTE (rld[r].in_reg);
                  mode = GET_MODE (rld[r].in_reg);
                }
 #ifdef AUTO_INC_DEC
@@ -5600,7 +5682,16 @@ choose_reload_regs (struct insn_chain *chain)
                regno = subreg_regno (rld[r].in);
 #endif
 
-             if (regno >= 0 && reg_last_reload_reg[regno] != 0)
+             if (regno >= 0
+                 && reg_last_reload_reg[regno] != 0
+#ifdef CANNOT_CHANGE_MODE_CLASS
+                 /* Verify that the register it's in can be used in
+                    mode MODE.  */
+                 && !REG_CANNOT_CHANGE_MODE_P (REGNO (reg_last_reload_reg[regno]),
+                                               GET_MODE (reg_last_reload_reg[regno]),
+                                               mode)
+#endif
+                 )
                {
                  enum reg_class class = rld[r].class, last_class;
                  rtx last_reg = reg_last_reload_reg[regno];
@@ -5620,13 +5711,6 @@ choose_reload_regs (struct insn_chain *chain)
 
                  if ((GET_MODE_SIZE (GET_MODE (last_reg))
                       >= GET_MODE_SIZE (need_mode))
-#ifdef CANNOT_CHANGE_MODE_CLASS
-                     /* Verify that the register in "i" can be obtained
-                        from LAST_REG.  */
-                     && !REG_CANNOT_CHANGE_MODE_P (REGNO (last_reg),
-                                                   GET_MODE (last_reg),
-                                                   mode)
-#endif
                      && reg_reloaded_contents[i] == regno
                      && TEST_HARD_REG_BIT (reg_reloaded_valid, i)
                      && HARD_REGNO_MODE_OK (i, rld[r].mode)
@@ -7508,6 +7592,23 @@ emit_reload_insns (struct insn_chain *chain)
          rtx out = ((rld[r].out && REG_P (rld[r].out))
                     ? rld[r].out : rld[r].out_reg);
          int nregno = REGNO (out);
+
+         /* REG_RTX is now set or clobbered by the main instruction.
+            As the comment above explains, forget_old_reloads_1 only
+            sees the original instruction, and there is no guarantee
+            that the original instruction also clobbered REG_RTX.
+            For example, if find_reloads sees that the input side of
+            a matched operand pair dies in this instruction, it may
+            use the input register as the reload register.
+
+            Calling forget_old_reloads_1 is a waste of effort if
+            REG_RTX is also the output register.
+
+            If we know that REG_RTX holds the value of a pseudo
+            register, the code after the call will record that fact.  */
+         if (rld[r].reg_rtx && rld[r].reg_rtx != out)
+           forget_old_reloads_1 (rld[r].reg_rtx, NULL_RTX, NULL);
+
          if (nregno >= FIRST_PSEUDO_REGISTER)
            {
              rtx src_reg, store_insn = NULL_RTX;
@@ -7759,8 +7860,7 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
       if (insn)
        {
          /* Add a REG_EQUIV note so that find_equiv_reg can find it.  */
-         REG_NOTES (insn)
-           = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+         set_unique_reg_note (insn, REG_EQUIV, in);
          return insn;
        }
 
@@ -7769,7 +7869,7 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
 
       gen_reload (out, op1, opnum, type);
       insn = emit_insn (gen_add2_insn (out, op0));
-      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+      set_unique_reg_note (insn, REG_EQUIV, in);
     }
 
 #ifdef SECONDARY_MEMORY_NEEDED
@@ -7829,8 +7929,7 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
       insn = emit_insn_if_valid_for_reload (insn);
       if (insn)
        {
-         REG_NOTES (insn)
-           = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+         set_unique_reg_note (insn, REG_EQUIV, in);
          return insn;
        }
 
@@ -7926,6 +8025,9 @@ delete_output_reload (rtx insn, int j, int last_reload_reg)
        }
     }
   n_occurrences = count_occurrences (PATTERN (insn), reg, 0);
+  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
+    n_occurrences += count_occurrences (CALL_INSN_FUNCTION_USAGE (insn),
+                                       reg, 0);
   if (substed)
     n_occurrences += count_occurrences (PATTERN (insn),
                                        eliminate_regs (substed, 0,