]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/auto-inc-dec.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / auto-inc-dec.c
index 64a370655f6996eda3d7a9b6ce8141aa159ed816..c531df8815ccfbd148fc3c59a68ed262ccd6f93c 100644 (file)
@@ -1,5 +1,5 @@
 /* Discovery of auto-inc and auto-dec instructions.
-   Copyright (C) 2006-2014 Free Software Foundation, Inc.
+   Copyright (C) 2006-2021 Free Software Foundation, Inc.
    Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
 
 This file is part of GCC.
@@ -21,24 +21,22 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
+#include "backend.h"
+#include "target.h"
 #include "rtl.h"
-#include "tm_p.h"
-#include "hard-reg-set.h"
-#include "basic-block.h"
+#include "tree.h"
+#include "predict.h"
+#include "df.h"
 #include "insn-config.h"
-#include "regs.h"
-#include "flags.h"
-#include "function.h"
-#include "except.h"
-#include "diagnostic-core.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
 #include "recog.h"
+#include "cfgrtl.h"
 #include "expr.h"
 #include "tree-pass.h"
-#include "df.h"
 #include "dbgcnt.h"
-#include "target.h"
+#include "print-rtl.h"
+#include "valtrack.h"
 
 /* This pass was originally removed from flow.c. However there is
    almost nothing that remains of that code.
@@ -56,6 +54,21 @@ along with GCC; see the file COPYING3.  If not see
            ...
            *(a += c) pre
 
+        or, alternately,
+
+           a <- b + c
+           ...
+           *b
+
+        becomes
+
+           a <- b
+           ...
+           *(a += c) post
+
+        This uses a post-add, but it's handled as FORM_PRE_ADD because
+        the "increment" insn appears before the memory access.
+
 
       (2) FORM_PRE_INC
            a += c
@@ -64,6 +77,7 @@ along with GCC; see the file COPYING3.  If not see
 
         becomes
 
+           ...
            *(a += c) pre
 
 
@@ -78,8 +92,8 @@ along with GCC; see the file COPYING3.  If not see
         becomes
 
            b <- a
-           ...
            *(b += c) post
+           ...
 
 
       (4) FORM_POST_INC
@@ -90,6 +104,8 @@ along with GCC; see the file COPYING3.  If not see
         becomes
 
            *(a += c) post
+           ...
+
 
   There are three types of values of c.
 
@@ -110,7 +126,6 @@ along with GCC; see the file COPYING3.  If not see
   before the ref or +c if the increment was after the ref, then if we
   can do the combination but switch the pre/post bit.  */
 
