re PR tree-optimization/21576 (FRE does not eliminate a redundant builtin call.)

2005-05-15  Daniel Berlin  <dberlin@dberlin.org>

	Fix PR tree-optimization/21576

	* tree-ssa-pre.c (expression_node_pool): New pool.
	(comparison_node_pool): Ditto.
	(list_node_pool): Ditto.
	(pool_copy_list): New function.
	(phi_translate): Handle CALL_EXPR.
	(valid_in_set): Ditto.
	(create_expression_by_pieces): Ditto.
	(insert_into_preds_of_block): Ditto.
	(insert_aux): Ditto.
	(compute_avail): Ditto.
	(create_value_expr_from): Handle TREE_LIST and CALL_EXPR.
	(can_value_number_call): New function.
	(find_leader): Update comment.
	(init_pre): Create new pools.
	(fini_pre): Free new pools.
	(pass_pre): Add TODO_update_ssa for the future when we are going
	to need vops.
	* tree-vn.c (expressions_equal_p): Handle TREE_LIST.
	(set_value_handle): Ditto.
	(get_value_handle): Ditto.

From-SVN: r99759
This commit is contained in:
Daniel Berlin 2005-05-16 01:12:07 +00:00 committed by Daniel Berlin
parent cea02b6e7e
commit 43da81be39
9 changed files with 419 additions and 20 deletions

View File

@ -1,3 +1,28 @@
2005-05-15 Daniel Berlin <dberlin@dberlin.org>
Fix PR tree-optimization/21576
* tree-ssa-pre.c (expression_node_pool): New pool.
(comparison_node_pool): Ditto.
(list_node_pool): Ditto.
(pool_copy_list): New function.
(phi_translate): Handle CALL_EXPR.
(valid_in_set): Ditto.
(create_expression_by_pieces): Ditto.
(insert_into_preds_of_block): Ditto.
(insert_aux): Ditto.
(compute_avail): Ditto.
(create_value_expr_from): Handle TREE_LIST and CALL_EXPR.
(can_value_number_call): New function.
(find_leader): Update comment.
(init_pre): Create new pools.
(fini_pre): Free new pools.
(pass_pre): Add TODO_update_ssa for the future when we are going
to need vops.
* tree-vn.c (expressions_equal_p): Handle TREE_LIST.
(set_value_handle): Ditto.
(get_value_handle): Ditto.
2005-05-15 Richard Earnshaw <richard.earnshaw@arm.com>
* arm.c (thumb_unexpanded_epilogue): Delete unused variable 'mode'.

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-dom1-details" } */
/* { dg-options "-O2 -fdump-tree-fre-details" } */
int t(int a) __attribute__ ((const));
void q (void);
void
@ -12,5 +12,5 @@ threading(int a,int b)
}
}
/* We should thread the jump twice and eliminate it. */
/* { dg-final { scan-tree-dump-times "Replaced.* t " 1 "dom1"} } */
/* { dg-final { cleanup-tree-dump "dom1" } } */
/* { dg-final { scan-tree-dump-times "Replaced.* t " 1 "fre"} } */
/* { dg-final { cleanup-tree-dump "fre" } } */

View File

@ -0,0 +1,14 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fno-tree-dominator-opts -fdump-tree-fre-stats" } */
double cos (double);
void link_error();
void f(double a)
{
double b = cos (a);
double c = cos (a);
if (b != c)
link_error();
}
/* { dg-final { scan-tree-dump-times "Eliminated: 1" 1 "fre"} } */
/* { dg-final { cleanup-tree-dump "fre" } } */

View File

@ -0,0 +1,21 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-pre-stats" } */
double cos (double);
double f(double a)
{
double b;
double c,d;
if (a < 2.0)
{
c = cos (a);
}
else
{
c = 1.0;
}
d = cos (a);
return d + c;
}
/* { dg-final { scan-tree-dump-times "Eliminated: 1" 1 "pre"} } */
/* { dg-final { cleanup-tree-dump "pre" } } */

View File

@ -0,0 +1,27 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-pre-stats" } */
double cos (double) __attribute__ ((const));
double sin (double) __attribute__ ((const));
double f(double a)
{
double b;
double c,d;
double (*fp) (double) __attribute__ ((const));
/* Fully redundant call, but we need a phi node to merge the results. */
if (a < 2.0)
{
fp = sin;
c = fp (a);
}
else
{
c = 1.0;
fp = cos;
c = fp (a);
}
d = fp (a);
return d + c;
}
/* { dg-final { scan-tree-dump-times "Eliminated: 1" 1 "pre"} } */
/* { dg-final { cleanup-tree-dump "pre" } } */

