OSDN Git Service

PR rtl-optimization/42621
[pf3gnuchains/gcc-fork.git] / gcc / jump.c
index e61e2fb..c95c211 100644 (file)
@@ -1,13 +1,13 @@
 /* Optimize jump instructions, for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997
 /* 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, 2005
+   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009
    Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
    Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -16,9 +16,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
 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, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 /* This is the pathetic reminder of old fame of the jump-optimization pass
    of the compiler.  Now it contains basically a set of utility functions to
 
 /* This is the pathetic reminder of old fame of the jump-optimization pass
    of the compiler.  Now it contains basically a set of utility functions to
@@ -68,13 +67,16 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 
 static void init_label_info (rtx);
 static void mark_all_labels (rtx);
 
 static void init_label_info (rtx);
 static void mark_all_labels (rtx);
+static void mark_jump_label_1 (rtx, rtx, bool, bool);
+static void mark_jump_label_asm (rtx, rtx);
 static void redirect_exp_1 (rtx *, rtx, rtx, rtx);
 static int invert_exp_1 (rtx, rtx);
 static int returnjump_p_1 (rtx *, void *);
 \f
 static void redirect_exp_1 (rtx *, rtx, rtx, rtx);
 static int invert_exp_1 (rtx, rtx);
 static int returnjump_p_1 (rtx *, void *);
 \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
-   instructions.  */
+/* This function rebuilds the JUMP_LABEL field and REG_LABEL_TARGET
+   notes in jumping insns and REG_LABEL_OPERAND notes in non-jumping
+   instructions and jumping insns that have labels as operands
+   (e.g. cbranchsi4).  */
 void
 rebuild_jump_labels (rtx f)
 {
 void
 rebuild_jump_labels (rtx f)
 {
@@ -112,6 +114,8 @@ cleanup_barriers (void)
       if (BARRIER_P (insn))
        {
          prev = prev_nonnote_insn (insn);
       if (BARRIER_P (insn))
        {
          prev = prev_nonnote_insn (insn);
+         if (!prev)
+           continue;
          if (BARRIER_P (prev))
            delete_insn (insn);
          else if (prev != PREV_INSN (insn))
          if (BARRIER_P (prev))
            delete_insn (insn);
          else if (prev != PREV_INSN (insn))
@@ -121,49 +125,63 @@ cleanup_barriers (void)
   return 0;
 }
 
   return 0;
 }
 
-struct tree_opt_pass pass_cleanup_barriers =
+struct rtl_opt_pass pass_cleanup_barriers =
 {
 {
+ {
+  RTL_PASS,
   "barriers",                           /* name */
   NULL,                                 /* gate */
   cleanup_barriers,                     /* execute */
   NULL,                                 /* sub */
   NULL,                                 /* next */
   0,                                    /* static_pass_number */
   "barriers",                           /* name */
   NULL,                                 /* gate */
   cleanup_barriers,                     /* execute */
   NULL,                                 /* sub */
   NULL,                                 /* next */
   0,                                    /* static_pass_number */
-  0,                                    /* tv_id */
+  TV_NONE,                              /* tv_id */
   0,                                    /* properties_required */
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   0,                                    /* properties_required */
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func,                       /* todo_flags_finish */
-  0                                     /* letter */
+  TODO_dump_func                        /* todo_flags_finish */
+ }
 };
 
 \f
 };
 
 \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
-   largest INSN_UID found.  */
+/* Initialize LABEL_NUSES and JUMP_LABEL fields, add REG_LABEL_TARGET
+   for remaining targets for JUMP_P.  Delete any REG_LABEL_OPERAND
+   notes whose labels don't occur in the insn any more.  */
+
 static void
 init_label_info (rtx f)
 {
   rtx insn;
 
   for (insn = f; insn; insn = NEXT_INSN (insn))
 static void
 init_label_info (rtx f)
 {
   rtx insn;
 
   for (insn = f; insn; insn = NEXT_INSN (insn))
-    if (LABEL_P (insn))
-      LABEL_NUSES (insn) = (LABEL_PRESERVE_P (insn) != 0);
-    else if (JUMP_P (insn))
-      JUMP_LABEL (insn) = 0;
-    else if (NONJUMP_INSN_P (insn) || CALL_P (insn))
-      {
-       rtx note, next;
+    {
+      if (LABEL_P (insn))
+       LABEL_NUSES (insn) = (LABEL_PRESERVE_P (insn) != 0);
+
+      /* REG_LABEL_TARGET notes (including the JUMP_LABEL field) are
+        sticky and not reset here; that way we won't lose association
+        with a label when e.g. the source for a target register
+        disappears out of reach for targets that may use jump-target
+        registers.  Jump transformations are supposed to transform
+        any REG_LABEL_TARGET notes.  The target label reference in a
+        branch may disappear from the branch (and from the
+        instruction before it) for other reasons, like register
+        allocation.  */
+
+      if (INSN_P (insn))
+       {
+         rtx note, next;
 
 
-       for (note = REG_NOTES (insn); note; note = next)
-         {
-           next = XEXP (note, 1);
-           if (REG_NOTE_KIND (note) == REG_LABEL
-               && ! reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
-             remove_note (insn, note);
-         }
-      }
+         for (note = REG_NOTES (insn); note; note = next)
+           {
+             next = XEXP (note, 1);
+             if (REG_NOTE_KIND (note) == REG_LABEL_OPERAND
+                 && ! reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
+               remove_note (insn, note);
+           }
+       }
+    }
 }
 
 /* Mark the label each jump jumps to.
 }
 
 /* Mark the label each jump jumps to.
@@ -173,34 +191,69 @@ static void
 mark_all_labels (rtx f)
 {
   rtx insn;
 mark_all_labels (rtx f)
 {
   rtx insn;
+  rtx prev_nonjump_insn = NULL;
 
   for (insn = f; insn; insn = NEXT_INSN (insn))
     if (INSN_P (insn))
       {
        mark_jump_label (PATTERN (insn), insn, 0);
 
   for (insn = f; insn; insn = NEXT_INSN (insn))
     if (INSN_P (insn))
       {
        mark_jump_label (PATTERN (insn), insn, 0);
-       if (! INSN_DELETED_P (insn) && JUMP_P (insn))
+
+       /* If the previous non-jump insn sets something to a label,
+          something that this jump insn uses, make that label the primary
+          target of this insn if we don't yet have any.  That previous
+          insn must be a single_set and not refer to more than one label.
+          The jump insn must not refer to other labels as jump targets
+          and must be a plain (set (pc) ...), maybe in a parallel, and
+          may refer to the item being set only directly or as one of the
+          arms in an IF_THEN_ELSE.  */
+       if (! INSN_DELETED_P (insn)
+           && JUMP_P (insn)
+           && JUMP_LABEL (insn) == NULL)
          {
          {
-           /* When we know the LABEL_REF contained in a REG used in
-              an indirect jump, we'll have a REG_LABEL note so that
-              flow can tell where it's going.  */
-           if (JUMP_LABEL (insn) == 0)
+           rtx label_note = NULL;
+           rtx pc = pc_set (insn);
+           rtx pc_src = pc != NULL ? SET_SRC (pc) : NULL;
+
+           if (prev_nonjump_insn != NULL)
+             label_note
+               = find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL);
+
+           if (label_note != NULL && pc_src != NULL)
              {
              {
-               rtx label_note = find_reg_note (insn, REG_LABEL, NULL_RTX);
-               if (label_note)
+               rtx label_set = single_set (prev_nonjump_insn);
+               rtx label_dest
+                 = label_set != NULL ? SET_DEST (label_set) : NULL;
+
+               if (label_set != NULL
+                   /* The source must be the direct LABEL_REF, not a
+                      PLUS, UNSPEC, IF_THEN_ELSE etc.  */
+                   && GET_CODE (SET_SRC (label_set)) == LABEL_REF
+                   && (rtx_equal_p (label_dest, pc_src)
+                       || (GET_CODE (pc_src) == IF_THEN_ELSE
+                           && (rtx_equal_p (label_dest, XEXP (pc_src, 1))
+                               || rtx_equal_p (label_dest,
+                                               XEXP (pc_src, 2))))))
+
                  {
                  {
-                   /* But a LABEL_REF around the REG_LABEL note, so
-                      that we can canonicalize it.  */
-                   rtx label_ref = gen_rtx_LABEL_REF (Pmode,
-                                                      XEXP (label_note, 0));
-
-                   mark_jump_label (label_ref, insn, 0);
-                   XEXP (label_note, 0) = XEXP (label_ref, 0);
-                   JUMP_LABEL (insn) = XEXP (label_note, 0);
+                   /* The CODE_LABEL referred to in the note must be the
+                      CODE_LABEL in the LABEL_REF of the "set".  We can
+                      conveniently use it for the marker function, which
+                      requires a LABEL_REF wrapping.  */
+                   gcc_assert (XEXP (label_note, 0)
+                               == XEXP (SET_SRC (label_set), 0));
+
+                   mark_jump_label_1 (label_set, insn, false, true);
+                   gcc_assert (JUMP_LABEL (insn)
+                               == XEXP (SET_SRC (label_set), 0));
                  }
              }
          }
                  }
              }
          }
+       else if (! INSN_DELETED_P (insn))
+         prev_nonjump_insn = insn;
       }
       }
-  
+    else if (LABEL_P (insn))
+      prev_nonjump_insn = NULL;
+
   /* 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 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.  */
@@ -227,62 +280,6 @@ mark_all_labels (rtx f)
     }
 }
 \f
     }
 }
 \f