-#ifdef AUTO_INC_DEC
 
 enum form
 {
@@ -156,14 +171,14 @@ enum gen_form
 static rtx mem_tmp;
 
 static enum inc_state
-set_inc_state (HOST_WIDE_INT val, int size)
+set_inc_state (HOST_WIDE_INT val, poly_int64 size)
 {
   if (val == 0)
     return INC_ZERO;
   if (val < 0)
-    return (val == -size) ? INC_NEG_SIZE : INC_NEG_ANY;
+    return known_eq (val, -size) ? INC_NEG_SIZE : INC_NEG_ANY;
   else
-    return (val == size) ? INC_POS_SIZE : INC_POS_ANY;
+    return known_eq (val, size) ? INC_POS_SIZE : INC_POS_ANY;
 }
 
 /* The DECISION_TABLE that describes what form, if any, the increment
@@ -295,7 +310,7 @@ init_decision_table (void)
 
 static struct inc_insn
 {
-  rtx insn;           /* The insn being parsed.  */
+  rtx_insn *insn;     /* The insn being parsed.  */
   rtx pat;            /* The pattern of the insn.  */
   bool reg1_is_const; /* True if reg1 is const, false if reg1 is a reg.  */
   enum form form;
@@ -355,7 +370,7 @@ dump_inc_insn (FILE *file)
 
 static struct mem_insn
 {
-  rtx insn;           /* The insn being parsed.  */
+  rtx_insn *insn;     /* The insn being parsed.  */
   rtx pat;            /* The pattern of the insn.  */
   rtx *mem_loc;       /* The address of the field that holds the mem */
                       /* that is to be replaced.  */
@@ -397,9 +412,10 @@ dump_mem_insn (FILE *file)
    must be compared with the current block.
 */
 
-static rtx *reg_next_use = NULL;
-static rtx *reg_next_inc_use = NULL;
-static rtx *reg_next_def = NULL;
+static rtx_insn **reg_next_debug_use = NULL;
+static rtx_insn **reg_next_use = NULL;
+static rtx_insn **reg_next_inc_use = NULL;
+static rtx_insn **reg_next_def = NULL;
 
 
 /* Move dead note that match PATTERN to TO_INSN from FROM_INSN.  We do
@@ -408,7 +424,7 @@ static rtx *reg_next_def = NULL;
    does not appear that there are any other kinds of relevant notes.  */
 
 static void
-move_dead_notes (rtx to_insn, rtx from_insn, rtx pattern)
+move_dead_notes (rtx_insn *to_insn, rtx_insn *from_insn, rtx pattern)
 {
   rtx note;
   rtx next_note;
@@ -432,24 +448,6 @@ move_dead_notes (rtx to_insn, rtx from_insn, rtx pattern)
     }
 }
 
-
-/* Create a mov insn DEST_REG <- SRC_REG and insert it before
-   NEXT_INSN.  */
-
-static rtx
-insert_move_insn_before (rtx next_insn, rtx dest_reg, rtx src_reg)
-{
-  rtx insns;
-
-  start_sequence ();
-  emit_move_insn (dest_reg, src_reg);
-  insns = get_insns ();
-  end_sequence ();
-  emit_insn_before (insns, next_insn);
-  return insns;
-}
-
-
 /* Change mem_insn.mem_loc so that uses NEW_ADDR which has an
    increment of INC_REG.  To have reached this point, the change is a
    legitimate one from a dataflow point of view.  The only questions
@@ -469,10 +467,11 @@ attempt_change (rtx new_addr, rtx inc_reg)
      handled mov free.  */
 
   basic_block bb = BLOCK_FOR_INSN (mem_insn.insn);
-  rtx mov_insn = NULL;
+  rtx_insn *mov_insn = NULL;
   int regno;
   rtx mem = *mem_insn.mem_loc;
-  enum machine_mode mode = GET_MODE (mem);
+  machine_mode mode = GET_MODE (mem);
+  int align = MEM_ALIGN (mem);
   rtx new_mem;
   int old_cost = 0;
   int new_cost = 0;
@@ -480,10 +479,24 @@ attempt_change (rtx new_addr, rtx inc_reg)
 
   PUT_MODE (mem_tmp, mode);
   XEXP (mem_tmp, 0) = new_addr;
+  set_mem_align (mem_tmp, align);
 
-  old_cost = (set_src_cost (mem, speed)
+  old_cost = (set_src_cost (mem, mode, speed)
              + set_rtx_cost (PATTERN (inc_insn.insn), speed));
-  new_cost = set_src_cost (mem_tmp, speed);
+
+  new_cost = set_src_cost (mem_tmp, mode, speed);
+
+  /* In the FORM_PRE_ADD and FORM_POST_ADD cases we emit an extra move
+     whose cost we should account for.  */
+  if (inc_insn.form == FORM_PRE_ADD
+      || inc_insn.form == FORM_POST_ADD)
+    {
+      start_sequence ();
+      emit_move_insn (inc_insn.reg_res, inc_insn.reg0);
+      mov_insn = get_insns ();
+      end_sequence ();
+      new_cost += seq_cost (mov_insn, speed);
+    }
 
   /* The first item of business is to see if this is profitable.  */
   if (old_cost < new_cost)
@@ -515,44 +528,120 @@ attempt_change (rtx new_addr, rtx inc_reg)
       /* Replace the addition with a move.  Do it at the location of
         the addition since the operand of the addition may change
         before the memory reference.  */
-      mov_insn = insert_move_insn_before (inc_insn.insn,
-                                         inc_insn.reg_res, inc_insn.reg0);
-      move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);
+      gcc_assert (mov_insn);
+      emit_insn_before (mov_insn, inc_insn.insn);
+      regno = REGNO (inc_insn.reg0);
+      /* ??? Could REGNO possibly be used in MEM_INSN other than in
+        the MEM address, and still die there, so that move_dead_notes
+        would incorrectly move the note?  */
+      if (reg_next_use[regno] == mem_insn.insn)
+       move_dead_notes (mov_insn, mem_insn.insn, inc_insn.reg0);
+      else
+       move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);
 
       regno = REGNO (inc_insn.reg_res);
+      if (reg_next_debug_use && reg_next_debug_use[regno]
+         && BLOCK_FOR_INSN (reg_next_debug_use[regno]) == bb)
+       {
+         rtx adjres = gen_rtx_PLUS (GET_MODE (inc_insn.reg_res),
+                                    inc_insn.reg_res, inc_insn.reg1);
+         if (dump_file)
+           fprintf (dump_file, "adjusting debug insns\n");
+         propagate_for_debug (PREV_INSN (reg_next_debug_use[regno]),
+                              mem_insn.insn,
+                              inc_insn.reg_res, adjres, bb);
+         reg_next_debug_use[regno] = NULL;
+       }
       reg_next_def[regno] = mov_insn;
       reg_next_use[regno] = NULL;
