X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fjump.c;h=52cbbca43dbdfa672c53d231049656bffba329c9;hb=c68bd46083d86464113b032f900387e92ddb4955;hp=e61e2fba9d7d53b6f1da21dbbedc8bfd811a4255;hpb=7602144104e3056b755cf0b2ca0338bf17ff246d;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/jump.c b/gcc/jump.c index e61e2fba9d7..52cbbca43db 100644 --- a/gcc/jump.c +++ b/gcc/jump.c @@ -1,13 +1,13 @@ /* 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 - Free Software Foundation, Inc. + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, + 2011 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 @@ -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 -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 +. */ /* 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 @@ -30,7 +29,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA JUMP_LABEL internal field. With this we can detect labels that become unused because of the deletion of all the jumps that formerly used them. The JUMP_LABEL info is sometimes looked - at by later passes. + at by later passes. For return insns, it contains either a + RETURN or a SIMPLE_RETURN rtx. The subroutines redirect_jump and invert_jump are used from other passes as well. */ @@ -48,11 +48,10 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "insn-attr.h" #include "recog.h" #include "function.h" +#include "basic-block.h" #include "expr.h" -#include "real.h" #include "except.h" -#include "diagnostic.h" -#include "toplev.h" +#include "diagnostic-core.h" #include "reload.h" #include "predict.h" #include "timevar.h" @@ -68,15 +67,15 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 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 *); -/* 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. */ -void -rebuild_jump_labels (rtx f) +/* Worker for rebuild_jump_labels and rebuild_jump_labels_chain. */ +static void +rebuild_jump_labels_1 (rtx f, bool count_forced) { rtx insn; @@ -88,11 +87,31 @@ rebuild_jump_labels (rtx f) closely enough to delete them here, so make sure their reference count doesn't drop to zero. */ - for (insn = forced_labels; insn; insn = XEXP (insn, 1)) - if (LABEL_P (XEXP (insn, 0))) - LABEL_NUSES (XEXP (insn, 0))++; + if (count_forced) + for (insn = forced_labels; insn; insn = XEXP (insn, 1)) + if (LABEL_P (XEXP (insn, 0))) + LABEL_NUSES (XEXP (insn, 0))++; timevar_pop (TV_REBUILD_JUMP); } + +/* 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) +{ + rebuild_jump_labels_1 (f, true); +} + +/* This function is like rebuild_jump_labels, but doesn't run over + forced_labels. It can be used on insn chains that aren't the + main function chain. */ +void +rebuild_jump_labels_chain (rtx chain) +{ + rebuild_jump_labels_1 (chain, false); +} /* Some old code expects exactly one BARRIER as the NEXT_INSN of a non-fallthru insn. This is not generally true, as multiple barriers @@ -112,6 +131,8 @@ cleanup_barriers (void) 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)) @@ -121,49 +142,111 @@ cleanup_barriers (void) 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 */ - 0, /* tv_id */ + TV_NONE, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ + 0 /* todo_flags_finish */ + } }; -/* 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)) - 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); + } + } + } +} + +/* A subroutine of mark_all_labels. Trivially propagate a simple label + load into a jump_insn that uses it. */ + +static void +maybe_propagate_label_ref (rtx jump_insn, rtx prev_nonjump_insn) +{ + rtx label_note, pc, pc_src; + + pc = pc_set (jump_insn); + pc_src = pc != NULL ? SET_SRC (pc) : NULL; + label_note = find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL); + + /* 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 (label_note != NULL && pc_src != NULL) + { + 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)))))) + { + /* 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, jump_insn, false, true); + + gcc_assert (JUMP_LABEL (jump_insn) == XEXP (label_note, 0)); + } + } } /* Mark the label each jump jumps to. @@ -174,49 +257,30 @@ mark_all_labels (rtx f) { rtx insn; - 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)) - { - /* 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 = find_reg_note (insn, REG_LABEL, NULL_RTX); - if (label_note) - { - /* 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); - } - } - } - } - - /* 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) { + /* In cfglayout mode, we don't bother with trivial next-insn + propagation of LABEL_REFs into JUMP_LABEL. This will be + handled by other optimizers using better algorithms. */ + FOR_BB_INSNS (bb, insn) + { + gcc_assert (! INSN_DELETED_P (insn)); + if (NONDEBUG_INSN_P (insn)) + mark_jump_label (PATTERN (insn), insn, 0); + } + + /* 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. */ 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)) { @@ -225,62 +289,28 @@ mark_all_labels (rtx 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) + else { - next = NEXT_INSN (insn); - if (NOTE_P (insn) - && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END - || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)) + rtx prev_nonjump_insn = NULL; + for (insn = f; insn; insn = NEXT_INSN (insn)) { - /* 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 + if (INSN_DELETED_P (insn)) + ; + else if (LABEL_P (insn)) + prev_nonjump_insn = NULL; + else if (NONDEBUG_INSN_P (insn)) { - 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; + mark_jump_label (PATTERN (insn), insn, 0); + if (JUMP_P (insn)) + { + if (JUMP_LABEL (insn) == NULL && prev_nonjump_insn != NULL) + maybe_propagate_label_ref (insn, prev_nonjump_insn); + } + else + prev_nonjump_insn = insn; } } - else - last = insn; } - - /* There were no real instructions. */ - if (start == past_end) - return true; - - end = last; - - *startp = start; - *endp = end; - return false; } /* Given a comparison (CODE ARG0 ARG1), inside an insn, INSN, return a code @@ -290,7 +320,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 -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; @@ -311,8 +342,9 @@ reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn { #ifdef REVERSE_CONDITION return REVERSE_CONDITION (code, mode); -#endif +#else return reverse_condition (code); +#endif } /* Try a few special cases based on the comparison code. */ @@ -347,7 +379,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)) { - 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 @@ -355,11 +387,14 @@ reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn 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 = 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)) { @@ -391,7 +426,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. */ - if (GET_CODE (arg0) == CONST_INT + if (CONST_INT_P (arg0) || (GET_MODE (arg0) != VOIDmode && GET_MODE_CLASS (mode) != MODE_CC && !HONOR_NANS (mode))) @@ -403,7 +438,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 -reversed_comparison_code (rtx comparison, rtx insn) +reversed_comparison_code (const_rtx comparison, const_rtx insn) { if (!COMPARISON_P (comparison)) return UNKNOWN; @@ -415,7 +450,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 -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) @@ -711,7 +746,7 @@ comparison_dominates_p (enum rtx_code code1, enum rtx_code code2) /* 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 @@ -726,9 +761,9 @@ simplejump_p (rtx insn) 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) @@ -741,10 +776,10 @@ condjump_p (rtx insn) return (GET_CODE (x) == IF_THEN_ELSE && ((GET_CODE (XEXP (x, 2)) == PC && (GET_CODE (XEXP (x, 1)) == LABEL_REF - || GET_CODE (XEXP (x, 1)) == RETURN)) + || ANY_RETURN_P (XEXP (x, 1)))) || (GET_CODE (XEXP (x, 1)) == PC && (GET_CODE (XEXP (x, 2)) == LABEL_REF - || GET_CODE (XEXP (x, 2)) == RETURN)))); + || ANY_RETURN_P (XEXP (x, 2)))))); } /* Return nonzero if INSN is a (possibly) conditional jump inside a @@ -754,9 +789,9 @@ condjump_p (rtx insn) 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; @@ -773,11 +808,11 @@ condjump_in_parallel_p (rtx insn) return 0; if (XEXP (SET_SRC (x), 2) == pc_rtx && (GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF - || GET_CODE (XEXP (SET_SRC (x), 1)) == RETURN)) + || ANY_RETURN_P (XEXP (SET_SRC (x), 1)))) return 1; if (XEXP (SET_SRC (x), 1) == pc_rtx && (GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF - || GET_CODE (XEXP (SET_SRC (x), 2)) == RETURN)) + || ANY_RETURN_P (XEXP (SET_SRC (x), 2)))) return 1; return 0; } @@ -785,7 +820,7 @@ condjump_in_parallel_p (rtx insn) /* Return set of PC, otherwise NULL. */ rtx -pc_set (rtx insn) +pc_set (const_rtx insn) { rtx pat; if (!JUMP_P (insn)) @@ -806,9 +841,9 @@ pc_set (rtx insn) 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) @@ -826,9 +861,9 @@ any_uncondjump_p (rtx insn) 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) @@ -839,14 +874,15 @@ any_condjump_p (rtx insn) a = GET_CODE (XEXP (SET_SRC (x), 1)); b = GET_CODE (XEXP (SET_SRC (x), 2)); - return ((b == PC && (a == LABEL_REF || a == RETURN)) - || (a == PC && (b == LABEL_REF || b == RETURN))); + return ((b == PC && (a == LABEL_REF || a == RETURN || a == SIMPLE_RETURN)) + || (a == PC + && (b == LABEL_REF || b == RETURN || b == SIMPLE_RETURN))); } /* Return the label of a conditional jump. */ rtx -condjump_label (rtx insn) +condjump_label (const_rtx insn) { rtx x = pc_set (insn); @@ -871,10 +907,26 @@ returnjump_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) { 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 SIMPLE_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) { @@ -883,11 +935,27 @@ returnjump_p (rtx insn) 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 -onlyjump_p (rtx insn) +onlyjump_p (const_rtx insn) { rtx set; @@ -905,13 +973,22 @@ onlyjump_p (rtx insn) return 1; } +/* Return true iff INSN is a jump and its JUMP_LABEL is a label, not + NULL or a return. */ +bool +jump_to_label_p (rtx insn) +{ + return (JUMP_P (insn) + && JUMP_LABEL (insn) != NULL && !ANY_RETURN_P (JUMP_LABEL (insn))); +} + #ifdef HAVE_cc0 /* Return nonzero if X is an RTX that only sets the condition codes and has no side effects. */ int -only_sets_cc0_p (rtx x) +only_sets_cc0_p (const_rtx x) { if (! x) return 0; @@ -928,7 +1005,7 @@ only_sets_cc0_p (rtx x) but also does other things. */ int -sets_cc0_p (rtx x) +sets_cc0_p (const_rtx x) { if (! x) return 0; @@ -957,12 +1034,15 @@ sets_cc0_p (rtx x) } #endif -/* 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. + For returnjumps, the JUMP_LABEL will also be set as appropriate. Note that two labels separated by a loop-beginning note must be kept distinct if we have not yet done loop-optimization, @@ -973,6 +1053,23 @@ sets_cc0_p (rtx x) 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; @@ -988,8 +1085,17 @@ mark_jump_label (rtx x, rtx insn, int in_mem) case CALL: return; + case RETURN: + case SIMPLE_RETURN: + if (is_target) + { + gcc_assert (JUMP_LABEL (insn) == NULL || JUMP_LABEL (insn) == x); + JUMP_LABEL (insn) = x; + } + return; + case MEM: - in_mem = 1; + in_mem = true; break; case SEQUENCE: @@ -1004,9 +1110,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)) - mark_jump_label (get_pool_constant (x), insn, in_mem); + mark_jump_label_1 (get_pool_constant (x), insn, in_mem, is_target); 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); @@ -1014,7 +1130,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) - && NOTE_LINE_NUMBER (label) == NOTE_INSN_DELETED_LABEL) + && NOTE_KIND (label) == NOTE_INSN_DELETED_LABEL) break; gcc_assert (LABEL_P (label)); @@ -1029,17 +1145,23 @@ mark_jump_label (rtx x, rtx insn, int in_mem) 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 { - /* 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; @@ -1054,7 +1176,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++) - 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; @@ -1063,19 +1186,41 @@ mark_jump_label (rtx x, rtx insn, int in_mem) } 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') - 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; - 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); +} /* Delete insn INSN from the chain of insns and update label ref counts and delete insns now unreachable. @@ -1110,25 +1255,15 @@ delete_related_insns (rtx insn) /* If deleting a jump, decrement the count of the label, and delete the label if it is now unused. */ - if (JUMP_P (insn) && JUMP_LABEL (insn)) + if (jump_to_label_p (insn)) { 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. @@ -1141,9 +1276,7 @@ delete_related_insns (rtx insn) /* 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; @@ -1157,10 +1290,12 @@ delete_related_insns (rtx insn) 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)) - 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) @@ -1175,9 +1310,7 @@ delete_related_insns (rtx insn) 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. */ @@ -1204,6 +1337,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; } @@ -1247,6 +1386,18 @@ delete_for_peephole (rtx from, rtx to) is also an unconditional jump in that case. */ } +/* A helper function for redirect_exp_1; examines its input X and returns + either a LABEL_REF around a label, or a RETURN if X was NULL. */ +static rtx +redirect_target (rtx x) +{ + if (x == NULL_RTX) + return ret_rtx; + if (!ANY_RETURN_P (x)) + return gen_rtx_LABEL_REF (Pmode, x); + return x; +} + /* Throughout LOC, redirect OLABEL to NLABEL. Treat null OLABEL or NLABEL as a return. Accrue modifications into the change group. */ @@ -1258,37 +1409,31 @@ redirect_exp_1 (rtx *loc, rtx olabel, rtx nlabel, rtx insn) int i; const char *fmt; - if (code == LABEL_REF) - { - if (XEXP (x, 0) == olabel) - { - rtx n; - if (nlabel) - n = gen_rtx_LABEL_REF (Pmode, nlabel); - else - n = gen_rtx_RETURN (VOIDmode); - - validate_change (insn, loc, n, 1); - return; - } - } - else if (code == RETURN && olabel == 0) + if ((code == LABEL_REF && XEXP (x, 0) == olabel) + || x == olabel) { - 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); + x = redirect_target (nlabel); + if (GET_CODE (x) == LABEL_REF && loc == &PATTERN (insn)) + x = gen_rtx_SET (VOIDmode, pc_rtx, x); validate_change (insn, loc, x, 1); return; } - if (code == SET && nlabel == 0 && SET_DEST (x) == pc_rtx + if (code == SET && SET_DEST (x) == pc_rtx + && ANY_RETURN_P (nlabel) && GET_CODE (SET_SRC (x)) == LABEL_REF && XEXP (SET_SRC (x), 0) == olabel) { - validate_change (insn, loc, gen_rtx_RETURN (VOIDmode), 1); + validate_change (insn, loc, nlabel, 1); + 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; } @@ -1314,9 +1459,18 @@ int redirect_jump_1 (rtx jump, rtx nlabel) { int ochanges = num_validated_changes (); - rtx *loc; + rtx *loc, asmop; - if (GET_CODE (PATTERN (jump)) == PARALLEL) + gcc_assert (nlabel != NULL_RTX); + 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); @@ -1329,17 +1483,31 @@ redirect_jump_1 (rtx jump, rtx nlabel) jump target label is unused as a result, it and the code following it may be deleted. - If NLABEL is zero, we are to turn the jump into a (possibly conditional) - RETURN insn. + Normally, NLABEL will be a label, but it may also be a RETURN rtx; + in that case we are to turn the jump into a (possibly conditional) + return insn. The return value will be 1 if the change was made, 0 if it wasn't - (this can only occur for NLABEL == 0). */ + (this can only occur when trying to produce return insns). */ int redirect_jump (rtx jump, rtx nlabel, int delete_unused) { rtx olabel = JUMP_LABEL (jump); + if (!nlabel) + { + /* If there is no label, we are asked to redirect to the EXIT block. + When before the epilogue is emitted, return/simple_return cannot be + created so we return 0 immediately. After the epilogue is emitted, + we always expect a label, either a non-null label, or a + return/simple_return RTX. */ + + if (!epilogue_completed) + return 0; + gcc_unreachable (); + } + if (nlabel == olabel) return 1; @@ -1351,7 +1519,7 @@ redirect_jump (rtx jump, rtx nlabel, int delete_unused) } /* 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 @@ -1360,18 +1528,21 @@ redirect_jump_2 (rtx jump, rtx olabel, rtx nlabel, int delete_unused, { 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. */ gcc_assert (delete_unused >= 0); JUMP_LABEL (jump) = nlabel; - if (nlabel) + if (!ANY_RETURN_P (nlabel)) ++LABEL_NUSES (nlabel); /* Update labels in any REG_EQUAL note. */ if ((note = find_reg_note (jump, REG_EQUAL, NULL_RTX)) != NULL_RTX) { - if (!nlabel || (invert && !invert_exp_1 (XEXP (note, 0), jump))) + if (ANY_RETURN_P (nlabel) + || (invert && !invert_exp_1 (XEXP (note, 0), jump))) remove_note (jump, note); else { @@ -1380,7 +1551,8 @@ redirect_jump_2 (rtx jump, rtx olabel, rtx nlabel, int delete_unused, } } - if (olabel && --LABEL_NUSES (olabel) == 0 && delete_unused > 0 + if (!ANY_RETURN_P (olabel) + && --LABEL_NUSES (olabel) == 0 && delete_unused > 0 /* Undefined labels will remain outside the insn stream. */ && INSN_UID (olabel)) delete_related_insns (olabel); @@ -1440,10 +1612,11 @@ invert_jump_1 (rtx jump, rtx nlabel) 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); - + if (num_validated_changes () == ochanges) return 0; @@ -1476,10 +1649,10 @@ invert_jump (rtx jump, rtx nlabel, int delete_unused) reversed. */ int -rtx_renumbered_equal_p (rtx x, rtx y) +rtx_renumbered_equal_p (const_rtx x, const_rtx y) { int i; - enum rtx_code code = GET_CODE (x); + const enum rtx_code code = GET_CODE (x); const char *fmt; if (x == y) @@ -1491,6 +1664,7 @@ rtx_renumbered_equal_p (rtx x, rtx y) { 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; @@ -1507,10 +1681,12 @@ rtx_renumbered_equal_p (rtx x, rtx y) 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; } } @@ -1528,10 +1704,12 @@ rtx_renumbered_equal_p (rtx x, rtx y) 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; } } @@ -1586,6 +1764,10 @@ rtx_renumbered_equal_p (rtx x, rtx y) 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)) @@ -1615,7 +1797,13 @@ rtx_renumbered_equal_p (rtx x, rtx y) case 'i': if (XINT (x, i) != XINT (y, i)) - return 0; + { + if (((code == ASM_OPERANDS && i == 6) + || (code == ASM_INPUT && i == 1)) + && locator_eq (XINT (x, i), XINT (y, i))) + break; + return 0; + } break; case 't': @@ -1661,7 +1849,7 @@ rtx_renumbered_equal_p (rtx x, rtx y) return -1. Any rtx is valid for X. */ int -true_regnum (rtx x) +true_regnum (const_rtx x) { if (REG_P (x)) { @@ -1673,20 +1861,24 @@ true_regnum (rtx x) { 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 -reg_or_subregno (rtx reg) +reg_or_subregno (const_rtx reg) { if (GET_CODE (reg) == SUBREG) reg = SUBREG_REG (reg);