OSDN Git Service

* config/xtensa/xtensa.c (xtensa_expand_builtin): Use CALL_EXPR_FN.
[pf3gnuchains/gcc-fork.git] / gcc / jump.c
index 8cd9da2..e61e2fb 100644 (file)
@@ -1,6 +1,7 @@
 /* Optimize jump instructions, for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997
-   1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -16,11 +17,11 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 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.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 /* This is the pathetic reminder of old fame of the jump-optimization pass
-   of the compiler.  Now it contains basically set of utility function to
+   of the compiler.  Now it contains basically a set of utility functions to
    operate with jumps.
 
    Each CODE_LABEL has a count of the times it is used
@@ -55,6 +56,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "reload.h"
 #include "predict.h"
 #include "timevar.h"
+#include "tree-pass.h"
+#include "target.h"
 
 /* Optimize jump y; x: ... y: jumpif... x?
    Don't know if it is worth bothering with.  */
@@ -65,13 +68,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 static void init_label_info (rtx);
 static void mark_all_labels (rtx);
-static void delete_computation (rtx);
 static void redirect_exp_1 (rtx *, rtx, rtx, rtx);
-static int redirect_exp (rtx, rtx, rtx);
-static void invert_exp_1 (rtx);
-static int invert_exp (rtx);
+static int invert_exp_1 (rtx, rtx);
 static int returnjump_p_1 (rtx *, void *);
-static void delete_prior_computation (rtx, rtx);
 \f
 /* Alternate entry into the jump optimizer.  This entry point only rebuilds
    the JUMP_LABEL field in jumping insns and REG_LABEL notes in non-jumping
@@ -103,7 +102,7 @@ rebuild_jump_labels (rtx f)
    This simple pass moves barriers and removes duplicates so that the
    old code is happy.
  */
-void
+unsigned int
 cleanup_barriers (void)
 {
   rtx insn, next, prev;
@@ -114,50 +113,31 @@ cleanup_barriers (void)
        {
          prev = prev_nonnote_insn (insn);
          if (BARRIER_P (prev))
-           delete_barrier (insn);
+           delete_insn (insn);
          else if (prev != PREV_INSN (insn))
            reorder_insns (insn, insn, prev);
        }
     }
+  return 0;
 }
 
-void
-purge_line_number_notes (rtx f)
+struct tree_opt_pass pass_cleanup_barriers =
 {
-  rtx last_note = 0;
-  rtx insn;
-  /* Delete extraneous line number notes.
-     Note that two consecutive notes for different lines are not really
-     extraneous.  There should be some indication where that line belonged,
-     even if it became empty.  */
+  "barriers",                           /* name */
+  NULL,                                 /* gate */
+  cleanup_barriers,                     /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  0,                                    /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func,                       /* todo_flags_finish */
+  0                                     /* letter */
+};
 
-  for (insn = f; insn; insn = NEXT_INSN (insn))
-    if (NOTE_P (insn))
-      {
-       if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
-         /* Any previous line note was for the prologue; gdb wants a new
-            note after the prologue even if it is for the same line.  */
-         last_note = NULL_RTX;
-       else if (NOTE_LINE_NUMBER (insn) >= 0)
-         {
-           /* Delete this note if it is identical to previous note.  */
-           if (last_note
-#ifdef USE_MAPPED_LOCATION
-               && NOTE_SOURCE_LOCATION (insn) == NOTE_SOURCE_LOCATION (last_note)
-#else
-               && NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last_note)
-               && NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last_note)
-#endif
-)
-             {
-               delete_related_insns (insn);
-               continue;
-             }
-
-           last_note = insn;
-         }
-      }
-}
 \f
 /* Initialize LABEL_NUSES and JUMP_LABEL fields.  Delete any REG_LABEL
    notes whose labels don't occur in the insn any more.  Returns the
@@ -210,7 +190,7 @@ mark_all_labels (rtx f)
                  {
                    /* But a LABEL_REF around the REG_LABEL note, so
                       that we can canonicalize it.  */