+
       regno = REGNO (inc_insn.reg0);
+      if (reg_next_debug_use && reg_next_debug_use[regno]
+         && BLOCK_FOR_INSN (reg_next_debug_use[regno]) == bb
+         && find_reg_note (mov_insn, REG_DEAD, inc_insn.reg0))
+       {
+         if (dump_file)
+           fprintf (dump_file, "remapping debug insns\n");
+         propagate_for_debug (PREV_INSN (reg_next_debug_use[regno]),
+                              mem_insn.insn,
+                              inc_insn.reg0, inc_insn.reg_res, bb);
+         reg_next_debug_use[regno] = NULL;
+       }
       reg_next_use[regno] = mov_insn;
       df_recompute_luids (bb);
       break;
 
     case FORM_POST_INC:
       regno = REGNO (inc_insn.reg_res);
+      if (reg_next_debug_use && reg_next_debug_use[regno]
+         && BLOCK_FOR_INSN (reg_next_debug_use[regno]) == bb)
+       {
+         rtx adjres = gen_rtx_MINUS (GET_MODE (inc_insn.reg_res),
+                                     inc_insn.reg_res, inc_insn.reg1);
+         if (dump_file)
+           fprintf (dump_file, "adjusting debug insns\n");
+         propagate_for_debug (PREV_INSN (reg_next_debug_use[regno]),
+                              inc_insn.insn,
+                              inc_insn.reg_res, adjres, bb);
+         reg_next_debug_use[regno] = NULL;
+       }
       if (reg_next_use[regno] == reg_next_inc_use[regno])
        reg_next_inc_use[regno] = NULL;
 
       /* Fallthru.  */
     case FORM_PRE_INC:
       regno = REGNO (inc_insn.reg_res);
+      /* Despite the fall-through, we won't run this twice: we'll have
+        already cleared reg_next_debug_use[regno] before falling
+        through.  */
+      if (reg_next_debug_use && reg_next_debug_use[regno]
+         && BLOCK_FOR_INSN (reg_next_debug_use[regno]) == bb)
+       {
+         rtx adjres = gen_rtx_PLUS (GET_MODE (inc_insn.reg_res),
+                                    inc_insn.reg_res, inc_insn.reg1);
+         if (dump_file)
+           fprintf (dump_file, "adjusting debug insns\n");
+         propagate_for_debug (PREV_INSN (reg_next_debug_use[regno]),
+                              mem_insn.insn,
+                              inc_insn.reg_res, adjres, bb);
+         if (DF_INSN_LUID (mem_insn.insn)
+             < DF_INSN_LUID (reg_next_debug_use[regno]))
+           reg_next_debug_use[regno] = NULL;
+       }
       reg_next_def[regno] = mem_insn.insn;
       reg_next_use[regno] = NULL;
 
       break;
 
     case FORM_POST_ADD:
-      mov_insn = insert_move_insn_before (mem_insn.insn,
-                                         inc_insn.reg_res, inc_insn.reg0);
+      gcc_assert (mov_insn);
+      emit_insn_before (mov_insn, mem_insn.insn);
       move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);
 
       /* Do not move anything to the mov insn because the instruction
         pointer for the main iteration has not yet hit that.  It is
         still pointing to the mem insn. */
       regno = REGNO (inc_insn.reg_res);
+      /* The pseudo is now set earlier, so it must have been dead in
+        that range, and dead registers cannot be referenced in debug
+        insns.  */
+      gcc_assert (!(reg_next_debug_use && reg_next_debug_use[regno]
+                   && BLOCK_FOR_INSN (reg_next_debug_use[regno]) == bb));
       reg_next_def[regno] = mem_insn.insn;
       reg_next_use[regno] = NULL;
 
       regno = REGNO (inc_insn.reg0);
+      if (reg_next_debug_use && reg_next_debug_use[regno]
+         && BLOCK_FOR_INSN (reg_next_debug_use[regno]) == bb
+         && find_reg_note (mov_insn, REG_DEAD, inc_insn.reg0))
+       {
+         if (dump_file)
+           fprintf (dump_file, "remapping debug insns\n");
+         propagate_for_debug (PREV_INSN (reg_next_debug_use[regno]),
+                              inc_insn.insn,
+                              inc_insn.reg0, inc_insn.reg_res, bb);
+         reg_next_debug_use[regno] = NULL;
+       }
       reg_next_use[regno] = mem_insn.insn;
       if ((reg_next_use[regno] == reg_next_inc_use[regno])
          || (reg_next_inc_use[regno] == inc_insn.insn))
@@ -610,9 +699,9 @@ try_merge (void)
     inc_insn.reg_res : mem_insn.reg0;
 
   /* The width of the mem being accessed.  */
-  int size = GET_MODE_SIZE (GET_MODE (mem));
-  rtx last_insn = NULL;
-  enum machine_mode reg_mode = GET_MODE (inc_reg);
+  poly_int64 size = GET_MODE_SIZE (GET_MODE (mem));
+  rtx_insn *last_insn = NULL;
+  machine_mode reg_mode = GET_MODE (inc_reg);
 
   switch (inc_insn.form)
     {
@@ -668,25 +757,21 @@ try_merge (void)
       if (dump_file)
        fprintf (dump_file, "trying SIMPLE_PRE_INC\n");
       return attempt_change (gen_rtx_PRE_INC (reg_mode, inc_reg), inc_reg);
-      break;
 
     case SIMPLE_POST_INC:    /* size++  */
       if (dump_file)
        fprintf (dump_file, "trying SIMPLE_POST_INC\n");
       return attempt_change (gen_rtx_POST_INC (reg_mode, inc_reg), inc_reg);
-      break;
 
     case SIMPLE_PRE_DEC:     /* --size  */
       if (dump_file)
        fprintf (dump_file, "trying SIMPLE_PRE_DEC\n");
       return attempt_change (gen_rtx_PRE_DEC (reg_mode, inc_reg), inc_reg);
-      break;
 
     case SIMPLE_POST_DEC:    /* size--  */
       if (dump_file)
        fprintf (dump_file, "trying SIMPLE_POST_DEC\n");
       return attempt_change (gen_rtx_POST_DEC (reg_mode, inc_reg), inc_reg);
-      break;
 
     case DISP_PRE:           /* ++con   */
       if (dump_file)
@@ -697,7 +782,6 @@ try_merge (void)
                                                               inc_reg,
                                                               inc_insn.reg1)),
                             inc_reg);
-      break;
 
     case DISP_POST:          /* con++   */
       if (dump_file)
@@ -708,7 +792,6 @@ try_merge (void)
                                                                inc_reg,
                                                                inc_insn.reg1)),
                             inc_reg);
-      break;
 
     case REG_PRE:            /* ++reg   */
       if (dump_file)
@@ -719,7 +802,6 @@ try_merge (void)
                                                               inc_reg,
                                                               inc_insn.reg1)),
                             inc_reg);
-      break;
 
     case REG_POST:            /* reg++   */
       if (dump_file)
@@ -730,7 +812,6 @@ try_merge (void)
                                                                inc_reg,
                                                                inc_insn.reg1)),
                             inc_reg);
-      break;
     }
 }
 
@@ -738,10 +819,10 @@ try_merge (void)
    NEXT_ARRAY) or defines (if reg_next_def is passed in NEXT_ARRAY)
    REGNO in BB.  */
 
-static rtx
-get_next_ref (int regno, basic_block bb, rtx *next_array)
+static rtx_insn *
+get_next_ref (int regno, basic_block bb, rtx_insn **next_array)
 {
-  rtx insn = next_array[regno];
+  rtx_insn *insn = next_array[regno];
 
   /* Lazy about cleaning out the next_arrays.  */
   if (insn && BLOCK_FOR_INSN (insn) != bb)
@@ -754,28 +835,6 @@ get_next_ref (int regno, basic_block bb, rtx *next_array)
 }
 
 
-/* Reverse the operands in a mem insn.  */
-
-static void
-reverse_mem (void)
-{
-  rtx tmp = mem_insn.reg1;
-  mem_insn.reg1 = mem_insn.reg0;
-  mem_insn.reg0 = tmp;
-}
-
-
-/* Reverse the operands in a inc insn.  */
-
-static void
-reverse_inc (void)
-{
-  rtx tmp = inc_insn.reg1;
-  inc_insn.reg1 = inc_insn.reg0;
-  inc_insn.reg0 = tmp;
-}
-
-
 /* Return true if INSN is of a form "a = b op c" where a and b are
    regs.  op is + if c is a reg and +|- if c is a const.  Fill in
    INC_INSN with what is found.
@@ -787,7 +846,7 @@ reverse_inc (void)
    processed.  */
 
 static bool
