X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fjump.c;h=7fe34f186616abec64ad05f187abeb2f456558c3;hb=ab60f4744c30010543431a65dd47d34870f9bb43;hp=cd51764f03adbfda01360920a64e737944296946;hpb=e47a6f81edf98a062ad29cdb62590c1c7fdeb37a;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/jump.c b/gcc/jump.c index cd51764f03a..7fe34f18661 100644 --- a/gcc/jump.c +++ b/gcc/jump.c @@ -1,6 +1,6 @@ /* 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, 2007 + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GCC. @@ -67,13 +67,15 @@ along with GCC; see the file COPYING3. If not see static void init_label_info (rtx); static void mark_all_labels (rtx); +static void mark_jump_label_1 (rtx, rtx, bool, bool); 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. */ +/* 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) { @@ -111,6 +113,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)) @@ -120,49 +124,63 @@ 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 */ + TODO_dump_func /* 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); + } + } + } } /* Mark the label each jump jumps to. @@ -172,34 +190,69 @@ static void 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); - 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. */ @@ -300,7 +353,7 @@ reversed_comparison_code_parts (enum rtx_code code, const_rtx arg0, return UNKNOWN; /* These CONST_CAST's are okay because prev_nonnote_insn just - returns it's argument and we assign it to a const_rtx + 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); @@ -818,18 +871,54 @@ 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 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) { + /* Handle delayed branches. */ + if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); + if (!JUMP_P (insn)) return 0; + 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. */ @@ -904,12 +993,14 @@ sets_cc0_p (const_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. Note that two labels separated by a loop-beginning note must be kept distinct if we have not yet done loop-optimization, @@ -920,6 +1011,19 @@ sets_cc0_p (const_rtx x) void mark_jump_label (rtx x, rtx insn, int in_mem) { + 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; @@ -936,7 +1040,7 @@ mark_jump_label (rtx x, rtx insn, int in_mem) return; case MEM: - in_mem = 1; + in_mem = true; break; case SEQUENCE: @@ -951,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)) - 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); @@ -976,17 +1090,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; @@ -1001,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++) - 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; @@ -1010,15 +1131,21 @@ 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); } } } @@ -1062,20 +1189,10 @@ delete_related_insns (rtx 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. @@ -1104,10 +1221,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) @@ -1151,6 +1270,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; } @@ -1239,6 +1364,15 @@ redirect_exp_1 (rtx *loc, rtx olabel, rtx nlabel, rtx insn) 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--) { @@ -1307,6 +1441,8 @@ 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. */ @@ -1438,6 +1574,7 @@ rtx_renumbered_equal_p (const_rtx x, const_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; @@ -1454,10 +1591,12 @@ rtx_renumbered_equal_p (const_rtx x, const_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; } } @@ -1475,10 +1614,12 @@ rtx_renumbered_equal_p (const_rtx x, const_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; } } @@ -1620,13 +1761,17 @@ true_regnum (const_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; }