-/* 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)
-{
-  rtx start = *startp;
-  rtx end = *endp;
-
-  rtx insn;
-  rtx next;
-  rtx last = NULL;
-  rtx past_end = NEXT_INSN (end);
-
-  for (insn = start; insn != past_end; insn = next)
-    {
-      next = NEXT_INSN (insn);
-      if (NOTE_P (insn)
-         && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
-             || 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
-           {
-             rtx prev = PREV_INSN (insn);
-             PREV_INSN (insn) = PREV_INSN (start);
-             NEXT_INSN (insn) = start;
-             NEXT_INSN (PREV_INSN (insn)) = insn;
-             PREV_INSN (NEXT_INSN (insn)) = insn;
-             NEXT_INSN (prev) = next;
-             PREV_INSN (next) = prev;
-           }
-       }
-      else
-       last = insn;
-    }
-
-  /* There were no real instructions.  */
-  if (start == past_end)
-    return true;
-
-  end = last;
-
-  *startp = start;
-  *endp = end;
-  return false;
-}
-\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
 /* 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
@@ -290,7 +287,8 @@ squeeze_notes (rtx* startp, rtx* endp)
    description should define REVERSIBLE_CC_MODE and REVERSE_CONDITION macros
    to help this function avoid overhead in these cases.  */
 enum rtx_code
    description should define REVERSIBLE_CC_MODE and REVERSE_CONDITION macros
    to help this function avoid overhead in these cases.  */
 enum rtx_code
-reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn)
+reversed_comparison_code_parts (enum rtx_code code, const_rtx arg0,
+                               const_rtx arg1, const_rtx insn)
 {
   enum machine_mode mode;
 
 {
   enum machine_mode mode;
 
@@ -347,7 +345,7 @@ reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn
 
   if (GET_MODE_CLASS (mode) == MODE_CC || CC0_P (arg0))
     {
 
   if (GET_MODE_CLASS (mode) == MODE_CC || CC0_P (arg0))
     {
-      rtx prev;
+      const_rtx prev;
       /* Try to search for the comparison to determine the real mode.
          This code is expensive, but with sane machine description it
          will be never used, since REVERSIBLE_CC_MODE will return true
       /* Try to search for the comparison to determine the real mode.
          This code is expensive, but with sane machine description it
          will be never used, since REVERSIBLE_CC_MODE will return true
@@ -355,11 +353,14 @@ reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn
       if (! insn)
        return UNKNOWN;
 
       if (! insn)
        return UNKNOWN;
 
-      for (prev = prev_nonnote_insn (insn);
+      /* These CONST_CAST's are okay because prev_nonnote_insn just
+        returns its argument and we assign it to a const_rtx
+        variable.  */
+      for (prev = prev_nonnote_insn (CONST_CAST_RTX(insn));
           prev != 0 && !LABEL_P (prev);
           prev != 0 && !LABEL_P (prev);
-          prev = prev_nonnote_insn (prev))
+          prev = prev_nonnote_insn (CONST_CAST_RTX(prev)))
        {
        {
-         rtx set = set_of (arg0, prev);
+         const_rtx set = set_of (arg0, prev);
          if (set && GET_CODE (set) == SET
              && rtx_equal_p (SET_DEST (set), arg0))
            {
          if (set && GET_CODE (set) == SET
              && rtx_equal_p (SET_DEST (set), arg0))
            {
@@ -391,7 +392,7 @@ reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn
 
   /* Test for an integer condition, or a floating-point comparison
      in which NaNs can be ignored.  */
 
   /* Test for an integer condition, or a floating-point comparison
      in which NaNs can be ignored.  */
-  if (GET_CODE (arg0) == CONST_INT
+  if (CONST_INT_P (arg0)
       || (GET_MODE (arg0) != VOIDmode
          && GET_MODE_CLASS (mode) != MODE_CC
          && !HONOR_NANS (mode)))
       || (GET_MODE (arg0) != VOIDmode
          && GET_MODE_CLASS (mode) != MODE_CC
          && !HONOR_NANS (mode)))
@@ -403,7 +404,7 @@ reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn
 /* A wrapper around the previous function to take COMPARISON as rtx
    expression.  This simplifies many callers.  */
 enum rtx_code
 /* A wrapper around the previous function to take COMPARISON as rtx
    expression.  This simplifies many callers.  */
 enum rtx_code
-reversed_comparison_code (rtx comparison, rtx insn)
+reversed_comparison_code (const_rtx comparison, const_rtx insn)
 {
   if (!COMPARISON_P (comparison))
     return UNKNOWN;
 {
   if (!COMPARISON_P (comparison))
     return UNKNOWN;
@@ -415,7 +416,7 @@ reversed_comparison_code (rtx comparison, rtx insn)
 /* Return comparison with reversed code of EXP.
    Return NULL_RTX in case we fail to do the reversal.  */
 rtx
 /* 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)
+reversed_comparison (const_rtx exp, enum machine_mode mode)
 {
   enum rtx_code reversed_code = reversed_comparison_code (exp, NULL_RTX);
   if (reversed_code == UNKNOWN)
 {
   enum rtx_code reversed_code = reversed_comparison_code (exp, NULL_RTX);
   if (reversed_code == UNKNOWN)
@@ -711,7 +712,7 @@ comparison_dominates_p (enum rtx_code code1, enum rtx_code code2)
 /* Return 1 if INSN is an unconditional jump and nothing else.  */
 
 int
 /* Return 1 if INSN is an unconditional jump and nothing else.  */
 
 int
-simplejump_p (rtx insn)
+simplejump_p (const_rtx insn)
 {
   return (JUMP_P (insn)
          && GET_CODE (PATTERN (insn)) == SET
 {
   return (JUMP_P (insn)
          && GET_CODE (PATTERN (insn)) == SET
@@ -726,9 +727,9 @@ simplejump_p (rtx insn)
    branch and compare insns.  Use any_condjump_p instead whenever possible.  */
 
 int
    branch and compare insns.  Use any_condjump_p instead whenever possible.  */
 
 int
-condjump_p (rtx insn)
+condjump_p (const_rtx insn)
 {
 {
-  rtx x = PATTERN (insn);
+  const_rtx x = PATTERN (insn);
 
   if (GET_CODE (x) != SET
       || GET_CODE (SET_DEST (x)) != PC)
 
   if (GET_CODE (x) != SET
       || GET_CODE (SET_DEST (x)) != PC)
@@ -754,9 +755,9 @@ condjump_p (rtx insn)
    branch and compare insns.  Use any_condjump_p instead whenever possible.  */
 
 int
    branch and compare insns.  Use any_condjump_p instead whenever possible.  */
 
 int
-condjump_in_parallel_p (rtx insn)
+condjump_in_parallel_p (const_rtx insn)
 {
 {
-  rtx x = PATTERN (insn);
+  const_rtx x = PATTERN (insn);
 
   if (GET_CODE (x) != PARALLEL)
     return 0;
 
   if (GET_CODE (x) != PARALLEL)
     return 0;
@@ -785,7 +786,7 @@ condjump_in_parallel_p (rtx insn)
 /* Return set of PC, otherwise NULL.  */
 
 rtx
 /* Return set of PC, otherwise NULL.  */
 
 rtx
-pc_set (rtx insn)
+pc_set (const_rtx insn)
 {
   rtx pat;
   if (!JUMP_P (insn))
 {
   rtx pat;
   if (!JUMP_P (insn))
@@ -806,9 +807,9 @@ pc_set (rtx insn)
    possibly bundled inside a PARALLEL.  */
 
 int
    possibly bundled inside a PARALLEL.  */
 
 int
-any_uncondjump_p (rtx insn)
+any_uncondjump_p (const_rtx insn)
 {
 {
-  rtx x = pc_set (insn);
+  const_rtx x = pc_set (insn);
   if (!x)
     return 0;
   if (GET_CODE (SET_SRC (x)) != LABEL_REF)
   if (!x)
     return 0;
   if (GET_CODE (SET_SRC (x)) != LABEL_REF)
@@ -826,9 +827,9 @@ any_uncondjump_p (rtx insn)
    Note that unlike condjump_p it returns false for unconditional jumps.  */
 
 int
    Note that unlike condjump_p it returns false for unconditional jumps.  */
 
 int
-any_condjump_p (rtx insn)
+any_condjump_p (const_rtx insn)
 {
 {
-  rtx x = pc_set (insn);
+  const_rtx x = pc_set (insn);
   enum rtx_code a, b;
 
   if (!x)
   enum rtx_code a, b;
 
   if (!x)
@@ -846,7 +847,7 @@ any_condjump_p (rtx insn)
 /* Return the label of a conditional jump.  */
 
 rtx
 /* Return the label of a conditional jump.  */
 
 rtx
-condjump_label (rtx insn)
+condjump_label (const_rtx insn)
 {
   rtx x = pc_set (insn);
 
 {
   rtx x = pc_set (insn);
 
@@ -871,10 +872,25 @@ returnjump_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
 {
   rtx x = *loc;
 
 {
   rtx x = *loc;
 
-  return x && (GET_CODE (x) == RETURN
-              || (GET_CODE (x) == SET && SET_IS_RETURN_P (x)));
+  if (x == NULL)
+    return false;
+
+  switch (GET_CODE (x))
+    {
+    case RETURN:
+    case EH_RETURN:
+      return true;
+
+    case SET:
+      return SET_IS_RETURN_P (x);
+
+    default:
+      return false;
+    }
 }
 
 }
 
+/* Return TRUE if INSN is a return jump.  */
+
 int
 returnjump_p (rtx insn)
 {
 int
 returnjump_p (rtx insn)
 {
@@ -883,11 +899,27 @@ returnjump_p (rtx insn)
   return for_each_rtx (&PATTERN (insn), returnjump_p_1, NULL);
 }
 
   return for_each_rtx (&PATTERN (insn), returnjump_p_1, NULL);
 }
 
+/* Return true if INSN is a (possibly conditional) return insn.  */
+
+static int
+eh_returnjump_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  return *loc && GET_CODE (*loc) == EH_RETURN;
+}
+
+int
+eh_returnjump_p (rtx insn)
+{
+  if (!JUMP_P (insn))
+    return 0;
+  return for_each_rtx (&PATTERN (insn), eh_returnjump_p_1, NULL);
+}
+
 /* Return true if INSN is a jump that only transfers control and
    nothing more.  */
 
 int
 /* Return true if INSN is a jump that only transfers control and
    nothing more.  */
 
 int
-onlyjump_p (rtx insn)
+onlyjump_p (const_rtx insn)
 {
   rtx set;
 
 {
   rtx set;
 
@@ -911,7 +943,7 @@ onlyjump_p (rtx insn)
    and has no side effects.  */
 
 int
    and has no side effects.  */
 
 int
-only_sets_cc0_p (rtx x)
+only_sets_cc0_p (const_rtx x)
 {
   if (! x)
     return 0;
 {
   if (! x)
     return 0;
@@ -928,7 +960,7 @@ only_sets_cc0_p (rtx x)
    but also does other things.  */
 
 int
    but also does other things.  */
 
 int
-sets_cc0_p (rtx x)
+sets_cc0_p (const_rtx x)
 {
   if (! x)
     return 0;
 {
   if (! x)
     return 0;
@@ -957,12 +989,14 @@ sets_cc0_p (rtx x)
 }
 #endif
 \f
 }
 #endif
 \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).
-   If INSN is an INSN or a CALL_INSN and there is at least one CODE_LABEL
-   referenced in INSN, add a REG_LABEL note containing that label to INSN.
-   Also, when there are consecutive labels, canonicalize on the last of them.
+/* 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 as a jump target, then store the last
+   one in JUMP_LABEL (INSN).  For a tablejump, this must be the label
+   for the ADDR_VEC.  Store any other jump targets as REG_LABEL_TARGET
+   notes.  If INSN is an INSN or a CALL_INSN or non-target operands of
+   a JUMP_INSN, and there is at least one CODE_LABEL referenced in
+   INSN, add a REG_LABEL_OPERAND note containing that label to INSN.
 
    Note that two labels separated by a loop-beginning note
    must be kept distinct if we have not yet done loop-optimization,
 
    Note that two labels separated by a loop-beginning note
    must be kept distinct if we have not yet done loop-optimization,
@@ -973,6 +1007,23 @@ sets_cc0_p (rtx x)
 void
 mark_jump_label (rtx x, rtx insn, int in_mem)
 {
 void
 mark_jump_label (rtx x, rtx insn, int in_mem)
 {
+  rtx asmop = extract_asm_operands (x);
+  if (asmop)
+    mark_jump_label_asm (asmop, insn);
+  else
+    mark_jump_label_1 (x, insn, in_mem != 0,
+                      (insn != NULL && x == PATTERN (insn) && JUMP_P (insn)));
+}
+
+/* Worker function for mark_jump_label.  IN_MEM is TRUE when X occurs
+   within a (MEM ...).  IS_TARGET is TRUE when X is to be treated as a
+   jump-target; when the JUMP_LABEL field of INSN should be set or a
+   REG_LABEL_TARGET note should be added, not a REG_LABEL_OPERAND
+   note.  */
+
+static void
+mark_jump_label_1 (rtx x, rtx insn, bool in_mem, bool is_target)
+{
   RTX_CODE code = GET_CODE (x);
   int i;
   const char *fmt;
   RTX_CODE code = GET_CODE (x);
   int i;
   const char *fmt;
@@ -989,7 +1040,7 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
       return;
 
     case MEM:
       return;
 
     case MEM:
-      in_mem = 1;
+      in_mem = true;
       break;
 
     case SEQUENCE:
       break;
 
     case SEQUENCE:
@@ -1004,9 +1055,19 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
 
       /* If this is a constant-pool reference, see if it is a label.  */
       if (CONSTANT_POOL_ADDRESS_P (x))
 
       /* If this is a constant-pool reference, see if it is a label.  */
       if (CONSTANT_POOL_ADDRESS_P (x))
-       mark_jump_label (get_pool_constant (x), insn, in_mem);
+       mark_jump_label_1 (get_pool_constant (x), insn, in_mem, is_target);
       break;
 
       break;
 
+      /* Handle operands in the condition of an if-then-else as for a
+        non-jump insn.  */
+    case IF_THEN_ELSE:
+      if (!is_target)
+       break;
+      mark_jump_label_1 (XEXP (x, 0), insn, in_mem, false);
+      mark_jump_label_1 (XEXP (x, 1), insn, in_mem, true);
+      mark_jump_label_1 (XEXP (x, 2), insn, in_mem, true);
+      return;
+
     case LABEL_REF:
       {
        rtx label = XEXP (x, 0);
     case LABEL_REF:
       {
        rtx label = XEXP (x, 0);
@@ -1014,7 +1075,7 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
        /* Ignore remaining references to unreachable labels that
           have been deleted.  */
        if (NOTE_P (label)
        /* Ignore remaining references to unreachable labels that
           have been deleted.  */
        if (NOTE_P (label)
-           && NOTE_LINE_NUMBER (label) == NOTE_INSN_DELETED_LABEL)
+           && NOTE_KIND (label) == NOTE_INSN_DELETED_LABEL)
          break;
 
        gcc_assert (LABEL_P (label));
          break;
 
        gcc_assert (LABEL_P (label));
@@ -1029,17 +1090,23 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
 
        if (insn)
          {
 
        if (insn)
          {
-           if (JUMP_P (insn))
+           if (is_target
+               /* Do not change a previous setting of JUMP_LABEL.  If the
+                  JUMP_LABEL slot is occupied by a different label,
+                  create a note for this label.  */
+               && (JUMP_LABEL (insn) == NULL || JUMP_LABEL (insn) == label))
              JUMP_LABEL (insn) = label;
            else
              {
              JUMP_LABEL (insn) = label;
            else
              {
-               /* Add a REG_LABEL note for LABEL unless there already
-                  is one.  All uses of a label, except for labels
-                  that are the targets of jumps, must have a
-                  REG_LABEL note.  */
-               if (! find_reg_note (insn, REG_LABEL, label))
-                 REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, label,
-                                                       REG_NOTES (insn));
+               enum reg_note kind
+                 = is_target ? REG_LABEL_TARGET : REG_LABEL_OPERAND;
+
+               /* Add a REG_LABEL_OPERAND or REG_LABEL_TARGET note
+                  for LABEL unless there already is one.  All uses of
+                  a label, except for the primary target of a jump,
+                  must have such a note.  */
+               if (! find_reg_note (insn, kind, label))
+                 add_reg_note (insn, kind, label);
              }
          }
        return;
              }
          }
        return;
@@ -1054,7 +1121,8 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
          int eltnum = code == ADDR_DIFF_VEC ? 1 : 0;
 
          for (i = 0; i < XVECLEN (x, eltnum); i++)
          int eltnum = code == ADDR_DIFF_VEC ? 1 : 0;
 
          for (i = 0; i < XVECLEN (x, eltnum); i++)
-           mark_jump_label (XVECEXP (x, eltnum, i), NULL_RTX, in_mem);
+           mark_jump_label_1 (XVECEXP (x, eltnum, i), NULL_RTX, in_mem,
+                              is_target);
        }
       return;
 
        }
       return;
 
@@ -1063,19 +1131,41 @@ mark_jump_label (rtx x, rtx insn, int in_mem)
     }
 
   fmt = GET_RTX_FORMAT (code);
     }
 
   fmt = GET_RTX_FORMAT (code);
+
+  /* The primary target of a tablejump is the label of the ADDR_VEC,
+     which is canonically mentioned *last* in the insn.  To get it
+     marked as JUMP_LABEL, we iterate over items in reverse order.  */
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-       mark_jump_label (XEXP (x, i), insn, in_mem);
+       mark_jump_label_1 (XEXP (x, i), insn, in_mem, is_target);
       else if (fmt[i] == 'E')
        {
          int j;
       else if (fmt[i] == 'E')
        {
          int j;
-         for (j = 0; j < XVECLEN (x, i); j++)
-           mark_jump_label (XVECEXP (x, i, j), insn, in_mem);
+
+         for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+           mark_jump_label_1 (XVECEXP (x, i, j), insn, in_mem,
+                              is_target);
        }
     }
 }
 
        }
     }
 }
 
+/* Worker function for mark_jump_label.  Handle asm insns specially.
+   In particular, output operands need not be considered so we can
+   avoid re-scanning the replicated asm_operand.  Also, the asm_labels
+   need to be considered targets.  */
+
+static void
+mark_jump_label_asm (rtx asmop, rtx insn)
+{
+  int i;
+
+  for (i = ASM_OPERANDS_INPUT_LENGTH (asmop) - 1; i >= 0; --i)
+    mark_jump_label_1 (ASM_OPERANDS_INPUT (asmop, i), insn, false, false);
+
+  for (i = ASM_OPERANDS_LABEL_LENGTH (asmop) - 1; i >= 0; --i)
+    mark_jump_label_1 (ASM_OPERANDS_LABEL (asmop, i), insn, false, true);
+}
 \f
 /* Delete insn INSN from the chain of insns and update label ref counts
    and delete insns now unreachable.
 \f
 /* Delete insn INSN from the chain of insns and update label ref counts
    and delete insns now unreachable.
@@ -1115,20 +1205,10 @@ delete_related_insns (rtx insn)
       rtx lab = JUMP_LABEL (insn), lab_next;
 
       if (LABEL_NUSES (lab) == 0)
       rtx lab = JUMP_LABEL (insn), lab_next;
 
       if (LABEL_NUSES (lab) == 0)
-       {
-         /* This can delete NEXT or PREV,
-            either directly if NEXT is JUMP_LABEL (INSN),
-            or indirectly through more levels of jumps.  */
-         delete_related_insns (lab);
-
-         /* I feel a little doubtful about this loop,
-            but I see no clean and sure alternative way
-            to find the first insn after INSN that is not now deleted.
-            I hope this works.  */
-         while (next && INSN_DELETED_P (next))
-           next = NEXT_INSN (next);
-         return next;
-       }
+       /* This can delete NEXT or PREV,
+          either directly if NEXT is JUMP_LABEL (INSN),
+          or indirectly through more levels of jumps.  */
+       delete_related_insns (lab);
       else if (tablejump_p (insn, NULL, &lab_next))
        {
          /* If we're deleting the tablejump, delete the dispatch table.
       else if (tablejump_p (insn, NULL, &lab_next))
        {
          /* If we're deleting the tablejump, delete the dispatch table.
@@ -1141,9 +1221,7 @@ delete_related_insns (rtx insn)
 
   /* Likewise if we're deleting a dispatch table.  */
 
 
   /* Likewise if we're deleting a dispatch table.  */
 
-  if (JUMP_P (insn)
-      && (GET_CODE (PATTERN (insn)) == ADDR_VEC
-         || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
+  if (JUMP_TABLE_DATA_P (insn))
     {
       rtx pat = PATTERN (insn);
       int i, diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC;
     {
       rtx pat = PATTERN (insn);
       int i, diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC;
@@ -1157,10 +1235,12 @@ delete_related_insns (rtx insn)
       return next;
     }
 
       return next;
     }
 
-  /* Likewise for an ordinary INSN / CALL_INSN with a REG_LABEL note.  */
-  if (NONJUMP_INSN_P (insn) || CALL_P (insn))
+  /* Likewise for any JUMP_P / INSN / CALL_INSN with a
+     REG_LABEL_OPERAND or REG_LABEL_TARGET note.  */
+  if (INSN_P (insn))
     for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
     for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
-      if (REG_NOTE_KIND (note) == REG_LABEL
+      if ((REG_NOTE_KIND (note) == REG_LABEL_OPERAND
+          || REG_NOTE_KIND (note) == REG_LABEL_TARGET)
          /* This could also be a NOTE_INSN_DELETED_LABEL note.  */
          && LABEL_P (XEXP (note, 0)))
        if (LABEL_NUSES (XEXP (note, 0)) == 0)
          /* This could also be a NOTE_INSN_DELETED_LABEL note.  */
          && LABEL_P (XEXP (note, 0)))
        if (LABEL_NUSES (XEXP (note, 0)) == 0)
@@ -1175,9 +1255,7 @@ delete_related_insns (rtx insn)
 
   if (was_code_label
       && NEXT_INSN (insn) != 0
 
   if (was_code_label
       && NEXT_INSN (insn) != 0
-      && JUMP_P (NEXT_INSN (insn))
-      && (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC
-         || GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC))
+      && JUMP_TABLE_DATA_P (NEXT_INSN (insn)))
     next = delete_related_insns (NEXT_INSN (insn));
 
   /* If INSN was a label, delete insns following it if now unreachable.  */
     next = delete_related_insns (NEXT_INSN (insn));
 
   /* If INSN was a label, delete insns following it if now unreachable.  */
@@ -1204,6 +1282,12 @@ delete_related_insns (rtx insn)
        }
     }
 
        }
     }
 
+  /* I feel a little doubtful about this loop,
+     but I see no clean and sure alternative way
+     to find the first insn after INSN that is not now deleted.
+     I hope this works.  */
+  while (next && INSN_DELETED_P (next))
+    next = NEXT_INSN (next);
   return next;
 }
 \f
   return next;
 }
 \f
@@ -1292,6 +1376,15 @@ redirect_exp_1 (rtx *loc, rtx olabel, rtx nlabel, rtx insn)
       return;
     }
 
       return;
     }
 
+  if (code == IF_THEN_ELSE)
+    {
+      /* Skip the condition of an IF_THEN_ELSE.  We only want to
+         change jump destinations, not eventual label comparisons.  */
+      redirect_exp_1 (&XEXP (x, 1), olabel, nlabel, insn);
+      redirect_exp_1 (&XEXP (x, 2), olabel, nlabel, insn);
+      return;
+    }
+
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
@@ -1314,9 +1407,17 @@ int
 redirect_jump_1 (rtx jump, rtx nlabel)
 {
   int ochanges = num_validated_changes ();
 redirect_jump_1 (rtx jump, rtx nlabel)
 {
   int ochanges = num_validated_changes ();
-  rtx *loc;
+  rtx *loc, asmop;
 
 
-  if (GET_CODE (PATTERN (jump)) == PARALLEL)
+  asmop = extract_asm_operands (PATTERN (jump));
+  if (asmop)
+    {
+      if (nlabel == NULL)
+       return 0;
+      gcc_assert (ASM_OPERANDS_LABEL_LENGTH (asmop) == 1);
+      loc = &ASM_OPERANDS_LABEL (asmop, 0);
+    }
+  else if (GET_CODE (PATTERN (jump)) == PARALLEL)
     loc = &XVECEXP (PATTERN (jump), 0, 0);
   else
     loc = &PATTERN (jump);
     loc = &XVECEXP (PATTERN (jump), 0, 0);
   else
     loc = &PATTERN (jump);
@@ -1351,7 +1452,7 @@ redirect_jump (rtx jump, rtx nlabel, int delete_unused)
 }
 
 /* Fix up JUMP_LABEL and label ref counts after OLABEL has been replaced with
 }
 
 /* Fix up JUMP_LABEL and label ref counts after OLABEL has been replaced with
-   NLABEL in JUMP.  
+   NLABEL in JUMP.
    If DELETE_UNUSED is positive, delete related insn to OLABEL if its ref
    count has dropped to zero.  */
 void
    If DELETE_UNUSED is positive, delete related insn to OLABEL if its ref
    count has dropped to zero.  */
 void
@@ -1360,6 +1461,8 @@ redirect_jump_2 (rtx jump, rtx olabel, rtx nlabel, int delete_unused,
 {
   rtx note;
 
 {
   rtx note;
 
+  gcc_assert (JUMP_LABEL (jump) == olabel);
+
   /* 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.  */
   /* 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.  */
@@ -1440,10 +1543,11 @@ invert_jump_1 (rtx jump, rtx nlabel)
   int ok;
 
   ochanges = num_validated_changes ();
   int ok;
 
   ochanges = num_validated_changes ();
-  gcc_assert (x);
+  if (x == NULL)
+    return 0;
   ok = invert_exp_1 (SET_SRC (x), jump);
   gcc_assert (ok);
   ok = invert_exp_1 (SET_SRC (x), jump);
   gcc_assert (ok);
-  
+
   if (num_validated_changes () == ochanges)
     return 0;
 
   if (num_validated_changes () == ochanges)
     return 0;
 
@@ -1476,10 +1580,10 @@ invert_jump (rtx jump, rtx nlabel, int delete_unused)
    reversed.  */
 
 int
    reversed.  */
 
 int
-rtx_renumbered_equal_p (rtx x, rtx y)
+rtx_renumbered_equal_p (const_rtx x, const_rtx y)
 {
   int i;
 {
   int i;
-  enum rtx_code code = GET_CODE (x);
+  const enum rtx_code code = GET_CODE (x);
   const char *fmt;
 
   if (x == y)
   const char *fmt;
 
   if (x == y)
@@ -1491,6 +1595,7 @@ rtx_renumbered_equal_p (rtx x, rtx y)
     {
       int reg_x = -1, reg_y = -1;
       int byte_x = 0, byte_y = 0;
     {
       int reg_x = -1, reg_y = -1;
       int byte_x = 0, byte_y = 0;
+      struct subreg_info info;
 
       if (GET_MODE (x) != GET_MODE (y))
        return 0;
 
       if (GET_MODE (x) != GET_MODE (y))
        return 0;
@@ -1507,10 +1612,12 @@ rtx_renumbered_equal_p (rtx x, rtx y)
 
          if (reg_renumber[reg_x] >= 0)
            {
 
          if (reg_renumber[reg_x] >= 0)
            {
-             reg_x = subreg_regno_offset (reg_renumber[reg_x],
-                                          GET_MODE (SUBREG_REG (x)),
-                                          byte_x,
-                                          GET_MODE (x));
+             subreg_get_info (reg_renumber[reg_x],
+                              GET_MODE (SUBREG_REG (x)), byte_x,
+                              GET_MODE (x), &info);
+             if (!info.representable_p)
+               return 0;
+             reg_x = info.offset;
              byte_x = 0;
            }
        }
              byte_x = 0;
            }
        }
@@ -1528,10 +1635,12 @@ rtx_renumbered_equal_p (rtx x, rtx y)
 
          if (reg_renumber[reg_y] >= 0)
            {
 
          if (reg_renumber[reg_y] >= 0)
            {
-             reg_y = subreg_regno_offset (reg_renumber[reg_y],
-                                          GET_MODE (SUBREG_REG (y)),
-                                          byte_y,
-                                          GET_MODE (y));
+             subreg_get_info (reg_renumber[reg_y],
+                              GET_MODE (SUBREG_REG (y)), byte_y,
+                              GET_MODE (y), &info);
+             if (!info.representable_p)
+               return 0;
+             reg_y = info.offset;
              byte_y = 0;
            }
        }
              byte_y = 0;
            }
        }
@@ -1586,6 +1695,10 @@ rtx_renumbered_equal_p (rtx x, rtx y)
   if (GET_MODE (x) != GET_MODE (y))
     return 0;
 
   if (GET_MODE (x) != GET_MODE (y))
     return 0;
 
+  /* MEMs refering to different address space are not equivalent.  */
+  if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (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.  */
   if (targetm.commutative_p (x, UNKNOWN))
   /* For commutative operations, the RTX match if the operand match in any
      order.  Also handle the simple binary and unary cases without a loop.  */
   if (targetm.commutative_p (x, UNKNOWN))
@@ -1661,7 +1774,7 @@ rtx_renumbered_equal_p (rtx x, rtx y)
    return -1.  Any rtx is valid for X.  */
 
 int
    return -1.  Any rtx is valid for X.  */
 
 int
-true_regnum (rtx x)
+true_regnum (const_rtx x)
 {
   if (REG_P (x))
     {
 {
   if (REG_P (x))
     {
@@ -1673,20 +1786,24 @@ true_regnum (rtx x)
     {
       int base = true_regnum (SUBREG_REG (x));
       if (base >= 0
     {
       int base = true_regnum (SUBREG_REG (x));
       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));
+         && base < FIRST_PSEUDO_REGISTER)
+       {
+         struct subreg_info info;
+
+         subreg_get_info (REGNO (SUBREG_REG (x)),
+                          GET_MODE (SUBREG_REG (x)),
+                          SUBREG_BYTE (x), GET_MODE (x), &info);
+
+         if (info.representable_p)
+           return base + info.offset;
+       }
     }
   return -1;
 }
 
 /* Return regno of the register REG and handle subregs too.  */
 unsigned int
     }
   return -1;
 }
 
 /* Return regno of the register REG and handle subregs too.  */
 unsigned int
-reg_or_subregno (rtx reg)
+reg_or_subregno (const_rtx reg)
 {
   if (GET_CODE (reg) == SUBREG)
     reg = SUBREG_REG (reg);
 {
   if (GET_CODE (reg) == SUBREG)
     reg = SUBREG_REG (reg);