diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-01-23 21:05:21 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-01-23 21:05:21 +0000 |
commit | 124ac4e4e3be031b5d9e0d9c855684053af1bf22 (patch) | |
tree | 1957c990b77f48c80e149663c064a9f71d8e783a /gcc/cse.c | |
parent | a3278934c71197997b32261f7e782730fcf5162a (diff) | |
download | gcc-124ac4e4e3be031b5d9e0d9c855684053af1bf22.tar.gz |
PR gcc/1532
* cse.c (cse_change_cc_mode): New static function.
(cse_change_cc_mode_insns, cse_cc_succs): Likewise.
(cse_condition_code_reg): New function.
* rtl.h (cse_condition_code_reg): Declare.
* toplev.c (rest_of_handle_cse2): Call cse_condition_code_reg.
* target.h (struct gcc_target): Add fixed_condition_code_regs and
cc_modes_compatible.
* target-def.h (TARGET_FIXED_CONDITION_CODE_REGS): Define.
(TARGET_CC_MODES_COMPATIBLE): Define.
(TARGET_INITIALIZER): Add new initializers.
* targhooks.c (default_cc_modes_compatible): New function.
* targhooks.c (default_cc_modes_compatible): Declare.
* hooks.c (hook_bool_intp_intp_false): New function.
* hooks.h (hook_bool_intp_intp_false): Declare.
* config/i386/i386.c (TARGET_FIXED_CONDITION_CODE_REGS): Define.
(TARGET_CC_MODES_COMPATIBLE): Define.
(ix86_fixed_condition_code_regs): New static function.
(ix86_cc_modes_compatible): Likewise.
* doc/tm.texi (Condition Code): Document new hooks.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@76454 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/cse.c')
-rw-r--r-- | gcc/cse.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/gcc/cse.c b/gcc/cse.c index d5a37fdbaa0..1ec01f9793a 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -658,6 +658,9 @@ static void flush_hash_table (void); static bool insn_live_p (rtx, int *); static bool set_live_p (rtx, rtx, int *); static bool dead_libcall_p (rtx, int *); +static int cse_change_cc_mode (rtx *, void *); +static void cse_change_cc_mode_insns (rtx, rtx, rtx); +static enum machine_mode cse_cc_succs (basic_block, rtx, rtx, bool); /* Nonzero if X has the form (PLUS frame-pointer integer). We check for virtual regs here because the simplify_*_operation routines are called @@ -7665,3 +7668,316 @@ delete_trivially_dead_insns (rtx insns, int nreg) timevar_pop (TV_DELETE_TRIVIALLY_DEAD); return ndead; } + +/* This function is called via for_each_rtx. The argument, NEWREG, is + a condition code register with the desired mode. If we are looking + at the same register in a different mode, replace it with + NEWREG. */ + +static int +cse_change_cc_mode (rtx *loc, void *data) +{ + rtx newreg = (rtx) data; + + if (*loc + && GET_CODE (*loc) == REG + && REGNO (*loc) == REGNO (newreg) + && GET_MODE (*loc) != GET_MODE (newreg)) + { + *loc = newreg; + return -1; + } + return 0; +} + +/* Change the mode of any reference to the register REGNO (NEWREG) to + GET_MODE (NEWREG), starting at START. Stop before END. Stop at + any instruction after START which modifies NEWREG. */ + +static void +cse_change_cc_mode_insns (rtx start, rtx end, rtx newreg) +{ + rtx insn; + + for (insn = start; insn != end; insn = NEXT_INSN (insn)) + { + if (! INSN_P (insn)) + continue; + + if (insn != start && reg_set_p (newreg, insn)) + return; + + for_each_rtx (&PATTERN (insn), cse_change_cc_mode, newreg); + for_each_rtx (®_NOTES (insn), cse_change_cc_mode, newreg); + } +} + +/* BB is a basic block which finishes with CC_REG as a condition code + register which is set to CC_SRC. Look through the successors of BB + to find blocks which have a single predecessor (i.e., this one), + and look through those blocks for an assignment to CC_REG which is + equivalent to CC_SRC. CAN_CHANGE_MODE indicates whether we are + permitted to change the mode of CC_SRC to a compatible mode. This + returns VOIDmode if no equivalent assignments were found. + Otherwise it returns the mode which CC_SRC should wind up with. + + The main complexity in this function is handling the mode issues. + We may have more than one duplicate which we can eliminate, and we + try to find a mode which will work for multiple duplicates. */ + +static enum machine_mode +cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode) +{ + bool found_equiv; + enum machine_mode mode; + unsigned int insn_count; + edge e; + rtx insns[2]; + enum machine_mode modes[2]; + rtx last_insns[2]; + unsigned int i; + rtx newreg; + + /* We expect to have two successors. Look at both before picking + the final mode for the comparison. If we have more successors + (i.e., some sort of table jump, although that seems unlikely), + then we require all beyond the first two to use the same + mode. */ + + found_equiv = false; + mode = GET_MODE (cc_src); + insn_count = 0; + for (e = bb->succ; e; e = e->succ_next) + { + rtx insn; + rtx end; + + if (e->flags & EDGE_COMPLEX) + continue; + + if (! e->dest->pred + || e->dest->pred->pred_next + || e->dest == EXIT_BLOCK_PTR) + continue; + + end = NEXT_INSN (BB_END (e->dest)); + for (insn = BB_HEAD (e->dest); insn != end; insn = NEXT_INSN (insn)) + { + rtx set; + + if (! INSN_P (insn)) + continue; + + /* If CC_SRC is modified, we have to stop looking for + something which uses it. */ + if (modified_in_p (cc_src, insn)) + break; + + /* Check whether INSN sets CC_REG to CC_SRC. */ + set = single_set (insn); + if (set + && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == REGNO (cc_reg)) + { + bool found; + enum machine_mode set_mode; + enum machine_mode comp_mode; + + found = false; + set_mode = GET_MODE (SET_SRC (set)); + comp_mode = set_mode; + if (rtx_equal_p (cc_src, SET_SRC (set))) + found = true; + else if (GET_CODE (cc_src) == COMPARE + && GET_CODE (SET_SRC (set)) == COMPARE + && GET_MODE (cc_src) != set_mode + && rtx_equal_p (XEXP (cc_src, 0), + XEXP (SET_SRC (set), 0)) + && rtx_equal_p (XEXP (cc_src, 1), + XEXP (SET_SRC (set), 1))) + + { + comp_mode = (*targetm.cc_modes_compatible) (mode, set_mode); + if (comp_mode != VOIDmode + && (can_change_mode || comp_mode == mode)) + found = true; + } + + if (found) + { + found_equiv = true; + if (insn_count < sizeof insns / sizeof insn[0]) + { + insns[insn_count] = insn; + modes[insn_count] = set_mode; + last_insns[insn_count] = end; + ++insn_count; + + /* Sanity check. */ + if (! can_change_mode && mode != comp_mode) + abort (); + + mode = comp_mode; + } + else + { + if (set_mode != mode) + break; + /* INSN sets CC_REG to a value equal to CC_SRC + with the right mode. We can simply delete + it. */ + delete_insn (insn); + } + + /* We found an instruction to delete. Keep looking, + in the hopes of finding a three-way jump. */ + continue; + } + + /* We found an instruction which sets the condition + code, so don't look any farther. */ + break; + } + + /* If INSN sets CC_REG in some other way, don't look any + farther. */ + if (reg_set_p (cc_reg, insn)) + break; + } + + /* If we fell off the bottom of the block, we can keep looking + through successors. We pass CAN_CHANGE_MODE as false because + we aren't prepared to handle compatibility between the + further blocks and this block. */ + if (insn == end) + { + if (cse_cc_succs (e->dest, cc_reg, cc_src, false) != VOIDmode) + found_equiv = true; + } + } + + if (! found_equiv) + return VOIDmode; + + /* Now INSN_COUNT is the number of instructions we found which set + CC_REG to a value equivalent to CC_SRC. The instructions are in + INSNS. The modes used by those instructions are in MODES. */ + + newreg = NULL_RTX; + for (i = 0; i < insn_count; ++i) + { + if (modes[i] != mode) + { + /* We need to change the mode of CC_REG in INSNS[i] and + subsequent instructions. */ + if (! newreg) + { + if (GET_MODE (cc_reg) == mode) + newreg = cc_reg; + else + newreg = gen_rtx_REG (mode, REGNO (cc_reg)); + } + cse_change_cc_mode_insns (NEXT_INSN (insns[i]), last_insns[i], + newreg); + } + + delete_insn (insns[i]); + } + + return mode; +} + +/* If we have a fixed condition code register (or two), walk through + the instructions and try to eliminate duplicate assignments. */ + +void +cse_condition_code_reg (void) +{ + unsigned int cc_regno_1; + unsigned int cc_regno_2; + rtx cc_reg_1; + rtx cc_reg_2; + basic_block bb; + + if (! (*targetm.fixed_condition_code_regs) (&cc_regno_1, &cc_regno_2)) + return; + + cc_reg_1 = gen_rtx_REG (CCmode, cc_regno_1); + if (cc_regno_2 != INVALID_REGNUM) + cc_reg_2 = gen_rtx_REG (CCmode, cc_regno_2); + else + cc_reg_2 = NULL_RTX; + + FOR_EACH_BB (bb) + { + rtx last_insn; + rtx cc_reg; + rtx insn; + rtx cc_src_insn; + rtx cc_src; + enum machine_mode mode; + + /* Look for blocks which end with a conditional jump based on a + condition code register. Then look for the instruction which + sets the condition code register. Then look through the + successor blocks for instructions which set the condition + code register to the same value. There are other possible + uses of the condition code register, but these are by far the + most common and the ones which we are most likely to be able + to optimize. */ + + last_insn = BB_END (bb); + if (GET_CODE (last_insn) != JUMP_INSN) + continue; + + if (reg_referenced_p (cc_reg_1, PATTERN (last_insn))) + cc_reg = cc_reg_1; + else if (cc_reg_2 && reg_referenced_p (cc_reg_2, PATTERN (last_insn))) + cc_reg = cc_reg_2; + else + continue; + + cc_src_insn = NULL_RTX; + cc_src = NULL_RTX; + for (insn = PREV_INSN (last_insn); + insn && insn != PREV_INSN (BB_HEAD (bb)); + insn = PREV_INSN (insn)) + { + rtx set; + + if (! INSN_P (insn)) + continue; + set = single_set (insn); + if (set + && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == REGNO (cc_reg)) + { + cc_src_insn = insn; + cc_src = SET_SRC (set); + break; + } + else if (reg_set_p (cc_reg, insn)) + break; + } + + if (! cc_src_insn) + continue; + + if (modified_between_p (cc_src, cc_src_insn, NEXT_INSN (last_insn))) + continue; + + /* Now CC_REG is a condition code register used for a + conditional jump at the end of the block, and CC_SRC, in + CC_SRC_INSN, is the value to which that condition code + register is set, and CC_SRC is still meaningful at the end of + the basic block. */ + + mode = cse_cc_succs (bb, cc_reg, cc_src, true); + if (mode != GET_MODE (cc_src) && mode != VOIDmode) + { + PUT_MODE (cc_src, mode); + cse_change_cc_mode_insns (cc_src_insn, NEXT_INSN (last_insn), + gen_rtx_REG (mode, REGNO (cc_reg))); + } + } +} |