-                   rtx label_ref = gen_rtx_LABEL_REF (VOIDmode,
+                   rtx label_ref = gen_rtx_LABEL_REF (Pmode,
                                                       XEXP (label_note, 0));
 
                    mark_jump_label (label_ref, insn, 0);
@@ -220,13 +200,38 @@ mark_all_labels (rtx f)
              }
          }
       }
+  
+  /* If we are in cfglayout mode, there may be non-insns between the
+     basic blocks.  If those non-insns represent tablejump data, they
+     contain label references that we must record.  */
+  if (current_ir_type () == IR_RTL_CFGLAYOUT)
+    {
+      basic_block bb;
+      rtx insn;
+      FOR_EACH_BB (bb)
+       {
+         for (insn = bb->il.rtl->header; insn; insn = NEXT_INSN (insn))
+           if (INSN_P (insn))
+             {
+               gcc_assert (JUMP_TABLE_DATA_P (insn));
+               mark_jump_label (PATTERN (insn), insn, 0);
+             }
+
+         for (insn = bb->il.rtl->footer; insn; insn = NEXT_INSN (insn))
+           if (INSN_P (insn))
+             {
+               gcc_assert (JUMP_TABLE_DATA_P (insn));
+               mark_jump_label (PATTERN (insn), insn, 0);
+             }
+       }
+    }
 }
 \f
-/* Move all block-beg, block-end, loop-beg, loop-cont, loop-vtop, loop-end,
-   notes between START and END out before START.  START and END may be such
-   notes.  Returns the values of the new starting and ending insns, which
-   may be different if the original ones were such notes.
-   Return true if there were only such notes and no real instructions.  */
+/* Move all block-beg, block-end and loop-beg notes between START and END out
+   before START.  START and END may be such notes.  Returns the values of the
+   new starting and ending insns, which may be different if the original ones
+   were such notes.  Return true if there were only such notes and no real
+   instructions.  */
 
 bool
 squeeze_notes (rtx* startp, rtx* endp)
@@ -244,12 +249,12 @@ squeeze_notes (rtx* startp, rtx* endp)
       next = NEXT_INSN (insn);
       if (NOTE_P (insn)
          && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
-             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
-             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
-             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
-             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT
-             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_VTOP))
+             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG))
        {
+         /* BLOCK_BEG or BLOCK_END notes only exist in the `final' pass.  */
+         gcc_assert (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_BEG
+                     && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_END);
+
          if (insn == start)
            start = next;
          else
@@ -278,48 +283,6 @@ squeeze_notes (rtx* startp, rtx* endp)
   return false;
 }
 \f
