OSDN Git Service

2002-03-03 Aldy Hernandez <aldyh@redhat.com>
[pf3gnuchains/gcc-fork.git] / gcc / regmove.c
index cbbeb83..aa1c17b 100644 (file)
@@ -1,23 +1,23 @@
 /* Move registers around to reduce number of move instructions needed.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 
 /* This module looks for cases where matching constraints would force
@@ -37,15 +37,24 @@ Boston, MA 02111-1307, USA.  */
 #include "flags.h"
 #include "function.h"
 #include "expr.h"
-#include "insn-flags.h"
 #include "basic-block.h"
+#include "except.h"
 #include "toplev.h"
+#include "reload.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
 
 static int perhaps_ends_bb_p   PARAMS ((rtx));
 static int optimize_reg_copy_1 PARAMS ((rtx, rtx, rtx));
 static void optimize_reg_copy_2        PARAMS ((rtx, rtx, rtx));
 static void optimize_reg_copy_3        PARAMS ((rtx, rtx, rtx));
-static rtx gen_add3_insn       PARAMS ((rtx, rtx, rtx));
 static void copy_src_to_dest   PARAMS ((rtx, rtx, rtx, int));
 static int *regmove_bb_head;
 
@@ -62,7 +71,7 @@ static void flags_set_1 PARAMS ((rtx, rtx, void *));
 
 static int try_auto_increment PARAMS ((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
 static int find_matches PARAMS ((rtx, struct match *));
-static void replace_in_call_usage PARAMS ((rtx *, int, rtx, rtx));
+static void replace_in_call_usage PARAMS ((rtx *, unsigned int, rtx, rtx));
 static int fixup_match_1 PARAMS ((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
 ;
 static int reg_is_remote_constant_p PARAMS ((rtx, rtx, rtx));
@@ -84,27 +93,6 @@ regclass_compatible_p (class0, class1)
              && ! CLASS_LIKELY_SPILLED_P (class1)));
 }
 
-/* Generate and return an insn body to add r1 and c,
-   storing the result in r0.  */
-static rtx
-gen_add3_insn (r0, r1, c)
-     rtx r0, r1, c;
-{
-  int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
-
-    if (icode == CODE_FOR_nothing
-      || ! ((*insn_data[icode].operand[0].predicate)
-           (r0, insn_data[icode].operand[0].mode))
-      || ! ((*insn_data[icode].operand[1].predicate)
-           (r1, insn_data[icode].operand[1].mode))
-      || ! ((*insn_data[icode].operand[2].predicate)
-           (c, insn_data[icode].operand[2].mode)))
-    return NULL_RTX;
-
-  return (GEN_FCN (icode) (r0, r1, c));
-}
-
-
 /* INC_INSN is an instruction that adds INCREMENT to REG.
    Try to fold INC_INSN as a post/pre in/decrement into INSN.
    Iff INC_INSN_SET is nonzero, inc_insn has a destination different from src.
@@ -123,7 +111,7 @@ try_auto_increment (insn, inc_insn, inc_insn_set, reg, increment, pre)
       /* Can't use the size of SET_SRC, we might have something like
         (sign_extend:SI (mem:QI ...  */
       rtx use = find_use_as_address (pset, reg, 0);
-      if (use != 0 && use != (rtx) 1)
+      if (use != 0 && use != (rtx) (size_t) 1)
        {
          int size = GET_MODE_SIZE (GET_MODE (use));
          if (0
@@ -160,11 +148,7 @@ try_auto_increment (insn, inc_insn, inc_insn_set, reg, increment, pre)
                    = gen_rtx_EXPR_LIST (REG_INC,
                                         reg, REG_NOTES (insn));
                  if (! inc_insn_set)
-                   {
-                     PUT_CODE (inc_insn, NOTE);
-                     NOTE_LINE_NUMBER (inc_insn) = NOTE_INSN_DELETED;
-                     NOTE_SOURCE_FILE (inc_insn) = 0;
-                   }
+                   delete_insn (inc_insn);
                  return 1;
                }
            }
@@ -255,7 +239,7 @@ mark_flags_life_zones (flags)
     {
       enum machine_mode mode = (flags ? HImode : VOIDmode);
       rtx insn;
-      for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
        PUT_MODE (insn, mode);
       return;
     }
@@ -350,7 +334,7 @@ static int *regno_src_regno;
    a candidate for tying to a hard register, since the output might in
    turn be a candidate to be tied to a different hard register.  */
 static int
-replacement_quality(reg)
+replacement_quality (reg)
      rtx reg;
 {
   int src_regno;
@@ -396,11 +380,11 @@ static int perhaps_ends_bb_p (insn)
       /* A CALL_INSN might be the last insn of a basic block, if it is inside
         an EH region or if there are nonlocal gotos.  Note that this test is
         very conservative.  */
-      return flag_exceptions || nonlocal_goto_handler_labels;
-
+      if (nonlocal_goto_handler_labels)
+       return 1;
+      /* FALLTHRU */
     default:
-      /* All others never end a basic block.  */
-      return 0;
+      return can_throw_internal (insn);
     }
 }
 \f
