]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/combine-stack-adj.c
PR fortran/95090 - ICE: identifier overflow
[thirdparty/gcc.git] / gcc / combine-stack-adj.c
index ca94515e842ed9547f84acf9fb55ff4649cb0bc0..281166bf6955aa69056b0c3a9126e233b5a5555b 100644 (file)
@@ -1,7 +1,5 @@
 /* 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, 2012 Free Software Foundation, Inc.
+   Copyright (C) 1987-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -43,31 +41,18 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
 #include "rtl.h"
-#include "tm_p.h"
+#include "df.h"
 #include "insn-config.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
 #include "recog.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
-#include "function.h"
-#include "expr.h"
-#include "basic-block.h"
-#include "df.h"
-#include "except.h"
-#include "reload.h"
+#include "cfgrtl.h"
 #include "tree-pass.h"
+#include "rtl-iter.h"
 
 \f
-/* Turn STACK_GROWS_DOWNWARD into a boolean.  */
-#ifdef STACK_GROWS_DOWNWARD
-#undef STACK_GROWS_DOWNWARD
-#define STACK_GROWS_DOWNWARD 1
-#else
-#define STACK_GROWS_DOWNWARD 0
-#endif
-
 /* This structure records two kinds of stack references between stack
    adjusting instructions: stack references in memory addresses for
    regular insns and all stack references for debug insns.  */
@@ -75,19 +60,20 @@ along with GCC; see the file COPYING3.  If not see
 struct csa_reflist
 {
   HOST_WIDE_INT sp_offset;
-  rtx insn, *ref;
+  rtx_insn *insn;
+  rtx *ref;
   struct csa_reflist *next;
 };
 
 static int stack_memref_p (rtx);
-static rtx single_set_for_csa (rtx);
+static rtx single_set_for_csa (rtx_insn *);
 static void free_csa_reflist (struct csa_reflist *);