-/* Return the label before INSN, or put a new label there.  */
-
-rtx
-get_label_before (rtx insn)
-{
-  rtx label;
-
-  /* Find an existing label at this point
-     or make a new one if there is none.  */
-  label = prev_nonnote_insn (insn);
-
-  if (label == 0 || !LABEL_P (label))
-    {
-      rtx prev = PREV_INSN (insn);
-
-      label = gen_label_rtx ();
-      emit_label_after (label, prev);
-      LABEL_NUSES (label) = 0;
-    }
-  return label;
-}
-
-/* Return the label after INSN, or put a new label there.  */
-
-rtx
-get_label_after (rtx insn)
-{
-  rtx label;
-
-  /* Find an existing label at this point
-     or make a new one if there is none.  */
-  label = next_nonnote_insn (insn);
-
-  if (label == 0 || !LABEL_P (label))
-    {
-      label = gen_label_rtx ();
-      emit_label_after (label, insn);
-      LABEL_NUSES (label) = 0;
-    }
-  return label;
-}
-\f
 /* Given a comparison (CODE ARG0 ARG1), inside an insn, INSN, return a code
    of reversed comparison if it is possible to do so.  Otherwise return UNKNOWN.
    UNKNOWN may be returned in case we are having CC_MODE compare and we don't
@@ -448,6 +411,20 @@ reversed_comparison_code (rtx comparison, rtx insn)
                                         XEXP (comparison, 0),
                                         XEXP (comparison, 1), insn);
 }
+
+/* Return comparison with reversed code of EXP.
+   Return NULL_RTX in case we fail to do the reversal.  */
+rtx
+reversed_comparison (rtx exp, enum machine_mode mode)
+{
+  enum rtx_code reversed_code = reversed_comparison_code (exp, NULL_RTX);
+  if (reversed_code == UNKNOWN)
+    return NULL_RTX;
+  else
+    return simplify_gen_relational (reversed_code, mode, VOIDmode,
+                                    XEXP (exp, 0), XEXP (exp, 1));
+}
+
 \f
 /* Given an rtx-code for a comparison, return the code for the negated
    comparison.  If no such code exists, return UNKNOWN.
@@ -496,7 +473,7 @@ reverse_condition (enum rtx_code code)
       return UNKNOWN;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -539,7 +516,7 @@ reverse_condition_maybe_unordered (enum rtx_code code)
       return LTGT;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -585,7 +562,7 @@ swap_condition (enum rtx_code code)
       return UNLE;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -616,7 +593,7 @@ unsigned_condition (enum rtx_code code)
       return LEU;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -645,7 +622,7 @@ signed_condition (enum rtx_code code)
       return LE;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 \f
@@ -768,8 +745,6 @@ condjump_p (rtx insn)
                || (GET_CODE (XEXP (x, 1)) == PC
                    && (GET_CODE (XEXP (x, 2)) == LABEL_REF
                        || GET_CODE (XEXP (x, 2)) == RETURN))));
-
-  return 0;
 }
 
 /* Return nonzero if INSN is a (possibly) conditional jump inside a
@@ -982,66 +957,6 @@ sets_cc0_p (rtx x)
 }
 #endif
 \f
-/* Follow any unconditional jump at LABEL;
-   return the ultimate label reached by any such chain of jumps.
-   Return null if the chain ultimately leads to a return instruction.
-   If LABEL is not followed by a jump, return LABEL.
-   If the chain loops or we can't find end, return LABEL,
-   since that tells caller to avoid changing the insn.
-
-   If RELOAD_COMPLETED is 0, we do not chain across a NOTE_INSN_LOOP_BEG or
-   a USE or CLOBBER.  */
-
-rtx
-follow_jumps (rtx label)
-{
-  rtx insn;
-  rtx next;
-  rtx value = label;
-  int depth;
-
-  for (depth = 0;
-       (depth < 10
-       && (insn = next_active_insn (value)) != 0
-       && JUMP_P (insn)
-       && ((JUMP_LABEL (insn) != 0 && any_uncondjump_p (insn)
-            && onlyjump_p (insn))
-           || GET_CODE (PATTERN (insn)) == RETURN)
-       && (next = NEXT_INSN (insn))
-       && BARRIER_P (next));
-       depth++)
-    {
-      /* Don't chain through the insn that jumps into a loop
-        from outside the loop,
-        since that would create multiple loop entry jumps
-        and prevent loop optimization.  */
-      rtx tem;
-      if (!reload_completed)
-       for (tem = value; tem != insn; tem = NEXT_INSN (tem))
-         if (NOTE_P (tem)
-             && (NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG
-                 /* ??? Optional.  Disables some optimizations, but makes
-                    gcov output more accurate with -O.  */
-                 || (flag_test_coverage && NOTE_LINE_NUMBER (tem) > 0)))
-           return value;
-
-      /* If we have found a cycle, make the insn jump to itself.  */
-      if (JUMP_LABEL (insn) == label)
-       return label;
-
-      tem = next_active_insn (JUMP_LABEL (insn));
-      if (tem && (GET_CODE (PATTERN (tem)) == ADDR_VEC
-                 || GET_CODE (PATTERN (tem)) == ADDR_DIFF_VEC))
-       break;
-
-      value = JUMP_LABEL (insn);
-    }
-  if (depth == 10)
-    return label;
-  return value;
-}
-
-\f
 /* Find all CODE_LABELs referred to in X, and increment their use counts.
    If INSN is a JUMP_INSN and there is at least one CODE_LABEL referenced
    in INSN, then store one of them in JUMP_LABEL (INSN).
@@ -1077,6 +992,12 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
       in_mem = 1;
       break;
 
+    case SEQUENCE:
+      for (i = 0; i < XVECLEN (x, 0); i++)
+       mark_jump_label (PATTERN (XVECEXP (x, 0, i)),
+                        XVECEXP (x, 0, i), 0);
+      return;
+
     case SYMBOL_REF:
       if (!in_mem)
        return;
@@ -1096,8 +1017,7 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
            && NOTE_LINE_NUMBER (label) == NOTE_INSN_DELETED_LABEL)
          break;
 
-       if (!LABEL_P (label))
-         abort ();
+       gcc_assert (LABEL_P (label));
 
        /* Ignore references to labels of containing functions.  */
        if (LABEL_REF_NONLOCAL_P (x))
