X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fregmove.c;h=008b22cae3ed878f527089db5a160d4ab271dbd8;hb=fd944c82b484fee0514fefed73ce1622127976cc;hp=fea96c9dabe2f1597f3d252abf7df6afbf141c0a;hpb=ed420a2578e99e494f74b297b8b016e1e9405e11;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/regmove.c b/gcc/regmove.c index fea96c9dabe..008b22cae3e 100644 --- a/gcc/regmove.c +++ b/gcc/regmove.c @@ -1,5 +1,6 @@ /* Move registers around to reduce number of move instructions needed. - Copyright (C) 1987, 88, 89, 92-98, 1999 Free Software Foundation, Inc. + Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000 Free Software Foundation, Inc. This file is part of GNU CC. @@ -27,10 +28,10 @@ Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "rtl.h" /* stdio.h must precede rtl.h for FFS. */ +#include "tm_p.h" #include "insn-config.h" #include "recog.h" #include "output.h" -#include "reload.h" #include "regs.h" #include "hard-reg-set.h" #include "flags.h" @@ -40,11 +41,12 @@ Boston, MA 02111-1307, USA. */ #include "basic-block.h" #include "toplev.h" -static int optimize_reg_copy_1 PROTO((rtx, rtx, rtx)); -static void optimize_reg_copy_2 PROTO((rtx, rtx, rtx)); -static void optimize_reg_copy_3 PROTO((rtx, rtx, rtx)); -static rtx gen_add3_insn PROTO((rtx, rtx, rtx)); -static void copy_src_to_dest PROTO((rtx, rtx, rtx, int, int)); +static int perhaps_ends_bb_p PARAMS ((rtx)); +static int optimize_reg_copy_1 PARAMS ((rtx, rtx, rtx)); +static void optimize_reg_copy_2 PARAMS ((rtx, rtx, rtx)); +static void optimize_reg_copy_3 PARAMS ((rtx, rtx, rtx)); +static rtx gen_add3_insn PARAMS ((rtx, rtx, rtx)); +static void copy_src_to_dest PARAMS ((rtx, rtx, rtx, int)); static int *regmove_bb_head; struct match { @@ -54,18 +56,19 @@ struct match { int early_clobber[MAX_RECOG_OPERANDS]; }; -static rtx discover_flags_reg PROTO((void)); -static void mark_flags_life_zones PROTO((rtx)); -static void flags_set_1 PROTO((rtx, rtx)); +static rtx discover_flags_reg PARAMS ((void)); +static void mark_flags_life_zones PARAMS ((rtx)); +static void flags_set_1 PARAMS ((rtx, rtx, void *)); -static int try_auto_increment PROTO((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int)); -static int find_matches PROTO((rtx, struct match *)); -static int fixup_match_1 PROTO((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *)) +static int try_auto_increment PARAMS ((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int)); +static int find_matches PARAMS ((rtx, struct match *)); +static int fixup_match_1 PARAMS ((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *)) ; -static int reg_is_remote_constant_p PROTO((rtx, rtx, rtx)); -static int stable_but_for_p PROTO((rtx, rtx, rtx)); -static int regclass_compatible_p PROTO((int, int)); -static int loop_depth; +static int reg_is_remote_constant_p PARAMS ((rtx, rtx, rtx)); +static int stable_and_no_regs_but_for_p PARAMS ((rtx, rtx, rtx)); +static int regclass_compatible_p PARAMS ((int, int)); +static int replacement_quality PARAMS ((rtx)); +static int fixup_match_2 PARAMS ((rtx, rtx, rtx, rtx, FILE *)); /* Return non-zero if registers with CLASS1 and CLASS2 can be merged without causing too much register allocation problems. */ @@ -89,9 +92,12 @@ gen_add3_insn (r0, r1, c) int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code; if (icode == CODE_FOR_nothing - || ! (*insn_operand_predicate[icode][0]) (r0, insn_operand_mode[icode][0]) - || ! (*insn_operand_predicate[icode][1]) (r1, insn_operand_mode[icode][1]) - || ! (*insn_operand_predicate[icode][2]) (c, insn_operand_mode[icode][2])) + || ! ((*insn_data[icode].operand[0].predicate) + (r0, insn_data[icode].operand[0].mode)) + || ! ((*insn_data[icode].operand[1].predicate) + (r1, insn_data[icode].operand[1].mode)) + || ! ((*insn_data[icode].operand[2].predicate) + (c, insn_data[icode].operand[2].mode))) return NULL_RTX; return (GEN_FCN (icode) (r0, r1, c)); @@ -279,7 +285,7 @@ mark_flags_life_zones (flags) alive, death, birth. This lets more important info overwrite the mode of lesser info. */ - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + if (INSN_P (insn)) { #ifdef HAVE_cc0 /* In the cc0 case, death is not marked in reg notes, @@ -296,7 +302,7 @@ mark_flags_life_zones (flags) /* In either case, birth is denoted simply by it's presence as the destination of a set. */ flags_set_1_set = 0; - note_stores (PATTERN (insn), flags_set_1); + note_stores (PATTERN (insn), flags_set_1, NULL); if (flags_set_1_set) { live = 1; @@ -316,8 +322,9 @@ mark_flags_life_zones (flags) /* A subroutine of mark_flags_life_zones, called through note_stores. */ static void -flags_set_1 (x, pat) +flags_set_1 (x, pat, data) rtx x, pat; + void *data ATTRIBUTE_UNUSED; { if (GET_CODE (pat) == SET && reg_overlap_mentioned_p (x, flags_set_1_rtx)) @@ -331,7 +338,7 @@ static int *regno_src_regno; the choice. The main objective is to avoid using a register that is a candidate for tying to a hard register, since the output might in turn be a candidate to be tied to a different hard register. */ -int +static int replacement_quality(reg) rtx reg; { @@ -361,7 +368,31 @@ replacement_quality(reg) when the registers get tied. */ return 2; } + +/* Return 1 if INSN might end a basic block. */ + +static int perhaps_ends_bb_p (insn) + rtx insn; +{ + switch (GET_CODE (insn)) + { + case CODE_LABEL: + case JUMP_INSN: + /* These always end a basic block. */ + return 1; + + case CALL_INSN: + /* A CALL_INSN might be the last insn of a basic block, if it is inside + an EH region or if there are nonlocal gotos. Note that this test is + very conservative. */ + return flag_exceptions || nonlocal_goto_handler_labels; + default: + /* All others never end a basic block. */ + return 0; + } +} + /* INSN is a copy from SRC to DEST, both registers, and SRC does not die in INSN. @@ -397,21 +428,11 @@ optimize_reg_copy_1 (insn, dest, src) for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p)) { - if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) - break; - /* ??? We can't scan past the end of a basic block without updating - the register lifetime info (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if it is inside - an EH region. There is no easy way to tell, so we just always break - when we see a CALL_INSN if flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (p) == CALL_INSN) + the register lifetime info (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (p)) break; - - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + else if (! INSN_P (p)) continue; if (reg_set_p (src, p) || reg_set_p (dest, p) @@ -458,20 +479,7 @@ optimize_reg_copy_1 (insn, dest, src) && (sregno >= FIRST_PSEUDO_REGISTER || ! reg_overlap_mentioned_p (src, PATTERN (q)))) - { - /* We assume that a register is used exactly once per - insn in the REG_N_REFS updates below. If this is not - correct, no great harm is done. - - Since we do not know if we will change the lifetime of - SREGNO or DREGNO, we must not update REG_LIVE_LENGTH - or REG_N_CALLS_CROSSED at this time. */ - if (sregno >= FIRST_PSEUDO_REGISTER) - REG_N_REFS (sregno) -= loop_depth; - - if (dregno >= FIRST_PSEUDO_REGISTER) - REG_N_REFS (dregno) += loop_depth; - } + ; else { validate_replace_rtx (dest, src, q); @@ -535,6 +543,14 @@ optimize_reg_copy_1 (insn, dest, src) REG_NOTES (insn) = note; } + /* DEST is also dead if INSN has a REG_UNUSED note for DEST. */ + if (! dest_death + && (dest_death = find_regno_note (insn, REG_UNUSED, dregno))) + { + PUT_REG_NOTE_KIND (dest_death, REG_DEAD); + remove_note (insn, dest_death); + } + /* Put death note of DEST on P if we saw it die. */ if (dest_death) { @@ -590,21 +606,11 @@ optimize_reg_copy_2 (insn, dest, src) for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p)) { - if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) - break; - /* ??? We can't scan past the end of a basic block without updating - the register lifetime info (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if it is inside - an EH region. There is no easy way to tell, so we just always break - when we see a CALL_INSN if flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (p) == CALL_INSN) + the register lifetime info (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (p)) break; - - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + else if (! INSN_P (p)) continue; set = single_set (p); @@ -616,18 +622,10 @@ optimize_reg_copy_2 (insn, dest, src) /* Set to stop at next insn. */ for (q = insn; q != NEXT_INSN (p); q = NEXT_INSN (q)) - if (GET_RTX_CLASS (GET_CODE (q)) == 'i') + if (INSN_P (q)) { if (reg_mentioned_p (dest, PATTERN (q))) - { - PATTERN (q) = replace_rtx (PATTERN (q), dest, src); - - /* We assume that a register is used exactly once per - insn in the updates below. If this is not correct, - no great harm is done. */ - REG_N_REFS (dregno) -= loop_depth; - REG_N_REFS (sregno) += loop_depth; - } + PATTERN (q) = replace_rtx (PATTERN (q), dest, src); if (GET_CODE (q) == CALL_INSN) @@ -673,25 +671,15 @@ optimize_reg_copy_3 (insn, dest, src) || ! find_reg_note (insn, REG_DEAD, src_reg) || REG_N_SETS (src_no) != 1) return; - for (p = PREV_INSN (insn); ! reg_set_p (src_reg, p); p = PREV_INSN (p)) - { - if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) - return; + for (p = PREV_INSN (insn); p && ! reg_set_p (src_reg, p); p = PREV_INSN (p)) + /* ??? We can't scan past the end of a basic block without updating + the register lifetime info (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (p)) + break; + + if (! p) + return; - /* ??? We can't scan past the end of a basic block without updating - the register lifetime info (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if it is inside - an EH region. There is no easy way to tell, so we just always break - when we see a CALL_INSN if flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (p) == CALL_INSN) - return; - - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') - continue; - } if (! (set = single_set (p)) || GET_CODE (SET_SRC (set)) != MEM || SET_DEST (set) != src_reg) @@ -722,7 +710,7 @@ optimize_reg_copy_3 (insn, dest, src) subreg = gen_rtx_SUBREG (old_mode, src_reg, 0); while (p = NEXT_INSN (p), p != insn) { - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + if (! INSN_P (p)) continue; /* Make a tenative change. */ @@ -745,11 +733,10 @@ optimize_reg_copy_3 (insn, dest, src) instead moving the value to dest directly before the operation. */ static void -copy_src_to_dest (insn, src, dest, loop_depth, old_max_uid) +copy_src_to_dest (insn, src, dest, old_max_uid) rtx insn; rtx src; rtx dest; - int loop_depth; int old_max_uid; { rtx seq; @@ -773,6 +760,7 @@ copy_src_to_dest (insn, src, dest, loop_depth, old_max_uid) if (GET_CODE (src) == REG && REG_LIVE_LENGTH (REGNO (src)) > 0 && GET_CODE (dest) == REG + && !RTX_UNCHANGING_P (dest) && REG_LIVE_LENGTH (REGNO (dest)) > 0 && (set = single_set (insn)) != NULL_RTX && !reg_mentioned_p (dest, SET_SRC (set)) @@ -834,8 +822,7 @@ copy_src_to_dest (insn, src, dest, loop_depth, old_max_uid) /* Update the various register tables. */ dest_regno = REGNO (dest); - REG_N_SETS (dest_regno) += loop_depth; - REG_N_REFS (dest_regno) += loop_depth; + REG_N_SETS (dest_regno) ++; REG_LIVE_LENGTH (dest_regno)++; if (REGNO_FIRST_UID (dest_regno) == insn_uid) REGNO_FIRST_UID (dest_regno) = move_uid; @@ -898,7 +885,7 @@ reg_is_remote_constant_p (reg, insn, first) { rtx s; - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + if (! INSN_P (p)) continue; s = single_set (p); if (s != 0 @@ -934,7 +921,7 @@ reg_is_remote_constant_p (reg, insn, first) /* cse disrupts preincrement / postdecrement squences when it finds a hard register as ultimate source, like the frame pointer. */ -int +static int fixup_match_2 (insn, dst, src, offset, regmove_dump_file) rtx insn, dst, src, offset; FILE *regmove_dump_file; @@ -954,22 +941,11 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file) { rtx pset; - if (GET_CODE (p) == CODE_LABEL - || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) - break; - /* ??? We can't scan past the end of a basic block without updating - the register lifetime info (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if it is inside - an EH region. There is no easy way to tell, so we just always break - when we see a CALL_INSN if flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (p) == CALL_INSN) + the register lifetime info (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (p)) break; - - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + else if (! INSN_P (p)) continue; if (find_regno_note (p, REG_DEAD, REGNO (dst))) @@ -997,9 +973,6 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file) REG_N_CALLS_CROSSED (REGNO (dst)) += num_calls; } - REG_N_REFS (REGNO (dst)) += loop_depth; - REG_N_REFS (REGNO (src)) -= loop_depth; - if (regmove_dump_file) fprintf (regmove_dump_file, "Fixed operand of insn %d.\n", @@ -1009,12 +982,9 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file) for (p = PREV_INSN (insn); p; p = PREV_INSN (p)) { if (GET_CODE (p) == CODE_LABEL - || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) + || GET_CODE (p) == JUMP_INSN) break; - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + if (! INSN_P (p)) continue; if (reg_overlap_mentioned_p (dst, PATTERN (p))) { @@ -1026,12 +996,9 @@ fixup_match_2 (insn, dst, src, offset, regmove_dump_file) for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p)) { if (GET_CODE (p) == CODE_LABEL - || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) + || GET_CODE (p) == JUMP_INSN) break; - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + if (! INSN_P (p)) continue; if (reg_overlap_mentioned_p (dst, PATTERN (p))) { @@ -1089,22 +1056,20 @@ regmove_optimize (f, nregs, regmove_dump_file) can supress some optimizations in those zones. */ mark_flags_life_zones (discover_flags_reg ()); - regno_src_regno = (int *)alloca (sizeof *regno_src_regno * nregs); + regno_src_regno = (int *) xmalloc (sizeof *regno_src_regno * nregs); for (i = nregs; --i >= 0; ) regno_src_regno[i] = -1; - regmove_bb_head = (int *)alloca (sizeof (int) * (old_max_uid + 1)); + regmove_bb_head = (int *) xmalloc (sizeof (int) * (old_max_uid + 1)); for (i = old_max_uid; i >= 0; i--) regmove_bb_head[i] = -1; for (i = 0; i < n_basic_blocks; i++) regmove_bb_head[INSN_UID (BLOCK_HEAD (i))] = i; /* A forward/backward pass. Replace output operands with input operands. */ - loop_depth = 1; - for (pass = 0; pass <= 2; pass++) { if (! flag_regmove && pass >= flag_expensive_optimizations) - return; + goto done; if (regmove_dump_file) fprintf (regmove_dump_file, "Starting %s pass...\n", @@ -1116,14 +1081,6 @@ regmove_optimize (f, nregs, regmove_dump_file) rtx set; int op_no, match_no; - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - loop_depth++; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - loop_depth--; - } - set = single_set (insn); if (! set) continue; @@ -1162,7 +1119,6 @@ regmove_optimize (f, nregs, regmove_dump_file) if (! flag_regmove) continue; -#ifdef REGISTER_CONSTRAINTS if (! find_matches (insn, &match)) continue; @@ -1221,7 +1177,7 @@ regmove_optimize (f, nregs, regmove_dump_file) continue; if (match.early_clobber[match_no] - && count_occurrences (PATTERN (insn), src) > 1) + && count_occurrences (PATTERN (insn), src, 0) > 1) continue; /* Make sure match_operand is the destination. */ @@ -1260,18 +1216,9 @@ regmove_optimize (f, nregs, regmove_dump_file) if (regmove_dump_file) fprintf (regmove_dump_file, "Starting backward pass...\n"); - loop_depth = 1; - for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - loop_depth++; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - loop_depth--; - } - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + if (INSN_P (insn)) { int op_no, match_no; int success = 0; @@ -1334,7 +1281,7 @@ regmove_optimize (f, nregs, regmove_dump_file) continue; if (match.early_clobber[match_no] - && count_occurrences (PATTERN (insn), src) > 1) + && count_occurrences (PATTERN (insn), src, 0) > 1) continue; /* Make sure match_no is the destination. */ @@ -1416,24 +1363,12 @@ regmove_optimize (f, nregs, regmove_dump_file) { rtx pset; - if (GET_CODE (p) == CODE_LABEL - || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) - break; - /* ??? We can't scan past the end of a basic block without updating the register lifetime info - (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if - it is inside an EH region. There is no easy way to tell, - so we just always break when we see a CALL_INSN if - flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (p) == CALL_INSN) + (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (p)) break; - - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + else if (! INSN_P (p)) continue; length++; @@ -1519,22 +1454,6 @@ regmove_optimize (f, nregs, regmove_dump_file) REG_LIVE_LENGTH (srcno) = 2; } - /* We assume that a register is used exactly once per - insn in the updates above. If this is not correct, - no great harm is done. */ - - REG_N_REFS (dstno) += 2 * loop_depth; - REG_N_REFS (srcno) -= 2 * loop_depth; - - /* If that was the only time src was set, - and src was not live at the start of the - function, we know that we have no more - references to src; clear REG_N_REFS so it - won't make reload do any work. */ - if (REG_N_SETS (REGNO (src)) == 0 - && ! regno_uninitialized (REGNO (src))) - REG_N_REFS (REGNO (src)) = 0; - if (regmove_dump_file) fprintf (regmove_dump_file, "Fixed operand %d of insn %d matching operand %d.\n", @@ -1547,12 +1466,10 @@ regmove_optimize (f, nregs, regmove_dump_file) /* If we weren't able to replace any of the alternatives, try an alternative appoach of copying the source to the destination. */ if (!success && copy_src != NULL_RTX) - copy_src_to_dest (insn, copy_src, copy_dst, loop_depth, - old_max_uid); + copy_src_to_dest (insn, copy_src, copy_dst, old_max_uid); } } -#endif /* REGISTER_CONSTRAINTS */ /* In fixup_match_1, some insns may have been inserted after basic block ends. Fix that here. */ @@ -1566,6 +1483,11 @@ regmove_optimize (f, nregs, regmove_dump_file) new = next, next = NEXT_INSN (new); BLOCK_END (i) = new; } + + done: + /* Clean up. */ + free (regno_src_regno); + free (regmove_bb_head); } /* Returns nonzero if INSN's pattern has matching constraints for any operand. @@ -1655,6 +1577,7 @@ find_matches (insn, matchp) DST is operand number MATCH_NUMBER in INSN. If BACKWARD is nonzero, we have been called in a backward pass. Return nonzero for success. */ + static int fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, match_number, regmove_dump_file) @@ -1667,10 +1590,16 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, int success = 0; int num_calls = 0, s_num_calls = 0; enum rtx_code code = NOTE; - HOST_WIDE_INT insn_const, newconst; + HOST_WIDE_INT insn_const = 0, newconst; rtx overlap = 0; /* need to move insn ? */ - rtx src_note = find_reg_note (insn, REG_DEAD, src), dst_note; - int length, s_length, true_loop_depth; + rtx src_note = find_reg_note (insn, REG_DEAD, src), dst_note = NULL_RTX; + int length, s_length; + + /* If SRC is marked as unchanging, we may not change it. + ??? Maybe we could get better code by removing the unchanging bit + instead, and changing it back if we don't succeed? */ + if (RTX_UNCHANGING_P (src)) + return 0; if (! src_note) { @@ -1688,7 +1617,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, && XEXP (SET_SRC (set), 0) == src && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT) insn_const = INTVAL (XEXP (SET_SRC (set), 1)); - else if (! stable_but_for_p (SET_SRC (set), src, dst)) + else if (! stable_and_no_regs_but_for_p (SET_SRC (set), src, dst)) return 0; else /* We might find a src_note while scanning. */ @@ -1714,21 +1643,11 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, for (length = s_length = 0, p = NEXT_INSN (insn); p; p = NEXT_INSN (p)) { - if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN - || (GET_CODE (p) == NOTE - && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END))) - break; - /* ??? We can't scan past the end of a basic block without updating - the register lifetime info (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if it is - inside an EH region. There is no easy way to tell, so we just - always break when we see a CALL_INSN if flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (p) == CALL_INSN) + the register lifetime info (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (p)) break; - - if (GET_RTX_CLASS (GET_CODE (p)) != 'i') + else if (! INSN_P (p)) continue; length++; @@ -1755,7 +1674,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, if (! src_note) { rtx q; - rtx set2; + rtx set2 = NULL_RTX; /* If an optimization is done, the value of SRC while P is executed will be changed. Check that this is OK. */ @@ -1763,32 +1682,18 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, break; for (q = p; q; q = NEXT_INSN (q)) { - if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN - || (GET_CODE (q) == NOTE - && (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END))) - { - q = 0; - break; - } - /* ??? We can't scan past the end of a basic block without updating the register lifetime info - (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if - it is inside an EH region. There is no easy way to tell, - so we just always break when we see a CALL_INSN if - flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (q) == CALL_INSN) + (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (q)) { q = 0; break; } - - if (GET_RTX_CLASS (GET_CODE (q)) != 'i') + else if (! INSN_P (q)) continue; - if (reg_overlap_mentioned_p (src, PATTERN (q)) - || reg_set_p (src, q)) + else if (reg_overlap_mentioned_p (src, PATTERN (q)) + || reg_set_p (src, q)) break; } if (q) @@ -1833,8 +1738,9 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, /* Reject out of range shifts. */ if (code != PLUS && (newconst < 0 - || (newconst - >= GET_MODE_BITSIZE (GET_MODE (SET_SRC (set2)))))) + || ((unsigned HOST_WIDE_INT) newconst + >= (GET_MODE_BITSIZE (GET_MODE + (SET_SRC (set2))))))) break; if (code == PLUS) { @@ -1886,8 +1792,6 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, if (! success) return 0; - true_loop_depth = backward ? 2 - loop_depth : loop_depth; - /* Remove the death note for DST from P. */ remove_note (p, dst_note); if (code == MINUS) @@ -1899,7 +1803,6 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, post_inc = 0; validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (insn_const), 0); REG_N_SETS (REGNO (src))++; - REG_N_REFS (REGNO (src)) += true_loop_depth; REG_LIVE_LENGTH (REGNO (src))++; } if (overlap) @@ -1924,8 +1827,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, /* emit_insn_after_with_line_notes has no return value, so search for the new insn. */ insn = p; - while (GET_RTX_CLASS (GET_CODE (insn)) != 'i' - || PATTERN (insn) != pat) + while (! INSN_P (insn) || PATTERN (insn) != pat) insn = PREV_INSN (insn); REG_NOTES (insn) = notes; @@ -1938,37 +1840,24 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, if (! overlap && (code == PLUS || code == MINUS)) { rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); - rtx q, set2; + rtx q, set2 = NULL_RTX; int num_calls2 = 0, s_length2 = 0; if (note && CONSTANT_P (XEXP (note, 0))) { for (q = PREV_INSN (insn); q; q = PREV_INSN(q)) { - if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN - || (GET_CODE (q) == NOTE - && (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END))) - { - q = 0; - break; - } - /* ??? We can't scan past the end of a basic block without updating the register lifetime info - (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if - it is inside an EH region. There is no easy way to tell, - so we just always break when we see a CALL_INSN if - flag_exceptions is nonzero. */ - if (flag_exceptions && GET_CODE (q) == CALL_INSN) + (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (q)) { q = 0; break; } - - if (GET_RTX_CLASS (GET_CODE (q)) != 'i') + else if (! INSN_P (q)) continue; + s_length2++; if (reg_set_p (src, q)) { @@ -1991,7 +1880,6 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, NOTE_SOURCE_FILE (q) = 0; REG_N_SETS (REGNO (src))--; REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2; - REG_N_REFS (REGNO (src)) -= true_loop_depth; REG_LIVE_LENGTH (REGNO (src)) -= s_length2; insn_const = 0; } @@ -2021,29 +1909,20 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, inc_dest = post_inc_set ? SET_DEST (post_inc_set) : src; for (q = post_inc; (q = NEXT_INSN (q)); ) { - if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN - || (GET_CODE (q) == NOTE - && (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END))) - break; - /* ??? We can't scan past the end of a basic block without updating - the register lifetime info (REG_DEAD/basic_block_live_at_start). - A CALL_INSN might be the last insn of a basic block, if it - is inside an EH region. There is no easy way to tell so we - just always break when we see a CALL_INSN if flag_exceptions - is nonzero. */ - if (flag_exceptions && GET_CODE (q) == CALL_INSN) + the register lifetime info + (REG_DEAD/basic_block_live_at_start). */ + if (perhaps_ends_bb_p (q)) break; - - if (GET_RTX_CLASS (GET_CODE (q)) != 'i') + else if (! INSN_P (q)) continue; - if (src != inc_dest && (reg_overlap_mentioned_p (src, PATTERN (q)) - || reg_set_p (src, q))) + else if (src != inc_dest + && (reg_overlap_mentioned_p (src, PATTERN (q)) + || reg_set_p (src, q))) break; - if (reg_set_p (inc_dest, q)) + else if (reg_set_p (inc_dest, q)) break; - if (reg_overlap_mentioned_p (inc_dest, PATTERN (q))) + else if (reg_overlap_mentioned_p (inc_dest, PATTERN (q))) { try_auto_increment (q, post_inc, post_inc_set, inc_dest, newconst, 1); @@ -2051,6 +1930,7 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, } } } + /* Move the death note for DST to INSN if it is used there. */ if (reg_overlap_mentioned_p (dst, PATTERN (insn))) @@ -2085,23 +1965,6 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, if (REG_LIVE_LENGTH (REGNO (dst)) < 2) REG_LIVE_LENGTH (REGNO (dst)) = 2; } - - /* We assume that a register is used exactly once per - insn in the updates above. If this is not correct, - no great harm is done. */ - - REG_N_REFS (REGNO (src)) += 2 * true_loop_depth; - REG_N_REFS (REGNO (dst)) -= 2 * true_loop_depth; - - /* If that was the only time dst was set, - and dst was not live at the start of the - function, we know that we have no more - references to dst; clear REG_N_REFS so it - won't make reload do any work. */ - if (REG_N_SETS (REGNO (dst)) == 0 - && ! regno_uninitialized (REGNO (dst))) - REG_N_REFS (REGNO (dst)) = 0; - if (regmove_dump_file) fprintf (regmove_dump_file, "Fixed operand %d of insn %d matching operand %d.\n", @@ -2110,10 +1973,16 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number, } -/* return nonzero if X is stable but for mentioning SRC or mentioning / - changing DST . If in doubt, presume it is unstable. */ +/* return nonzero if X is stable and mentions no regsiters but for + mentioning SRC or mentioning / changing DST . If in doubt, presume + it is unstable. + The rationale is that we want to check if we can move an insn easily + while just paying attention to SRC and DST. A register is considered + stable if it has the RTX_UNCHANGING_P bit set, but that would still + leave the burden to update REG_DEAD / REG_UNUSED notes, so we don't + want any registers but SRC and DST. */ static int -stable_but_for_p (x, src, dst) +stable_and_no_regs_but_for_p (x, src, dst) rtx x, src, dst; { RTX_CODE code = GET_CODE (x); @@ -2124,15 +1993,416 @@ stable_but_for_p (x, src, dst) int i; const char *fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e' && ! stable_but_for_p (XEXP (x, i), src, dst)) + if (fmt[i] == 'e' + && ! stable_and_no_regs_but_for_p (XEXP (x, i), src, dst)) return 0; return 1; } case 'o': - if (x == src || x == dst) - return 1; + if (code == REG) + return x == src || x == dst; + /* If this is a MEM, look inside - there might be a register hidden in + the address of an unchanging MEM. */ + if (code == MEM + && ! stable_and_no_regs_but_for_p (XEXP (x, 0), src, dst)) + return 0; /* fall through */ default: return ! rtx_unstable_p (x); } } + +/* Track stack adjustments and stack memory references. Attempt to + reduce the number of stack adjustments by back-propogating across + the memory references. + + This is intended primarily for use with targets that do not define + ACCUMULATE_OUTGOING_ARGS. It is of significantly more value to + targets that define PREFERRED_STACK_BOUNDARY more aligned than + STACK_BOUNDARY (e.g. x86), or if not all registers can be pushed + (e.g. x86 fp regs) which would ordinarily have to be implemented + as a sub/mov pair due to restrictions in calls.c. + + Propogation stops when any of the insns that need adjusting are + (a) no longer valid because we've exceeded their range, (b) a + non-trivial push instruction, or (c) a call instruction. + + Restriction B is based on the assumption that push instructions + are smaller or faster. If a port really wants to remove all + pushes, it should have defined ACCUMULATE_OUTGOING_ARGS. The + one exception that is made is for an add immediately followed + by a push. */ + +/* This structure records stack memory references between stack adjusting + instructions. */ + +struct csa_memlist +{ + HOST_WIDE_INT sp_offset; + rtx insn, *mem; + struct csa_memlist *next; +}; + +static int stack_memref_p PARAMS ((rtx)); +static rtx single_set_for_csa PARAMS ((rtx)); +static void free_csa_memlist PARAMS ((struct csa_memlist *)); +static struct csa_memlist *record_one_stack_memref + PARAMS ((rtx, rtx *, struct csa_memlist *)); +static int try_apply_stack_adjustment + PARAMS ((rtx, struct csa_memlist *, HOST_WIDE_INT, HOST_WIDE_INT)); +static void combine_stack_adjustments_for_block PARAMS ((basic_block)); +static int record_stack_memrefs PARAMS ((rtx *, void *)); + + +/* Main entry point for stack adjustment combination. */ + +void +combine_stack_adjustments () +{ + int i; + + for (i = 0; i < n_basic_blocks; ++i) + combine_stack_adjustments_for_block (BASIC_BLOCK (i)); +} + +/* Recognize a MEM of the form (sp) or (plus sp const). */ + +static int +stack_memref_p (x) + rtx x; +{ + if (GET_CODE (x) != MEM) + return 0; + x = XEXP (x, 0); + + if (x == stack_pointer_rtx) + return 1; + if (GET_CODE (x) == PLUS + && XEXP (x, 0) == stack_pointer_rtx + && GET_CODE (XEXP (x, 1)) == CONST_INT) + return 1; + + return 0; +} + +/* Recognize either normal single_set or the hack in i386.md for + tying fp and sp adjustments. */ + +static rtx +single_set_for_csa (insn) + rtx insn; +{ + int i; + rtx tmp = single_set (insn); + if (tmp) + return tmp; + + if (GET_CODE (insn) != INSN + || GET_CODE (PATTERN (insn)) != PARALLEL) + return NULL_RTX; + + tmp = PATTERN (insn); + if (GET_CODE (XVECEXP (tmp, 0, 0)) != SET) + return NULL_RTX; + + for (i = 1; i < XVECLEN (tmp, 0); ++i) + { + rtx this = XVECEXP (tmp, 0, i); + + /* The special case is allowing a no-op set. */ + if (GET_CODE (this) == SET + && SET_SRC (this) == SET_DEST (this)) + ; + else if (GET_CODE (this) != CLOBBER + && GET_CODE (this) != USE) + return NULL_RTX; + } + + return XVECEXP (tmp, 0, 0); +} + +/* Free the list of csa_memlist nodes. */ + +static void +free_csa_memlist (memlist) + struct csa_memlist *memlist; +{ + struct csa_memlist *next; + for (; memlist ; memlist = next) + { + next = memlist->next; + free (memlist); + } +} + +/* Create a new csa_memlist node from the given memory reference. + It is already known that the memory is stack_memref_p. */ + +static struct csa_memlist * +record_one_stack_memref (insn, mem, next_memlist) + rtx insn, *mem; + struct csa_memlist *next_memlist; +{ + struct csa_memlist *ml; + + ml = (struct csa_memlist *) xmalloc (sizeof (*ml)); + + if (XEXP (*mem, 0) == stack_pointer_rtx) + ml->sp_offset = 0; + else + ml->sp_offset = INTVAL (XEXP (XEXP (*mem, 0), 1)); + + ml->insn = insn; + ml->mem = mem; + ml->next = next_memlist; + + return ml; +} + +/* Attempt to apply ADJUST to the stack adjusting insn INSN, as well + as each of the memories in MEMLIST. Return true on success. */ + +static int +try_apply_stack_adjustment (insn, memlist, new_adjust, delta) + rtx insn; + struct csa_memlist *memlist; + HOST_WIDE_INT new_adjust, delta; +{ + struct csa_memlist *ml; + rtx set; + + /* We know INSN matches single_set_for_csa, because that's what we + recognized earlier. However, if INSN is not single_set, it is + doing double duty as a barrier for frame pointer memory accesses, + which we are not recording. Therefore, an adjust insn that is not + single_set may not have a positive delta applied. */ + + if (delta > 0 && ! single_set (insn)) + return 0; + set = single_set_for_csa (insn); + validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); + + for (ml = memlist; ml ; ml = ml->next) + { + HOST_WIDE_INT c = ml->sp_offset - delta; + rtx new = gen_rtx_MEM (GET_MODE (*ml->mem), + plus_constant (stack_pointer_rtx, c)); + + /* Don't reference memory below the stack pointer. */ + if (c < 0) + { + cancel_changes (0); + return 0; + } + + MEM_COPY_ATTRIBUTES (new, *ml->mem); + validate_change (ml->insn, ml->mem, new, 1); + } + + if (apply_change_group ()) + { + /* Succeeded. Update our knowledge of the memory references. */ + for (ml = memlist; ml ; ml = ml->next) + ml->sp_offset -= delta; + + return 1; + } + else + return 0; +} + +/* Called via for_each_rtx and used to record all stack memory references in + the insn and discard all other stack pointer references. */ +struct record_stack_memrefs_data +{ + rtx insn; + struct csa_memlist *memlist; +}; + +static int +record_stack_memrefs (xp, data) + rtx *xp; + void *data; +{ + rtx x = *xp; + struct record_stack_memrefs_data *d = + (struct record_stack_memrefs_data *) data; + if (!x) + return 0; + switch (GET_CODE (x)) + { + case MEM: + if (!reg_mentioned_p (stack_pointer_rtx, x)) + return -1; + /* We are not able to handle correctly all possible memrefs containing + stack pointer, so this check is neccesary. */ + if (stack_memref_p (x)) + { + d->memlist = record_one_stack_memref (d->insn, xp, d->memlist); + return -1; + } + return 1; + case REG: + /* ??? We want be able to handle non-memory stack pointer references + later. For now just discard all insns refering to stack pointer + outside mem expressions. We would probably want to teach + validate_replace to simplify expressions first. */ + if (x == stack_pointer_rtx) + return 1; + break; + default: + break; + } + return 0; +} + +/* Subroutine of combine_stack_adjustments, called for each basic block. */ + +static void +combine_stack_adjustments_for_block (bb) + basic_block bb; +{ + HOST_WIDE_INT last_sp_adjust = 0; + rtx last_sp_set = NULL_RTX; + struct csa_memlist *memlist = NULL; + rtx pending_delete; + rtx insn, next; + struct record_stack_memrefs_data data; + + for (insn = bb->head; ; insn = next) + { + rtx set; + + pending_delete = NULL_RTX; + next = NEXT_INSN (insn); + + if (! INSN_P (insn)) + goto processed; + + set = single_set_for_csa (insn); + if (set) + { + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + + /* Find constant additions to the stack pointer. */ + if (dest == stack_pointer_rtx + && GET_CODE (src) == PLUS + && XEXP (src, 0) == stack_pointer_rtx + && GET_CODE (XEXP (src, 1)) == CONST_INT) + { + HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1)); + + /* If we've not seen an adjustment previously, record + it now and continue. */ + if (! last_sp_set) + { + last_sp_set = insn; + last_sp_adjust = this_adjust; + goto processed; + } + + /* If not all recorded memrefs can be adjusted, or the + adjustment is now too large for a constant addition, + we cannot merge the two stack adjustments. */ + if (! try_apply_stack_adjustment (last_sp_set, memlist, + last_sp_adjust + this_adjust, + this_adjust)) + { + free_csa_memlist (memlist); + memlist = NULL; + last_sp_set = insn; + last_sp_adjust = this_adjust; + goto processed; + } + + /* It worked! */ + pending_delete = insn; + last_sp_adjust += this_adjust; + + /* If, by some accident, the adjustments cancel out, + delete both insns and start from scratch. */ + if (last_sp_adjust == 0) + { + if (last_sp_set == bb->head) + bb->head = NEXT_INSN (last_sp_set); + flow_delete_insn (last_sp_set); + + free_csa_memlist (memlist); + memlist = NULL; + last_sp_set = NULL_RTX; + } + + goto processed; + } + + /* Find a predecrement of exactly the previous adjustment and + turn it into a direct store. Obviously we can't do this if + there were any intervening uses of the stack pointer. */ + if (memlist == NULL + && GET_CODE (dest) == MEM + && ((GET_CODE (XEXP (dest, 0)) == PRE_DEC + && (last_sp_adjust + == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))) + || (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY + && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS + && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx + && (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) + == CONST_INT) + && (INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)) + == -last_sp_adjust))) + && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx + && ! reg_mentioned_p (stack_pointer_rtx, src) + && memory_address_p (GET_MODE (dest), stack_pointer_rtx) + && validate_change (insn, &SET_DEST (set), + change_address (dest, VOIDmode, + stack_pointer_rtx), 0)) + { + if (last_sp_set == bb->head) + bb->head = NEXT_INSN (last_sp_set); + flow_delete_insn (last_sp_set); + + free_csa_memlist (memlist); + memlist = NULL; + last_sp_set = NULL_RTX; + last_sp_adjust = 0; + goto processed; + } + } + + data.insn = insn; + data.memlist = memlist; + if (GET_CODE (insn) != CALL_INSN && last_sp_set + && !for_each_rtx (&PATTERN (insn), record_stack_memrefs, &data)) + { + memlist = data.memlist; + goto processed; + } + memlist = data.memlist; + + /* Otherwise, we were not able to process the instruction. + Do not continue collecting data across such a one. */ + if (last_sp_set + && (GET_CODE (insn) == CALL_INSN + || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) + { + free_csa_memlist (memlist); + memlist = NULL; + last_sp_set = NULL_RTX; + last_sp_adjust = 0; + } + + processed: + if (insn == bb->end) + break; + + if (pending_delete) + flow_delete_insn (pending_delete); + } + + if (pending_delete) + { + bb->end = PREV_INSN (pending_delete); + flow_delete_insn (pending_delete); + } +}