-static struct csa_reflist *record_one_stack_ref (rtx, rtx *,
+static struct csa_reflist *record_one_stack_ref (rtx_insn *, rtx *,
                                                 struct csa_reflist *);
-static int try_apply_stack_adjustment (rtx, struct csa_reflist *,
-                                      HOST_WIDE_INT, HOST_WIDE_INT);
-static void combine_stack_adjustments_for_block (basic_block);
-static int record_stack_refs (rtx *, void *);
+static bool try_apply_stack_adjustment (rtx_insn *, struct csa_reflist *,
+                                       HOST_WIDE_INT, HOST_WIDE_INT,
+                                       bitmap, rtx_insn *);
+static void combine_stack_adjustments_for_block (basic_block, bitmap);
 
 
 /* Main entry point for stack adjustment combination.  */
@@ -96,9 +82,12 @@ static void
 combine_stack_adjustments (void)
 {
   basic_block bb;
+  bitmap live = BITMAP_ALLOC (&reg_obstack);
+
+  FOR_EACH_BB_FN (bb, cfun)
+    combine_stack_adjustments_for_block (bb, live);
 
-  FOR_EACH_BB (bb)
-    combine_stack_adjustments_for_block (bb);
+  BITMAP_FREE (live);
 }
 
 /* Recognize a MEM of the form (sp) or (plus sp const).  */
@@ -124,7 +113,7 @@ stack_memref_p (rtx x)
    tying fp and sp adjustments.  */
 
 static rtx
-single_set_for_csa (rtx insn)
+single_set_for_csa (rtx_insn *insn)
 {
   int i;
   rtx tmp = single_set (insn);
@@ -173,7 +162,7 @@ free_csa_reflist (struct csa_reflist *reflist)
    predicate stack_memref_p or a REG representing the stack pointer.  */
 
 static struct csa_reflist *
-record_one_stack_ref (rtx insn, rtx *ref, struct csa_reflist *next_reflist)
+record_one_stack_ref (rtx_insn *insn, rtx *ref, struct csa_reflist *next_reflist)
 {
   struct csa_reflist *ml;
 
@@ -191,22 +180,103 @@ record_one_stack_ref (rtx insn, rtx *ref, struct csa_reflist *next_reflist)
   return ml;
 }
 
+/* We only know how to adjust the CFA; no other frame-related changes
+   may appear in any insn to be deleted.  */
+
+static bool
+no_unhandled_cfa (rtx_insn *insn)
+{
+  if (!RTX_FRAME_RELATED_P (insn))
+    return true;
+
+  /* No CFA notes at all is a legacy interpretation like
+     FRAME_RELATED_EXPR, and is context sensitive within
+     the prologue state machine.  We can't handle that here.  */
+  bool has_cfa_adjust = false;
+
+  for (rtx link = REG_NOTES (insn); link; link = XEXP (link, 1))
+    switch (REG_NOTE_KIND (link))
+      {
+      default:
+        break;
+      case REG_CFA_ADJUST_CFA:
+       has_cfa_adjust = true;
+       break;
+
+      case REG_FRAME_RELATED_EXPR:
+      case REG_CFA_DEF_CFA:
+      case REG_CFA_OFFSET:
+      case REG_CFA_REGISTER:
+      case REG_CFA_EXPRESSION:
+      case REG_CFA_RESTORE:
+      case REG_CFA_SET_VDRAP:
+      case REG_CFA_WINDOW_SAVE:
+      case REG_CFA_FLUSH_QUEUE:
+      case REG_CFA_TOGGLE_RA_MANGLE:
+       return false;
+      }
+
+  return has_cfa_adjust;
+}
+
 /* Attempt to apply ADJUST to the stack adjusting insn INSN, as well
    as each of the memories and stack references in REFLIST.  Return true
    on success.  */
 
-static int
-try_apply_stack_adjustment (rtx insn, struct csa_reflist *reflist,
-                           HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta)
+static bool
+try_apply_stack_adjustment (rtx_insn *insn, struct csa_reflist *reflist,
+                           HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta,
+                           bitmap live, rtx_insn *other_insn)
 {
   struct csa_reflist *ml;
   rtx set;
+  bool remove_equal = false;
 
   set = single_set_for_csa (insn);
   if (MEM_P (SET_DEST (set)))
     validate_change (insn, &SET_DEST (set),
                     replace_equiv_address (SET_DEST (set), stack_pointer_rtx),
                     1);
+  else if (REG_P (SET_SRC (set)))
+    {
+      if (other_insn == NULL_RTX || live == NULL)
+       return false;
+      rtx other_set = single_set_for_csa (other_insn);
+      if (SET_DEST (other_set) != stack_pointer_rtx
+         || GET_CODE (SET_SRC (other_set)) != PLUS
+         || XEXP (SET_SRC (other_set), 0) != stack_pointer_rtx
+         || !CONST_INT_P (XEXP (SET_SRC (other_set), 1)))
+       return false;
+      if (PATTERN (other_insn) != other_set)
+       {
+         if (GET_CODE (PATTERN (other_insn)) != PARALLEL)
+           return false;
+         int i;
+         rtx p = PATTERN (other_insn);
+         for (i = 0; i < XVECLEN (p, 0); ++i)
+           {
+             rtx this_rtx = XVECEXP (p, 0, i);
+             if (this_rtx == other_set)
+               continue;
+             if (GET_CODE (this_rtx) != CLOBBER)
+               return false;
+             if (!REG_P (XEXP (this_rtx, 0))
+                 || !HARD_REGISTER_P (XEXP (this_rtx, 0)))
+               return false;
+             unsigned int end_regno = END_REGNO (XEXP (this_rtx, 0));
+             for (unsigned int regno = REGNO (XEXP (this_rtx, 0));
+                  regno < end_regno; ++regno)
+               if (bitmap_bit_p (live, regno))
+                 return false;
+           }
+       }
+      validate_change (insn, &PATTERN (insn), copy_rtx (PATTERN (other_insn)),
+                      1);
+      set = single_set_for_csa (insn);
+      validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust),
+                      1);
+      remove_equal = true;
+    }
   else
     validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1);
 
