OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / combine-stack-adj.c
index d267b70..273252e 100644 (file)
@@ -1,7 +1,7 @@
 /* Combine stack adjustments.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
    1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010 Free Software Foundation, Inc.
+   2010, 2012 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -296,68 +296,130 @@ record_stack_refs (rtx *xp, void *data)
   return 0;
 }
 
-/* Adjust or create REG_FRAME_RELATED_EXPR note when merging a stack
-   adjustment into a frame related insn.  */
+/* If INSN has a REG_ARGS_SIZE note, move it to LAST.
+   AFTER is true iff LAST follows INSN in the instruction stream.  */
 
 static void
-adjust_frame_related_expr (rtx last_sp_set, rtx insn,
-                          HOST_WIDE_INT this_adjust)
+maybe_move_args_size_note (rtx last, rtx insn, bool after)
 {
-  rtx note = find_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, NULL_RTX);
-  rtx new_expr = NULL_RTX;
+  rtx note, last_note;
 
-  if (note == NULL_RTX && RTX_FRAME_RELATED_P (insn))
+  note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+  if (note == NULL)
     return;
 
-  if (note
-      && GET_CODE (XEXP (note, 0)) == SEQUENCE
-      && XVECLEN (XEXP (note, 0), 0) >= 2)
+  last_note = find_reg_note (last, REG_ARGS_SIZE, NULL_RTX);
+  if (last_note)
     {
-      rtx expr = XEXP (note, 0);
-      rtx last = XVECEXP (expr, 0, XVECLEN (expr, 0) - 1);
-      int i;
-
-      if (GET_CODE (last) == SET
-         && RTX_FRAME_RELATED_P (last) == RTX_FRAME_RELATED_P (insn)
-         && SET_DEST (last) == stack_pointer_rtx
-         && GET_CODE (SET_SRC (last)) == PLUS
-         && XEXP (SET_SRC (last), 0) == stack_pointer_rtx
-         && CONST_INT_P (XEXP (SET_SRC (last), 1)))
-       {
-         XEXP (SET_SRC (last), 1)
-           = GEN_INT (INTVAL (XEXP (SET_SRC (last), 1)) + this_adjust);
-         return;
-       }
-
-      new_expr = gen_rtx_SEQUENCE (VOIDmode,
-                                  rtvec_alloc (XVECLEN (expr, 0) + 1));
-      for (i = 0; i < XVECLEN (expr, 0); i++)
-       XVECEXP (new_expr, 0, i) = XVECEXP (expr, 0, i);
+      /* The ARGS_SIZE notes are *not* cumulative.  They represent an
+        absolute value, and the "most recent" note wins.  */
+      if (!after)
+        XEXP (last_note, 0) = XEXP (note, 0);
     }
   else
+    add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0));
+}
+
+/* Return the next (or previous) active insn within BB.  */
+
+static rtx
+prev_active_insn_bb (basic_block bb, rtx insn)
+{
+  for (insn = PREV_INSN (insn);
+       insn != PREV_INSN (BB_HEAD (bb));
+       insn = PREV_INSN (insn))
+    if (active_insn_p (insn))
+      return insn;
+  return NULL_RTX;
+}
+
+static rtx
+next_active_insn_bb (basic_block bb, rtx insn)
+{
+  for (insn = NEXT_INSN (insn);
+       insn != NEXT_INSN (BB_END (bb));
+       insn = NEXT_INSN (insn))
+    if (active_insn_p (insn))
+      return insn;
+  return NULL_RTX;
+}
+
+/* If INSN has a REG_ARGS_SIZE note, if possible move it to PREV.  Otherwise
+   search for a nearby candidate within BB where we can stick the note.  */
+
+static void
+force_move_args_size_note (basic_block bb, rtx prev, rtx insn)
+{
+  rtx note, test, next_candidate, prev_candidate;
+
+  /* If PREV exists, tail-call to the logic in the other function.  */
+  if (prev)
     {
-      new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
-      if (note)
-       XVECEXP (new_expr, 0, 0) = XEXP (note, 0);
-      else
-       {
-         rtx expr = copy_rtx (single_set_for_csa (last_sp_set));
+      maybe_move_args_size_note (prev, insn, false);
+      return;
+    }
 
-         XEXP (SET_SRC (expr), 1)
-           = GEN_INT (INTVAL (XEXP (SET_SRC (expr), 1)) - this_adjust);
-         RTX_FRAME_RELATED_P (expr) = 1;
-         XVECEXP (new_expr, 0, 0) = expr;
+  /* First, make sure there's anything that needs doing.  */
+  note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+  if (note == NULL)
+    return;
+
+  /* We need to find a spot between the previous and next exception points
+     where we can place the note and "properly" deallocate the arguments.  */
+  next_candidate = prev_candidate = NULL;
+
+  /* It is often the case that we have insns in the order:
+       call
+       add sp (previous deallocation)
+       sub sp (align for next arglist)
+       push arg
+     and the add/sub cancel.  Therefore we begin by searching forward.  */
+
+  test = insn;
+  while ((test = next_active_insn_bb (bb, test)) != NULL)
+    {
+      /* Found an existing note: nothing to do.  */
+      if (find_reg_note (test, REG_ARGS_SIZE, NULL_RTX))
+        return;
+      /* Found something that affects unwinding.  Stop searching.  */
+      if (CALL_P (test) || !insn_nothrow_p (test))
+       break;
+      if (next_candidate == NULL)
+       next_candidate = test;
+    }
+
+  test = insn;
+  while ((test = prev_active_insn_bb (bb, test)) != NULL)
+    {
+      rtx tnote;
+      /* Found a place that seems logical to adjust the stack.  */
+      tnote = find_reg_note (test, REG_ARGS_SIZE, NULL_RTX);
+      if (tnote)
+       {
+         XEXP (tnote, 0) = XEXP (note, 0);
+         return;
        }
+      if (prev_candidate == NULL)
+       prev_candidate = test;
+      /* Found something that affects unwinding.  Stop searching.  */
+      if (CALL_P (test) || !insn_nothrow_p (test))
+       break;
     }
 
-  XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1)
-    = copy_rtx (single_set_for_csa (insn));
-  RTX_FRAME_RELATED_P (XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1))
-    = RTX_FRAME_RELATED_P (insn);
-  if (note)
-    XEXP (note, 0) = new_expr;
+  if (prev_candidate)
+    test = prev_candidate;
+  else if (next_candidate)
+    test = next_candidate;
   else