@@ -1156,203 +1076,6 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
     }
 }
 
-/* If all INSN does is set the pc, delete it,
-   and delete the insn that set the condition codes for it
-   if that's what the previous thing was.  */
-
-void
-delete_jump (rtx insn)
-{
-  rtx set = single_set (insn);
-
-  if (set && GET_CODE (SET_DEST (set)) == PC)
-    delete_computation (insn);
-}
-
-/* Verify INSN is a BARRIER and delete it.  */
-
-void
-delete_barrier (rtx insn)
-{
-  if (!BARRIER_P (insn))
-    abort ();
-
-  delete_insn (insn);
-}
-
-/* Recursively delete prior insns that compute the value (used only by INSN
-   which the caller is deleting) stored in the register mentioned by NOTE
-   which is a REG_DEAD note associated with INSN.  */
-
-static void
-delete_prior_computation (rtx note, rtx insn)
-{
-  rtx our_prev;
-  rtx reg = XEXP (note, 0);
-
-  for (our_prev = prev_nonnote_insn (insn);
-       our_prev && (NONJUMP_INSN_P (our_prev)
-                   || CALL_P (our_prev));
-       our_prev = prev_nonnote_insn (our_prev))
-    {
-      rtx pat = PATTERN (our_prev);
-
-      /* If we reach a CALL which is not calling a const function
-        or the callee pops the arguments, then give up.  */
-      if (CALL_P (our_prev)
-         && (! CONST_OR_PURE_CALL_P (our_prev)
-             || GET_CODE (pat) != SET || GET_CODE (SET_SRC (pat)) != CALL))
-       break;
-
-      /* If we reach a SEQUENCE, it is too complex to try to
-        do anything with it, so give up.  We can be run during
-        and after reorg, so SEQUENCE rtl can legitimately show
-        up here.  */
-      if (GET_CODE (pat) == SEQUENCE)
-       break;
-
-      if (GET_CODE (pat) == USE
-         && NONJUMP_INSN_P (XEXP (pat, 0)))
-       /* reorg creates USEs that look like this.  We leave them
-          alone because reorg needs them for its own purposes.  */
-       break;
-
-      if (reg_set_p (reg, pat))
-       {
-         if (side_effects_p (pat) && !CALL_P (our_prev))
-           break;
-
-         if (GET_CODE (pat) == PARALLEL)
-           {
-             /* If we find a SET of something else, we can't
-                delete the insn.  */
-
-             int i;
-
-             for (i = 0; i < XVECLEN (pat, 0); i++)
-               {
-                 rtx part = XVECEXP (pat, 0, i);
-
-                 if (GET_CODE (part) == SET
-                     && SET_DEST (part) != reg)
-                   break;
-               }
-
-             if (i == XVECLEN (pat, 0))
-               delete_computation (our_prev);
-           }
-         else if (GET_CODE (pat) == SET
-                  && REG_P (SET_DEST (pat)))
-           {
-             int dest_regno = REGNO (SET_DEST (pat));
-             int dest_endregno
-               = (dest_regno
-                  + (dest_regno < FIRST_PSEUDO_REGISTER
-                     ? hard_regno_nregs[dest_regno]
-                                       [GET_MODE (SET_DEST (pat))] : 1));
-             int regno = REGNO (reg);
-             int endregno
-               = (regno
-                  + (regno < FIRST_PSEUDO_REGISTER
-                     ? hard_regno_nregs[regno][GET_MODE (reg)] : 1));
-
-             if (dest_regno >= regno
-                 && dest_endregno <= endregno)
-               delete_computation (our_prev);
-
-             /* We may have a multi-word hard register and some, but not
-                all, of the words of the register are needed in subsequent
-                insns.  Write REG_UNUSED notes for those parts that were not
-                needed.  */
-             else if (dest_regno <= regno
-                      && dest_endregno >= endregno)
-               {
-                 int i;
-
-                 REG_NOTES (our_prev)
-                   = gen_rtx_EXPR_LIST (REG_UNUSED, reg,
-                                        REG_NOTES (our_prev));
-
-                 for (i = dest_regno; i < dest_endregno; i++)
-                   if (! find_regno_note (our_prev, REG_UNUSED, i))
-                     break;
-
-                 if (i == dest_endregno)
-                   delete_computation (our_prev);
-               }
-           }
-
-         break;
-       }
-
-      /* If PAT references the register that dies here, it is an
-        additional use.  Hence any prior SET isn't dead.  However, this
-        insn becomes the new place for the REG_DEAD note.  */
-      if (reg_overlap_mentioned_p (reg, pat))
-       {
-         XEXP (note, 1) = REG_NOTES (our_prev);
-         REG_NOTES (our_prev) = note;
-         break;
-       }
-    }
-}
-
-/* Delete INSN and recursively delete insns that compute values used only
-   by INSN.  This uses the REG_DEAD notes computed during flow analysis.
-   If we are running before flow.c, we need do nothing since flow.c will
-   delete dead code.  We also can't know if the registers being used are
-   dead or not at this point.
-
-   Otherwise, look at all our REG_DEAD notes.  If a previous insn does
-   nothing other than set a register that dies in this insn, we can delete
-   that insn as well.
-
-   On machines with CC0, if CC0 is used in this insn, we may be able to
-   delete the insn that set it.  */
-
-static void
-delete_computation (rtx insn)
-{
-  rtx note, next;
-
-#ifdef HAVE_cc0
-  if (reg_referenced_p (cc0_rtx, PATTERN (insn)))
-    {
-      rtx prev = prev_nonnote_insn (insn);
-      /* We assume that at this stage
-        CC's are always set explicitly
-        and always immediately before the jump that
-        will use them.  So if the previous insn
-        exists to set the CC's, delete it
-        (unless it performs auto-increments, etc.).  */
-      if (prev && NONJUMP_INSN_P (prev)
-         && sets_cc0_p (PATTERN (prev)))
-       {
-         if (sets_cc0_p (PATTERN (prev)) > 0
-             && ! side_effects_p (PATTERN (prev)))
-           delete_computation (prev);
-         else
-           /* Otherwise, show that cc0 won't be used.  */
-           REG_NOTES (prev) = gen_rtx_EXPR_LIST (REG_UNUSED,
-                                                 cc0_rtx, REG_NOTES (prev));
-       }
-    }
-#endif
-
-  for (note = REG_NOTES (insn); note; note = next)
-    {
-      next = XEXP (note, 1);
-
-      if (REG_NOTE_KIND (note) != REG_DEAD
-         /* Verify that the REG_NOTE is legitimate.  */
-         || !REG_P (XEXP (note, 0)))
-       continue;
-
-      delete_prior_computation (note, insn);
-    }
-
-  delete_related_insns (insn);
-}
 \f
 /* Delete insn INSN from the chain of insns and update label ref counts
    and delete insns now unreachable.
@@ -1465,8 +1188,7 @@ delete_related_insns (rtx insn)
       while (next)
        {
          code = GET_CODE (next);
-         if (code == NOTE
-             && NOTE_LINE_NUMBER (next) != NOTE_INSN_FUNCTION_END)
+         if (code == NOTE)
            next = NEXT_INSN (next);
          /* Keep going past other deleted labels to delete what follows.  */
          else if (code == CODE_LABEL && INSN_DELETED_P (next))