@@ -232,74 +302,78 @@ try_apply_stack_adjustment (rtx insn, struct csa_reflist *reflist,
       for (ml = reflist; ml ; ml = ml->next)
        ml->sp_offset -= delta;
 
-      return 1;
+      if (remove_equal)
+       remove_reg_equal_equiv_notes (insn);
+      return true;
     }
   else
-    return 0;
+    return false;
 }
 
-/* Called via for_each_rtx and used to record all stack memory and other
-   references in the insn and discard all other stack pointer references.  */
-struct record_stack_refs_data
-{
-  rtx insn;
-  struct csa_reflist *reflist;
-};
+/* For non-debug insns, record all stack memory references in INSN
+   and return true if there were no other (unrecorded) references to the
+   stack pointer.  For debug insns, record all stack references regardless
+   of context and unconditionally return true.  */
 
-static int
-record_stack_refs (rtx *xp, void *data)
+static bool
+record_stack_refs (rtx_insn *insn, struct csa_reflist **reflist)
 {
-  rtx x = *xp;
-  struct record_stack_refs_data *d =
-    (struct record_stack_refs_data *) data;
-  if (!x)
-    return 0;
-  switch (GET_CODE (x))
+  subrtx_ptr_iterator::array_type array;
+  FOR_EACH_SUBRTX_PTR (iter, array, &PATTERN (insn), NONCONST)
     {
-    case MEM:
-      if (!reg_mentioned_p (stack_pointer_rtx, x))
-       return -1;
-      /* We are not able to handle correctly all possible memrefs containing
-         stack pointer, so this check is necessary.  */
-      if (stack_memref_p (x))
-       {
-         d->reflist = record_one_stack_ref (d->insn, xp, d->reflist);
-         return -1;
-       }
-      /* Try harder for DEBUG_INSNs, handle e.g. (mem (mem (sp + 16) + 4).  */
-      return !DEBUG_INSN_P (d->insn);
-    case REG:
-      /* ??? We want be able to handle non-memory stack pointer
-        references later.  For now just discard all insns referring to
-        stack pointer outside mem expressions.  We would probably
-        want to teach validate_replace to simplify expressions first.
-
-        We can't just compare with STACK_POINTER_RTX because the
-        reference to the stack pointer might be in some other mode.
-        In particular, an explicit clobber in an asm statement will
-        result in a QImode clobber.
-
-        In DEBUG_INSNs, we want to replace all occurrences, otherwise
-        they will cause -fcompare-debug failures.  */
-      if (REGNO (x) == STACK_POINTER_REGNUM)
+      rtx *loc = *iter;
+      rtx x = *loc;
+      switch (GET_CODE (x))
        {
-         if (!DEBUG_INSN_P (d->insn))
-           return 1;
-         d->reflist = record_one_stack_ref (d->insn, xp, d->reflist);
-         return -1;
+       case MEM:
+         if (!reg_mentioned_p (stack_pointer_rtx, x))
+           iter.skip_subrtxes ();
+         /* We are not able to handle correctly all possible memrefs
+            containing stack pointer, so this check is necessary.  */
+         else if (stack_memref_p (x))
+           {
+             *reflist = record_one_stack_ref (insn, loc, *reflist);
+             iter.skip_subrtxes ();
+           }
+         /* Try harder for DEBUG_INSNs, handle e.g.
+            (mem (mem (sp + 16) + 4).  */
+         else if (!DEBUG_INSN_P (insn))
+           return false;
+         break;
+
+       case REG:
+         /* ??? We want be able to handle non-memory stack pointer
+            references later.  For now just discard all insns referring to
+            stack pointer outside mem expressions.  We would probably
+            want to teach validate_replace to simplify expressions first.
+
+            We can't just compare with STACK_POINTER_RTX because the
+            reference to the stack pointer might be in some other mode.
+            In particular, an explicit clobber in an asm statement will
+            result in a QImode clobber.
+
+            In DEBUG_INSNs, we want to replace all occurrences, otherwise
+            they will cause -fcompare-debug failures.  */
+         if (REGNO (x) == STACK_POINTER_REGNUM)
+           {
+             if (!DEBUG_INSN_P (insn))
+               return false;
+             *reflist = record_one_stack_ref (insn, loc, *reflist);
+           }
+         break;
+
+       default:
+         break;
        }
-      break;
-    default:
-      break;
     }
-  return 0;
+  return true;
 }
 
 /* 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
-maybe_move_args_size_note (rtx last, rtx insn, bool after)
+maybe_move_args_size_note (rtx_insn *last, rtx_insn *insn, bool after)
 {
   rtx note, last_note;
 
@@ -319,37 +393,76 @@ maybe_move_args_size_note (rtx last, rtx insn, bool after)
     add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0));
 }
 
+/* Merge any REG_CFA_ADJUST_CFA note from SRC into DST.
+   AFTER is true iff DST follows SRC in the instruction stream.  */
+
+static void
+maybe_merge_cfa_adjust (rtx_insn *dst, rtx_insn *src, bool after)
+{
+  rtx snote = NULL, dnote = NULL;
+  rtx sexp, dexp;
+  rtx exp1, exp2;
+
+  if (RTX_FRAME_RELATED_P (src))
+    snote = find_reg_note (src, REG_CFA_ADJUST_CFA, NULL_RTX);
+  if (snote == NULL)
+    return;
+  sexp = XEXP (snote, 0);
+
+  if (RTX_FRAME_RELATED_P (dst))
+    dnote = find_reg_note (dst, REG_CFA_ADJUST_CFA, NULL_RTX);
+  if (dnote == NULL)
+    {
+      add_reg_note (dst, REG_CFA_ADJUST_CFA, sexp);
+      return;
+    }
+  dexp = XEXP (dnote, 0);
+
+  gcc_assert (GET_CODE (sexp) == SET);
+  gcc_assert (GET_CODE (dexp) == SET);
+
+  if (after)
+    exp1 = dexp, exp2 = sexp;
+  else
+    exp1 = sexp, exp2 = dexp;
+
+  SET_SRC (exp1) = simplify_replace_rtx (SET_SRC (exp1), SET_DEST (exp2),
+                                        SET_SRC (exp2));
+  XEXP (dnote, 0) = exp1;
+}
+
 /* Return the next (or previous) active insn within BB.  */
 