@@ -427,7 +411,7 @@ optimize_reg_copy_1 (insn, dest, src)
   int sregno = REGNO (src);
   int dregno = REGNO (dest);
 
-  /* We don't want to mess with hard regs if register classes are small. */
+  /* We don't want to mess with hard regs if register classes are small.  */
   if (sregno == dregno
       || (SMALL_REGISTER_CLASSES
          && (sregno < FIRST_PSEUDO_REGISTER
@@ -693,6 +677,9 @@ optimize_reg_copy_3 (insn, dest, src)
 
   if (! (set = single_set (p))
       || GET_CODE (SET_SRC (set)) != MEM
+      /* If there's a REG_EQUIV note, this must be an insn that loads an
+        argument.  Prefer keeping the note over doing this optimization.  */
+      || find_reg_note (p, REG_EQUIV, NULL_RTX)
       || SET_DEST (set) != src_reg)
     return;
 
@@ -718,7 +705,7 @@ optimize_reg_copy_3 (insn, dest, src)
 
   /* Now walk forward making additional replacements.  We want to be able
      to undo all the changes if a later substitution fails.  */
-  subreg = gen_rtx_SUBREG (old_mode, src_reg, 0);
+  subreg = gen_lowpart_SUBREG (old_mode, src_reg);
   while (p = NEXT_INSN (p), p != insn)
     {
       if (! INSN_P (p))
@@ -737,6 +724,12 @@ optimize_reg_copy_3 (insn, dest, src)
       PUT_MODE (src_reg, old_mode);
       XEXP (src, 0) = src_reg;
     }
+  else
+    {
+      rtx note = find_reg_note (p, REG_EQUAL, NULL_RTX);
+      if (note)
+       remove_note (p, note);
+    }
 }
 
 \f
@@ -870,7 +863,7 @@ reg_is_remote_constant_p (reg, insn, first)
      rtx insn;
      rtx first;
 {
-  register rtx p;
+  rtx p;
 
   if (REG_N_SETS (REGNO (reg)) != 1)
     return 0;
@@ -1050,6 +1043,12 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file)
   return 0;
 }
 
+/* Main entry for the register move optimization.
+   F is the first instruction.
+   NREGS is one plus the highest pseudo-reg number used in the instruction.
+   REGMOVE_DUMP_FILE is a stream for output of a trace of actions taken
+   (or 0 if none should be output).  */
+
 void
 regmove_optimize (f, nregs, regmove_dump_file)
      rtx f;
@@ -1063,6 +1062,11 @@ regmove_optimize (f, nregs, regmove_dump_file)
   int i;
   rtx copy_src, copy_dst;
 
+  /* ??? Hack.  Regmove doesn't examine the CFG, and gets mightily
+     confused by non-call exceptions ending blocks.  */
+  if (flag_non_call_exceptions)
+    return;
+
   /* Find out where a potential flags register is live, and so that we
      can supress some optimizations in those zones.  */
   mark_flags_life_zones (discover_flags_reg ());
@@ -1100,12 +1104,12 @@ regmove_optimize (f, nregs, regmove_dump_file)
              && (GET_CODE (SET_SRC (set)) == SIGN_EXTEND
                  || GET_CODE (SET_SRC (set)) == ZERO_EXTEND)
              && GET_CODE (XEXP (SET_SRC (set), 0)) == REG
-             && GET_CODE (SET_DEST(set)) == REG)
+             && GET_CODE (SET_DEST (set)) == REG)
            optimize_reg_copy_3 (insn, SET_DEST (set), SET_SRC (set));
 
          if (flag_expensive_optimizations && ! pass
              && GET_CODE (SET_SRC (set)) == REG
-             && GET_CODE (SET_DEST(set)) == REG)
+             && GET_CODE (SET_DEST (set)) == REG)
            {
              /* If this is a register-register copy where SRC is not dead,
                 see if we can optimize it.  If this optimization succeeds,
@@ -1120,7 +1124,7 @@ regmove_optimize (f, nregs, regmove_dump_file)
                  if (regno_src_regno[REGNO (SET_DEST (set))] < 0
                      && SET_SRC (set) != SET_DEST (set))
                    {
-                     int srcregno = REGNO (SET_SRC(set));
+                     int srcregno = REGNO (SET_SRC (set));
                      if (regno_src_regno[srcregno] >= 0)
                        srcregno = regno_src_regno[srcregno];
                      regno_src_regno[REGNO (SET_DEST (set))] = srcregno;
@@ -1164,7 +1168,7 @@ regmove_optimize (f, nregs, regmove_dump_file)
                {
                  src_subreg
                    = gen_rtx_SUBREG (GET_MODE (SUBREG_REG (dst)),
-                                     src, SUBREG_WORD (dst));
+                                     src, SUBREG_BYTE (dst));
                  dst = SUBREG_REG (dst);
                }
              if (GET_CODE (dst) != REG
@@ -1195,7 +1199,7 @@ regmove_optimize (f, nregs, regmove_dump_file)
              if (recog_data.operand[match_no] != SET_DEST (set))
                continue;
 
-             /* If the operands already match, then there is nothing to do. */
+             /* If the operands already match, then there is nothing to do.  */
              if (operands_match_p (src, dst))
                continue;
 
@@ -1214,6 +1218,9 @@ regmove_optimize (f, nregs, regmove_dump_file)
              if (! regclass_compatible_p (src_class, dst_class))
                continue;
 
+             if (GET_MODE (src) != GET_MODE (dst))
+               continue;
+
              if (fixup_match_1 (insn, set, src, src_subreg, dst, pass,
                                 op_no, match_no,
                                 regmove_dump_file))
@@ -1267,10 +1274,11 @@ regmove_optimize (f, nregs, regmove_dump_file)
 
              if (GET_CODE (dst) != REG
                  || REGNO (dst) < FIRST_PSEUDO_REGISTER
-                 || REG_LIVE_LENGTH (REGNO (dst)) < 0)
+                 || REG_LIVE_LENGTH (REGNO (dst)) < 0
+                 || RTX_UNCHANGING_P (dst))
                continue;
 