@@ -1542,7 +1264,7 @@ redirect_exp_1 (rtx *loc, rtx olabel, rtx nlabel, rtx insn)
        {
          rtx n;
          if (nlabel)
-           n = gen_rtx_LABEL_REF (VOIDmode, nlabel);
+           n = gen_rtx_LABEL_REF (Pmode, nlabel);
          else
            n = gen_rtx_RETURN (VOIDmode);
 
@@ -1552,7 +1274,10 @@ redirect_exp_1 (rtx *loc, rtx olabel, rtx nlabel, rtx insn)
     }
   else if (code == RETURN && olabel == 0)
     {
-      x = gen_rtx_LABEL_REF (VOIDmode, nlabel);
+      if (nlabel)
+       x = gen_rtx_LABEL_REF (Pmode, nlabel);
+      else
+       x = gen_rtx_RETURN (VOIDmode);
       if (loc == &PATTERN (insn))
        x = gen_rtx_SET (VOIDmode, pc_rtx, x);
       validate_change (insn, loc, x, 1);
@@ -1581,25 +1306,6 @@ redirect_exp_1 (rtx *loc, rtx olabel, rtx nlabel, rtx insn)
     }
 }
 
-/* Similar, but apply the change group and report success or failure.  */
-
-static int
-redirect_exp (rtx olabel, rtx nlabel, rtx insn)
-{
-  rtx *loc;
-
-  if (GET_CODE (PATTERN (insn)) == PARALLEL)
-    loc = &XVECEXP (PATTERN (insn), 0, 0);
-  else
-    loc = &PATTERN (insn);
-
-  redirect_exp_1 (loc, olabel, nlabel, insn);
-  if (num_validated_changes () == 0)
-    return 0;
-
-  return apply_change_group ();
-}
-
 /* Make JUMP go to NLABEL instead of where it jumps now.  Accrue
    the modifications into the change group.  Return false if we did
    not see how to do that.  */