View File

@ -0,0 +1,26 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-pre-stats" } */
double cos (double) __attribute__ ((const));
double sin (double) __attribute__ ((const));
double f(double a)
{
double b;
double c,d;
double (*fp) (double) __attribute__ ((const));
/* Partially redundant call */
if (a < 2.0)
{
fp = sin;
c = fp (a);
}
else
{
c = 1.0;
fp = cos;
}
d = fp (a);
return d + c;
}
/* { dg-final { scan-tree-dump-times "Eliminated: 1" 1 "pre"} } */
/* { dg-final { cleanup-tree-dump "pre" } } */

View File

@ -0,0 +1,13 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fno-tree-dominator-opts -fdump-tree-fre-stats" } */
int
foo (unsigned long a)
{
int b = __builtin_clzl (a);
int c = __builtin_clzl (a);
if (b == c)
return 1;
return 0;
}
/* { dg-final { scan-tree-dump-times "Eliminated: 1" 1 "fre"} } */
/* { dg-final { cleanup-tree-dump "fre" } } */

View File

@ -305,6 +305,9 @@ static alloc_pool value_set_node_pool;
static alloc_pool binary_node_pool;
static alloc_pool unary_node_pool;
static alloc_pool reference_node_pool;
static alloc_pool comparison_node_pool;
static alloc_pool expression_node_pool;
static alloc_pool list_node_pool;
static bitmap_obstack grand_bitmap_obstack;
/* Set of blocks with statements that have had its EH information
@ -855,6 +858,35 @@ fully_constant_expression (tree t)
return t;
}
/* Return a copy of a chain of nodes, chained through the TREE_CHAIN field.
For example, this can copy a list made of TREE_LIST nodes.
Allocates the nodes in list_node_pool*/
static tree
pool_copy_list (tree list)
{
tree head;
tree prev, next;
if (list == 0)
return 0;
head = pool_alloc (list_node_pool);
memcpy (head, list, tree_size (list));
prev = head;
next = TREE_CHAIN (list);
while (next)
{
TREE_CHAIN (prev) = pool_alloc (list_node_pool);
memcpy (TREE_CHAIN (prev), next, tree_size (next));
prev = TREE_CHAIN (prev);
next = TREE_CHAIN (next);
}
return head;
}
/* Translate EXPR using phis in PHIBLOCK, so that it has the values of
the phis in PRED. Return NULL if we can't find a leader for each
part of the translated expression. */
@ -879,6 +911,89 @@ phi_translate (tree expr, value_set_t set, basic_block pred,
switch (TREE_CODE_CLASS (TREE_CODE (expr)))
{
case tcc_expression:
{
if (TREE_CODE (expr) != CALL_EXPR)
return NULL;
else
{
tree oldop0 = TREE_OPERAND (expr, 0);
tree oldarglist = TREE_OPERAND (expr, 1);
tree oldop2 = TREE_OPERAND (expr, 2);
tree newop0;
tree newarglist;
tree newop2 = NULL;
tree oldwalker;
tree newwalker;
tree newexpr;
bool listchanged = false;
/* Call expressions are kind of weird because they have an
argument list. We don't want to value number the list
as one value number, because that doesn't make much
sense, and just breaks the support functions we call,
which expect TREE_OPERAND (call_expr, 2) to be a
TREE_LIST. */
newop0 = phi_translate (find_leader (set, oldop0),
set, pred, phiblock);
if (newop0 == NULL)
return NULL;
if (oldop2)
{
newop2 = phi_translate (find_leader (set, oldop2),
set, pred, phiblock);
if (newop2 == NULL)
return NULL;
}
/* phi translate the argument list piece by piece.
We could actually build the list piece by piece here,
but it's likely to not be worth the memory we will save,
unless you have millions of call arguments. */
newarglist = pool_copy_list (oldarglist);
for (oldwalker = oldarglist, newwalker = newarglist;
oldwalker && newwalker;
oldwalker = TREE_CHAIN (oldwalker),
newwalker = TREE_CHAIN (newwalker))
{
tree oldval = TREE_VALUE (oldwalker);
tree newval;
if (oldval)
{
newval = phi_translate (find_leader (set, oldval),
set, pred, phiblock);
if (newval == NULL)
return NULL;
if (newval != oldval)
{
listchanged = true;
TREE_VALUE (newwalker) = get_value_handle (newval);
}
}
}
if (listchanged)
vn_lookup_or_add (newarglist, NULL);
if (listchanged || (newop0 != oldop0) || (oldop2 != newop2))
{
newexpr = pool_alloc (expression_node_pool);
memcpy (newexpr, expr, tree_size (expr));
TREE_OPERAND (newexpr, 0) = newop0 == oldop0 ? oldop0 : get_value_handle (newop0);
TREE_OPERAND (newexpr, 1) = listchanged ? newarglist : oldarglist;
TREE_OPERAND (newexpr, 2) = newop2 == oldop2 ? oldop2 : get_value_handle (newop2);
create_tree_ann (newexpr);
vn_lookup_or_add (newexpr, NULL);
expr = newexpr;
phi_trans_add (oldexpr, newexpr, pred);
}
}
}
return expr;
case tcc_reference:
/* XXX: Until we have PRE of loads working, none will be ANTIC. */
return NULL;
@ -1084,10 +1199,10 @@ find_leader (value_set_t set, tree val)
we have a leader for each part of the expression (if it consists of
values), or the expression is an SSA_NAME.
NB: We never should run into a case where we have SSA_NAME +
NB: We never should run into a case where we have SSA_NAME +
SSA_NAME or SSA_NAME + value. The sets valid_in_set is called on,
the ANTIC sets, will only ever have SSA_NAME's or binary value
expression (IE VALUE1 + VALUE2) */
the ANTIC sets, will only ever have SSA_NAME's or value expressions
(IE VALUE1 + VALUE2, *VALUE1, VALUE1 < VALUE2) */
static bool
valid_in_set (value_set_t set, tree expr)
@ -1107,7 +1222,31 @@ valid_in_set (value_set_t set, tree expr)
tree op1 = TREE_OPERAND (expr, 0);
return set_contains_value (set, op1);
}
case tcc_expression:
{
if (TREE_CODE (expr) == CALL_EXPR)
{
tree op0 = TREE_OPERAND (expr, 0);
tree arglist = TREE_OPERAND (expr, 1);
tree op2 = TREE_OPERAND (expr, 2);
/* Check the non-list operands first. */
if (!set_contains_value (set, op0)
|| (op2 && !set_contains_value (set, op2)))
return false;
/* Now check the operands. */
for (; arglist; arglist = TREE_CHAIN (arglist))
{
if (!set_contains_value (set, TREE_VALUE (arglist)))
return false;
}
return true;
}
return false;
}
case tcc_reference:
/* XXX: Until PRE of loads works, no reference nodes are ANTIC. */
return false;
@ -1189,7 +1328,7 @@ compute_antic_aux (basic_block block, bool block_has_abnormal_pred_edge)
translate through. */
else if (single_succ_p (block))
{
phi_translate_set (ANTIC_OUT, ANTIC_IN(single_succ (block)),
phi_translate_set (ANTIC_OUT, ANTIC_IN (single_succ (block)),
block, single_succ (block));
}
/* If we have multiple successors, we take the intersection of all of
@ -1359,6 +1498,42 @@ create_expression_by_pieces (basic_block block, tree expr, tree stmts)
switch (TREE_CODE_CLASS (TREE_CODE (expr)))
{
case tcc_expression:
{
tree op0, op2;
tree arglist;
tree genop0, genop2;
tree genarglist;
tree walker, genwalker;
gcc_assert (TREE_CODE (expr) == CALL_EXPR);
genop2 = NULL;
op0 = TREE_OPERAND (expr, 0);
arglist = TREE_OPERAND (expr, 1);
op2 = TREE_OPERAND (expr, 2);
genop0 = find_or_generate_expression (block, op0, stmts);
genarglist = copy_list (arglist);
for (walker = arglist, genwalker = genarglist;
genwalker && walker;
genwalker = TREE_CHAIN (genwalker), walker = TREE_CHAIN (walker))
{
TREE_VALUE (genwalker) = find_or_generate_expression (block,
TREE_VALUE (walker),
stmts);
}
if (op2)
genop2 = find_or_generate_expression (block, op2, stmts);
folded = fold (build (TREE_CODE (expr), TREE_TYPE (expr),
genop0, genarglist, genop2));
break;
}
break;
case tcc_binary:
case tcc_comparison:
{
@ -1499,7 +1674,8 @@ insert_into_preds_of_block (basic_block block, value_set_node_t node,
eprime = avail[bprime->index];
if (BINARY_CLASS_P (eprime)
|| COMPARISON_CLASS_P (eprime)
|| UNARY_CLASS_P (eprime))
|| UNARY_CLASS_P (eprime)
|| TREE_CODE (eprime) == CALL_EXPR)
{
builtexpr = create_expression_by_pieces (bprime,
eprime,
@ -1613,7 +1789,8 @@ insert_aux (basic_block block)
{
if (BINARY_CLASS_P (node->expr)
|| COMPARISON_CLASS_P (node->expr)
|| UNARY_CLASS_P (node->expr))
|| UNARY_CLASS_P (node->expr)
|| TREE_CODE (node->expr) == CALL_EXPR)
{
tree *avail;
tree val;
@ -1817,17 +1994,55 @@ create_value_expr_from (tree expr, basic_block block, tree stmt)
gcc_assert (TREE_CODE_CLASS (code) == tcc_unary
|| TREE_CODE_CLASS (code) == tcc_binary
|| TREE_CODE_CLASS (code) == tcc_comparison
|| TREE_CODE_CLASS (code) == tcc_reference);
|| TREE_CODE_CLASS (code) == tcc_reference
|| TREE_CODE_CLASS (code) == tcc_expression
|| TREE_CODE_CLASS (code) == tcc_exceptional);
if (TREE_CODE_CLASS (code) == tcc_unary)
pool = unary_node_pool;
else if (TREE_CODE_CLASS (code) == tcc_reference)
pool = reference_node_pool;
else
else if (TREE_CODE_CLASS (code) == tcc_binary)
pool = binary_node_pool;
else if (TREE_CODE_CLASS (code) == tcc_comparison)
pool = comparison_node_pool;
else if (TREE_CODE_CLASS (code) == tcc_exceptional)
{
gcc_assert (code == TREE_LIST);
pool = list_node_pool;
}
else
{
gcc_assert (code == CALL_EXPR);
pool = expression_node_pool;
}
vexpr = pool_alloc (pool);
memcpy (vexpr, expr, tree_size (expr));
/* This case is only for TREE_LIST's that appear as part of
CALL_EXPR's. Anything else is a bug, but we can't easily verify
this, hence tihs comment. TREE_LIST is not handled by the
general case below is because they don't have a fixed length, or
operands, so you can't access purpose/value/chain through
TREE_OPERAND macros. */
if (code == TREE_LIST)
{
tree temp = NULL_TREE;
if (TREE_CHAIN (vexpr))
temp = create_value_expr_from (TREE_CHAIN (vexpr), block, stmt);
TREE_CHAIN (vexpr) = temp ? temp : TREE_CHAIN (vexpr);
/* This is the equivalent of inserting op into EXP_GEN like we
do below */
if (!is_undefined_value (TREE_VALUE (vexpr)))
value_insert_into_set (EXP_GEN (block), TREE_VALUE (vexpr));
TREE_VALUE (vexpr) = vn_lookup_or_add (TREE_VALUE (vexpr), NULL);
return vexpr;
}
for (i = 0; i < TREE_CODE_LENGTH (code); i++)
{
@ -1846,18 +2061,32 @@ create_value_expr_from (tree expr, basic_block block, tree stmt)
return NULL;
}
/* Recursively value-numberize reference ops */
/* Recursively value-numberize reference ops and tree lists. */
if (REFERENCE_CLASS_P (op))
{
tree tempop = create_value_expr_from (op, block, stmt);
op = tempop ? tempop : op;
val = vn_lookup_or_add (op, stmt);
}
else if (TREE_CODE (op) == TREE_LIST)
{
tree tempop;
gcc_assert (TREE_CODE (expr) == CALL_EXPR);
tempop = create_value_expr_from (op, block, stmt);
op = tempop ? tempop : op;
vn_lookup_or_add (op, NULL);
/* Unlike everywhere else, we do *not* want to replace the
TREE_LIST itself with a value number, because support
functions we call will blow up. */
val = op;
}
else
/* Create a value handle for OP and add it to VEXPR. */
val = vn_lookup_or_add (op, NULL);
if (!is_undefined_value (op))
if (!is_undefined_value (op) && TREE_CODE (op) != TREE_LIST)
value_insert_into_set (EXP_GEN (block), op);
if (TREE_CODE (val) == VALUE_HANDLE)
@ -1870,6 +2099,22 @@ create_value_expr_from (tree expr, basic_block block, tree stmt)
}
/* Return true if we can value number a call. This is true if we have
a pure or constant call. */
static bool
can_value_number_call (tree stmt)
{
tree call = get_call_expr_in (stmt);
/* This is a temporary restriction until we translate vuses through
phi nodes. */
if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS))
return false;
if (call_expr_flags (call) & (ECF_PURE | ECF_CONST))
return true;
return false;
}
/* Compute the AVAIL set for all basic blocks.
This function performs value numbering of the statements in each basic
@ -1964,7 +2209,9 @@ compute_avail (void)
if (UNARY_CLASS_P (rhs)
|| BINARY_CLASS_P (rhs)
|| COMPARISON_CLASS_P (rhs)
|| REFERENCE_CLASS_P (rhs))
|| REFERENCE_CLASS_P (rhs)
|| (TREE_CODE (rhs) == CALL_EXPR
&& can_value_number_call (stmt)))
{
/* For binary, unary, and reference expressions,
create a duplicate expression with the operands
@ -2245,6 +2492,12 @@ init_pre (bool do_fre)
tree_code_size (NEGATE_EXPR), 30);
reference_node_pool = create_alloc_pool ("Reference tree nodes",
tree_code_size (ARRAY_REF), 30);
expression_node_pool = create_alloc_pool ("Expression tree nodes",
tree_code_size (CALL_EXPR), 30);
list_node_pool = create_alloc_pool ("List tree nodes",
tree_code_size (TREE_LIST), 30);
comparison_node_pool = create_alloc_pool ("Comparison tree nodes",
tree_code_size (EQ_EXPR), 30);
FOR_ALL_BB (bb)
{
EXP_GEN (bb) = set_new (true);
@ -2273,6 +2526,9 @@ fini_pre (bool do_fre)
free_alloc_pool (binary_node_pool);
free_alloc_pool (reference_node_pool);
free_alloc_pool (unary_node_pool);
free_alloc_pool (list_node_pool);
free_alloc_pool (expression_node_pool);
free_alloc_pool (comparison_node_pool);
htab_delete (phi_translate_table);
remove_fake_exit_edges ();
@ -2398,7 +2654,8 @@ struct tree_opt_pass pass_pre =
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func | TODO_ggc_collect | TODO_verify_ssa, /* todo_flags_finish */
TODO_update_ssa | TODO_dump_func | TODO_ggc_collect
| TODO_verify_ssa, /* todo_flags_finish */
0 /* letter */
};