-             /* If the operands already match, then there is nothing to do. */
+             /* If the operands already match, then there is nothing to do.  */
              if (operands_match_p (src, dst))
                continue;
 
@@ -1285,6 +1293,14 @@ regmove_optimize (f, nregs, regmove_dump_file)
              if (! set)
                continue;
 
+             /* Note that single_set ignores parts of a parallel set for
+                which one of the destinations is REG_UNUSED.  We can't
+                handle that here, since we can wind up rewriting things
+                such that a single register is set twice within a single
+                parallel.  */
+             if (reg_set_p (src, insn))
+               continue;
+
              /* match_no/dst must be a write-only operand, and
                 operand_operand/src must be a read-only operand.  */
              if (match.use[op_no] != READ
@@ -1312,19 +1328,22 @@ regmove_optimize (f, nregs, regmove_dump_file)
                }
              src_class = reg_preferred_class (REGNO (src));
              dst_class = reg_preferred_class (REGNO (dst));
-             if (! regclass_compatible_p (src_class, dst_class))
+
+             if (! (src_note = find_reg_note (insn, REG_DEAD, src)))
                {
-                 if (!copy_src)
-                   {
-                     copy_src = src;
-                     copy_dst = dst;
-                   }
+                 /* We used to force the copy here like in other cases, but
+                    it produces worse code, as it eliminates no copy
+                    instructions and the copy emitted will be produced by
+                    reload anyway.  On patterns with multiple alternatives,
+                    there may be better sollution availble.
+
+                    In particular this change produced slower code for numeric
+                    i387 programs.  */
+
                  continue;
                }
 
