OSDN Git Service

2001-05-03 David O'Brien <obrien@FreeBSD.org>
[pf3gnuchains/gcc-fork.git] / gcc / regmove.c
index 69d7f88..a151d6f 100644 (file)
@@ -1,6 +1,6 @@
 /* Move registers around to reduce number of move instructions needed.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000 Free Software Foundation, Inc.
+   1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -37,10 +37,21 @@ 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));
@@ -61,6 +72,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 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));
@@ -137,13 +149,23 @@ try_auto_increment (insn, inc_insn, inc_insn_set, reg, increment, pre)
            {
              if (inc_insn_set)
                validate_change
-                 (inc_insn, 
+                 (inc_insn,
                   &SET_SRC (inc_insn_set),
                   XEXP (SET_SRC (inc_insn_set), 0), 1);
              validate_change (insn, &XEXP (use, 0),
                               gen_rtx_fmt_e (inc_code, Pmode, reg), 1);
              if (apply_change_group ())
                {
+                 /* If there is a REG_DEAD note on this insn, we must
+                    change this not to REG_UNUSED meaning that the register
+                    is set, but the value is dead.  Failure to do so will
+                    result in a sched1 abort -- when it recomputes lifetime
+                    information, the number of REG_DEAD notes will have
+                    changed.  */
+                 rtx note = find_reg_note (insn, REG_DEAD, reg);
+                 if (note)
+                   PUT_MODE (note, REG_UNUSED);
+
                  REG_NOTES (insn)
                    = gen_rtx_EXPR_LIST (REG_INC,
                                         reg, REG_NOTES (insn));
@@ -175,7 +197,7 @@ discover_flags_reg ()
   tmp = gen_rtx_REG (word_mode, 10000);
   tmp = gen_add3_insn (tmp, tmp, GEN_INT (2));
 
-  /* If we get something that isn't a simple set, or a 
+  /* If we get something that isn't a simple set, or a
      [(set ..) (clobber ..)], this whole function will go wrong.  */
   if (GET_CODE (tmp) == SET)
     return NULL_RTX;
@@ -208,7 +230,7 @@ discover_flags_reg ()
 /* It is a tedious task identifying when the flags register is live and
    when it is safe to optimize.  Since we process the instruction stream
    multiple times, locate and record these live zones by marking the
-   mode of the instructions -- 
+   mode of the instructions --
 
    QImode is used on the instruction at which the flags becomes live.
 
@@ -236,7 +258,7 @@ mark_flags_life_zones (flags)
   else if (flags != cc0_rtx)
     flags = pc_rtx;
 #endif
-    
+
   /* Simple cases first: if no flags, clear all modes.  If confusing,
      mark the entire function as being in a flags shadow.  */
   if (flags == NULL_RTX || flags == pc_rtx)
@@ -284,7 +306,7 @@ mark_flags_life_zones (flags)
             alive, death, birth.  This lets more important info
             overwrite the mode of lesser info.  */
 
-         if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+         if (INSN_P (insn))
            {
 #ifdef HAVE_cc0
              /* In the cc0 case, death is not marked in reg notes,
@@ -367,13 +389,37 @@ replacement_quality(reg)
      when the registers get tied.  */
   return 2;
 }
+\f
+/* Return 1 if INSN might end a basic block.  */
 
+static int perhaps_ends_bb_p (insn)
+     rtx insn;
+{
+  switch (GET_CODE (insn))
+    {
+    case CODE_LABEL:
+    case JUMP_INSN:
+      /* These always end a basic block.  */
+      return 1;
+
+    case CALL_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.  */
+      if (nonlocal_goto_handler_labels)
+       return 1;
+      /* FALLTHRU */
+    default:
+      return can_throw_internal (insn);
+    }
+}
+\f
 /* INSN is a copy from SRC to DEST, both registers, and SRC does not die
    in INSN.
 
    Search forward to see if SRC dies before either it or DEST is modified,
    but don't scan past the end of a basic block.  If so, we can replace SRC
-   with DEST and let SRC die in INSN. 
+   with DEST and let SRC die in INSN.
 
    This will reduce the number of registers live in that range and may enable
    DEST to be tied to SRC, thus often saving one register in addition to a
@@ -403,18 +449,11 @@ optimize_reg_copy_1 (insn, dest, src)
 
   for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
     {
-      if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
-       break;
-
       /* ??? We can't scan past the end of a basic block without updating
-        the register lifetime info (REG_DEAD/basic_block_live_at_start).
-        A CALL_INSN might be the last insn of a basic block, if it is inside
-        an EH region.  There is no easy way to tell, so we just always break
-        when we see a CALL_INSN if flag_exceptions is nonzero.  */
-      if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+        the register lifetime info (REG_DEAD/basic_block_live_at_start).  */
+      if (perhaps_ends_bb_p (p))
        break;