View File

@ -119,9 +119,25 @@ expressions_equal_p (tree e1, tree e2)
te1 = TREE_TYPE (e1);
te2 = TREE_TYPE (e2);
if (TREE_CODE (e1) == TREE_CODE (e2)
&& (te1 == te2 || lang_hooks.types_compatible_p (te1, te2))
&& operand_equal_p (e1, e2, OEP_PURE_SAME))
if (TREE_CODE (e1) == TREE_LIST && TREE_CODE (e2) == TREE_LIST)
{
tree lop1 = e1;
tree lop2 = e2;
for (lop1 = e1, lop2 = e2;
lop1 || lop2;
lop1 = TREE_CHAIN (lop1), lop2 = TREE_CHAIN (lop2))
{
if (!lop1 || !lop2)
return false;
if (!expressions_equal_p (TREE_VALUE (lop1), TREE_VALUE (lop2)))
return false;
}
return true;
}
else if (TREE_CODE (e1) == TREE_CODE (e2)
&& (te1 == te2 || lang_hooks.types_compatible_p (te1, te2))
&& operand_equal_p (e1, e2, OEP_PURE_SAME))
return true;
return false;
@ -166,7 +182,7 @@ set_value_handle (tree e, tree v)
{
if (TREE_CODE (e) == SSA_NAME)
SSA_NAME_VALUE (e) = v;
else if (EXPR_P (e) || DECL_P (e))
else if (EXPR_P (e) || DECL_P (e) || TREE_CODE (e) == TREE_LIST)
get_tree_ann (e)->common.value_handle = v;
else
/* Do nothing. Constants are their own value handles. */
@ -271,7 +287,7 @@ get_value_handle (tree expr)
if (TREE_CODE (expr) == SSA_NAME)
return SSA_NAME_VALUE (expr);
else if (EXPR_P (expr) || DECL_P (expr))
else if (EXPR_P (expr) || DECL_P (expr) || TREE_CODE (expr) == TREE_LIST)
{
tree_ann_t ann = tree_ann (expr);
return ((ann) ? ann->common.value_handle : NULL_TREE);