@@ -1633,14 +1339,31 @@ int
 redirect_jump (rtx jump, rtx nlabel, int delete_unused)
 {
   rtx olabel = JUMP_LABEL (jump);
-  rtx note;
 
   if (nlabel == olabel)
     return 1;
 
-  if (! redirect_exp (olabel, nlabel, jump))
+  if (! redirect_jump_1 (jump, nlabel) || ! apply_change_group ())
     return 0;
 
+  redirect_jump_2 (jump, olabel, nlabel, delete_unused, 0);
+  return 1;
+}
+
+/* Fix up JUMP_LABEL and label ref counts after OLABEL has been replaced with
+   NLABEL in JUMP.  
+   If DELETE_UNUSED is positive, delete related insn to OLABEL if its ref
+   count has dropped to zero.  */
+void
+redirect_jump_2 (rtx jump, rtx olabel, rtx nlabel, int delete_unused,
+                int invert)
+{
+  rtx note;
+
+  /* Negative DELETE_UNUSED used to be used to signalize behavior on
+     moving FUNCTION_END note.  Just sanity check that no user still worry
+     about this.  */
+  gcc_assert (delete_unused >= 0);
   JUMP_LABEL (jump) = nlabel;
   if (nlabel)
     ++LABEL_NUSES (nlabel);
@@ -1648,56 +1371,29 @@ redirect_jump (rtx jump, rtx nlabel, int delete_unused)
   /* Update labels in any REG_EQUAL note.  */
   if ((note = find_reg_note (jump, REG_EQUAL, NULL_RTX)) != NULL_RTX)
     {
-      if (nlabel && olabel)
+      if (!nlabel || (invert && !invert_exp_1 (XEXP (note, 0), jump)))
+       remove_note (jump, note);
+      else
        {
-         rtx dest = XEXP (note, 0);
-
-         if (GET_CODE (dest) == IF_THEN_ELSE)
-           {
-             if (GET_CODE (XEXP (dest, 1)) == LABEL_REF
-                 && XEXP (XEXP (dest, 1), 0) == olabel)
-               XEXP (XEXP (dest, 1), 0) = nlabel;
-             if (GET_CODE (XEXP (dest, 2)) == LABEL_REF
-                 && XEXP (XEXP (dest, 2), 0) == olabel)
-               XEXP (XEXP (dest, 2), 0) = nlabel;
-           }
-         else
-           remove_note (jump, note);
+         redirect_exp_1 (&XEXP (note, 0), olabel, nlabel, jump);
+         confirm_change_group ();
        }
-      else
-        remove_note (jump, note);
     }
 
-  /* If we're eliding the jump over exception cleanups at the end of a
-     function, move the function end note so that -Wreturn-type works.  */
-  if (olabel && nlabel
-      && NEXT_INSN (olabel)
-      && NOTE_P (NEXT_INSN (olabel))
-      && NOTE_LINE_NUMBER (NEXT_INSN (olabel)) == NOTE_INSN_FUNCTION_END)
-    emit_note_after (NOTE_INSN_FUNCTION_END, nlabel);
-
-  if (olabel && --LABEL_NUSES (olabel) == 0 && delete_unused
+  if (olabel && --LABEL_NUSES (olabel) == 0 && delete_unused > 0
       /* Undefined labels will remain outside the insn stream.  */
       && INSN_UID (olabel))
     delete_related_insns (olabel);
-
-  return 1;
+  if (invert)
+    invert_br_probabilities (jump);
 }
 
-/* Invert the jump condition of rtx X contained in jump insn, INSN.
-   Accrue the modifications into the change group.  */
-
-static void
-invert_exp_1 (rtx insn)
+/* Invert the jump condition X contained in jump insn INSN.  Accrue the
+   modifications into the change group.  Return nonzero for success.  */
+static int
+invert_exp_1 (rtx x, rtx insn)
 {
-  RTX_CODE code;
-  rtx x = pc_set (insn);
-
-  if (!x)
-    abort ();
-  x = SET_SRC (x);
-
-  code = GET_CODE (x);
+  RTX_CODE code = GET_CODE (x);
 
   if (code == IF_THEN_ELSE)
     {
@@ -1719,30 +1415,16 @@ invert_exp_1 (rtx insn)
                                           GET_MODE (comp), XEXP (comp, 0),
                                           XEXP (comp, 1)),
                           1);
-         return;
+         return 1;
        }
 
       tem = XEXP (x, 1);
       validate_change (insn, &XEXP (x, 1), XEXP (x, 2), 1);
       validate_change (insn, &XEXP (x, 2), tem, 1);
