diff gcc/combine-stack-adj.c @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents f6334be47118
children 84e7813d76e9
line wrap: on
line diff
--- a/gcc/combine-stack-adj.c	Sun Aug 21 07:07:55 2011 +0900
+++ b/gcc/combine-stack-adj.c	Fri Oct 27 22:46:09 2017 +0900
@@ -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 Free Software Foundation, Inc.
+   Copyright (C) 1987-2017 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -43,33 +41,18 @@
 #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 "output.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 "timevar.h"
+#include "cfgrtl.h"
 #include "tree-pass.h"
+#include "rtl-iter.h"
 
 
-/* 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.  */
@@ -77,19 +60,19 @@
 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 *,
+static int try_apply_stack_adjustment (rtx_insn *, 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 *);
 
 
 /* Main entry point for stack adjustment combination.  */
@@ -99,7 +82,7 @@
 {
   basic_block bb;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     combine_stack_adjustments_for_block (bb);
 }
 
@@ -126,7 +109,7 @@
    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);
@@ -175,7 +158,7 @@
    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;
 
@@ -193,12 +176,51 @@
   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,
+try_apply_stack_adjustment (rtx_insn *insn, struct csa_reflist *reflist,
 			    HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta)
 {
   struct csa_reflist *ml;
@@ -214,7 +236,8 @@
 
   for (ml = reflist; ml ; ml = ml->next)
     {
-      rtx new_addr = plus_constant (stack_pointer_rtx, ml->sp_offset - delta);
+      rtx new_addr = plus_constant (Pmode, stack_pointer_rtx,
+				    ml->sp_offset - delta);
       rtx new_val;
 
       if (MEM_P (*ml->ref))
@@ -239,125 +262,228 @@
     return 0;
 }
 
-/* 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))
+      rtx *loc = *iter;
+      rtx x = *loc;
+      switch (GET_CODE (x))
 	{
-	  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;
 	}
-      /* 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.
+    }
+  return true;
+}
 
-	 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.
+/* 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_insn *last, rtx_insn *insn, bool after)
+{
+  rtx note, last_note;
 
-	 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 (d->insn))
-	    return 1;
-	  d->reflist = record_one_stack_ref (d->insn, xp, d->reflist);
-	  return -1;
-	}
-      break;
-    default:
-      break;
+  note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+  if (note == NULL)
+    return;
+
+  last_note = find_reg_note (last, REG_ARGS_SIZE, NULL_RTX);
+  if (last_note)
+    {
+      /* 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);
     }
-  return 0;
+  else
+    add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0));
 }
 
-/* Adjust or create REG_FRAME_RELATED_EXPR note when merging a stack
-   adjustment into a frame related insn.  */
+/* Merge any REG_CFA_ADJUST_CFA note from SRC into DST.
+   AFTER is true iff DST follows SRC in the instruction stream.  */
 
 static void
-adjust_frame_related_expr (rtx last_sp_set, rtx insn,
-			   HOST_WIDE_INT this_adjust)
+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_insn *
+prev_active_insn_bb (basic_block bb, rtx_insn *insn)
 {
-  rtx note = find_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, NULL_RTX);
-  rtx new_expr = NULL_RTX;
+  for (insn = PREV_INSN (insn);
+       insn != PREV_INSN (BB_HEAD (bb));
+       insn = PREV_INSN (insn))
+    if (active_insn_p (insn))
+      return insn;
+  return NULL;
+}
+
+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;
+}
 
-  if (note == NULL_RTX && RTX_FRAME_RELATED_P (insn))
+/* 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_insn *prev, rtx_insn *insn)
+{
+  rtx note;
+  rtx_insn *test, *next_candidate, *prev_candidate;
+
+  /* If PREV exists, tail-call to the logic in the other function.  */
+  if (prev)
+    {
+      maybe_move_args_size_note (prev, insn, false);
+      return;
+    }
+
+  /* First, make sure there's anything that needs doing.  */
+  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)
-    {
-      rtx expr = XEXP (note, 0);
-      rtx last = XVECEXP (expr, 0, XVECLEN (expr, 0) - 1);
-      int i;
+  /* 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.  */
 
-      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)))
+  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 (SET_SRC (last), 1)
-	    = GEN_INT (INTVAL (XEXP (SET_SRC (last), 1)) + this_adjust);
+	  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;
+    }
 
