Commit b57cca0b by Jakub Jelinek Committed by Jakub Jelinek

re PR rtl-optimization/51933 (wrong code due to -free)

	PR rtl-optimization/51933
	* ree.c (transform_ifelse): Return true right away if dstreg is
	already wider or equal to cand->mode.
	(enum ext_modified_kind, struct ext_modified, ext_state): New types.
	(make_defs_and_copies_lists): Remove defs_list and copies_list
	arguments, add state argument, just truncate state->work_list
	instead of always allocating and freeing the vector.  Assert that
	get_defs succeeds instead of returning 2.  Changed return type to
	bool.
	(merge_def_and_ext): Add state argument.  If SET_DEST doesn't
	have ext_src_mode, see if it has been modified already with the
	right kind of extension and has been extended before from the
	ext_src_mode.  If SET_DEST is already wider or equal to cand->mode,
	just return true.  Remember the original mode in state->modified
	array.
	(combine_reaching_defs): Add state argument.  Don't allocate and
	free here def_list, copied_list and vec vectors, instead just
	VEC_truncate the vectors in *state.  Don't handle outcome == 2
	here.
	(find_and_remove_re): Set DF_DEFER_INSN_RESCAN df flag.
	Add state variable, clear vectors in it, initialize state.modified
	if needed.  Free all the vectors at the end and state.modified too.
	Don't skip a candidate if the extension expression has been modified.

	* gcc.c-torture/execute/pr51933.c: New test.