-static rtx
-prev_active_insn_bb (basic_block bb, rtx insn)
+static rtx_insn *
+prev_active_insn_bb (basic_block bb, rtx_insn *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;
+  return NULL;
 }
 
-static rtx
-next_active_insn_bb (basic_block bb, rtx insn)
+static rtx_insn *
+next_active_insn_bb (basic_block bb, rtx_insn *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;
+  return NULL;
 }
 
 /* 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)
+force_move_args_size_note (basic_block bb, rtx_insn *prev, rtx_insn *insn)
 {
-  rtx note, test, next_candidate, prev_candidate;
+  rtx note;
+  rtx_insn *test, *next_candidate, *prev_candidate;
 
   /* If PREV exists, tail-call to the logic in the other function.  */
   if (prev)
@@ -424,16 +537,21 @@ force_move_args_size_note (basic_block bb, rtx prev, rtx insn)
 /* Subroutine of combine_stack_adjustments, called for each basic block.  */
 
 static void
-combine_stack_adjustments_for_block (basic_block bb)
+combine_stack_adjustments_for_block (basic_block bb, bitmap live)
 {
   HOST_WIDE_INT last_sp_adjust = 0;
-  rtx last_sp_set = NULL_RTX;
-  rtx last2_sp_set = NULL_RTX;
+  rtx_insn *last_sp_set = NULL;
+  rtx_insn *last2_sp_set = NULL;
+  bitmap last_sp_live = NULL;
   struct csa_reflist *reflist = NULL;
-  rtx insn, next, set;
-  struct record_stack_refs_data data;
+  bitmap copy = NULL;
+  rtx_insn *insn, *next;
+  rtx set;
   bool end_of_block = false;
 
+  bitmap_copy (live, DF_LR_IN (bb));
+  df_simulate_initialize_forwards (bb, live);
+
   for (insn = BB_HEAD (bb); !end_of_block ; insn = next)
     {
       end_of_block = insn == BB_END (bb);
@@ -443,25 +561,49 @@ combine_stack_adjustments_for_block (basic_block bb)
        continue;
 
       set = single_set_for_csa (insn);
+      if (set && find_reg_note (insn, REG_STACK_CHECK, NULL_RTX))
+       set = NULL_RTX;
       if (set)
        {
          rtx dest = SET_DEST (set);
          rtx src = SET_SRC (set);
+         HOST_WIDE_INT this_adjust = 0;
 
          /* Find constant additions to the stack pointer.  */
          if (dest == stack_pointer_rtx
              && GET_CODE (src) == PLUS
              && XEXP (src, 0) == stack_pointer_rtx
              && CONST_INT_P (XEXP (src, 1)))
+           this_adjust = INTVAL (XEXP (src, 1));
+         /* Or such additions turned by postreload into a store of
+            equivalent register.  */
+         else if (dest == stack_pointer_rtx
+                  && REG_P (src)
+                  && REGNO (src) != STACK_POINTER_REGNUM)
+           if (rtx equal = find_reg_note (insn, REG_EQUAL, NULL_RTX))
+             if (GET_CODE (XEXP (equal, 0)) == PLUS
+                 && XEXP (XEXP (equal, 0), 0) == stack_pointer_rtx
+                 && CONST_INT_P (XEXP (XEXP (equal, 0), 1)))
+               this_adjust = INTVAL (XEXP (XEXP (equal, 0), 1));
+
+         if (this_adjust)
            {
-             HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1));
-
              /* If we've not seen an adjustment previously, record
                 it now and continue.  */
              if (! last_sp_set)
                {
                  last_sp_set = insn;
                  last_sp_adjust = this_adjust;
+                 if (REG_P (src))
+                   {
+                     if (copy == NULL)
+                       copy = BITMAP_ALLOC (&reg_obstack);
+                     last_sp_live = copy;
+                     bitmap_copy (last_sp_live, live);
+                   }
+                 else
+                   last_sp_live = NULL;
+                 df_simulate_one_insn_forwards (bb, insn, live);
                  continue;
                }
 
@@ -472,7 +614,7 @@ combine_stack_adjustments_for_block (basic_block bb)
                 Also we need to be careful to not move stack pointer
                 such that we create stack accesses outside the allocated
                 area.  We can combine an allocation into the first insn,
-                or a deallocation into the second insn.  We can not
+                or a deallocation into the second insn.  We cannot
                 combine an allocation followed by a deallocation.
 
                 The only somewhat frequent occurrence of the later is when
@@ -489,14 +631,20 @@ combine_stack_adjustments_for_block (basic_block bb)
              /* Combine an allocation into the first instruction.  */
              if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0)
                {
-                 if (try_apply_stack_adjustment (last_sp_set, reflist,
-                                                 last_sp_adjust + this_adjust,
-                                                 this_adjust))
+                 if (no_unhandled_cfa (insn)
+                     && try_apply_stack_adjustment (last_sp_set, reflist,
+                                                    last_sp_adjust
+                                                    + this_adjust,
+                                                    this_adjust,
+                                                    last_sp_live,
+                                                    insn))
                    {
                      /* It worked!  */
                      maybe_move_args_size_note (last_sp_set, insn, false);
+                     maybe_merge_cfa_adjust (last_sp_set, insn, false);
                      delete_insn (insn);
                      last_sp_adjust += this_adjust;
+                     last_sp_live = NULL;
                      continue;
                    }
                }
@@ -506,17 +654,24 @@ combine_stack_adjustments_for_block (basic_block bb)
              else if (STACK_GROWS_DOWNWARD
                       ? last_sp_adjust >= 0 : last_sp_adjust <= 0)
                {
-                 if (try_apply_stack_adjustment (insn, reflist,
-                                                 last_sp_adjust + this_adjust,
-                                                 -last_sp_adjust))
+                 if (no_unhandled_cfa (last_sp_set)
+                     && !REG_P (src)
+                     && try_apply_stack_adjustment (insn, reflist,
+                                                    last_sp_adjust
+                                                    + this_adjust,
+                                                    -last_sp_adjust,
+                                                    NULL, NULL))
                    {
                      /* It worked!  */
                      maybe_move_args_size_note (insn, last_sp_set, true);
+                     maybe_merge_cfa_adjust (insn, last_sp_set, true);
                      delete_insn (last_sp_set);
                      last_sp_set = insn;
                      last_sp_adjust += this_adjust;
+                     last_sp_live = NULL;
                      free_csa_reflist (reflist);
                      reflist = NULL;
+                     df_simulate_one_insn_forwards (bb, insn, live);
                      continue;
                    }
                }
@@ -526,9 +681,10 @@ combine_stack_adjustments_for_block (basic_block bb)
                 delete the old deallocation insn.  */
              if (last_sp_set)
                {
-                 if (last_sp_adjust == 0)
+                 if (last_sp_adjust == 0 && no_unhandled_cfa (last_sp_set))
                    {
                      maybe_move_args_size_note (insn, last_sp_set, true);
+                     maybe_merge_cfa_adjust (insn, last_sp_set, true);
                      delete_insn (last_sp_set);
                    }
                  else
@@ -538,6 +694,16 @@ combine_stack_adjustments_for_block (basic_block bb)
              reflist = NULL;
              last_sp_set = insn;
              last_sp_adjust = this_adjust;
+             if (REG_P (src))
+               {
+                 if (copy == NULL)
+                   copy = BITMAP_ALLOC (&reg_obstack);
+                 last_sp_live = copy;
+                 bitmap_copy (last_sp_live, live);
+               }
+             else
+               last_sp_live = NULL;
+             df_simulate_one_insn_forwards (bb, insn, live);
              continue;
            }
 
@@ -548,11 +714,11 @@ combine_stack_adjustments_for_block (basic_block bb)
          if (MEM_P (dest)
              && ((STACK_GROWS_DOWNWARD
                   ? (GET_CODE (XEXP (dest, 0)) == PRE_DEC
-                     && last_sp_adjust
-                        == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))
+                     && known_eq (last_sp_adjust,
+                                  GET_MODE_SIZE (GET_MODE (dest))))
                   : (GET_CODE (XEXP (dest, 0)) == PRE_INC
-                     && last_sp_adjust
-                        == -(HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest))))
+                     && known_eq (-last_sp_adjust,
+                                  GET_MODE_SIZE (GET_MODE (dest)))))
                  || ((STACK_GROWS_DOWNWARD
                       ? last_sp_adjust >= 0 : last_sp_adjust <= 0)
                      && GET_CODE (XEXP (dest, 0)) == PRE_MODIFY
@@ -567,7 +733,8 @@ combine_stack_adjustments_for_block (basic_block bb)
              && !reg_mentioned_p (stack_pointer_rtx, src)
              && memory_address_p (GET_MODE (dest), stack_pointer_rtx)
              && try_apply_stack_adjustment (insn, reflist, 0,
-                                            -last_sp_adjust))
+                                            -last_sp_adjust,
+                                            NULL, NULL))
            {
              if (last2_sp_set)
                maybe_move_args_size_note (last2_sp_set, last_sp_set, false);
@@ -576,21 +743,19 @@ combine_stack_adjustments_for_block (basic_block bb)
              delete_insn (last_sp_set);
              free_csa_reflist (reflist);
              reflist = NULL;
-             last_sp_set = NULL_RTX;
+             last_sp_set = NULL;
              last_sp_adjust = 0;
+             last_sp_live = NULL;
+             df_simulate_one_insn_forwards (bb, insn, live);
              continue;
            }
        }
 
-      data.insn = insn;
-      data.reflist = reflist;
-      if (!CALL_P (insn) && last_sp_set
-         && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data))
+      if (!CALL_P (insn) && last_sp_set && record_stack_refs (insn, &reflist))
        {
-          reflist = data.reflist;
-          continue;
+         df_simulate_one_insn_forwards (bb, insn, live);
+         continue;
        }
-      reflist = data.reflist;
 
       /* Otherwise, we were not able to process the instruction.
         Do not continue collecting data across such a one.  */
@@ -605,10 +770,13 @@ combine_stack_adjustments_for_block (basic_block bb)
            }
          free_csa_reflist (reflist);
          reflist = NULL;
-         last2_sp_set = NULL_RTX;
-         last_sp_set = NULL_RTX;
+         last2_sp_set = NULL;
+         last_sp_set = NULL;
          last_sp_adjust = 0;
+         last_sp_live = NULL;
        }
+
+      df_simulate_one_insn_forwards (bb, insn, live);
     }
 
   if (last_sp_set && last_sp_adjust == 0)
@@ -619,11 +787,52 @@ combine_stack_adjustments_for_block (basic_block bb)
 
   if (reflist)
     free_csa_reflist (reflist);
+  if (copy)
+    BITMAP_FREE (copy);
 }
 \f