-
-      if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+      else if (! INSN_P (p))
        continue;
 
       if (reg_set_p (src, p) || reg_set_p (dest, p)
@@ -588,18 +627,11 @@ optimize_reg_copy_2 (insn, dest, src)
 
   for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
     {
-      if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
-       break;
-
       /* ??? We can't scan past the end of a basic block without updating
-        the register lifetime info (REG_DEAD/basic_block_live_at_start).
-        A CALL_INSN might be the last insn of a basic block, if it is inside
-        an EH region.  There is no easy way to tell, so we just always break
-        when we see a CALL_INSN if flag_exceptions is nonzero.  */
-      if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+        the register lifetime info (REG_DEAD/basic_block_live_at_start).  */
+      if (perhaps_ends_bb_p (p))
        break;
-
-      if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+      else if (! INSN_P (p))
        continue;
 
       set = single_set (p);
@@ -611,7 +643,7 @@ optimize_reg_copy_2 (insn, dest, src)
 
          /* Set to stop at next insn.  */
          for (q = insn; q != NEXT_INSN (p); q = NEXT_INSN (q))
-           if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
+           if (INSN_P (q))
              {
                if (reg_mentioned_p (dest, PATTERN (q)))
                  PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
@@ -661,21 +693,11 @@ optimize_reg_copy_3 (insn, dest, src)
       || REG_N_SETS (src_no) != 1)
     return;
   for (p = PREV_INSN (insn); p && ! reg_set_p (src_reg, p); p = PREV_INSN (p))
-    {
-      if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
-       return;
-
-      /* ??? We can't scan past the end of a basic block without updating
-        the register lifetime info (REG_DEAD/basic_block_live_at_start).
-        A CALL_INSN might be the last insn of a basic block, if it is inside
-        an EH region.  There is no easy way to tell, so we just always break
-        when we see a CALL_INSN if flag_exceptions is nonzero.  */
-      if (flag_exceptions && GET_CODE (p) == CALL_INSN)
-       return;
+    /* ??? We can't scan past the end of a basic block without updating
+       the register lifetime info (REG_DEAD/basic_block_live_at_start).  */
+    if (perhaps_ends_bb_p (p))
+      break;
 
-      if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
-       continue;
-    }
   if (! p)
     return;
 
@@ -706,10 +728,10 @@ 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 (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+      if (! INSN_P (p))
        continue;
 
       /* Make a tenative change.  */
@@ -884,7 +906,7 @@ reg_is_remote_constant_p (reg, insn, first)
     {
       rtx s;
 
-      if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+      if (! INSN_P (p))
        continue;
       s = single_set (p);
       if (s != 0
@@ -940,19 +962,11 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file)
     {
       rtx pset;
 
-      if (GET_CODE (p) == CODE_LABEL
-          || GET_CODE (p) == JUMP_INSN)
-        break;
-
       /* ??? We can't scan past the end of a basic block without updating
-        the register lifetime info (REG_DEAD/basic_block_live_at_start).
-        A CALL_INSN might be the last insn of a basic block, if it is inside
-        an EH region.  There is no easy way to tell, so we just always break
-        when we see a CALL_INSN if flag_exceptions is nonzero.  */
-      if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+        the register lifetime info (REG_DEAD/basic_block_live_at_start).  */
+      if (perhaps_ends_bb_p (p))
        break;
-
-      if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+      else if (! INSN_P (p))
         continue;
 
       if (find_regno_note (p, REG_DEAD, REGNO (dst)))
@@ -991,7 +1005,7 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file)
                  if (GET_CODE (p) == CODE_LABEL
                      || GET_CODE (p) == JUMP_INSN)
                    break;
-                 if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+                 if (! INSN_P (p))
                    continue;
                  if (reg_overlap_mentioned_p (dst, PATTERN (p)))
                    {
@@ -1005,7 +1019,7 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file)
                  if (GET_CODE (p) == CODE_LABEL
                      || GET_CODE (p) == JUMP_INSN)
                    break;
-                 if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+                 if (! INSN_P (p))
                    continue;
                  if (reg_overlap_mentioned_p (dst, PATTERN (p)))
                    {
@@ -1059,6 +1073,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 ());
@@ -1160,7 +1179,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
@@ -1209,7 +1228,7 @@ regmove_optimize (f, nregs, regmove_dump_file)
              dst_class = reg_preferred_class (REGNO (dst));
              if (! regclass_compatible_p (src_class, dst_class))
                continue;
-         
+
              if (fixup_match_1 (insn, set, src, src_subreg, dst, pass,
                                 op_no, match_no,
                                 regmove_dump_file))
@@ -1225,7 +1244,7 @@ regmove_optimize (f, nregs, regmove_dump_file)
 
   for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
     {
-      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+      if (INSN_P (insn))
        {
          int op_no, match_no;
          int success = 0;
@@ -1370,21 +1389,12 @@ regmove_optimize (f, nregs, regmove_dump_file)
                {
                  rtx pset;
 
-                 if (GET_CODE (p) == CODE_LABEL
-                     || GET_CODE (p) == JUMP_INSN)
-                   break;
-
                  /* ??? We can't scan past the end of a basic block without
                     updating the register lifetime info
-                    (REG_DEAD/basic_block_live_at_start).
-                    A CALL_INSN might be the last insn of a basic block, if
-                    it is inside an EH region.  There is no easy way to tell,
-                    so we just always break when we see a CALL_INSN if
-                    flag_exceptions is nonzero.  */
-                 if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+                    (REG_DEAD/basic_block_live_at_start).  */
+                 if (perhaps_ends_bb_p (p))
                    break;
-
-                 if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+                 else if (! INSN_P (p))
                    continue;
 
                  length++;
@@ -1587,12 +1597,52 @@ find_matches (insn, matchp)
   return any_matches;
 }
 
+/* Try to replace all occurrences of DST_REG with SRC in LOC, that is
+   assumed to be in INSN.  */
+
+static void
+replace_in_call_usage (loc, dst_reg, src, insn)
+     rtx *loc;
+     int dst_reg;
+     rtx src;
+     rtx insn;
+{
+  rtx x = *loc;
+  enum rtx_code code;
+  const char *fmt;
+  int i, j;
+
+  if (! x)
+    return;
+
+  code = GET_CODE (x);
+  if (code == REG)
+    {
+      if (REGNO (x) != dst_reg)
+       return;
+
+      validate_change (insn, loc, src, 1);
+
+      return;
+    }
+
+  /* Process each of our operands recursively.  */
+  fmt = GET_RTX_FORMAT (code);
+  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+    if (*fmt == 'e')
+      replace_in_call_usage (&XEXP (x, i), dst_reg, src, insn);
+    else if (*fmt == 'E')
+      for (j = 0; j < XVECLEN (x, i); j++)
+       replace_in_call_usage (& XVECEXP (x, i, j), dst_reg, src, insn);
+}
+
 /* Try to replace output operand DST in SET, with input operand SRC.  SET is
    the only set in INSN.  INSN has just been recognized and constrained.
    SRC is operand number OPERAND_NUMBER in INSN.
    DST is operand number MATCH_NUMBER in INSN.
    If BACKWARD is nonzero, we have been called in a backward pass.
    Return nonzero for success.  */
+
 static int
 fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
               match_number, regmove_dump_file)
@@ -1658,18 +1708,15 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
 
   for (length = s_length = 0, p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
     {
-      if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
-       break;
+      if (GET_CODE (p) == CALL_INSN)
+       replace_in_call_usage (& CALL_INSN_FUNCTION_USAGE (p),
+                              REGNO (dst), src, p);
 
       /* ??? We can't scan past the end of a basic block without updating
-        the register lifetime info (REG_DEAD/basic_block_live_at_start).
-        A CALL_INSN might be the last insn of a basic block, if it is
-        inside an EH region.  There is no easy way to tell, so we just
-        always break when we see a CALL_INSN if flag_exceptions is nonzero.  */
-      if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+        the register lifetime info (REG_DEAD/basic_block_live_at_start).  */
+      if (perhaps_ends_bb_p (p))
        break;
-
-      if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+      else if (! INSN_P (p))
        continue;
 
       length++;
@@ -1704,29 +1751,18 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
                break;
              for (q = p; q; q = NEXT_INSN (q))
                {
-                 if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN)
-                   {
-                     q = 0;
-                     break;
-                   }
-
                  /* ??? We can't scan past the end of a basic block without
                     updating the register lifetime info
-                    (REG_DEAD/basic_block_live_at_start).
-                    A CALL_INSN might be the last insn of a basic block, if
-                    it is inside an EH region.  There is no easy way to tell,
-                    so we just always break when we see a CALL_INSN if
-                    flag_exceptions is nonzero.  */
-                 if (flag_exceptions && GET_CODE (q) == CALL_INSN)
+                    (REG_DEAD/basic_block_live_at_start).  */
+                 if (perhaps_ends_bb_p (q))
                    {
                      q = 0;
                      break;
                    }
-
-                 if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
+                 else if (! INSN_P (q))
                    continue;
-                 if (reg_overlap_mentioned_p (src, PATTERN (q))
-                     || reg_set_p (src, q))
+                 else if (reg_overlap_mentioned_p (src, PATTERN (q))
+                          || reg_set_p (src, q))
                    break;
                }
              if (q)
@@ -1771,8 +1807,9 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
                  /* Reject out of range shifts.  */
                  if (code != PLUS
                      && (newconst < 0
-                         || (newconst
-                             >= GET_MODE_BITSIZE (GET_MODE (SET_SRC (set2))))))
+                         || ((unsigned HOST_WIDE_INT) newconst
+                             >= (GET_MODE_BITSIZE (GET_MODE
+                                                   (SET_SRC (set2)))))))
                    break;
                  if (code == PLUS)
                    {
@@ -1859,8 +1896,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
          /* emit_insn_after_with_line_notes has no
             return value, so search for the new insn.  */
          insn = p;
-         while (GET_RTX_CLASS (GET_CODE (insn)) != 'i'
-                || PATTERN (insn) != pat)
+         while (! INSN_P (insn) || PATTERN (insn) != pat)
            insn = PREV_INSN (insn);
 
          REG_NOTES (insn) = notes;
@@ -1880,27 +1916,17 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
        {
          for (q = PREV_INSN (insn); q; q = PREV_INSN(q))
            {
-             if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN)
-               {
-                 q = 0;
-                 break;
-               }
-
              /* ??? We can't scan past the end of a basic block without
                 updating the register lifetime info
-                (REG_DEAD/basic_block_live_at_start).
-                A CALL_INSN might be the last insn of a basic block, if
-                it is inside an EH region.  There is no easy way to tell,
-                so we just always break when we see a CALL_INSN if
-                flag_exceptions is nonzero.  */
-             if (flag_exceptions && GET_CODE (q) == CALL_INSN)
+                (REG_DEAD/basic_block_live_at_start).  */
+             if (perhaps_ends_bb_p (q))
                {
                  q = 0;
                  break;
                }
-
-             if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
+             else if (! INSN_P (q))
                continue;
+
              s_length2++;
              if (reg_set_p (src, q))
                {
@@ -1952,26 +1978,20 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
       inc_dest = post_inc_set ? SET_DEST (post_inc_set) : src;
       for (q = post_inc; (q = NEXT_INSN (q)); )
        {
-         if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN)
-           break;
-
          /* ??? We can't scan past the end of a basic block without updating
-            the register lifetime info (REG_DEAD/basic_block_live_at_start).
-            A CALL_INSN might be the last insn of a basic block, if it
-            is inside an EH region.  There is no easy way to tell so we
-            just always break when we see a CALL_INSN if flag_exceptions
-            is nonzero.  */
-         if (flag_exceptions && GET_CODE (q) == CALL_INSN)
+            the register lifetime info
+            (REG_DEAD/basic_block_live_at_start). */
+         if (perhaps_ends_bb_p (q))
            break;
-
-         if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
+         else if (! INSN_P (q))
            continue;
-         if (src != inc_dest && (reg_overlap_mentioned_p (src, PATTERN (q))
-                                 || reg_set_p (src, q)))
+         else if (src != inc_dest
+                  && (reg_overlap_mentioned_p (src, PATTERN (q))
+                      || reg_set_p (src, q)))
            break;
-         if (reg_set_p (inc_dest, q))
+         else if (reg_set_p (inc_dest, q))
            break;
-         if (reg_overlap_mentioned_p (inc_dest, PATTERN (q)))
+         else if (reg_overlap_mentioned_p (inc_dest, PATTERN (q)))
            {
              try_auto_increment (q, post_inc,
                                  post_inc_set, inc_dest, newconst, 1);
@@ -1979,6 +1999,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
            }
        }
     }
+
   /* Move the death note for DST to INSN if it is used
      there.  */
   if (reg_overlap_mentioned_p (dst, PATTERN (insn)))
@@ -2060,7 +2081,7 @@ stable_and_no_regs_but_for_p (x, src, dst)
     }
 }
 \f
-/* Track stack adjustments and stack memory references.  Attempt to 
+/* Track stack adjustments and stack memory references.  Attempt to
    reduce the number of stack adjustments by back-propogating across
    the memory references.
 
@@ -2099,7 +2120,7 @@ static struct csa_memlist *record_one_stack_memref
 static int try_apply_stack_adjustment
   PARAMS ((rtx, struct csa_memlist *, HOST_WIDE_INT, HOST_WIDE_INT));
 static void combine_stack_adjustments_for_block PARAMS ((basic_block));
-static int record_stack_memrefs        PARAMS ((rtx *, void *));
+static int record_stack_memrefs        PARAMS ((rtx *, void *));
 
 
 /* Main entry point for stack adjustment combination.  */
@@ -2219,14 +2240,6 @@ 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);
 
@@ -2236,13 +2249,6 @@ try_apply_stack_adjustment (insn, memlist, new_adjust, 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);
     }
@@ -2291,11 +2297,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:
@@ -2306,7 +2317,7 @@ record_stack_memrefs (xp, data)
 
 /* Subroutine of combine_stack_adjustments, called for each basic block.  */
 
-static void 
+static void
 combine_stack_adjustments_for_block (bb)
      basic_block bb;
 {
@@ -2352,35 +2363,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 ocurrence 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!  */
+                     flow_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;
            }
 
@@ -2388,9 +2427,17 @@ combine_stack_adjustments_for_block (bb)
             turn it into a direct store.  Obviously we can't do this if
             there were any intervening uses of the stack pointer.  */
          if (memlist == NULL
-             && last_sp_adjust == GET_MODE_SIZE (GET_MODE (dest))
              && GET_CODE (dest) == MEM
-             && GET_CODE (XEXP (dest, 0)) == PRE_DEC
+             && ((GET_CODE (XEXP (dest, 0)) == PRE_DEC
+                  && (last_sp_adjust
+                      == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest))))
+                 || (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY
+                     && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS
+                     && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx
+                     && (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1))
+                         == CONST_INT)
+                     && (INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1))
+                         == -last_sp_adjust)))
              && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx
              && ! reg_mentioned_p (stack_pointer_rtx, src)
              && memory_address_p (GET_MODE (dest), stack_pointer_rtx)
@@ -2420,7 +2467,7 @@ combine_stack_adjustments_for_block (bb)
        }
       memlist = data.memlist;
 
-      /* Otherwise, we were not able to process the instruction. 
+      /* Otherwise, we were not able to process the instruction.
         Do not continue collecting data across such a one.  */
       if (last_sp_set
          && (GET_CODE (insn) == CALL_INSN