-parse_add_or_inc (rtx insn, bool before_mem)
+parse_add_or_inc (rtx_insn *insn, bool before_mem)
 {
   rtx pat = single_set (insn);
   if (!pat)
@@ -808,6 +867,12 @@ parse_add_or_inc (rtx insn, bool before_mem)
   inc_insn.pat = pat;
   inc_insn.reg_res = SET_DEST (pat);
   inc_insn.reg0 = XEXP (SET_SRC (pat), 0);
+
+  /* Block any auto increment of the frame pointer since it expands into
+     an addition and cannot be removed by copy propagation.  */
+  if (inc_insn.reg0 == frame_pointer_rtx)
+    return false;
+
   if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg0))
     inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC;
   else
@@ -844,7 +909,7 @@ parse_add_or_inc (rtx insn, bool before_mem)
        {
          /* Reverse the two operands and turn *_ADD into *_INC since
             a = c + a.  */
-         reverse_inc ();
+         std::swap (inc_insn.reg0, inc_insn.reg1);
          inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC;
          return true;
        }
@@ -858,13 +923,15 @@ parse_add_or_inc (rtx insn, bool before_mem)
 
 /* A recursive function that checks all of the mem uses in
    ADDRESS_OF_X to see if any single one of them is compatible with
-   what has been found in inc_insn.
+   what has been found in inc_insn.  To avoid accidental matches, we
+   will only find MEMs with FINDREG, be it inc_insn.reg_res, be it
+   inc_insn.reg0.
 
    -1 is returned for success.  0 is returned if nothing was found and
    1 is returned for failure. */
 
 static int