From-SVN: r183416
parent b3aa0ca8
2012-01-23 Jakub Jelinek <jakub@redhat.com>
PR rtl-optimization/51933
* ree.c (transform_ifelse): Return true right away if dstreg is
already wider or equal to cand->mode.
(enum ext_modified_kind, struct ext_modified, ext_state): New types.
(make_defs_and_copies_lists): Remove defs_list and copies_list
arguments, add state argument, just truncate state->work_list
instead of always allocating and freeing the vector. Assert that
get_defs succeeds instead of returning 2. Changed return type to
bool.
(merge_def_and_ext): Add state argument. If SET_DEST doesn't
have ext_src_mode, see if it has been modified already with the
right kind of extension and has been extended before from the
ext_src_mode. If SET_DEST is already wider or equal to cand->mode,
just return true. Remember the original mode in state->modified
array.
(combine_reaching_defs): Add state argument. Don't allocate and
free here def_list, copied_list and vec vectors, instead just
VEC_truncate the vectors in *state. Don't handle outcome == 2
here.
(find_and_remove_re): Set DF_DEFER_INSN_RESCAN df flag.
Add state variable, clear vectors in it, initialize state.modified
if needed. Free all the vectors at the end and state.modified too.
Don't skip a candidate if the extension expression has been modified.
2012-01-22 Douglas B Rupp <rupp@gnat.com> 2012-01-22 Douglas B Rupp <rupp@gnat.com>
PR target/47096 PR target/47096
......
/* 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, 2012 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
...@@ -380,6 +380,11 @@ transform_ifelse (ext_cand *cand, rtx def_insn) ...@@ -380,6 +380,11 @@ transform_ifelse (ext_cand *cand, rtx def_insn)
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);
/* If the conditional move already has the right or wider mode,
there is nothing to do. */
if (GET_MODE_SIZE (GET_MODE (dstreg)) >= GET_MODE_SIZE (cand->mode))
return true;
map_srcreg = gen_rtx_REG (cand->mode, REGNO (srcreg)); map_srcreg = gen_rtx_REG (cand->mode, REGNO (srcreg));
map_srcreg2 = gen_rtx_REG (cand->mode, REGNO (srcreg2)); map_srcreg2 = gen_rtx_REG (cand->mode, REGNO (srcreg2));
map_dstreg = gen_rtx_REG (cand->mode, REGNO (dstreg)); map_dstreg = gen_rtx_REG (cand->mode, REGNO (dstreg));
...@@ -466,43 +471,77 @@ is_cond_copy_insn (rtx insn, rtx *reg1, rtx *reg2) ...@@ -466,43 +471,77 @@ is_cond_copy_insn (rtx insn, rtx *reg1, rtx *reg2)
return false; return false;
} }
enum ext_modified_kind
{
/* The insn hasn't been modified by ree pass yet. */
EXT_MODIFIED_NONE,
/* Changed into zero extension. */
EXT_MODIFIED_ZEXT,
/* Changed into sign extension. */
EXT_MODIFIED_SEXT
};
struct ext_modified
{
/* Mode from which ree has zero or sign extended the destination. */
ENUM_BITFIELD(machine_mode) mode : 8;
/* Kind of modification of the insn. */
ENUM_BITFIELD(ext_modified_kind) kind : 2;
/* True if the insn is scheduled to be deleted. */
unsigned int deleted : 1;
};
/* Vectors used by combine_reaching_defs and its helpers. */
typedef struct ext_state
{
/* In order to avoid constant VEC_alloc/VEC_free, we keep these
4 vectors live through the entire find_and_remove_re and just
VEC_truncate them each time. */
VEC (rtx, heap) *defs_list;
VEC (rtx, heap) *copies_list;
VEC (rtx, heap) *modified_list;
VEC (rtx, heap) *work_list;
/* For instructions that have been successfully modified, this is
the original mode from which the insn is extending and
kind of extension. */
struct ext_modified *modified;
} ext_state;
/* 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, STATE->DEFS_LIST and STATE->COPIES_LIST. This is necessary because,
definition is a conditional copy, merging the extension with this definition if a reaching definition is a conditional copy, merging the extension with
is wrong. Conditional copies are merged by transitively merging their this definition is wrong. Conditional copies are merged by transitively
definitions. The defs_list is populated with all the reaching definitions merging their definitions. The defs_list is populated with all the reaching
of the extension instruction (EXTEND_INSN) which must be merged with an definitions of the extension instruction (EXTEND_INSN) which must be merged
extension. The copies_list contains all the conditional moves that will with an extension. The copies_list contains all the conditional moves that
later be extended into a wider mode conditional move if all the merges are will later be extended into a wider mode conditional move if all the merges
successful. The function returns 0 upon failure, 1 upon success and 2 when are successful. The function returns false upon failure, true upon
all definitions of the EXTEND_INSN have been previously merged. */ success. */
static int static bool
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, ext_state *state)
VEC (rtx,heap) **copies_list)
{ {
VEC (rtx,heap) *work_list = VEC_alloc (rtx, heap, 8);
rtx src_reg = XEXP (SET_SRC (set_pat), 0); rtx src_reg = XEXP (SET_SRC (set_pat), 0);
bool *is_insn_visited; bool *is_insn_visited;
int ret = 1; bool ret = true;
VEC_truncate (rtx, state->work_list, 0);
/* Initialize the work list. */ /* Initialize the work list. */
if (!get_defs (extend_insn, src_reg, &work_list)) if (!get_defs (extend_insn, src_reg, &state->work_list))
{ gcc_unreachable ();
VEC_free (rtx, heap, work_list);
/* The number of defs being equal to zero can only mean that all the
definitions have been previously merged. */
return 2;
}
is_insn_visited = XCNEWVEC (bool, max_insn_uid); is_insn_visited = XCNEWVEC (bool, max_insn_uid);
/* Perform transitive closure for conditional copies. */ /* Perform transitive closure for conditional copies. */
while (!VEC_empty (rtx, work_list)) while (!VEC_empty (rtx, state->work_list))
{ {
rtx def_insn = VEC_pop (rtx, work_list); rtx def_insn = VEC_pop (rtx, state->work_list);
rtx reg1, reg2; rtx reg1, reg2;
gcc_assert (INSN_UID (def_insn) < max_insn_uid); gcc_assert (INSN_UID (def_insn) < max_insn_uid);
...@@ -514,22 +553,21 @@ make_defs_and_copies_lists (rtx extend_insn, rtx set_pat, ...@@ -514,22 +553,21 @@ make_defs_and_copies_lists (rtx extend_insn, rtx set_pat,
if (is_cond_copy_insn (def_insn, &reg1, &reg2)) if (is_cond_copy_insn (def_insn, &reg1, &reg2))
{ {
/* Push it onto the copy list first. */ /* Push it onto the copy list first. */
VEC_safe_push (rtx, heap, *copies_list, def_insn); VEC_safe_push (rtx, heap, state->copies_list, def_insn);
/* Now perform the transitive closure. */ /* Now perform the transitive closure. */
if (!get_defs (def_insn, reg1, &work_list) if (!get_defs (def_insn, reg1, &state->work_list)
|| !get_defs (def_insn, reg2, &work_list)) || !get_defs (def_insn, reg2, &state->work_list))
{ {
ret = 0; ret = false;
break; break;
} }
} }
else else
VEC_safe_push (rtx, heap, *defs_list, def_insn); VEC_safe_push (rtx, heap, state->defs_list, def_insn);
} }
XDELETEVEC (is_insn_visited); XDELETEVEC (is_insn_visited);
VEC_free (rtx, heap, work_list);
return ret; return ret;
} }
...@@ -538,7 +576,7 @@ make_defs_and_copies_lists (rtx extend_insn, rtx set_pat, ...@@ -538,7 +576,7 @@ make_defs_and_copies_lists (rtx extend_insn, rtx set_pat,
on the SET pattern. */ on the SET pattern. */
static bool static bool
merge_def_and_ext (ext_cand *cand, rtx def_insn) merge_def_and_ext (ext_cand *cand, rtx def_insn, ext_state *state)
{ {
enum machine_mode ext_src_mode; enum machine_mode ext_src_mode;
enum rtx_code code; enum rtx_code code;
...@@ -577,10 +615,27 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn) ...@@ -577,10 +615,27 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn)
gcc_assert (sub_rtx != NULL); gcc_assert (sub_rtx != NULL);
if (GET_CODE (SET_DEST (*sub_rtx)) == REG if (REG_P (SET_DEST (*sub_rtx))
&& GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode) && (GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode
|| ((state->modified[INSN_UID (def_insn)].kind
== (cand->code == ZERO_EXTEND
? EXT_MODIFIED_ZEXT : EXT_MODIFIED_SEXT))
&& state->modified[INSN_UID (def_insn)].mode
== ext_src_mode)))
{ {
return combine_set_extension (cand, def_insn, sub_rtx); if (GET_MODE_SIZE (GET_MODE (SET_DEST (*sub_rtx)))
>= GET_MODE_SIZE (cand->mode))
return true;
/* If def_insn is already scheduled to be deleted, don't attempt
to modify it. */
if (state->modified[INSN_UID (def_insn)].deleted)
return false;
if (combine_set_extension (cand, def_insn, sub_rtx))
{
if (state->modified[INSN_UID (def_insn)].kind == EXT_MODIFIED_NONE)
state->modified[INSN_UID (def_insn)].mode = ext_src_mode;
return true;
}
} }
return false; return false;
...@@ -596,48 +651,31 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn) ...@@ -596,48 +651,31 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn)
and false upon failure. */ and false upon failure. */
static bool static bool
combine_reaching_defs (ext_cand *cand, rtx set_pat) combine_reaching_defs (ext_cand *cand, rtx set_pat, ext_state *state)
{ {
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; bool outcome;
VEC (rtx, heap) *defs_list, *copies_list, *vec;
defs_list = VEC_alloc (rtx, heap, 8);
copies_list = VEC_alloc (rtx, heap, 8);
outcome = make_defs_and_copies_lists (cand->insn, VEC_truncate (rtx, state->defs_list, 0);
set_pat, &defs_list, &copies_list); VEC_truncate (rtx, state->copies_list, 0);
/* outcome == 2 means that all the definitions for this extension have been outcome = make_defs_and_copies_lists (cand->insn, set_pat, state);
previously merged when handling other extensions. */
if (outcome == 2)
{
VEC_free (rtx, heap, defs_list);
VEC_free (rtx, heap, copies_list);
if (dump_file)
fprintf (dump_file, "All definitions have been previously merged.\n");
return true;
}
if (outcome == 0) if (!outcome)
{ return false;
VEC_free (rtx, heap, defs_list);
VEC_free (rtx, heap, copies_list);
return false;
}
merge_successful = true; merge_successful = true;
/* 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_truncate (rtx, state->modified_list, 0);
FOR_EACH_VEC_ELT (rtx, defs_list, defs_ix, def_insn) FOR_EACH_VEC_ELT (rtx, state->defs_list, defs_ix, def_insn)
{ {
if (merge_def_and_ext (cand, def_insn)) if (merge_def_and_ext (cand, def_insn, state))
VEC_safe_push (rtx, heap, vec, def_insn); VEC_safe_push (rtx, heap, state->modified_list, def_insn);
else else
{ {
merge_successful = false; merge_successful = false;
...@@ -649,12 +687,10 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat) ...@@ -649,12 +687,10 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat)
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, state->copies_list, i, def_insn)
{ {
if (transform_ifelse (cand, def_insn)) if (transform_ifelse (cand, def_insn))
{ VEC_safe_push (rtx, heap, state->modified_list, def_insn);
VEC_safe_push (rtx, heap, vec, def_insn);
}
else else
{ {
merge_successful = false; merge_successful = false;
...@@ -675,9 +711,12 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat) ...@@ -675,9 +711,12 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat)
if (dump_file) if (dump_file)
fprintf (dump_file, "All merges were successful.\n"); fprintf (dump_file, "All merges were successful.\n");
VEC_free (rtx, heap, vec); FOR_EACH_VEC_ELT (rtx, state->modified_list, i, def_insn)
VEC_free (rtx, heap, defs_list); if (state->modified[INSN_UID (def_insn)].kind == EXT_MODIFIED_NONE)
VEC_free (rtx, heap, copies_list); state->modified[INSN_UID (def_insn)].kind
= (cand->code == ZERO_EXTEND
? EXT_MODIFIED_ZEXT : EXT_MODIFIED_SEXT);
return true; return true;
} }
else else
...@@ -689,7 +728,7 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat) ...@@ -689,7 +728,7 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat)
{ {
fprintf (dump_file, fprintf (dump_file,
"Merge cancelled, non-mergeable definitions:\n"); "Merge cancelled, non-mergeable definitions:\n");
FOR_EACH_VEC_ELT (rtx, vec, i, def_insn) FOR_EACH_VEC_ELT (rtx, state->modified_list, i, def_insn)
print_rtl_single (dump_file, def_insn); print_rtl_single (dump_file, def_insn);
} }
} }
...@@ -700,10 +739,6 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat) ...@@ -700,10 +739,6 @@ combine_reaching_defs (ext_cand *cand, rtx set_pat)
cancel_changes (0); cancel_changes (0);
} }
VEC_free (rtx, heap, vec);
VEC_free (rtx, heap, defs_list);
VEC_free (rtx, heap, copies_list);
return false; return false;
} }
...@@ -829,26 +864,30 @@ find_and_remove_re (void) ...@@ -829,26 +864,30 @@ find_and_remove_re (void)
int num_re_opportunities = 0, num_realized = 0, i; int num_re_opportunities = 0, num_realized = 0, i;
VEC (ext_cand, heap) *reinsn_list; VEC (ext_cand, heap) *reinsn_list;
VEC (rtx, heap) *reinsn_del_list; VEC (rtx, heap) *reinsn_del_list;
ext_state state;
/* 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 ();
df_set_flags (DF_DEFER_INSN_RESCAN);
max_insn_uid = get_max_uid (); max_insn_uid = get_max_uid ();
reinsn_del_list = VEC_alloc (rtx, heap, 4); reinsn_del_list = NULL;
reinsn_list = find_removable_extensions (); reinsn_list = find_removable_extensions ();
state.defs_list = NULL;
state.copies_list = NULL;
state.modified_list = NULL;
state.work_list = NULL;
if (VEC_empty (ext_cand, reinsn_list))
state.modified = NULL;
else
state.modified = XCNEWVEC (struct ext_modified, max_insn_uid);
FOR_EACH_VEC_ELT (ext_cand, reinsn_list, i, curr_cand) FOR_EACH_VEC_ELT (ext_cand, reinsn_list, i, curr_cand)
{ {
num_re_opportunities++; num_re_opportunities++;
/* If the candidate insn is itself a definition insn for another
candidate, it may have been modified and the UD chain broken.
FIXME: the handling of successive extensions can be improved. */
if (!reg_mentioned_p (curr_cand->expr, PATTERN (curr_cand->insn)))
continue;
/* Try to combine the extension with the definition. */ /* Try to combine the extension with the definition. */
if (dump_file) if (dump_file)
{ {
...@@ -856,12 +895,13 @@ find_and_remove_re (void) ...@@ -856,12 +895,13 @@ find_and_remove_re (void)
print_rtl_single (dump_file, curr_cand->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), &state))
{ {
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);
state.modified[INSN_UID (curr_cand->insn)].deleted = 1;
} }
} }
...@@ -871,6 +911,11 @@ find_and_remove_re (void) ...@@ -871,6 +911,11 @@ find_and_remove_re (void)
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);
VEC_free (rtx, heap, state.defs_list);
VEC_free (rtx, heap, state.copies_list);
VEC_free (rtx, heap, state.modified_list);
VEC_free (rtx, heap, state.work_list);
XDELETEVEC (state.modified);
if (dump_file && num_re_opportunities > 0) if (dump_file && num_re_opportunities > 0)
fprintf (dump_file, "Elimination opportunities = %d realized = %d\n", fprintf (dump_file, "Elimination opportunities = %d realized = %d\n",
......
2012-01-23 Jakub Jelinek <jakub@redhat.com>
PR rtl-optimization/51933
* gcc.c-torture/execute/pr51933.c: New test.
2012-01-22 Douglas B Rupp <rupp@gnat.com> 2012-01-22 Douglas B Rupp <rupp@gnat.com>
* gcc.dg/builtins-config.h (HAVE_C99_RUNTIME): * gcc.dg/builtins-config.h (HAVE_C99_RUNTIME):
......
/* PR rtl-optimization/51933 */
static signed char v1;
static unsigned char v2[256], v3[256];
__attribute__((noclone, noinline)) void
foo (void)
{
asm volatile ("" : : "g" (&v1), "g" (&v2[0]), "g" (&v3[0]) : "memory");
}
__attribute__((noclone, noinline)) int
bar (const int x, const unsigned short *y, char *z)
{
int i;
unsigned short u;
if (!v1)
foo ();
for (i = 0; i < x; i++)
{
u = y[i];
z[i] = u < 0x0100 ? v2[u] : v3[u & 0xff];
}
z[x] = '\0';
return x;
}
int
main (void)
{
char buf[18];
unsigned short s[18];
unsigned char c[18] = "abcdefghijklmnopq";
int i;
for (i = 0; i < 256; i++)
{
v2[i] = i;
v3[i] = i + 1;
}
for (i = 0; i < 18; i++)
s[i] = c[i];
s[5] |= 0x600;
s[6] |= 0x500;
s[11] |= 0x2000;
s[15] |= 0x500;
foo ();
if (bar (17, s, buf) != 17
|| __builtin_memcmp (buf, "abcdeghhijkmmnoqq", 18) != 0)
__builtin_abort ();
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