OSDN Git Service

* tree-vect-transform.c (vect_update_inits_of_drs): Use
[pf3gnuchains/gcc-fork.git] / gcc / reload1.c
index 4543835..4d2dea5 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)
        {
@@ -1167,20 +1173,6 @@ reload (rtx first, int global)
       {
        rtx *pnote;
 
-       /* Clean up invalid ASMs so that they don't confuse later passes.
-          See PR 21299.  */
-       if (asm_noperands (PATTERN (insn)) >= 0)
-         {
-           extract_insn (insn);
-           if (!constrain_operands (1))
-             {
-               error_for_asm (insn,
-                              "%<asm%> operand has impossible constraints");
-               delete_insn (insn);
-               continue;
-             }
-         }
-
        if (CALL_P (insn))
          replace_pseudos_in (& CALL_INSN_FUNCTION_USAGE (insn),
                              VOIDmode, CALL_INSN_FUNCTION_USAGE (insn));
@@ -1239,8 +1231,22 @@ reload (rtx first, int global)
        add_auto_inc_notes (insn, PATTERN (insn));
 #endif
 
-       /* And simplify (subreg (reg)) if it appears as an operand.  */
+       /* Simplify (subreg (reg)) if it appears as an operand.  */
        cleanup_subreg_operands (insn);
+
+       /* Clean up invalid ASMs so that they don't confuse later passes.
+          See PR 21299.  */
+       if (asm_noperands (PATTERN (insn)) >= 0)
+         {
+           extract_insn (insn);
+           if (!constrain_operands (1))
+             {
+               error_for_asm (insn,
+                              "%<asm%> operand has impossible constraints");
+               delete_insn (insn);
+               continue;
+             }
+         }
       }
 
   /* If we are doing stack checking, give a warning if this function's
@@ -1359,7 +1365,7 @@ maybe_fix_stack_asms (void)
 
       /* Get the operand values and constraints out of the insn.  */
       decode_asm_operands (pat, recog_data.operand, recog_data.operand_loc,
-                          constraints, operand_mode);
+                          constraints, operand_mode, NULL);
 
       /* For every operand, see what registers are allowed.  */
       for (i = 0; i < noperands; i++)
@@ -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.  */
@@ -3059,7 +3101,8 @@ eliminate_regs_in_insn (rtx insn, int replace)
          rtx links;
          for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
            {
-             if (REG_NOTE_KIND (links) == REG_EQUAL
+             if ((REG_NOTE_KIND (links) == REG_EQUAL
+                  || REG_NOTE_KIND (links) == REG_EQUIV)
                  && GET_CODE (XEXP (links, 0)) == PLUS
                  && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT)
                {
@@ -3094,39 +3137,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 +3161,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;
@@ -3571,6 +3607,20 @@ update_eliminables (HARD_REG_SET *pset)
     SET_HARD_REG_BIT (*pset, HARD_FRAME_POINTER_REGNUM);
 }
 
+/* Return true if X is used as the target register of an elimination.  */
+
+bool
+elimination_target_reg_p (rtx x)
+{
+  struct elim_table *ep;
+
+  for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+    if (ep->to_rtx == x && ep->can_eliminate)
+      return true;
+
+  return false;
+}
+
 /* Initialize the table of registers to eliminate.  */
 
 static void
@@ -5647,7 +5697,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];
@@ -5667,13 +5726,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)
@@ -6285,15 +6337,23 @@ merge_assigned_reloads (rtx insn)
                transfer_replacements (i, j);
              }
 
-         /* If this is now RELOAD_OTHER, look for any reloads that load
-            parts of this operand and set them to RELOAD_FOR_OTHER_ADDRESS
-            if they were for inputs, RELOAD_OTHER for outputs.  Note that
-            this test is equivalent to looking for reloads for this operand
-            number.  */
-         /* We must take special care with RELOAD_FOR_OUTPUT_ADDRESS; it may
-            share registers with a RELOAD_FOR_INPUT, so we can not change it
-            to RELOAD_FOR_OTHER_ADDRESS.  We should never need to, since we
-            do not modify RELOAD_FOR_OUTPUT.  */
+         /* If this is now RELOAD_OTHER, look for any reloads that
+            load parts of this operand and set them to
+            RELOAD_FOR_OTHER_ADDRESS if they were for inputs,
+            RELOAD_OTHER for outputs.  Note that this test is
+            equivalent to looking for reloads for this operand
+            number.
+
+            We must take special care with RELOAD_FOR_OUTPUT_ADDRESS;
+            it may share registers with a RELOAD_FOR_INPUT, so we can
+            not change it to RELOAD_FOR_OTHER_ADDRESS.  We should
+            never need to, since we do not modify RELOAD_FOR_OUTPUT.
+
+            It is possible that the RELOAD_FOR_OPERAND_ADDRESS
+            instruction is assigned the same register as the earlier
+            RELOAD_FOR_OTHER_ADDRESS instruction.  Merging these two
+            instructions will cause the RELOAD_FOR_OTHER_ADDRESS
+            instruction to be deleted later on.  */
 
          if (rld[i].when_needed == RELOAD_OTHER)
            for (j = 0; j < n_reloads; j++)
@@ -6301,6 +6361,7 @@ merge_assigned_reloads (rtx insn)
                  && rld[j].when_needed != RELOAD_OTHER
                  && rld[j].when_needed != RELOAD_FOR_OTHER_ADDRESS
                  && rld[j].when_needed != RELOAD_FOR_OUTPUT_ADDRESS
+                 && rld[j].when_needed != RELOAD_FOR_OPERAND_ADDRESS
                  && (! conflicting_input
                      || rld[j].when_needed == RELOAD_FOR_INPUT_ADDRESS
                      || rld[j].when_needed == RELOAD_FOR_INPADDR_ADDRESS)
@@ -7119,10 +7180,6 @@ do_input_reload (struct insn_chain *chain, struct reload *rl, int j)
      actually no need to store the old value in it.  */
 
   if (optimize
-      /* Only attempt this for input reloads; for RELOAD_OTHER we miss
-        that there may be multiple uses of the previous output reload.
-        Restricting to RELOAD_FOR_INPUT is mostly paranoia.  */
-      && rl->when_needed == RELOAD_FOR_INPUT
       && (reload_inherited[j] || reload_override_in[j])
       && rl->reg_rtx
       && REG_P (rl->reg_rtx)
@@ -7555,6 +7612,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;
@@ -7806,17 +7880,17 @@ 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;
        }
 
       /* If that failed, copy the address register to the reload register.
         Then add the constant to the reload register.  */
 
+      gcc_assert (!reg_overlap_mentioned_p (out, op0));
       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
@@ -7876,8 +7950,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;
        }
 
@@ -7958,16 +8031,7 @@ delete_output_reload (rtx insn, int j, int last_reload_reg)
       if (rtx_equal_p (reg2, reg))
        {
          if (reload_inherited[k] || reload_override_in[k] || k == j)
-           {
-             n_inherited++;
-             reg2 = rld[k].out_reg;
-             if (! reg2)
-               continue;
-             while (GET_CODE (reg2) == SUBREG)
-               reg2 = XEXP (reg2, 0);
-             if (rtx_equal_p (reg2, reg))
-               n_inherited++;
-           }
+           n_inherited++;
          else
            return;
        }