+static unsigned int
+rest_of_handle_stack_adjustments (void)
+{
+  df_note_add_problem ();
+  df_analyze ();
+  combine_stack_adjustments ();
+  return 0;
+}
 
-static bool
-gate_handle_stack_adjustments (void)
+namespace {
+
+const pass_data pass_data_stack_adjustments =
+{
+  RTL_PASS, /* type */
+  "csa", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_COMBINE_STACK_ADJUST, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_df_finish, /* todo_flags_finish */
+};
+
+class pass_stack_adjustments : public rtl_opt_pass
+{
+public:
+  pass_stack_adjustments (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_stack_adjustments, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *)
+    {
+      return rest_of_handle_stack_adjustments ();
+    }
+
+}; // class pass_stack_adjustments
+
+bool
+pass_stack_adjustments::gate (function *)
 {
   /* This is kind of a heuristic.  We need to run combine_stack_adjustments
      even for machines with possibly nonzero TARGET_RETURN_POPS_ARGS
@@ -636,32 +845,10 @@ gate_handle_stack_adjustments (void)
   return flag_combine_stack_adjustments;
 }
 
-static unsigned int
-rest_of_handle_stack_adjustments (void)
-{
-  df_note_add_problem ();
-  df_analyze ();
-  combine_stack_adjustments ();
-  return 0;
-}
+} // anon namespace
 
-struct rtl_opt_pass pass_stack_adjustments =
+rtl_opt_pass *
+make_pass_stack_adjustments (gcc::context *ctxt)
 {
- {
-  RTL_PASS,
-  "csa",                                /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  gate_handle_stack_adjustments,        /* gate */
-  rest_of_handle_stack_adjustments,     /* execute */
-  NULL,                                 /* sub */
-  NULL,                                 /* next */
-  0,                                    /* static_pass_number */
-  TV_COMBINE_STACK_ADJUST,              /* tv_id */
-  0,                                    /* properties_required */
-  0,                                    /* properties_provided */
-  0,                                    /* properties_destroyed */
-  0,                                    /* todo_flags_start */
-  TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_ggc_collect,                     /* todo_flags_finish */
- }
-};
+  return new pass_stack_adjustments (ctxt);
+}