+      return 1;
     }
   else
-    abort ();
-}
-
-/* Invert the jump condition of conditional jump insn, INSN.
-
-   Return 1 if we can do so, 0 if we cannot find a way to do so that
-   matches a pattern.  */
-
-static int
-invert_exp (rtx insn)
-{
-  invert_exp_1 (insn);
-  if (num_validated_changes () == 0)
     return 0;
-
-  return apply_change_group ();
 }
 
 /* Invert the condition of the jump JUMP, and make it jump to label
@@ -1753,14 +1435,21 @@ invert_exp (rtx insn)
 int
 invert_jump_1 (rtx jump, rtx nlabel)
 {
+  rtx x = pc_set (jump);
   int ochanges;
+  int ok;
 
   ochanges = num_validated_changes ();
-  invert_exp_1 (jump);
+  gcc_assert (x);
+  ok = invert_exp_1 (SET_SRC (x), jump);
+  gcc_assert (ok);
+  
   if (num_validated_changes () == ochanges)
     return 0;
 
-  return redirect_jump_1 (jump, nlabel);
+  /* redirect_jump_1 will fail of nlabel == olabel, and the current use is
+     in Pmode, so checking this is not merely an optimization.  */
+  return nlabel == JUMP_LABEL (jump) || redirect_jump_1 (jump, nlabel);
 }
 
 /* Invert the condition of the jump JUMP, and make it jump to label
@@ -1769,30 +1458,14 @@ invert_jump_1 (rtx jump, rtx nlabel)
 int
 invert_jump (rtx jump, rtx nlabel, int delete_unused)
 {
-  /* We have to either invert the condition and change the label or
-     do neither.  Either operation could fail.  We first try to invert
-     the jump. If that succeeds, we try changing the label.  If that fails,
-     we invert the jump back to what it was.  */
-
-  if (! invert_exp (jump))
-    return 0;
+  rtx olabel = JUMP_LABEL (jump);
 
