re PR target/1532 (-O3 generates (obviously) redundant tests and jumps)

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.

From-SVN: r76454
This commit is contained in:
Ian Lance Taylor 2004-01-23 21:05:21 +00:00 committed by Ian Lance Taylor
parent f6c930a38a
commit e129d93a7b
12 changed files with 488 additions and 5 deletions

View File

@ -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 ...

View File

@ -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

316
gcc/cse.c
View File

@ -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 (&REG_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)));
}
}
}

View File

@ -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

View File

@ -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
@ -176,6 +176,13 @@ hook_bool_rtx_false (rtx a ATTRIBUTE_UNUSED)
return false;
}
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,

View File

@ -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);

View File

@ -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);

View File

@ -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, \

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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 ());