-      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);
-    }
+  if (prev_candidate)
+    test = prev_candidate;
+  else if (next_candidate)
+    test = next_candidate;
   else
     {
-      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));
-
-	  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;
-	}
+      /* ??? 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);
     }
-
-  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;
-  else
-    add_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, new_expr);
+  add_reg_note (test, REG_ARGS_SIZE, XEXP (note, 0));
 }
 
 /* Subroutine of combine_stack_adjustments, called for each basic block.  */
@@ -366,10 +492,11 @@
 combine_stack_adjustments_for_block (basic_block bb)
 {
   HOST_WIDE_INT last_sp_adjust = 0;
-  rtx last_sp_set = NULL_RTX;
+  rtx_insn *last_sp_set = NULL;
+  rtx_insn *last2_sp_set = NULL;
   struct csa_reflist *reflist = NULL;
-  rtx insn, next, set;
-  struct record_stack_refs_data data;
+  rtx_insn *insn, *next;
+  rtx set;
   bool end_of_block = false;
 
   for (insn = BB_HEAD (bb); !end_of_block ; insn = next)
@@ -381,6 +508,8 @@
 	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);
@@ -427,14 +556,15 @@
 	      /* 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))
 		    {
-		      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);
+		      maybe_merge_cfa_adjust (last_sp_set, insn, false);
 		      delete_insn (insn);
 		      last_sp_adjust += this_adjust;
 		      continue;
@@ -446,11 +576,15 @@
 	      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)
+		      && try_apply_stack_adjustment (insn, reflist,
+						     last_sp_adjust
+						     + this_adjust,
+						     -last_sp_adjust))
 		    {
 		      /* 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;
@@ -463,8 +597,17 @@
 	      /* 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 && 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
+		    last2_sp_set = last_sp_set;
+		}
 	      free_csa_reflist (reflist);
 	      reflist = NULL;
 	      last_sp_set = insn;
@@ -500,24 +643,22 @@
 	      && 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;
-	      last_sp_set = NULL_RTX;
+	      last_sp_set = NULL;
 	      last_sp_adjust = 0;
 	      continue;
 	    }
 	}
 
-      data.insn = insn;
-      data.reflist = reflist;
       if (!CALL_P (insn) && last_sp_set
-	  && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data))
-	{
-	   reflist = data.reflist;
-	   continue;
-	}
-      reflist = data.reflist;
+	  && record_stack_refs (insn, &reflist))
+	continue;
 
       /* Otherwise, we were not able to process the instruction.
 	 Do not continue collecting data across such a one.  */
@@ -526,65 +667,86 @@
 	      || 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;
-	  last_sp_set = NULL_RTX;
+	  last2_sp_set = NULL;
+	  last_sp_set = NULL;
 	  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);
 }
 
-
-static bool
-gate_handle_stack_adjustments (void)
-{
-  return flag_combine_stack_adjustments;
-}
-
 static unsigned int
 rest_of_handle_stack_adjustments (void)
 {
-  cleanup_cfg (flag_crossjumping ? CLEANUP_CROSSJUMP : 0);
+  df_note_add_problem ();
+  df_analyze ();
+  combine_stack_adjustments ();
+  return 0;
+}
+
+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
      and ACCUMULATE_OUTGOING_ARGS.  We expect that only ports having
      push instructions will have popping returns.  */
 #ifndef PUSH_ROUNDING
-  if (!ACCUMULATE_OUTGOING_ARGS)
+  if (ACCUMULATE_OUTGOING_ARGS)
+    return false;
 #endif
-    {
-      df_note_add_problem ();
-      df_analyze ();
-      combine_stack_adjustments ();
-    }
-  return 0;
+  return flag_combine_stack_adjustments;
 }
 
-struct rtl_opt_pass pass_stack_adjustments =
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_stack_adjustments (gcc::context *ctxt)
 {
- {
-  RTL_PASS,
-  "csa",                                /* name */
-  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_dump_func |
-  TODO_ggc_collect,                     /* todo_flags_finish */
- }
-};
+  return new pass_stack_adjustments (ctxt);
+}