diff options
-rw-r--r-- | gcc/ChangeLog | 23 | ||||
-rw-r--r-- | gcc/config/i386/i386.c | 68 | ||||
-rw-r--r-- | gcc/cse.c | 316 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 28 | ||||
-rw-r--r-- | gcc/hooks.c | 9 | ||||
-rw-r--r-- | gcc/hooks.h | 3 | ||||
-rw-r--r-- | gcc/rtl.h | 1 | ||||
-rw-r--r-- | gcc/target-def.h | 8 | ||||
-rw-r--r-- | gcc/target.h | 17 | ||||
-rw-r--r-- | gcc/targhooks.c | 8 | ||||
-rw-r--r-- | gcc/targhooks.h | 5 | ||||
-rw-r--r-- | gcc/toplev.c | 7 |
12 files changed, 488 insertions, 5 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1e4f176..48a9f84 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2004-01-23 Ian Lance Taylor <ian@wasabisystems.com> + + 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. + 2004-01-23 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE> * fixinc/inclhack.def (bad_lval): Renamed to ... diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 0cad3f1..79102cf 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -1,6 +1,6 @@ /* Subroutines used for code generation on IA-32. Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003 Free Software Foundation, Inc. + 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. @@ -793,6 +793,9 @@ static rtx maybe_get_pool_constant (rtx); static rtx ix86_expand_int_compare (enum rtx_code, rtx, rtx); static enum rtx_code ix86_prepare_fp_compare_args (enum rtx_code, rtx *, rtx *); +static bool ix86_fixed_condition_code_regs (unsigned int *, unsigned int *); +static enum machine_mode ix86_cc_modes_compatible (enum machine_mode, + enum machine_mode); static rtx get_thread_pointer (int); static rtx legitimize_tls_address (rtx, enum tls_model, int); static void get_pc_thunk_name (char [32], unsigned int); @@ -1010,6 +1013,11 @@ static void init_ext_80387_constants (void); #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST ix86_address_cost +#undef TARGET_FIXED_CONDITION_CODE_REGS +#define TARGET_FIXED_CONDITION_CODE_REGS ix86_fixed_condition_code_regs +#undef TARGET_CC_MODES_COMPATIBLE +#define TARGET_CC_MODES_COMPATIBLE ix86_cc_modes_compatible + #undef TARGET_MACHINE_DEPENDENT_REORG #define TARGET_MACHINE_DEPENDENT_REORG ix86_reorg @@ -8723,6 +8731,64 @@ ix86_cc_mode (enum rtx_code code, rtx op0, rtx op1) } } +/* Return the fixed registers used for condition codes. */ + +static bool +ix86_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) +{ + *p1 = FLAGS_REG; + *p2 = FPSR_REG; + return true; +} + +/* If two condition code modes are compatible, return a condition code + mode which is compatible with both. Otherwise, return + VOIDmode. */ + +static enum machine_mode +ix86_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) +{ + if (m1 == m2) + return m1; + + if (GET_MODE_CLASS (m1) != MODE_CC || GET_MODE_CLASS (m2) != MODE_CC) + return VOIDmode; + + if ((m1 == CCGCmode && m2 == CCGOCmode) + || (m1 == CCGOCmode && m2 == CCGCmode)) + return CCGCmode; + + switch (m1) + { + default: + abort (); + + case CCmode: + case CCGCmode: + case CCGOCmode: + case CCNOmode: + case CCZmode: + switch (m2) + { + default: + return VOIDmode; + + case CCmode: + case CCGCmode: + case CCGOCmode: + case CCNOmode: + case CCZmode: + return CCmode; + } + + case CCFPmode: + case CCFPUmode: + /* These are only compatible with themselves, which we already + checked above. */ + return VOIDmode; + } +} + /* Return true if we should use an FCOMI instruction for this fp comparison. */ int @@ -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))); + } + } +} diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 41f1fcb..8f1dfa7 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -5177,6 +5177,34 @@ follows: @end smallexample @end defmac +@deftypefn {Target Hook} bool TARGET_FIXED_CONDITION_CODE_REGS (unsigned int *, unsigned int *) +On targets which do not use @code{(cc0)}, and which use a hard +register rather than a pseudo-register to hold condition codes, the +regular CSE passes are often not able to identify cases in which the +hard register is set to a common value. Use this hook to enable a +small pass which optimizes such cases. This hook should return true +to enable this pass, and it should set the integers to which its +arguments point to the hard register numbers used for condition codes. +When there is only one such register, as is true on most systems, the +integer pointed to by the second argument should be set to +@code{INVALID_REGNUM}. + +The default version of this hook returns false. +@end deftypefn + +@deftypefn {Target Hook} enum machine_mode TARGET_CC_MODES_COMPATIBLE (enum machine_mode, enum machine_mode) +On targets which use multiple condition code modes in class +@code{MODE_CC}, it is sometimes the case that a comparison can be +validly done in more than one mode. On such a system, define this +target hook to take two mode arguments and to return a mode in which +both comparisons may be validly done. If there is no such mode, +return @code{VOIDmode}. + +The default version of this hook checks whether the modes are the +same. If they are, it returns that mode. If they are different, it +returns @code{VOIDmode}. +@end deftypefn + @node Costs @section Describing Relative Costs of Operations @cindex costs of instructions diff --git a/gcc/hooks.c b/gcc/hooks.c index 939ed844..a8cd665 100644 --- a/gcc/hooks.c +++ b/gcc/hooks.c @@ -1,5 +1,5 @@ /* General-purpose hooks. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -177,6 +177,13 @@ hook_bool_rtx_false (rtx a ATTRIBUTE_UNUSED) } bool +hook_bool_uintp_uintp_false (unsigned int *a ATTRIBUTE_UNUSED, + unsigned int *b ATTRIBUTE_UNUSED) +{ + return false; +} + +bool hook_bool_rtx_int_int_intp_false (rtx a ATTRIBUTE_UNUSED, int b ATTRIBUTE_UNUSED, int c ATTRIBUTE_UNUSED, diff --git a/gcc/hooks.h b/gcc/hooks.h index 7711115..583b504 100644 --- a/gcc/hooks.h +++ b/gcc/hooks.h @@ -1,5 +1,5 @@ /* General-purpose hooks. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -31,6 +31,7 @@ extern bool hook_bool_tree_hwi_hwi_tree_false (tree, HOST_WIDE_INT, HOST_WIDE_IN extern bool hook_bool_tree_hwi_hwi_tree_true (tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); extern bool hook_bool_rtx_false (rtx); +extern bool hook_bool_uintp_uintp_false (unsigned int *, unsigned int *); extern bool hook_bool_rtx_int_int_intp_false (rtx, int, int, int *); extern bool hook_bool_constcharptr_size_t_false (const char *, size_t); @@ -2013,6 +2013,7 @@ extern int cse_main (rtx, int, int, FILE *); #endif extern void cse_end_of_basic_block (rtx, struct cse_basic_block_data *, int, int, int); +extern void cse_condition_code_reg (void); /* In jump.c */ extern int comparison_dominates_p (enum rtx_code, enum rtx_code); diff --git a/gcc/target-def.h b/gcc/target-def.h index 9ff668d..321cd08 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -1,5 +1,5 @@ /* Default initializers for a generic GCC target. - Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -314,6 +314,10 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define TARGET_ENCODE_SECTION_INFO default_encode_section_info #endif +#define TARGET_FIXED_CONDITION_CODE_REGS hook_bool_uintp_uintp_false + +#define TARGET_CC_MODES_COMPATIBLE default_cc_modes_compatible + #define TARGET_MACHINE_DEPENDENT_REORG 0 #define TARGET_BUILD_BUILTIN_VA_LIST std_build_builtin_va_list @@ -380,6 +384,8 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. TARGET_RTX_COSTS, \ TARGET_ADDRESS_COST, \ TARGET_DWARF_REGISTER_SPAN, \ + TARGET_FIXED_CONDITION_CODE_REGS, \ + TARGET_CC_MODES_COMPATIBLE, \ TARGET_MACHINE_DEPENDENT_REORG, \ TARGET_BUILD_BUILTIN_VA_LIST, \ TARGET_GET_PCH_VALIDITY, \ diff --git a/gcc/target.h b/gcc/target.h index 787d203..21a12f0 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -377,6 +377,23 @@ struct gcc_target hook should return NULL_RTX. */ rtx (* dwarf_register_span) (rtx); + /* Fetch the fixed register(s) which hold condition codes, for + targets where it makes sense to look for duplicate assignments to + the condition codes. This should return true if there is such a + register, false otherwise. The arguments should be set to the + fixed register numbers. Up to two condition code registers are + supported. If there is only one for this target, the int pointed + at by the second argument should be set to -1. */ + bool (* fixed_condition_code_regs) (unsigned int *, unsigned int *); + + /* If two condition code modes are compatible, return a condition + code mode which is compatible with both, such that a comparison + done in the returned mode will work for both of the original + modes. If the condition code modes are not compatible, return + VOIDmode. */ + enum machine_mode (* cc_modes_compatible) (enum machine_mode, + enum machine_mode); + /* Do machine-dependent code transformations. Called just before delayed-branch scheduling. */ void (* machine_dependent_reorg) (void); diff --git a/gcc/targhooks.c b/gcc/targhooks.c index eaed481..308ada6 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -70,6 +70,14 @@ default_external_libcall (rtx fun ATTRIBUTE_UNUSED) #endif } +enum machine_mode +default_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) +{ + if (m1 == m2) + return m1; + return VOIDmode; +} + bool default_promote_function_args (tree fntype ATTRIBUTE_UNUSED) { diff --git a/gcc/targhooks.h b/gcc/targhooks.h index e5c5a2a..63525fe 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -1,5 +1,5 @@ /* Default target hook functions. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. @@ -20,6 +20,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA extern void default_external_libcall (rtx); +extern enum machine_mode default_cc_modes_compatible (enum machine_mode, + enum machine_mode); + extern bool default_promote_function_args (tree); extern bool default_promote_function_return (tree); extern bool default_promote_prototypes (tree); diff --git a/gcc/toplev.c b/gcc/toplev.c index 05e753a..4d16201 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -2902,6 +2902,13 @@ rest_of_handle_cse2 (tree decl, rtx insns) dump_flow_info (rtl_dump_file); /* CFG is no longer maintained up-to-date. */ tem = cse_main (insns, max_reg_num (), 1, rtl_dump_file); + + /* Run a pass to eliminate duplicated assignments to condition code + registers. We have to run this after bypass_jumps, because it + makes it harder for that pass to determine whether a jump can be + bypassed safely. */ + cse_condition_code_reg (); + purge_all_dead_edges (0); delete_trivially_dead_insns (insns, max_reg_num ()); |