Commit 282bc7b4 by Eric Botcazou Committed by Eric Botcazou

re PR rtl-optimization/51667 (new FAIL: 27_io/basic_*stream/* execution test with -m32)

	PR rtl-optimization/51667
	* ree.c (insn_merge_code): Delete.
	(is_insn_merge_attempted): Likewise.
	(get_insn_status): Likewise.
	(set_insn_status): Likewise.
	(struct ext_cand): Add CODE and MODE fields.
	(combine_set_extend): Rename to...
	(combine_set_extension): ...this.  Use above fields and tidy up.
	(transform_ifelse): Likewise.
	(get_defs): Return the chain of definitions.
	(is_this_a_cmove): Merge into...
	(is_cond_copy_insn): ...this.  Return bool.
	(make_defs_and_copies_lists): Adjust calls to get_defs and simplify.
	(merge_def_and_ext): Adjust call to combine_set_extend.
	(combine_reaching_defs): Remove calls to {g|s}et_insn_status.
	(struct extend_info): Rename to...
	(struct re_info): ...this.  Add DEF_MAP field.
	(add_ext_candidate): Merge into...
	(add_removable_extension): ...this.  Adjust calls to get_defs.  Ensure
	reaching definitions are associated with only one kind of extension.
	(find_removable_extensions): Create and destroy the definition map.
	(find_and_remove_re): Return void.  Change 'long' variables to 'int'.
	Do not deal with is_insn_merge_attempted.

From-SVN: r182694
parent 0bed228e
2011-12-27 Eric Botcazou <ebotcazou@adacore.com>
PR rtl-optimization/51667
* ree.c (insn_merge_code): Delete.
(is_insn_merge_attempted): Likewise.
(get_insn_status): Likewise.
(set_insn_status): Likewise.
(struct ext_cand): Add CODE and MODE fields.
(combine_set_extend): Rename to...
(combine_set_extension): ...this. Use above fields and tidy up.
(transform_ifelse): Likewise.
(get_defs): Return the chain of definitions.
(is_this_a_cmove): Merge into...
(is_cond_copy_insn): ...this. Return bool.
(make_defs_and_copies_lists): Adjust calls to get_defs and simplify.
(merge_def_and_ext): Adjust call to combine_set_extend.
(combine_reaching_defs): Remove calls to {g|s}et_insn_status.
(struct extend_info): Rename to...
(struct re_info): ...this. Add DEF_MAP field.
(add_ext_candidate): Merge into...
(add_removable_extension): ...this. Adjust calls to get_defs. Ensure
reaching definitions are associated with only one kind of extension.
(find_removable_extensions): Create and destroy the definition map.
(find_and_remove_re): Return void. Change 'long' variables to 'int'.
Do not deal with is_insn_merge_attempted.
2011-12-25 Jan Hubicka <jh@suse.cz> 2011-12-25 Jan Hubicka <jh@suse.cz>
PR middle-end/48641 PR middle-end/48641
/* Redundant Extension Elimination pass for the GNU compiler. /* Redundant Extension Elimination pass for the GNU compiler.
Copyright (C) 2010-2011 Free Software Foundation, Inc. Copyright (C) 2010, 2011 Free Software Foundation, Inc.
Contributed by Ilya Enkovich (ilya.enkovich@intel.com) Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
Based on the Redundant Zero-extension elimination pass contributed by Based on the Redundant Zero-extension elimination pass contributed by
Sriraman Tallam (tmsriram@google.com) and Silvius Rus (rus@google.com). Sriraman Tallam (tmsriram@google.com) and Silvius Rus (rus@google.com).
This file is part of GCC. This file is part of GCC.
...@@ -71,11 +71,11 @@ along with GCC; see the file COPYING3. If not see ...@@ -71,11 +71,11 @@ along with GCC; see the file COPYING3. If not see
extension differ from the other instructions. For instance, the extension differ from the other instructions. For instance, the
instruction *cmov ebx, eax* instruction *cmov ebx, eax*
zero-extends eax onto rax only when the move from ebx to eax happens. zero-extends eax onto rax only when the move from ebx to eax happens.
Otherwise, eax may not be zero-extended. Consider conditional move as Otherwise, eax may not be zero-extended. Consider conditional moves as
RTL instructions of the form RTL instructions of the form
(set (reg:SI x) (if_then_else (cond) (reg:SI y) (reg:SI z))). (set (reg:SI x) (if_then_else (cond) (reg:SI y) (reg:SI z))).
This pass tries to merge an extension with a conditional move by This pass tries to merge an extension with a conditional move by
actually merging the defintions of y and z with an extension and then actually merging the definitions of y and z with an extension and then
converting the conditional move into : converting the conditional move into :
(set (reg:DI x) (if_then_else (cond) (reg:DI y) (reg:DI z))). (set (reg:DI x) (if_then_else (cond) (reg:DI y) (reg:DI z))).
Since registers y and z are extended, register x will also be extended Since registers y and z are extended, register x will also be extended
...@@ -105,13 +105,13 @@ along with GCC; see the file COPYING3. If not see ...@@ -105,13 +105,13 @@ along with GCC; see the file COPYING3. If not see
........ ........
400315: b8 4e 00 00 00 mov $0x4e,%eax 400315: b8 4e 00 00 00 mov $0x4e,%eax
40031a: 0f af f8 imul %eax,%edi 40031a: 0f af f8 imul %eax,%edi
40031d: 89 ff mov %edi,%edi --> Useless extend 40031d: 89 ff mov %edi,%edi - useless extension
40031f: 8b 04 bd 60 19 40 00 mov 0x401960(,%rdi,4),%eax 40031f: 8b 04 bd 60 19 40 00 mov 0x401960(,%rdi,4),%eax
400326: c3 retq 400326: c3 retq
...... ......
400330: ba 2d 00 00 00 mov $0x2d,%edx 400330: ba 2d 00 00 00 mov $0x2d,%edx
400335: 0f af fa imul %edx,%edi 400335: 0f af fa imul %edx,%edi
400338: 89 ff mov %edi,%edi --> Useless extend 400338: 89 ff mov %edi,%edi - useless extension
40033a: 8b 04 bd 60 19 40 00 mov 0x401960(,%rdi,4),%eax 40033a: 8b 04 bd 60 19 40 00 mov 0x401960(,%rdi,4),%eax
400341: c3 retq 400341: c3 retq
...@@ -149,7 +149,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -149,7 +149,7 @@ along with GCC; see the file COPYING3. If not see
400365: 29 f0 sub %esi,%eax 400365: 29 f0 sub %esi,%eax
400367: 83 ff 65 cmp $0x65,%edi 400367: 83 ff 65 cmp $0x65,%edi
40036a: 0f 43 c2 cmovae %edx,%eax 40036a: 0f 43 c2 cmovae %edx,%eax
40036d: 89 c0 mov %eax,%eax --> Useless extend 40036d: 89 c0 mov %eax,%eax - useless extension
40036f: c3 retq 40036f: c3 retq
$ gcc -O2 -free bad_code.c $ gcc -O2 -free bad_code.c
...@@ -188,8 +188,8 @@ along with GCC; see the file COPYING3. If not see ...@@ -188,8 +188,8 @@ along with GCC; see the file COPYING3. If not see
10: 0f b6 0e movzbl (%rsi),%ecx 10: 0f b6 0e movzbl (%rsi),%ecx
13: 0f b6 46 01 movzbl 0x1(%rsi),%eax 13: 0f b6 46 01 movzbl 0x1(%rsi),%eax
17: 48 83 c6 02 add $0x2,%rsi 17: 48 83 c6 02 add $0x2,%rsi
1b: 0f b6 c9 movzbl %cl,%ecx --> Useless extend 1b: 0f b6 c9 movzbl %cl,%ecx - useless extension
1e: 0f b6 c0 movzbl %al,%eax --> Useless extend 1e: 0f b6 c0 movzbl %al,%eax - useless extension
21: 69 c9 8b 4c 00 00 imul $0x4c8b,%ecx,%ecx 21: 69 c9 8b 4c 00 00 imul $0x4c8b,%ecx,%ecx
27: 69 c0 46 96 00 00 imul $0x9646,%eax,%eax 27: 69 c0 46 96 00 00 imul $0x9646,%eax,%eax
...@@ -245,52 +245,28 @@ along with GCC; see the file COPYING3. If not see ...@@ -245,52 +245,28 @@ along with GCC; see the file COPYING3. If not see
#include "df.h" #include "df.h"
#include "cgraph.h" #include "cgraph.h"
/* This says if a register is newly created for the purpose of /* This structure represents a candidate for elimination. */
extension. */
enum insn_merge_code typedef struct GTY(()) ext_cand
{ {
MERGE_NOT_ATTEMPTED = 0, /* The expression. */
MERGE_SUCCESS const_rtx expr;
};
/* The kind of extension. */
enum rtx_code code;
/* This structure is used to hold data about candidate for /* The destination mode. */
elimination. */ enum machine_mode mode;
typedef struct GTY(()) ext_cand /* The instruction where it lives. */
{
rtx insn; rtx insn;
const_rtx expr; } ext_cand;
enum machine_mode src_mode;
} ext_cand, *ext_cand_ref;
DEF_VEC_O(ext_cand); DEF_VEC_O(ext_cand);
DEF_VEC_ALLOC_O(ext_cand, heap); DEF_VEC_ALLOC_O(ext_cand, heap);
/* This says if a INSN UID or its definition has already been merged
with a extension or not. */
static enum insn_merge_code *is_insn_merge_attempted;
static int max_insn_uid; static int max_insn_uid;
/* Return the merge code status for INSN. */
static enum insn_merge_code
get_insn_status (rtx insn)
{
gcc_assert (INSN_UID (insn) < max_insn_uid);
return is_insn_merge_attempted[INSN_UID (insn)];
}
/* Set the merge code status of INSN to CODE. */
static void
set_insn_status (rtx insn, enum insn_merge_code code)
{
gcc_assert (INSN_UID (insn) < max_insn_uid);
is_insn_merge_attempted[INSN_UID (insn)] = code;
}
/* Given a insn (CURR_INSN), an extension candidate for removal (CAND) /* Given a insn (CURR_INSN), an extension candidate for removal (CAND)
and a pointer to the SET rtx (ORIG_SET) that needs to be modified, and a pointer to the SET rtx (ORIG_SET) that needs to be modified,
this code modifies the SET rtx to a new SET rtx that extends the this code modifies the SET rtx to a new SET rtx that extends the
...@@ -302,97 +278,80 @@ set_insn_status (rtx insn, enum insn_merge_code code) ...@@ -302,97 +278,80 @@ set_insn_status (rtx insn, enum insn_merge_code code)
(set (reg a) (expression)) (set (reg a) (expression))
Transform : Transform :
(set (reg a) (extend (expression))) (set (reg a) (any_extend (expression)))
Special Cases : Special Cases :
If the expression is a constant or another extend directly If the expression is a constant or another extension, then directly
assign it to the register. */ assign it to the register. */
static bool static bool
combine_set_extend (ext_cand_ref cand, rtx curr_insn, rtx *orig_set) combine_set_extension (ext_cand *cand, rtx curr_insn, rtx *orig_set)
{ {
rtx temp_extension, simplified_temp_extension, new_set, new_const_int; rtx orig_src = SET_SRC (*orig_set);
rtx orig_src, cand_src; rtx new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set)));
rtx newreg; rtx new_set;
enum machine_mode dst_mode = GET_MODE (SET_DEST (cand->expr));
/* Change the SET rtx and validate it. */
orig_src = SET_SRC (*orig_set);
cand_src = SET_SRC (cand->expr);
new_set = NULL_RTX;
newreg = gen_rtx_REG (dst_mode, REGNO (SET_DEST (*orig_set)));
/* Merge constants by directly moving the constant into the
register under some conditions. */
/* Merge constants by directly moving the constant into the register under
some conditions. Recall that RTL constants are sign-extended. */
if (GET_CODE (orig_src) == CONST_INT if (GET_CODE (orig_src) == CONST_INT
&& HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (dst_mode)) && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (cand->mode))
{ {
if (INTVAL (orig_src) >= 0 || GET_CODE (cand_src) == SIGN_EXTEND) if (INTVAL (orig_src) >= 0 || cand->code == SIGN_EXTEND)
new_set = gen_rtx_SET (VOIDmode, newreg, orig_src); new_set = gen_rtx_SET (VOIDmode, new_reg, orig_src);
else else
{ {
/* Zero-extend the negative constant by masking out the bits outside /* Zero-extend the negative constant by masking out the bits outside
the source mode. */ the source mode. */
enum machine_mode src_mode = GET_MODE (SET_DEST (*orig_set)); enum machine_mode src_mode = GET_MODE (SET_DEST (*orig_set));
new_const_int rtx new_const_int
= GEN_INT (INTVAL (orig_src) & GET_MODE_MASK (src_mode)); = GEN_INT (INTVAL (orig_src) & GET_MODE_MASK (src_mode));
new_set = gen_rtx_SET (VOIDmode, newreg, new_const_int); new_set = gen_rtx_SET (VOIDmode, new_reg, new_const_int);
} }
} }
else if (GET_MODE (orig_src) == VOIDmode) else if (GET_MODE (orig_src) == VOIDmode)
{ {
/* This is mostly due to a call insn that should not be /* This is mostly due to a call insn that should not be optimized. */
optimized. */
return false; return false;
} }
else if (GET_CODE (orig_src) == GET_CODE (cand_src)) else if (GET_CODE (orig_src) == cand->code)
{ {
/* Here is a sequence of two extensions. Try to merge them into a /* Here is a sequence of two extensions. Try to merge them. */
single one. */ rtx temp_extension
= gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0));
temp_extension rtx simplified_temp_extension = simplify_rtx (temp_extension);
= gen_rtx_fmt_e (GET_CODE (orig_src), dst_mode, XEXP (orig_src, 0));
simplified_temp_extension = simplify_rtx (temp_extension);
if (simplified_temp_extension) if (simplified_temp_extension)
temp_extension = simplified_temp_extension; temp_extension = simplified_temp_extension;
new_set = gen_rtx_SET (VOIDmode, newreg, temp_extension); new_set = gen_rtx_SET (VOIDmode, new_reg, temp_extension);
} }
else if (GET_CODE (orig_src) == IF_THEN_ELSE) else if (GET_CODE (orig_src) == IF_THEN_ELSE)
{ {
/* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise,
in general, IF_THEN_ELSE should not be combined. */ in general, IF_THEN_ELSE should not be combined. */
return false; return false;
} }
else else
{ {
/* This is the normal case we expect. */ /* This is the normal case. */
rtx temp_extension
temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, orig_src);
= gen_rtx_fmt_e (GET_CODE (cand_src), dst_mode, orig_src); rtx simplified_temp_extension = simplify_rtx (temp_extension);
simplified_temp_extension = simplify_rtx (temp_extension);
if (simplified_temp_extension) if (simplified_temp_extension)
temp_extension = simplified_temp_extension; temp_extension = simplified_temp_extension;
new_set = gen_rtx_SET (VOIDmode, newreg, temp_extension); new_set = gen_rtx_SET (VOIDmode, new_reg, temp_extension);
} }
gcc_assert (new_set != NULL_RTX);
/* This change is a part of a group of changes. Hence, /* This change is a part of a group of changes. Hence,
validate_change will not try to commit the change. */ validate_change will not try to commit the change. */
if (validate_change (curr_insn, orig_set, new_set, true)) if (validate_change (curr_insn, orig_set, new_set, true))
{ {
if (dump_file) if (dump_file)
{ {
fprintf (dump_file, "Merged Instruction with EXTEND:\n"); fprintf (dump_file, "Merged instruction with extension:\n");
print_rtl_single (dump_file, curr_insn); print_rtl_single (dump_file, curr_insn);
} }
return true; return true;
} }
return false; return false;
} }
...@@ -405,7 +364,7 @@ combine_set_extend (ext_cand_ref cand, rtx curr_insn, rtx *orig_set) ...@@ -405,7 +364,7 @@ combine_set_extend (ext_cand_ref cand, rtx curr_insn, rtx *orig_set)
DEF_INSN is the if_then_else insn. */ DEF_INSN is the if_then_else insn. */
static bool static bool
transform_ifelse (ext_cand_ref cand, rtx def_insn) transform_ifelse (ext_cand *cand, rtx def_insn)
{ {
rtx set_insn = PATTERN (def_insn); rtx set_insn = PATTERN (def_insn);
rtx srcreg, dstreg, srcreg2; rtx srcreg, dstreg, srcreg2;
...@@ -413,250 +372,172 @@ transform_ifelse (ext_cand_ref cand, rtx def_insn) ...@@ -413,250 +372,172 @@ transform_ifelse (ext_cand_ref cand, rtx def_insn)
rtx ifexpr; rtx ifexpr;
rtx cond; rtx cond;
rtx new_set; rtx new_set;
enum machine_mode dst_mode = GET_MODE (SET_DEST (cand->expr));
gcc_assert (GET_CODE (set_insn) == SET); gcc_assert (GET_CODE (set_insn) == SET);
cond = XEXP (SET_SRC (set_insn), 0); cond = XEXP (SET_SRC (set_insn), 0);
dstreg = SET_DEST (set_insn); dstreg = SET_DEST (set_insn);
srcreg = XEXP (SET_SRC (set_insn), 1); srcreg = XEXP (SET_SRC (set_insn), 1);
srcreg2 = XEXP (SET_SRC (set_insn), 2); srcreg2 = XEXP (SET_SRC (set_insn), 2);
map_srcreg = gen_rtx_REG (dst_mode, REGNO (srcreg)); map_srcreg = gen_rtx_REG (cand->mode, REGNO (srcreg));
map_srcreg2 = gen_rtx_REG (dst_mode, REGNO (srcreg2)); map_srcreg2 = gen_rtx_REG (cand->mode, REGNO (srcreg2));
map_dstreg = gen_rtx_REG (dst_mode, REGNO (dstreg)); map_dstreg = gen_rtx_REG (cand->mode, REGNO (dstreg));
ifexpr = gen_rtx_IF_THEN_ELSE (dst_mode, cond, map_srcreg, map_srcreg2); ifexpr = gen_rtx_IF_THEN_ELSE (cand->mode, cond, map_srcreg, map_srcreg2);
new_set = gen_rtx_SET (VOIDmode, map_dstreg, ifexpr); new_set = gen_rtx_SET (VOIDmode, map_dstreg, ifexpr);
if (validate_change (def_insn, &PATTERN (def_insn), new_set, true)) if (validate_change (def_insn, &PATTERN (def_insn), new_set, true))
{ {
if (dump_file) if (dump_file)
{ {
fprintf (dump_file, "Cond_Move Instruction's mode extended :\n"); fprintf (dump_file,
"Mode of conditional move instruction extended:\n");
print_rtl_single (dump_file, def_insn); print_rtl_single (dump_file, def_insn);
} }
return true; return true;
} }
else
return false; return false;
} }
/* Function to get all the immediate definitions of an instruction. /* Get all the reaching definitions of an instruction. The definitions are
The reaching definitions are desired for WHICH_REG used in desired for REG used in INSN. Return the definition list or NULL if a
CURR_INSN. This function returns 0 if there was an error getting definition is missing. If DEST is non-NULL, additionally push the INSN
a definition. Upon success, this function returns the number of of the definitions onto DEST. */
definitions and stores the definitions in DEST. */
static int static struct df_link *
get_defs (rtx curr_insn, rtx which_reg, VEC (rtx,heap) **dest) get_defs (rtx insn, rtx reg, VEC (rtx,heap) **dest)
{ {
df_ref reg_info, *defs; df_ref reg_info, *defs;
struct df_link *def_chain; struct df_link *ref_chain, *ref_link;
int n_refs = 0;
defs = DF_INSN_USES (curr_insn);
reg_info = NULL; reg_info = NULL;
while (*defs) for (defs = DF_INSN_USES (insn); *defs; defs++)
{ {
reg_info = *defs; reg_info = *defs;
if (GET_CODE (DF_REF_REG (reg_info)) == SUBREG) if (GET_CODE (DF_REF_REG (reg_info)) == SUBREG)
return 0; return NULL;
if (REGNO (DF_REF_REG (reg_info)) == REGNO (which_reg)) if (REGNO (DF_REF_REG (reg_info)) == REGNO (reg))
break; break;
defs++;
} }
gcc_assert (reg_info != NULL && defs != NULL); gcc_assert (reg_info != NULL && defs != NULL);
def_chain = DF_REF_CHAIN (reg_info);
while (def_chain) ref_chain = DF_REF_CHAIN (reg_info);
for (ref_link = ref_chain; ref_link; ref_link = ref_link->next)
{ {
/* Problem getting some definition for this instruction. */ /* Problem getting some definition for this instruction. */
if (ref_link->ref == NULL)
if (def_chain->ref == NULL) return NULL;
return 0; if (DF_REF_INSN_INFO (ref_link->ref) == NULL)
if (DF_REF_INSN_INFO (def_chain->ref) == NULL) return NULL;
return 0;
def_chain = def_chain->next;
} }
def_chain = DF_REF_CHAIN (reg_info); if (dest)
for (ref_link = ref_chain; ref_link; ref_link = ref_link->next)
VEC_safe_push (rtx, heap, *dest, DF_REF_INSN (ref_link->ref));
if (dest == NULL) return ref_chain;
return 1;
while (def_chain)
{
VEC_safe_push (rtx, heap, *dest, DF_REF_INSN (def_chain->ref));
def_chain = def_chain->next;
n_refs++;
}
return n_refs;
} }
/* rtx function to check if this SET insn, EXPR, is a conditional copy insn : /* Return true if INSN is
(set (reg a ) (IF_THEN_ELSE (cond) (reg b) (reg c))) (SET (reg REGNO (def_reg)) (if_then_else (cond) (REG x1) (REG x2)))
Called from is_insn_cond_copy. DATA stores the two registers on each and store x1 and x2 in REG_1 and REG_2. */
side of the condition. */
static int static bool
is_this_a_cmove (rtx expr, void *data) is_cond_copy_insn (rtx insn, rtx *reg1, rtx *reg2)
{ {
/* Check for conditional (if-then-else) copy. */ rtx expr = single_set (insn);
if (GET_CODE (expr) == SET if (expr != NULL_RTX
&& GET_CODE (expr) == SET
&& GET_CODE (SET_DEST (expr)) == REG && GET_CODE (SET_DEST (expr)) == REG
&& GET_CODE (SET_SRC (expr)) == IF_THEN_ELSE && GET_CODE (SET_SRC (expr)) == IF_THEN_ELSE
&& GET_CODE (XEXP (SET_SRC (expr), 1)) == REG && GET_CODE (XEXP (SET_SRC (expr), 1)) == REG
&& GET_CODE (XEXP (SET_SRC (expr), 2)) == REG) && GET_CODE (XEXP (SET_SRC (expr), 2)) == REG)
{ {
((rtx *)data)[0] = XEXP (SET_SRC (expr), 1); *reg1 = XEXP (SET_SRC (expr), 1);
((rtx *)data)[1] = XEXP (SET_SRC (expr), 2); *reg2 = XEXP (SET_SRC (expr), 2);
return 1; return true;
}
return 0;
}
/* This returns 1 if it found
(SET (reg REGNO (def_reg)) (if_then_else (cond) (REG x1) (REG x2)))
in the DEF_INSN pattern. It stores the x1 and x2 in COPY_REG_1
and COPY_REG_2. */
static int
is_insn_cond_copy (rtx def_insn, rtx *copy_reg_1, rtx *copy_reg_2)
{
int type;
rtx set_expr;
rtx srcreg[2];
srcreg[0] = NULL_RTX;
srcreg[1] = NULL_RTX;
set_expr = single_set (def_insn);
if (set_expr == NULL_RTX)
return 0;
type = is_this_a_cmove (set_expr, (void *) srcreg);
if (type)
{
*copy_reg_1 = srcreg[0];
*copy_reg_2 = srcreg[1];
return type;
} }
return 0; return false;
} }
/* Reaching Definitions of the extended register could be conditional copies /* Reaching Definitions of the extended register could be conditional copies
or regular definitions. This function separates the two types into two or regular definitions. This function separates the two types into two
lists, DEFS_LIST and COPIES_LIST. This is necessary because, if a reaching lists, DEFS_LIST and COPIES_LIST. This is necessary because, if a reaching
definition is a conditional copy, combining the extend with this definition definition is a conditional copy, merging the extension with this definition
is wrong. Conditional copies are merged by transitively merging its is wrong. Conditional copies are merged by transitively merging their
definitions. The defs_list is populated with all the reaching definitions definitions. The defs_list is populated with all the reaching definitions
of the extension instruction (EXTEND_INSN) which must be merged with an of the extension instruction (EXTEND_INSN) which must be merged with an
extension. The copies_list contains all the conditional moves that will extension. The copies_list contains all the conditional moves that will
later be extended into a wider mode conditonal move if all the merges are later be extended into a wider mode conditional move if all the merges are
successful. The function returns false when there is a failure in getting successful. The function returns 0 upon failure, 1 upon success and 2 when
some definitions, like that of parameters. It returns 1 upon success, 0 all definitions of the EXTEND_INSN have been previously merged. */
upon failure and 2 when all definitions of the EXTEND_INSN were merged
previously. */
static int static int
make_defs_and_copies_lists (rtx extend_insn, rtx set_pat, make_defs_and_copies_lists (rtx extend_insn, rtx set_pat,
VEC (rtx,heap) **defs_list, VEC (rtx,heap) **defs_list,
VEC (rtx,heap) **copies_list) VEC (rtx,heap) **copies_list)
{ {
VEC (rtx,heap) *work_list = VEC_alloc (rtx, heap, 8);
rtx src_reg = XEXP (SET_SRC (set_pat), 0);
bool *is_insn_visited; bool *is_insn_visited;
VEC (rtx,heap) *work_list; int ret = 1;
rtx srcreg, copy_reg_1, copy_reg_2;
rtx def_insn;
int n_defs = 0;
int vec_index = 0;
int n_worklist = 0;
int i, is_copy;
srcreg = XEXP (SET_SRC (set_pat), 0);
work_list = VEC_alloc (rtx, heap, 8);
/* Initialize the Work List */ /* Initialize the work list. */
n_worklist = get_defs (extend_insn, srcreg, &work_list); if (!get_defs (extend_insn, src_reg, &work_list))
if (n_worklist == 0)
{ {
VEC_free (rtx, heap, work_list); VEC_free (rtx, heap, work_list);
/* The number of defs being equal to zero can only imply that all of its /* The number of defs being equal to zero can only mean that all the
definitions have been previously merged. */ definitions have been previously merged. */
return 2; return 2;
} }
is_insn_visited = XNEWVEC (bool, max_insn_uid); is_insn_visited = XCNEWVEC (bool, max_insn_uid);
for (i = 0; i < max_insn_uid; i++)
is_insn_visited[i] = false;
/* Perform transitive closure for conditional copies. */ /* Perform transitive closure for conditional copies. */
while (n_worklist > vec_index) while (!VEC_empty (rtx, work_list))
{ {
def_insn = VEC_index (rtx, work_list, vec_index); rtx def_insn = VEC_pop (rtx, work_list);
rtx reg1, reg2;
gcc_assert (INSN_UID (def_insn) < max_insn_uid); gcc_assert (INSN_UID (def_insn) < max_insn_uid);
if (is_insn_visited[INSN_UID (def_insn)]) if (is_insn_visited[INSN_UID (def_insn)])
{ continue;
vec_index++;
continue;
}
is_insn_visited[INSN_UID (def_insn)] = true; is_insn_visited[INSN_UID (def_insn)] = true;
copy_reg_1 = copy_reg_2 = NULL_RTX;
is_copy = is_insn_cond_copy (def_insn, &copy_reg_1, &copy_reg_2);
if (is_copy)
{
gcc_assert (copy_reg_1 && copy_reg_2);
/* Push it into the copy list first. */
VEC_safe_push (rtx, heap, *copies_list, def_insn);
/* Perform transitive closure here */ if (is_cond_copy_insn (def_insn, &reg1, &reg2))
{
n_defs = get_defs (def_insn, copy_reg_1, &work_list); /* Push it onto the copy list first. */
VEC_safe_push (rtx, heap, *copies_list, def_insn);
if (n_defs == 0)
{ /* Now perform the transitive closure. */
VEC_free (rtx, heap, work_list); if (!get_defs (def_insn, reg1, &work_list)
XDELETEVEC (is_insn_visited); || !get_defs (def_insn, reg2, &work_list))
return 0; {
} ret = 0;
n_worklist += n_defs; break;
}
n_defs = get_defs (def_insn, copy_reg_2, &work_list);
if (n_defs == 0)
{
VEC_free (rtx, heap, work_list);
XDELETEVEC (is_insn_visited);
return 0;
}
n_worklist += n_defs;
} }
else else
{ VEC_safe_push (rtx, heap, *defs_list, def_insn);
VEC_safe_push (rtx, heap, *defs_list, def_insn);
}
vec_index++;
} }
VEC_free (rtx, heap, work_list);
XDELETEVEC (is_insn_visited); XDELETEVEC (is_insn_visited);
return 1; VEC_free (rtx, heap, work_list);
return ret;
} }
/* Merge the DEF_INSN with an extension. Calls combine_set_extend /* Merge the DEF_INSN with an extension. Calls combine_set_extension
on the SET pattern. */ on the SET pattern. */
static bool static bool
merge_def_and_ext (ext_cand_ref cand, rtx def_insn) merge_def_and_ext (ext_cand *cand, rtx def_insn)
{ {
enum machine_mode ext_src_mode; enum machine_mode ext_src_mode;
enum rtx_code code; enum rtx_code code;
...@@ -698,7 +579,7 @@ merge_def_and_ext (ext_cand_ref cand, rtx def_insn) ...@@ -698,7 +579,7 @@ merge_def_and_ext (ext_cand_ref cand, rtx def_insn)
if (GET_CODE (SET_DEST (*sub_rtx)) == REG if (GET_CODE (SET_DEST (*sub_rtx)) == REG
&& GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode) && GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode)
{ {
return combine_set_extend (cand, def_insn, sub_rtx); return combine_set_extension (cand, def_insn, sub_rtx);
} }
return false; return false;
...@@ -714,18 +595,14 @@ merge_def_and_ext (ext_cand_ref cand, rtx def_insn) ...@@ -714,18 +595,14 @@ merge_def_and_ext (ext_cand_ref cand, rtx def_insn)
and false upon failure. */ and false upon failure. */
static bool static bool
combine_reaching_defs (ext_cand_ref cand, rtx set_pat) combine_reaching_defs (ext_cand *cand, rtx set_pat)
{ {
rtx def_insn; rtx def_insn;
bool merge_successful = true; bool merge_successful = true;
int i; int i;
int defs_ix; int defs_ix;
int outcome; int outcome;
/* To store the definitions that have been merged. */
VEC (rtx, heap) *defs_list, *copies_list, *vec; VEC (rtx, heap) *defs_list, *copies_list, *vec;
enum insn_merge_code merge_code;
defs_list = VEC_alloc (rtx, heap, 8); defs_list = VEC_alloc (rtx, heap, 8);
copies_list = VEC_alloc (rtx, heap, 8); copies_list = VEC_alloc (rtx, heap, 8);
...@@ -733,15 +610,14 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat) ...@@ -733,15 +610,14 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat)
outcome = make_defs_and_copies_lists (cand->insn, outcome = make_defs_and_copies_lists (cand->insn,
set_pat, &defs_list, &copies_list); set_pat, &defs_list, &copies_list);
/* outcome == 2 implies that all the definitions for this extension were /* outcome == 2 means that all the definitions for this extension have been
merged while previously when handling other extension. */ previously merged when handling other extensions. */
if (outcome == 2) if (outcome == 2)
{ {
VEC_free (rtx, heap, defs_list); VEC_free (rtx, heap, defs_list);
VEC_free (rtx, heap, copies_list); VEC_free (rtx, heap, copies_list);
if (dump_file) if (dump_file)
fprintf (dump_file, "All definitions have been merged previously.\n"); fprintf (dump_file, "All definitions have been previously merged.\n");
return true; return true;
} }
...@@ -756,13 +632,9 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat) ...@@ -756,13 +632,9 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat)
/* Go through the defs vector and try to merge all the definitions /* Go through the defs vector and try to merge all the definitions
in this vector. */ in this vector. */
vec = VEC_alloc (rtx, heap, 8); vec = VEC_alloc (rtx, heap, 8);
FOR_EACH_VEC_ELT (rtx, defs_list, defs_ix, def_insn) FOR_EACH_VEC_ELT (rtx, defs_list, defs_ix, def_insn)
{ {
merge_code = get_insn_status (def_insn);
gcc_assert (merge_code == MERGE_NOT_ATTEMPTED);
if (merge_def_and_ext (cand, def_insn)) if (merge_def_and_ext (cand, def_insn))
VEC_safe_push (rtx, heap, vec, def_insn); VEC_safe_push (rtx, heap, vec, def_insn);
else else
...@@ -774,7 +646,6 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat) ...@@ -774,7 +646,6 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat)
/* Now go through the conditional copies vector and try to merge all /* Now go through the conditional copies vector and try to merge all
the copies in this vector. */ the copies in this vector. */
if (merge_successful) if (merge_successful)
{ {
FOR_EACH_VEC_ELT (rtx, copies_list, i, def_insn) FOR_EACH_VEC_ELT (rtx, copies_list, i, def_insn)
...@@ -793,21 +664,15 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat) ...@@ -793,21 +664,15 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat)
if (merge_successful) if (merge_successful)
{ {
/* Commit the changes here if possible */ /* Commit the changes here if possible
/* XXX : Now, it is an all or nothing scenario. Even if one definition FIXME: It's an all-or-nothing scenario. Even if only one definition
cannot be merged we totally fail. In future, allow extensions to cannot be merged, we entirely give up. In the future, we should allow
be partially eliminated along those paths where the definitions could extensions to be partially eliminated along those paths where the
be merged. */ definitions could be merged. */
if (apply_change_group ()) if (apply_change_group ())
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "All merges were successful ....\n"); fprintf (dump_file, "All merges were successful.\n");
FOR_EACH_VEC_ELT (rtx, vec, i, def_insn)
{
set_insn_status (def_insn, MERGE_SUCCESS);
}
VEC_free (rtx, heap, vec); VEC_free (rtx, heap, vec);
VEC_free (rtx, heap, defs_list); VEC_free (rtx, heap, defs_list);
...@@ -819,12 +684,11 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat) ...@@ -819,12 +684,11 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat)
/* Changes need not be cancelled explicitly as apply_change_group /* Changes need not be cancelled explicitly as apply_change_group
does it. Print list of definitions in the dump_file for debug does it. Print list of definitions in the dump_file for debug
purposes. This extension cannot be deleted. */ purposes. This extension cannot be deleted. */
if (dump_file) if (dump_file)
{ {
FOR_EACH_VEC_ELT (rtx, vec, i, def_insn) FOR_EACH_VEC_ELT (rtx, vec, i, def_insn)
{ {
fprintf (dump_file, " Ummergable definitions : \n"); fprintf (dump_file, "Non-mergeable definitions:\n");
print_rtl_single (dump_file, def_insn); print_rtl_single (dump_file, def_insn);
} }
} }
...@@ -839,28 +703,23 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat) ...@@ -839,28 +703,23 @@ combine_reaching_defs (ext_cand_ref cand, rtx set_pat)
VEC_free (rtx, heap, vec); VEC_free (rtx, heap, vec);
VEC_free (rtx, heap, defs_list); VEC_free (rtx, heap, defs_list);
VEC_free (rtx, heap, copies_list); VEC_free (rtx, heap, copies_list);
return false; return false;
} }
/* Carry information about extensions while walking the RTL. */ /* This structure holds information while walking the RTL stream. */
struct extend_info struct re_info
{ {
/* The insn where the extension is. */ /* The current insn. */
rtx insn; rtx insn;
/* The list of candidates. */ /* The list of candidates. */
VEC (ext_cand, heap) *insn_list; VEC (ext_cand, heap) *insn_list;
};
static void /* The map of definition instructions to candidates. */
add_ext_candidate (VEC (ext_cand, heap) **exts, ext_cand **def_map;
rtx insn, const_rtx expr) };
{
ext_cand_ref ec = VEC_safe_push (ext_cand, heap, *exts, NULL);
ec->insn = insn;
ec->expr = expr;
}
/* Add an extension pattern that could be eliminated. This is called via /* Add an extension pattern that could be eliminated. This is called via
note_stores from find_removable_extensions. */ note_stores from find_removable_extensions. */
...@@ -868,29 +727,66 @@ add_ext_candidate (VEC (ext_cand, heap) **exts, ...@@ -868,29 +727,66 @@ add_ext_candidate (VEC (ext_cand, heap) **exts,
static void static void
add_removable_extension (rtx x ATTRIBUTE_UNUSED, const_rtx expr, void *data) add_removable_extension (rtx x ATTRIBUTE_UNUSED, const_rtx expr, void *data)
{ {
struct extend_info *rei = (struct extend_info *)data; struct re_info *rei = (struct re_info *)data;
enum rtx_code code;
enum machine_mode mode;
rtx src, dest; rtx src, dest;
/* We are looking for SET (REG N) (EXTEND (REG N)). */ /* We are looking for SET (REG N) (ANY_EXTEND (REG N)). */
if (GET_CODE (expr) != SET) if (GET_CODE (expr) != SET)
return; return;
src = SET_SRC (expr); src = SET_SRC (expr);
code = GET_CODE (src);
dest = SET_DEST (expr); dest = SET_DEST (expr);
mode = GET_MODE (dest);
if (REG_P (dest) if (REG_P (dest)
&& (GET_CODE (src) == ZERO_EXTEND || GET_CODE (src) == SIGN_EXTEND) && (code == SIGN_EXTEND || code == ZERO_EXTEND)
&& REG_P (XEXP (src, 0)) && REG_P (XEXP (src, 0))
&& REGNO (dest) == REGNO (XEXP (src, 0))) && REGNO (dest) == REGNO (XEXP (src, 0)))
{ {
if (get_defs (rei->insn, XEXP (src, 0), NULL)) struct df_link *defs, *def;
add_ext_candidate (&rei->insn_list, rei->insn, expr); ext_cand *cand;
else if (dump_file)
/* First, make sure we can get all the reaching definitions. */
defs = get_defs (rei->insn, XEXP (src, 0), NULL);
if (!defs)
{ {
fprintf (dump_file, "Cannot eliminate extension: \n"); if (dump_file)
print_rtl_single (dump_file, rei->insn); {
fprintf (dump_file, "No defs. Could be extending parameters.\n"); fprintf (dump_file, "Cannot eliminate extension:\n");
print_rtl_single (dump_file, rei->insn);
fprintf (dump_file, " because of missing definition(s)\n");
}
return;
} }
/* Second, make sure the reaching definitions don't feed another and
different extension. FIXME: this obviously can be improved. */
for (def = defs; def; def = def->next)
if ((cand = rei->def_map[INSN_UID(DF_REF_INSN (def->ref))])
&& (cand->code != code || cand->mode != mode))
{
if (dump_file)
{
fprintf (dump_file, "Cannot eliminate extension:\n");
print_rtl_single (dump_file, rei->insn);
fprintf (dump_file, " because of other extension\n");
}
return;
}
/* Then add the candidate to the list and insert the reaching definitions
into the definition map. */
cand = VEC_safe_push (ext_cand, heap, rei->insn_list, NULL);
cand->expr = expr;
cand->code = code;
cand->mode = mode;
cand->insn = rei->insn;
for (def = defs; def; def = def->next)
rei->def_map[INSN_UID(DF_REF_INSN (def->ref))] = cand;
} }
} }
...@@ -900,11 +796,12 @@ add_removable_extension (rtx x ATTRIBUTE_UNUSED, const_rtx expr, void *data) ...@@ -900,11 +796,12 @@ add_removable_extension (rtx x ATTRIBUTE_UNUSED, const_rtx expr, void *data)
static VEC (ext_cand, heap)* static VEC (ext_cand, heap)*
find_removable_extensions (void) find_removable_extensions (void)
{ {
struct extend_info rei; struct re_info rei;
basic_block bb; basic_block bb;
rtx insn; rtx insn;
rei.insn_list = VEC_alloc (ext_cand, heap, 8); rei.insn_list = VEC_alloc (ext_cand, heap, 8);
rei.def_map = XCNEWVEC (ext_cand *, max_insn_uid);
FOR_EACH_BB (bb) FOR_EACH_BB (bb)
FOR_BB_INSNS (bb, insn) FOR_BB_INSNS (bb, insn)
...@@ -916,81 +813,64 @@ find_removable_extensions (void) ...@@ -916,81 +813,64 @@ find_removable_extensions (void)
note_stores (PATTERN (insn), add_removable_extension, &rei); note_stores (PATTERN (insn), add_removable_extension, &rei);
} }
XDELETEVEC (rei.def_map);
return rei.insn_list; return rei.insn_list;
} }
/* This is the main function that checks the insn stream for redundant /* This is the main function that checks the insn stream for redundant
extensions and tries to remove them if possible. */ extensions and tries to remove them if possible. */
static unsigned int static void
find_and_remove_re (void) find_and_remove_re (void)
{ {
ext_cand_ref curr_cand; ext_cand *curr_cand;
rtx curr_insn = NULL_RTX; rtx curr_insn = NULL_RTX;
int i; int num_re_opportunities = 0, num_realized = 0, i;
int ix;
long num_realized = 0;
long num_re_opportunities = 0;
VEC (ext_cand, heap) *reinsn_list; VEC (ext_cand, heap) *reinsn_list;
VEC (rtx, heap) *reinsn_del_list; VEC (rtx, heap) *reinsn_del_list;
/* Construct DU chain to get all reaching definitions of each /* Construct DU chain to get all reaching definitions of each
extension instruction. */ extension instruction. */
df_chain_add_problem (DF_UD_CHAIN + DF_DU_CHAIN); df_chain_add_problem (DF_UD_CHAIN + DF_DU_CHAIN);
df_analyze (); df_analyze ();
max_insn_uid = get_max_uid (); max_insn_uid = get_max_uid ();
is_insn_merge_attempted
= XNEWVEC (enum insn_merge_code,
sizeof (enum insn_merge_code) * max_insn_uid);
for (i = 0; i < max_insn_uid; i++)
is_insn_merge_attempted[i] = MERGE_NOT_ATTEMPTED;
num_re_opportunities = num_realized = 0;
reinsn_del_list = VEC_alloc (rtx, heap, 4); reinsn_del_list = VEC_alloc (rtx, heap, 4);
reinsn_list = find_removable_extensions (); reinsn_list = find_removable_extensions ();
FOR_EACH_VEC_ELT (ext_cand, reinsn_list, ix, curr_cand) FOR_EACH_VEC_ELT (ext_cand, reinsn_list, i, curr_cand)
{ {
num_re_opportunities++; num_re_opportunities++;
/* Try to combine the extension with the definition here. */
/* Try to combine the extension with the definition. */
if (dump_file) if (dump_file)
{ {
fprintf (dump_file, "Trying to eliminate extension : \n"); fprintf (dump_file, "Trying to eliminate extension:\n");
print_rtl_single (dump_file, curr_insn); print_rtl_single (dump_file, curr_cand->insn);
} }
if (combine_reaching_defs (curr_cand, PATTERN (curr_cand->insn))) if (combine_reaching_defs (curr_cand, PATTERN (curr_cand->insn)))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Eliminated the extension...\n"); fprintf (dump_file, "Eliminated the extension.\n");
num_realized++; num_realized++;
VEC_safe_push (rtx, heap, reinsn_del_list, curr_cand->insn); VEC_safe_push (rtx, heap, reinsn_del_list, curr_cand->insn);
} }
} }
/* Delete all useless extensions here in one sweep. */ /* Delete all useless extensions here in one sweep. */
FOR_EACH_VEC_ELT (rtx, reinsn_del_list, ix, curr_insn) FOR_EACH_VEC_ELT (rtx, reinsn_del_list, i, curr_insn)
delete_insn (curr_insn); delete_insn (curr_insn);
free (is_insn_merge_attempted);
VEC_free (ext_cand, heap, reinsn_list); VEC_free (ext_cand, heap, reinsn_list);
VEC_free (rtx, heap, reinsn_del_list); VEC_free (rtx, heap, reinsn_del_list);
if (dump_file && num_re_opportunities > 0) if (dump_file && num_re_opportunities > 0)
fprintf (dump_file, "\n %s : num_re_opportunities = %ld " fprintf (dump_file, "Elimination opportunities = %d realized = %d\n",
"num_realized = %ld \n", num_re_opportunities, num_realized);
current_function_name (),
num_re_opportunities, num_realized);
df_finish_pass (false); df_finish_pass (false);
return 0;
} }
/* Find and remove redundant extensions. */ /* Find and remove redundant extensions. */
......
2011-12-27 Eric Botcazou <ebotcazou@adacore.com>
* gcc.c-torture/execute/20111227-1.c: New test.
2011-12-25 Jan Hubicka <jh@suse.cz> 2011-12-25 Jan Hubicka <jh@suse.cz>
PR middle-end/48641 PR middle-end/48641
......
/* PR rtl-optimization/51667 */
/* Testcase by Uros Bizjak <ubizjak@gmail.com> */
extern void abort (void);
void __attribute__((noinline,noclone))
bar (int a)
{
if (a != -1)
abort ();
}
void __attribute__((noinline,noclone))
foo (short *a, int t)
{
short r = *a;
if (t)
bar ((unsigned short) r);
else
bar ((signed short) r);
}
short v = -1;
int main(void)
{
foo (&v, 0);
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment