X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fregmove.c;h=008b22cae3ed878f527089db5a160d4ab271dbd8;hb=fd944c82b484fee0514fefed73ce1622127976cc;hp=b78ea1614614710d95cd34f642c971b5fd00b02b;hpb=88e8363aa4b6842b409864a8b7a87f8938a5b830;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/regmove.c b/gcc/regmove.c index b78ea161461..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. @@ -31,7 +32,6 @@ Boston, MA 02111-1307, USA. */ #include "insn-config.h" #include "recog.h" #include "output.h" -#include "reload.h" #include "regs.h" #include "hard-reg-set.h" #include "flags.h" @@ -41,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)); +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 { @@ -55,19 +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, void *)); +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_and_no_regs_but_for_p PROTO((rtx, rtx, rtx)); -static int regclass_compatible_p PROTO((int, int)); -static int replacement_quality PROTO((rtx)); -static int fixup_match_2 PROTO((rtx, rtx, rtx, rtx, FILE *)); +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. */ @@ -284,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, @@ -367,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. @@ -403,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) @@ -591,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); @@ -617,7 +622,7 @@ 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); @@ -667,24 +672,11 @@ optimize_reg_copy_3 (insn, dest, src) || REG_N_SETS (src_no) != 1) return; for (p = PREV_INSN (insn); p && ! 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; + /* ??? 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; - /* ??? 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 (! p) return; @@ -718,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. */ @@ -768,6 +760,7 @@ copy_src_to_dest (insn, src, dest, 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)) @@ -892,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 @@ -948,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))) @@ -1000,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))) { @@ -1017,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))) { @@ -1201,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. */ @@ -1242,7 +1218,7 @@ regmove_optimize (f, nregs, regmove_dump_file) for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + if (INSN_P (insn)) { int op_no, match_no; int success = 0; @@ -1305,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. */ @@ -1387,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++; @@ -1613,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) @@ -1625,9 +1590,9 @@ 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; + 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. @@ -1678,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++; @@ -1719,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. */ @@ -1727,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) @@ -1797,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) { @@ -1885,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; @@ -1899,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)) { @@ -1981,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); @@ -2011,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))) @@ -2091,3 +2011,398 @@ stable_and_no_regs_but_for_p (x, src, dst) 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); + } +}