-find_address (rtx *address_of_x)
+find_address (rtx *address_of_x, rtx findreg)
 {
   rtx x = *address_of_x;
   enum rtx_code code = GET_CODE (x);
@@ -873,9 +940,10 @@ find_address (rtx *address_of_x)
   int value = 0;
   int tem;
 
-  if (code == MEM && rtx_equal_p (XEXP (x, 0), inc_insn.reg_res))
+  if (code == MEM && findreg == inc_insn.reg_res
+      && rtx_equal_p (XEXP (x, 0), inc_insn.reg_res))
     {
-      /* Match with *reg0.  */
+      /* Match with *reg_res.  */
       mem_insn.mem_loc = address_of_x;
       mem_insn.reg0 = inc_insn.reg_res;
       mem_insn.reg1_is_const = true;
@@ -883,7 +951,21 @@ find_address (rtx *address_of_x)
       mem_insn.reg1 = GEN_INT (0);
       return -1;
     }
-  if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS
+  if (code == MEM && inc_insn.reg1_is_const && inc_insn.reg0
+      && findreg == inc_insn.reg0
+      && rtx_equal_p (XEXP (x, 0), inc_insn.reg0))
+    {
+      /* Match with *reg0, assumed to be equivalent to
+         *(reg_res - reg1_val); callers must check whether this is the case.  */
+      mem_insn.mem_loc = address_of_x;
+      mem_insn.reg0 = inc_insn.reg_res;
+      mem_insn.reg1_is_const = true;
+      mem_insn.reg1_val = -inc_insn.reg1_val;
+      mem_insn.reg1 = GEN_INT (mem_insn.reg1_val);
+      return -1;
+    }
+  if (code == MEM && findreg == inc_insn.reg_res
+      && GET_CODE (XEXP (x, 0)) == PLUS
       && rtx_equal_p (XEXP (XEXP (x, 0), 0), inc_insn.reg_res))
     {
       rtx b = XEXP (XEXP (x, 0), 1);
@@ -912,7 +994,7 @@ find_address (rtx *address_of_x)
     {
       /* If REG occurs inside a MEM used in a bit-field reference,
         that is unacceptable.  */
-      if (find_address (&XEXP (x, 0)))
+      if (find_address (&XEXP (x, 0), findreg))
        return 1;
     }
 
@@ -924,7 +1006,7 @@ find_address (rtx *address_of_x)
     {
       if (fmt[i] == 'e')
        {
-         tem = find_address (&XEXP (x, i));
+         tem = find_address (&XEXP (x, i), findreg);
          /* If this is the first use, let it go so the rest of the
             insn can be checked.  */
          if (value == 0)
@@ -938,7 +1020,7 @@ find_address (rtx *address_of_x)
          int j;
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
            {
-             tem = find_address (&XVECEXP (x, i, j));
+             tem = find_address (&XVECEXP (x, i, j), findreg);
              /* If this is the first use, let it go so the rest of
                 the insn can be checked.  */
              if (value == 0)
@@ -966,9 +1048,9 @@ find_address (rtx *address_of_x)
 static bool
 find_inc (bool first_try)
 {
-  rtx insn;
+  rtx_insn *insn;
   basic_block bb = BLOCK_FOR_INSN (mem_insn.insn);
-  rtx other_insn;
+  rtx_insn *other_insn;
   df_ref def;
 
   /* Make sure this reg appears only once in this insn.  */
@@ -1004,7 +1086,7 @@ find_inc (bool first_try)
         find this.  Only try it once though.  */
       if (first_try && !mem_insn.reg1_is_const)
        {
-         reverse_mem ();
+         std::swap (mem_insn.reg0, mem_insn.reg1);
          return find_inc (false);
        }
       else
@@ -1038,9 +1120,9 @@ find_inc (bool first_try)
     {
       /* Make sure that there is no insn that assigns to inc_insn.res
         between the mem_insn and the inc_insn.  */
-      rtx other_insn = get_next_ref (REGNO (inc_insn.reg_res),
-                                    BLOCK_FOR_INSN (mem_insn.insn),
-                                    reg_next_def);
+      rtx_insn *other_insn = get_next_ref (REGNO (inc_insn.reg_res),
+                                          BLOCK_FOR_INSN (mem_insn.insn),
+                                          reg_next_def);
       if (other_insn != inc_insn.insn)
        {
          if (dump_file)
@@ -1105,7 +1187,7 @@ find_inc (bool first_try)
                    return false;
 
                  if (!rtx_equal_p (mem_insn.reg0, inc_insn.reg0))
-                   reverse_inc ();
+                   std::swap (inc_insn.reg0, inc_insn.reg1);
                }
 
              other_insn
@@ -1129,7 +1211,7 @@ find_inc (bool first_try)
         then we just abandon this.  */
 
       int luid = DF_INSN_LUID (inc_insn.insn);
-      rtx other_insn;
+      rtx_insn *other_insn;
 
       /* Make sure this reg appears only once in this insn.  */
       if (count_occurrences (PATTERN (mem_insn.insn), mem_insn.reg1, 1) != 1)
@@ -1155,7 +1237,7 @@ find_inc (bool first_try)
                  /* See comment above on find_inc (false) call.  */
                  if (first_try)
                    {
-                     reverse_mem ();
+                     std::swap (mem_insn.reg0, mem_insn.reg1);
                      return find_inc (false);
                    }
                  else
@@ -1174,7 +1256,7 @@ find_inc (bool first_try)
            {
              /* We know that mem_insn.reg0 must equal inc_insn.reg1
                 or else we would not have found the inc insn.  */
-             reverse_mem ();
+             std::swap (mem_insn.reg0, mem_insn.reg1);
              if (!rtx_equal_p (mem_insn.reg0, inc_insn.reg0))
                {
                  /* See comment above on find_inc (false) call.  */
@@ -1213,7 +1295,7 @@ find_inc (bool first_try)
            {
              if (first_try)
                {
-                 reverse_mem ();
+                 std::swap (mem_insn.reg0, mem_insn.reg1);
                  return find_inc (false);
                }
              else
@@ -1332,8 +1414,8 @@ find_mem (rtx *address_of_x)
 static void
 merge_in_block (int max_reg, basic_block bb)
 {
-  rtx insn;
-  rtx curr;
+  rtx_insn *insn;
+  rtx_insn *curr;
   int success_in_block = 0;
 
   if (dump_file)
@@ -1341,16 +1423,27 @@ merge_in_block (int max_reg, basic_block bb)
 
   FOR_BB_INSNS_REVERSE_SAFE (bb, insn, curr)
     {
-      df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
       bool insn_is_add_or_inc = true;
 
       if (!NONDEBUG_INSN_P (insn))
-       continue;
+       {
+         if (DEBUG_BIND_INSN_P (insn))
+           {
+             df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
+             df_ref use;
+
+             if (dump_file)
+               dump_insn_slim (dump_file, insn);
+
+             FOR_EACH_INSN_INFO_USE (use, insn_info)
+               reg_next_debug_use[DF_REF_REGNO (use)] = insn;
+           }
+         continue;
+       }
 
-      /* This continue is deliberate.  We do not want the uses of the
-        jump put into reg_next_use because it is not considered safe to
-        combine a preincrement with a jump.  */
-      if (JUMP_P (insn))
+      /* Reload should handle auto-inc within a jump correctly, while LRA
+        is known to have issues with autoinc.  */
+      if (JUMP_P (insn) && targetm.lra_p ())
        continue;
 
       if (dump_file)
@@ -1378,7 +1471,7 @@ merge_in_block (int max_reg, basic_block bb)
                         clear of c because the inc insn is going to move
                         into the mem_insn.insn.  */
                      int luid = DF_INSN_LUID (mem_insn.insn);
-                     rtx other_insn
+                     rtx_insn *other_insn
                        = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_use);
 
                      if (other_insn && luid > DF_INSN_LUID (other_insn))
@@ -1394,7 +1487,106 @@ merge_in_block (int max_reg, basic_block bb)
                  if (dump_file)
                    dump_inc_insn (dump_file);
 
-                 if (ok && find_address (&PATTERN (mem_insn.insn)) == -1)
+                 if (ok && find_address (&PATTERN (mem_insn.insn),
+                                         inc_insn.reg_res) == -1)
+                   {
+                     if (dump_file)
+                       dump_mem_insn (dump_file);
+                     if (try_merge ())
+                       {
+                         success_in_block++;
+                         insn_is_add_or_inc = false;
+                       }
+                   }
+               }
+
+             if (insn_is_add_or_inc
+                 /* find_address will only recognize an address
+                    with a reg0 that's not reg_res when
+                    reg1_is_const, so cut it off early if we
+                    already know it won't match.  */
+                 && inc_insn.reg1_is_const
+                 && inc_insn.reg0
+                 && inc_insn.reg0 != inc_insn.reg_res)
+               {
+                 /* If we identified an inc_insn that uses two
+                    different pseudos, it's of the form
+
+                    (set reg_res (plus reg0 reg1))
+
+                    where reg1 is a constant (*).
+
+                    The next use of reg_res was not identified by
+                    find_address as a mem_insn that we could turn
+                    into auto-inc, so see if we find a suitable
+                    MEM in the next use of reg0, as long as it's
+                    before any subsequent use of reg_res:
+
+                    ... (mem (... reg0 ...)) ...
+
+                    ... reg_res ...
+
+                    In this case, we can turn the plus into a
+                    copy, and the reg0 in the MEM address into a
+                    post_inc of reg_res:
+
+                    (set reg_res reg0)
+
+                    ... (mem (... (post_add reg_res reg1) ...)) ...
+
+                    reg_res will then have the correct value at
+                    subsequent uses, and reg0 will remain
+                    unchanged.
+
+                    (*) We could support non-const reg1, but then
+                    we'd have to check that reg1 remains
+                    unchanged all the way to the modified MEM,
+                    and we'd have to extend find_address to
+                    represent a non-const negated reg1.  */
+                 regno = REGNO (inc_insn.reg0);
+                 rtx_insn *reg0_use = get_next_ref (regno, bb,
+                                                    reg_next_use);
+
+                 /* Give up if the next use of reg0 is after the next
+                    use of reg_res (same insn is ok; we might have
+                    found a MEM with reg_res before, and that failed,
+                    but now we try reg0, which might work), or defs
+                    of reg_res (same insn is not ok, we'd introduce
+                    another def in the same insn) or reg0.  */
+                 if (reg0_use)
+                   {
+                     int luid = DF_INSN_LUID (reg0_use);
+
+                     /* It might seem pointless to introduce an
+                        auto-inc if there's no subsequent use of
+                        reg_res (i.e., mem_insn.insn == NULL), but
+                        the next use might be in the next iteration
+                        of a loop, and it won't hurt if we make the
+                        change even if it's not needed.  */
+                     if (mem_insn.insn
+                         && luid > DF_INSN_LUID (mem_insn.insn))
+                       reg0_use = NULL;
+
+                     rtx_insn *other_insn
+                       = get_next_ref (REGNO (inc_insn.reg_res), bb,
+                                       reg_next_def);
+
+                     if (other_insn && luid >= DF_INSN_LUID (other_insn))
+                       reg0_use = NULL;
+
+                     other_insn
+                       = get_next_ref (REGNO (inc_insn.reg0), bb,
+                                       reg_next_def);
+
+                     if (other_insn && luid > DF_INSN_LUID (other_insn))
+                       reg0_use = NULL;
+                   }
+
+                 mem_insn.insn = reg0_use;
+
+                 if (mem_insn.insn
+                     && find_address (&PATTERN (mem_insn.insn),
+                                      inc_insn.reg0) == -1)
                    {
                      if (dump_file)
                        dump_mem_insn (dump_file);
@@ -1410,20 +1602,28 @@ merge_in_block (int max_reg, basic_block bb)
       else
        {
          insn_is_add_or_inc = false;
-         mem_insn.insn = insn;
-         if (find_mem (&PATTERN (insn)))
-           success_in_block++;
+         /* We can't use auto inc/dec for bare USEs and CLOBBERs,
+            since they aren't supposed to generate any code.  */
+         rtx_code code = GET_CODE (PATTERN (insn));
+         if (code != USE && code != CLOBBER)
+           {
+             mem_insn.insn = insn;
+             if (find_mem (&PATTERN (insn)))
+               success_in_block++;
+           }
        }
 
       /* If the inc insn was merged with a mem, the inc insn is gone
         and there is noting to update.  */
-      if (insn_info)
+      if (df_insn_info *insn_info = DF_INSN_INFO_GET (insn))
        {
          df_ref def, use;
 
          /* Need to update next use.  */
          FOR_EACH_INSN_INFO_DEF (def, insn_info)
            {
+             if (reg_next_debug_use)
+               reg_next_debug_use[DF_REF_REGNO (def)] = NULL;
              reg_next_use[DF_REF_REGNO (def)] = NULL;
              reg_next_inc_use[DF_REF_REGNO (def)] = NULL;
              reg_next_def[DF_REF_REGNO (def)] = insn;
@@ -1431,6 +1631,13 @@ merge_in_block (int max_reg, basic_block bb)
 
          FOR_EACH_INSN_INFO_USE (use, insn_info)
            {
+             if (reg_next_debug_use)
+               /* This may seem surprising, but we know we may only
+                  modify the value of a REG between an insn and the
+                  next nondebug use thereof.  Any debug uses after
+                  the next nondebug use can be left alone, the REG
+                  will hold the expected value there.  */
+               reg_next_debug_use[DF_REF_REGNO (use)] = NULL;
              reg_next_use[DF_REF_REGNO (use)] = insn;
              if (insn_is_add_or_inc)
                reg_next_inc_use[DF_REF_REGNO (use)] = insn;
@@ -1439,7 +1646,8 @@ merge_in_block (int max_reg, basic_block bb)
            }
        }
       else if (dump_file)
-       fprintf (dump_file, "skipping update of deleted insn %d\n", uid);
+       fprintf (dump_file, "skipping update of deleted insn %d\n",
+                INSN_UID (insn));
     }
 
   /* If we were successful, try again.  There may have been several
@@ -1449,6 +1657,8 @@ merge_in_block (int max_reg, basic_block bb)
     {
       /* In this case, we must clear these vectors since the trick of
         testing if the stale insn in the block will not work.  */
+      if (reg_next_debug_use)
+       memset (reg_next_debug_use, 0, max_reg * sizeof (rtx));
       memset (reg_next_use, 0, max_reg * sizeof (rtx));
       memset (reg_next_inc_use, 0, max_reg * sizeof (rtx));
       memset (reg_next_def, 0, max_reg * sizeof (rtx));
@@ -1457,8 +1667,6 @@ merge_in_block (int max_reg, basic_block bb)
     }
 }
 
-#endif
-
 /* Discover auto-inc auto-dec instructions.  */
 
 namespace {
@@ -1468,7 +1676,6 @@ const pass_data pass_data_inc_dec =
   RTL_PASS, /* type */
   "auto_inc_dec", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
-  true, /* has_execute */
   TV_AUTO_INC_DEC, /* tv_id */
   0, /* properties_required */
   0, /* properties_provided */
@@ -1487,11 +1694,10 @@ public:
   /* opt_pass methods: */
   virtual bool gate (function *)
     {
-#ifdef AUTO_INC_DEC
+      if (!AUTO_INC_DEC)
+       return false;
+
       return (optimize > 0 && flag_auto_inc_dec);
-#else
-      return false;
-#endif
     }
 
 
@@ -1502,7 +1708,9 @@ public:
 unsigned int
 pass_inc_dec::execute (function *fun ATTRIBUTE_UNUSED)
 {
-#ifdef AUTO_INC_DEC
+  if (!AUTO_INC_DEC)
+    return 0;
+
   basic_block bb;
   int max_reg = max_reg_num ();
 
@@ -1514,18 +1722,24 @@ pass_inc_dec::execute (function *fun ATTRIBUTE_UNUSED)
   df_note_add_problem ();
   df_analyze ();
 
-  reg_next_use = XCNEWVEC (rtx, max_reg);
-  reg_next_inc_use = XCNEWVEC (rtx, max_reg);
-  reg_next_def = XCNEWVEC (rtx, max_reg);
+  if (MAY_HAVE_DEBUG_BIND_INSNS)
+    reg_next_debug_use = XCNEWVEC (rtx_insn *, max_reg);
+  else
+    /* An earlier function may have had debug binds.  */
+    reg_next_debug_use = NULL;
+  reg_next_use = XCNEWVEC (rtx_insn *, max_reg);
+  reg_next_inc_use = XCNEWVEC (rtx_insn *, max_reg);
+  reg_next_def = XCNEWVEC (rtx_insn *, max_reg);
   FOR_EACH_BB_FN (bb, fun)
     merge_in_block (max_reg, bb);
 
+  free (reg_next_debug_use);
   free (reg_next_use);
   free (reg_next_inc_use);
   free (reg_next_def);
 
   mem_tmp = NULL;
-#endif
+
   return 0;
 }