-    add_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, new_expr);
+    {
+      /* ??? We *must* have a place, lest we ICE on the lost adjustment.
+        Options are: dummy clobber insn, nop, or prevent the removal of
+        the sp += 0 insn.  */
+      /* TODO: Find another way to indicate to the dwarf2 code that we
+        have not in fact lost an adjustment.  */
+      test = emit_insn_before (gen_rtx_CLOBBER (VOIDmode, const0_rtx), insn);
+    }
+  add_reg_note (test, REG_ARGS_SIZE, XEXP (note, 0));
 }
 
 /* Subroutine of combine_stack_adjustments, called for each basic block.  */
@@ -367,6 +429,7 @@ combine_stack_adjustments_for_block (basic_block bb)
 {
   HOST_WIDE_INT last_sp_adjust = 0;
   rtx last_sp_set = NULL_RTX;
+  rtx last2_sp_set = NULL_RTX;
   struct csa_reflist *reflist = NULL;
   rtx insn, next, set;
   struct record_stack_refs_data data;
@@ -431,10 +494,8 @@ combine_stack_adjustments_for_block (basic_block bb)
                                                  last_sp_adjust + this_adjust,
                                                  this_adjust))
                    {
-                     if (RTX_FRAME_RELATED_P (last_sp_set))
-                       adjust_frame_related_expr (last_sp_set, insn,
-                                                  this_adjust);
                      /* It worked!  */
+                     maybe_move_args_size_note (last_sp_set, insn, false);
                      delete_insn (insn);
                      last_sp_adjust += this_adjust;
                      continue;
@@ -451,6 +512,7 @@ combine_stack_adjustments_for_block (basic_block bb)
                                                  -last_sp_adjust))
                    {
                      /* It worked!  */
+                     maybe_move_args_size_note (insn, last_sp_set, true);
                      delete_insn (last_sp_set);
                      last_sp_set = insn;
                      last_sp_adjust += this_adjust;
@@ -463,8 +525,16 @@ combine_stack_adjustments_for_block (basic_block bb)
              /* Combination failed.  Restart processing from here.  If
                 deallocation+allocation conspired to cancel, we can
                 delete the old deallocation insn.  */
-             if (last_sp_set && last_sp_adjust == 0)
-               delete_insn (last_sp_set);
+             if (last_sp_set)
+               {
+                 if (last_sp_adjust == 0)
+                   {
+                     maybe_move_args_size_note (insn, last_sp_set, true);
+                     delete_insn (last_sp_set);
+                   }
+                 else
+                   last2_sp_set = last_sp_set;
+               }
              free_csa_reflist (reflist);
              reflist = NULL;
              last_sp_set = insn;
@@ -500,6 +570,10 @@ combine_stack_adjustments_for_block (basic_block bb)
              && try_apply_stack_adjustment (insn, reflist, 0,
                                             -last_sp_adjust))
            {
+             if (last2_sp_set)
+               maybe_move_args_size_note (last2_sp_set, last_sp_set, false);
+             else
+               maybe_move_args_size_note (insn, last_sp_set, true);
              delete_insn (last_sp_set);
              free_csa_reflist (reflist);
              reflist = NULL;
@@ -526,16 +600,23 @@ combine_stack_adjustments_for_block (basic_block bb)
              || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn))))
        {
          if (last_sp_set && last_sp_adjust == 0)
-           delete_insn (last_sp_set);
+           {
+             force_move_args_size_note (bb, last2_sp_set, last_sp_set);
+             delete_insn (last_sp_set);
+           }
          free_csa_reflist (reflist);
          reflist = NULL;
+         last2_sp_set = NULL_RTX;
          last_sp_set = NULL_RTX;
          last_sp_adjust = 0;
        }
     }
 
   if (last_sp_set && last_sp_adjust == 0)
-    delete_insn (last_sp_set);
+    {
+      force_move_args_size_note (bb, last2_sp_set, last_sp_set);
+      delete_insn (last_sp_set);
+    }
 
   if (reflist)
     free_csa_reflist (reflist);