-  if (redirect_jump (jump, nlabel, delete_unused))
+  if (invert_jump_1 (jump, nlabel) && apply_change_group ())
     {
-      /* Remove REG_EQUAL note if we have one.  */
-      rtx note = find_reg_note (jump, REG_EQUAL, NULL_RTX);
-      if (note)
-       remove_note (jump, note);
-
-      invert_br_probabilities (jump);
-
+      redirect_jump_2 (jump, olabel, nlabel, delete_unused, 1);
       return 1;
     }
-
-  if (! invert_exp (jump))
-    /* This should just be putting it back the way it was.  */
-    abort ();
-
+  cancel_changes (0);
   return 0;
 }
 
@@ -1800,15 +1473,7 @@ invert_jump (rtx jump, rtx nlabel, int delete_unused)
 /* Like rtx_equal_p except that it considers two REGs as equal
    if they renumber to the same value and considers two commutative
    operations to be the same if the order of the operands has been
-   reversed.
-
-   ??? Addition is not commutative on the PA due to the weird implicit
-   space register selection rules for memory addresses.  Therefore, we
-   don't consider a + b == b + a.
-
-   We could/should make this test a little tighter.  Possibly only
-   disabling it on the PA via some backend macro or only disabling this
-   case when the PLUS is inside a MEM.  */
+   reversed.  */
 
 int
 rtx_renumbered_equal_p (rtx x, rtx y)
@@ -1892,6 +1557,7 @@ rtx_renumbered_equal_p (rtx x, rtx y)
     case ADDR_VEC:
     case ADDR_DIFF_VEC:
     case CONST_INT:
+    case CONST_DOUBLE:
       return 0;
 
     case LABEL_REF:
@@ -1921,10 +1587,8 @@ rtx_renumbered_equal_p (rtx x, rtx y)
     return 0;
 
   /* For commutative operations, the RTX match if the operand match in any
-     order.  Also handle the simple binary and unary cases without a loop.
-
-     ??? Don't consider PLUS a commutative operator; see comments above.  */
-  if (COMMUTATIVE_P (x) && code != PLUS)
+     order.  Also handle the simple binary and unary cases without a loop.  */
+  if (targetm.commutative_p (x, UNKNOWN))
     return ((rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0))
             && rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 1)))
            || (rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 1))
@@ -1985,7 +1649,7 @@ rtx_renumbered_equal_p (rtx x, rtx y)
          break;
 
        default:
-         abort ();
+         gcc_unreachable ();
        }
     }
   return 1;
@@ -2008,7 +1672,11 @@ true_regnum (rtx x)
   if (GET_CODE (x) == SUBREG)
     {
       int base = true_regnum (SUBREG_REG (x));
-      if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
+      if (base >= 0
+         && base < FIRST_PSEUDO_REGISTER
+         && subreg_offset_representable_p (REGNO (SUBREG_REG (x)),
+                                           GET_MODE (SUBREG_REG (x)),
+                                           SUBREG_BYTE (x), GET_MODE (x)))
        return base + subreg_regno_offset (REGNO (SUBREG_REG (x)),
                                           GET_MODE (SUBREG_REG (x)),
                                           SUBREG_BYTE (x), GET_MODE (x));
@@ -2020,9 +1688,8 @@ true_regnum (rtx x)
 unsigned int
 reg_or_subregno (rtx reg)
 {
-  if (REG_P (reg))
-    return REGNO (reg);
   if (GET_CODE (reg) == SUBREG)
-    return REGNO (SUBREG_REG (reg));
-  abort ();
+    reg = SUBREG_REG (reg);
+  gcc_assert (REG_P (reg));
+  return REGNO (reg);
 }