-             /* Can not modify an earlier insn to set dst if this insn
-                uses an old value in the source.  */
-             if (reg_overlap_mentioned_p (dst, SET_SRC (set)))
+             if (! regclass_compatible_p (src_class, dst_class))
                {
                  if (!copy_src)
                    {
@@ -1334,7 +1353,9 @@ regmove_optimize (f, nregs, regmove_dump_file)
                  continue;
                }
 
-             if (! (src_note = find_reg_note (insn, REG_DEAD, src)))
+             /* Can not modify an earlier insn to set dst if this insn
+                uses an old value in the source.  */
+             if (reg_overlap_mentioned_p (dst, SET_SRC (set)))
                {
                  if (!copy_src)
                    {
@@ -1344,7 +1365,6 @@ regmove_optimize (f, nregs, regmove_dump_file)
                  continue;
                }
 
-
              /* If src is set once in a different basic block,
                 and is set equal to a constant, then do not use
                 it for this optimization, as this would make it
@@ -1560,21 +1580,30 @@ find_matches (insn, matchp)
            matchp->commutative[op_no] = op_no + 1;
            matchp->commutative[op_no + 1] = op_no;
            break;
+
          case '0': case '1': case '2': case '3': case '4':
          case '5': case '6': case '7': case '8': case '9':
-           c -= '0';
-           if (c < op_no && likely_spilled[(unsigned char) c])
-             break;
-           matchp->with[op_no] = c;
-           any_matches = 1;
-           if (matchp->commutative[op_no] >= 0)
-             matchp->with[matchp->commutative[op_no]] = c;
+           {
+             char *end;
+             unsigned long match_ul = strtoul (p - 1, &end, 10);
+             int match = match_ul;
+
+             p = end;
+
+             if (match < op_no && likely_spilled[match])
+               break;
+             matchp->with[op_no] = match;
+             any_matches = 1;
+             if (matchp->commutative[op_no] >= 0)
+               matchp->with[matchp->commutative[op_no]] = match;
+           }
            break;
+
          case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'h':
          case 'j': case 'k': case 'l': case 'p': case 'q': case 't': case 'u':
          case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
          case 'C': case 'D': case 'W': case 'Y': case 'Z':
-           if (CLASS_LIKELY_SPILLED_P (REG_CLASS_FROM_LETTER ((unsigned char)c)))
+           if (CLASS_LIKELY_SPILLED_P (REG_CLASS_FROM_LETTER ((unsigned char) c)))
              likely_spilled[op_no] = 1;
            break;
          }
@@ -1588,7 +1617,7 @@ find_matches (insn, matchp)
 static void
 replace_in_call_usage (loc, dst_reg, src, insn)
      rtx *loc;
-     int dst_reg;
+     unsigned int dst_reg;
      rtx src;
      rtx insn;
 {
@@ -1875,9 +1904,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
          rtx notes = REG_NOTES (insn);
 
          emit_insn_after_with_line_notes (pat, PREV_INSN (p), insn);
-         PUT_CODE (insn, NOTE);
-         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (insn) = 0;
+         delete_insn (insn);
          /* emit_insn_after_with_line_notes has no
             return value, so search for the new insn.  */
          insn = p;
@@ -1899,7 +1926,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
 
       if (note && CONSTANT_P (XEXP (note, 0)))
        {
-         for (q = PREV_INSN (insn); q; q = PREV_INSN(q))
+         for (q = PREV_INSN (insn); q; q = PREV_INSN (q))
            {
              /* ??? We can't scan past the end of a basic block without
                 updating the register lifetime info
@@ -1929,9 +1956,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
          if (q && set2 && SET_DEST (set2) == src && CONSTANT_P (SET_SRC (set2))
              && validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0))
            {
-             PUT_CODE (q, NOTE);
-             NOTE_LINE_NUMBER (q) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (q) = 0;
+             delete_insn (q);
              REG_N_SETS (REGNO (src))--;
              REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2;
              REG_LIVE_LENGTH (REGNO (src)) -= s_length2;
@@ -1965,7 +1990,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
        {
          /* ??? We can't scan past the end of a basic block without updating
             the register lifetime info
-            (REG_DEAD/basic_block_live_at_start). */
+            (REG_DEAD/basic_block_live_at_start).  */
          if (perhaps_ends_bb_p (q))
            break;
          else if (! INSN_P (q))
@@ -2067,7 +2092,7 @@ stable_and_no_regs_but_for_p (x, src, dst)
 }
 \f
 /* Track stack adjustments and stack memory references.  Attempt to
-   reduce the number of stack adjustments by back-propogating across
+   reduce the number of stack adjustments by back-propagating across
    the memory references.
 
    This is intended primarily for use with targets that do not define
@@ -2077,7 +2102,7 @@ stable_and_no_regs_but_for_p (x, src, dst)
    (e.g. x86 fp regs) which would ordinarily have to be implemented
    as a sub/mov pair due to restrictions in calls.c.
 
-   Propogation stops when any of the insns that need adjusting are
+   Propagation stops when any of the insns that need adjusting are
    (a) no longer valid because we've exceeded their range, (b) a
    non-trivial push instruction, or (c) a call instruction.
 
@@ -2225,33 +2250,15 @@ try_apply_stack_adjustment (insn, memlist, new_adjust, delta)
   struct csa_memlist *ml;
   rtx set;
 
-  /* We know INSN matches single_set_for_csa, because that's what we
-     recognized earlier.  However, if INSN is not single_set, it is
-     doing double duty as a barrier for frame pointer memory accesses,
-     which we are not recording.  Therefore, an adjust insn that is not
-     single_set may not have a positive delta applied.  */
-
-  if (delta > 0 && ! single_set (insn))
-    return 0;
   set = single_set_for_csa (insn);
   validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1);
 
   for (ml = memlist; ml ; ml = ml->next)
-    {
-      HOST_WIDE_INT c = ml->sp_offset - delta;
-      rtx new = gen_rtx_MEM (GET_MODE (*ml->mem),
-                            plus_constant (stack_pointer_rtx, c));
-
-      /* Don't reference memory below the stack pointer.  */
-      if (c < 0)
-       {
-         cancel_changes (0);
-         return 0;
-       }
-
-      MEM_COPY_ATTRIBUTES (new, *ml->mem);
-      validate_change (ml->insn, ml->mem, new, 1);
-    }
+    validate_change
+      (ml->insn, ml->mem,
+       replace_equiv_address_nv (*ml->mem,
+                                plus_constant (stack_pointer_rtx,
+                                               ml->sp_offset - delta)), 1);
 
   if (apply_change_group ())
     {
@@ -2289,7 +2296,7 @@ record_stack_memrefs (xp, data)
       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 neccesary.  */
+         stack pointer, so this check is necessary.  */
       if (stack_memref_p (x))
        {
          d->memlist = record_one_stack_memref (d->insn, xp, d->memlist);
@@ -2297,11 +2304,16 @@ record_stack_memrefs (xp, data)
        }
       return 1;
     case REG:
-      /* ??? We want be able to handle non-memory stack pointer references
-         later.  For now just discard all insns refering to stack pointer
-         outside mem expressions.  We would probably want to teach
-        validate_replace to simplify expressions first.  */
-      if (x == stack_pointer_rtx)
+      /* ??? We want be able to handle non-memory stack pointer
+        references later.  For now just discard all insns refering 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 explict clobber in an asm statement will
+        result in a QImode clober.  */
+      if (REGNO (x) == STACK_POINTER_REGNUM)
        return 1;
       break;
     default:
@@ -2358,35 +2370,63 @@ combine_stack_adjustments_for_block (bb)
 
              /* If not all recorded memrefs can be adjusted, or the
                 adjustment is now too large for a constant addition,
-                we cannot merge the two stack adjustments.  */
-             if (! try_apply_stack_adjustment (last_sp_set, memlist,
-                                               last_sp_adjust + this_adjust,
-                                               this_adjust))
+                we cannot merge the two stack adjustments.
+
+                Also we need to be carefull 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
+                combine an allocation followed by a deallocation.
+
+                The only somewhat frequent occurrence of the later is when
+                a function allocates a stack frame but does not use it.
+                For this case, we would need to analyze rtl stream to be
+                sure that allocated area is really unused.  This means not
+                only checking the memory references, but also all registers
+                or global memory references possibly containing a stack
+                frame address.
+
+                Perhaps the best way to address this problem is to teach
+                gcc not to allocate stack for objects never used.  */
+
+             /* Combine an allocation into the first instruction.  */
+             if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0)
                {
-                 free_csa_memlist (memlist);
-                 memlist = NULL;
-                 last_sp_set = insn;
-                 last_sp_adjust = this_adjust;
-                 goto processed;
+                 if (try_apply_stack_adjustment (last_sp_set, memlist,
+                                                 last_sp_adjust + this_adjust,
+                                                 this_adjust))
+                   {
+                     /* It worked!  */
+                     pending_delete = insn;
+                     last_sp_adjust += this_adjust;
+                     goto processed;
+                   }
                }
 
-             /* It worked!  */
-             pending_delete = insn;
-             last_sp_adjust += this_adjust;
-
-             /* If, by some accident, the adjustments cancel out,
-                delete both insns and start from scratch.  */
-             if (last_sp_adjust == 0)
+             /* Otherwise we have a deallocation.  Do not combine with
+                a previous allocation.  Combine into the second insn.  */
+             else if (STACK_GROWS_DOWNWARD
+                      ? last_sp_adjust >= 0 : last_sp_adjust <= 0)
                {
-                 if (last_sp_set == bb->head)
-                   bb->head = NEXT_INSN (last_sp_set);
-                 flow_delete_insn (last_sp_set);
-
-                 free_csa_memlist (memlist);
-                 memlist = NULL;
-                 last_sp_set = NULL_RTX;
+                 if (try_apply_stack_adjustment (insn, memlist,
+                                                 last_sp_adjust + this_adjust,
+                                                 -last_sp_adjust))
+                   {
+                     /* It worked!  */
+                     delete_insn (last_sp_set);
+                     last_sp_set = insn;
+                     last_sp_adjust += this_adjust;
+                     free_csa_memlist (memlist);
+                     memlist = NULL;
+                     goto processed;
+                   }
                }
 
+             /* Combination failed.  Restart processing from here.  */
+             free_csa_memlist (memlist);
+             memlist = NULL;
+             last_sp_set = insn;
+             last_sp_adjust = this_adjust;
              goto processed;
            }
 
@@ -2409,12 +2449,13 @@ combine_stack_adjustments_for_block (bb)
              && ! reg_mentioned_p (stack_pointer_rtx, src)
              && memory_address_p (GET_MODE (dest), stack_pointer_rtx)
              && validate_change (insn, &SET_DEST (set),
-                                 change_address (dest, VOIDmode,
-                                                 stack_pointer_rtx), 0))
+                                 replace_equiv_address (dest,
+                                                        stack_pointer_rtx),
+                                 0))
            {
              if (last_sp_set == bb->head)
                bb->head = NEXT_INSN (last_sp_set);
-             flow_delete_insn (last_sp_set);
+             delete_insn (last_sp_set);
 
              free_csa_memlist (memlist);
              memlist = NULL;
@@ -2451,12 +2492,9 @@ combine_stack_adjustments_for_block (bb)
        break;
 
       if (pending_delete)
-       flow_delete_insn (pending_delete);
+       delete_insn (pending_delete);
     }
 
   if (pending_delete)
-    {
-      bb->end = PREV_INSN (pending_delete);
-      flow_delete_insn (pending_delete);
-    }
+    delete_insn (pending_delete);
 }