tree-ssa-phiopt.c (minmax_replacement, [...]): New functions.

* tree-ssa-phiopt.c (minmax_replacement, blocks_in_phiopt_order):
	New functions.
	(tree_ssa_phiopt): Use blocks_in_phiopt_order and minmax_replacement.
	Remove unused removed_phis variable.
	(conditional_replacement): Use build1/build2.
	(abs_replacement): Use last_and_only_stmt and build1/build2.

	* gcc.dg/tree-ssa/phi-opt-5.c: New test.

From-SVN: r96439
This commit is contained in:
Zdenek Dvorak 2005-03-14 17:26:11 +01:00 committed by Zdenek Dvorak
parent dcd6de6d69
commit 8eaa0f34a3
4 changed files with 459 additions and 77 deletions

View File

@ -1,3 +1,12 @@
2005-03-14 Zdenek Dvorak <dvorakz@suse.cz>
* tree-ssa-phiopt.c (minmax_replacement, blocks_in_phiopt_order):
New functions.
(tree_ssa_phiopt): Use blocks_in_phiopt_order and minmax_replacement.
Remove unused removed_phis variable.
(conditional_replacement): Use build1/build2.
(abs_replacement): Use last_and_only_stmt and build1/build2.
2005-03-14 Zdenek Dvorak <dvorakz@suse.cz>
* builtin-attrs.def (ATTR_NOVOPS, ATTR_NOVOPS_LIST,

View File

@ -1,3 +1,7 @@
2005-03-14 Zdenek Dvorak <dvorakz@suse.cz>
* gcc.dg/tree-ssa/phi-opt-5.c: New test.
2005-03-14 Zdenek Dvorak <dvorakz@suse.cz>
* gcc.dg/tree-ssa/20050314-1.c: New test.

View File

@ -0,0 +1,58 @@
/* { dg-do compile } */
/* { dg-options "-O1 -ffinite-math-only -fdump-tree-phiopt1" } */
float repl1 (float varx)
{
if (varx < 0.0)
return 0.0;
else if (varx > 1.0)
return 1.0;
else
return varx;
}
/* Should be turned to
varx_4 = MIN_EXPR <1.0e+0, varx_2>;
varx_5 = MAX_EXPR <varx_4, 0.0>; */
/* { dg-final { scan-tree-dump "varx.*MIN_EXPR.*1\\.0" "phiopt1"} } */
/* { dg-final { scan-tree-dump "varx.*MAX_EXPR.*0\\.0" "phiopt1"} } */
float repl2 (float vary)
{
if (vary > 1.0)
return 1.0;
else if (vary < 0.0)
return 0.0;
else
return vary;
}
/* Should be turned to
vary_4 = MAX_EXPR <0.0, vary_2>;
vary_5 = MIN_EXPR <vary_4, 1.0e+0>; */
/* { dg-final { scan-tree-dump "vary.*MAX_EXPR.*0\\.0" "phiopt1"} } */
/* { dg-final { scan-tree-dump "vary.*MIN_EXPR.*1\\.0" "phiopt1"} } */
float repl3 (float varz, float vara, float varb)
{
if (varz > vara)
return vara;
else if (varz < varb)
return varb;
else
return varz;
}
/* Should be turned to
if (varz_2 > vara_3) goto <L4>; else goto <L1>;
<L1>:;
vara_6 = MAX_EXPR <varb_5, varz_2>; */
/* { dg-final { scan-tree-dump "if .*varz" "phiopt1"} } */
/* { dg-final { scan-tree-dump "vara.*MAX_EXPR" "phiopt1"} } */

View File

@ -41,10 +41,13 @@ static bool conditional_replacement (basic_block, basic_block, basic_block,
edge, edge, tree, tree, tree);
static bool value_replacement (basic_block, basic_block, basic_block,
edge, edge, tree, tree, tree);
static bool minmax_replacement (basic_block, basic_block, basic_block,
edge, edge, tree, tree, tree);
static bool abs_replacement (basic_block, basic_block, basic_block,
edge, edge, tree, tree, tree);
static void replace_phi_edge_with_variable (basic_block, basic_block, edge,
tree, tree);
static basic_block *blocks_in_phiopt_order (void);
/* This pass eliminates PHI nodes which can be trivially implemented as
an assignment from a conditional expression. i.e. if we have something
@ -102,6 +105,21 @@ static void replace_phi_edge_with_variable (basic_block, basic_block, edge,
bb2:
x = ABS_EXPR< a >;
Similarly,
bb0:
if (a <= b) goto bb2; else goto bb1;
bb1:
goto bb2;
bb2:
x = PHI (b (bb1), a (bb0));
Becomes
x = MIN_EXPR (a, b)
And the same transformation for MAX_EXPR.
bb1 will become unreachable and bb0 and bb2 will almost always be merged
into a single block. Similar transformations are done by the ifcvt
RTL optimizer. */
@ -110,15 +128,28 @@ static void
tree_ssa_phiopt (void)
{
basic_block bb;
basic_block *bb_order;
unsigned n, i;
/* Search every basic block for COND_EXPR we may be able to optimize
in reverse order so we can find more. */
FOR_EACH_BB_REVERSE (bb)
/* Search every basic block for COND_EXPR we may be able to optimize.
We walk the blocks in order that guarantees that a block with
a single predecessor is processed before the predecessor.
This ensures that we collapse inner ifs before visiting the
outer ones, and also that we do not try to visit a removed
block. */
bb_order = blocks_in_phiopt_order ();
n = n_basic_blocks;
for (i = 0; i < n; i++)
{
tree cond_expr;
tree phi;
basic_block bb1, bb2;
edge e1, e2;
tree arg0, arg1;
bb = bb_order[i];
cond_expr = last_stmt (bb);
/* Check to see if the last statement is a COND_EXPR. */
@ -174,25 +205,80 @@ tree_ssa_phiopt (void)
/* Check to make sure that there is only one PHI node.
TODO: we could do it with more than one iff the other PHI nodes
have the same elements for these two edges. */
if (phi && PHI_CHAIN (phi) == NULL)
{
tree arg0 = NULL, arg1 = NULL;
if (!phi || PHI_CHAIN (phi) != NULL)
continue;
arg0 = PHI_ARG_DEF_TREE (phi, e1->dest_idx);
arg1 = PHI_ARG_DEF_TREE (phi, e2->dest_idx);
arg0 = PHI_ARG_DEF_TREE (phi, e1->dest_idx);
arg1 = PHI_ARG_DEF_TREE (phi, e2->dest_idx);
/* We know something is wrong if we cannot find the edges in the PHI
node. */
gcc_assert (arg0 != NULL && arg1 != NULL);
/* We know something is wrong if we cannot find the edges in the PHI
node. */
gcc_assert (arg0 != NULL && arg1 != NULL);
/* Do the replacement of conditional if it can be done. */
if (conditional_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1)
|| value_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1)
|| abs_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1))
{
}
}
/* Do the replacement of conditional if it can be done. */
if (conditional_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1))
;
else if (value_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1))
;
else if (abs_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1))
;
else
minmax_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1);
}
free (bb_order);
}
/* Returns the list of basic blocks in the function in an order that guarantees
that if a block X has just a single predecessor Y, then Y is after X in the
ordering. */
static basic_block *
blocks_in_phiopt_order (void)
{
basic_block x, y;
basic_block *order = xmalloc (sizeof (basic_block) * n_basic_blocks);
unsigned n = n_basic_blocks, np, i;
sbitmap visited = sbitmap_alloc (last_basic_block + 2);
#define MARK_VISITED(BB) (SET_BIT (visited, (BB)->index + 2))
#define VISITED_P(BB) (TEST_BIT (visited, (BB)->index + 2))
sbitmap_zero (visited);
MARK_VISITED (ENTRY_BLOCK_PTR);
FOR_EACH_BB (x)
{
if (VISITED_P (x))
continue;
/* Walk the predecessors of x as long as they have precisely one
predecessor and add them to the list, so that they get stored
after x. */
for (y = x, np = 1;
single_pred_p (y) && !VISITED_P (single_pred (y));
y = single_pred (y))
np++;
for (y = x, i = n - np;
single_pred_p (y) && !VISITED_P (single_pred (y));
y = single_pred (y), i++)
{
order[i] = y;
MARK_VISITED (y);
}
order[i] = y;
MARK_VISITED (y);
gcc_assert (i == n - 1);
n -= np;
}
sbitmap_free (visited);
gcc_assert (n == 0);
return order;
#undef MARK_VISITED
#undef VISITED_P
}
/* Return TRUE if block BB has no executable statements, otherwise return
@ -324,11 +410,11 @@ conditional_replacement (basic_block cond_bb, basic_block middle_bb,
if (!COMPARISON_CLASS_P (old_result))
return false;
new1 = build (TREE_CODE (old_result), TREE_TYPE (old_result),
TREE_OPERAND (old_result, 0),
TREE_OPERAND (old_result, 1));
new1 = build2 (TREE_CODE (old_result), TREE_TYPE (old_result),
TREE_OPERAND (old_result, 0),
TREE_OPERAND (old_result, 1));
new1 = build (MODIFY_EXPR, TREE_TYPE (old_result), new_var, new1);
new1 = build2 (MODIFY_EXPR, TREE_TYPE (old_result), new_var, new1);
bsi_insert_after (&bsi, new1, BSI_NEW_STMT);
}
@ -357,7 +443,7 @@ conditional_replacement (basic_block cond_bb, basic_block middle_bb,
|| (e1 == true_edge && integer_onep (arg1))
|| (e1 == false_edge && integer_zerop (arg1)))
{
new = build (MODIFY_EXPR, TREE_TYPE (new_var1), new_var1, cond);
new = build2 (MODIFY_EXPR, TREE_TYPE (new_var1), new_var1, cond);
}
else
{
@ -379,7 +465,7 @@ conditional_replacement (basic_block cond_bb, basic_block middle_bb,
{
tree temp = TREE_OPERAND (cond, 0);
tree new_var_1 = make_rename_temp (TREE_TYPE (temp), NULL);
new = build (MODIFY_EXPR, TREE_TYPE (new_var_1), new_var_1, temp);
new = build2 (MODIFY_EXPR, TREE_TYPE (new_var_1), new_var_1, temp);
bsi_insert_after (&bsi, new, BSI_NEW_STMT);
cond = fold_convert (TREE_TYPE (result), new_var_1);
}
@ -391,7 +477,7 @@ conditional_replacement (basic_block cond_bb, basic_block middle_bb,
return false;
}
new = build (MODIFY_EXPR, TREE_TYPE (new_var1), new_var1, cond);
new = build2 (MODIFY_EXPR, TREE_TYPE (new_var1), new_var1, cond);
}
bsi_insert_after (&bsi, new, BSI_NEW_STMT);
@ -482,6 +568,257 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
return false;
}
/* The function minmax_replacement does the main work of doing the minmax
replacement. Return true if the replacement is done. Otherwise return
false.
BB is the basic block where the replacement is going to be done on. ARG0
is argument 0 from the PHI. Likewise for ARG1. */
static bool
minmax_replacement (basic_block cond_bb, basic_block middle_bb,
basic_block phi_bb, edge e0, edge e1, tree phi,
tree arg0, tree arg1)
{
tree result, type;
tree cond, new;
edge true_edge, false_edge;
enum tree_code cmp, minmax, ass_code;
tree smaller, larger, arg_true, arg_false;
block_stmt_iterator bsi, bsi_from;
type = TREE_TYPE (PHI_RESULT (phi));
/* The optimization may be unsafe due to NaNs. */
if (HONOR_NANS (TYPE_MODE (type)))
return false;
cond = COND_EXPR_COND (last_stmt (cond_bb));
cmp = TREE_CODE (cond);
result = PHI_RESULT (phi);
/* This transformation is only valid for order comparisons. Record which
operand is smaller/larger if the result of the comparison is true. */
if (cmp == LT_EXPR || cmp == LE_EXPR)
{
smaller = TREE_OPERAND (cond, 0);
larger = TREE_OPERAND (cond, 1);
}
else if (cmp == GT_EXPR || cmp == GE_EXPR)
{
smaller = TREE_OPERAND (cond, 1);
larger = TREE_OPERAND (cond, 0);
}
else
return false;
/* We need to know which is the true edge and which is the false
edge so that we know if have abs or negative abs. */
extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge);
/* Forward the edges over the middle basic block. */
if (true_edge->dest == middle_bb)
true_edge = EDGE_SUCC (true_edge->dest, 0);
if (false_edge->dest == middle_bb)
false_edge = EDGE_SUCC (false_edge->dest, 0);
if (true_edge == e0)
{
gcc_assert (false_edge == e1);
arg_true = arg0;
arg_false = arg1;
}
else
{
gcc_assert (false_edge == e0);
gcc_assert (true_edge == e1);
arg_true = arg1;
arg_false = arg0;
}
if (empty_block_p (middle_bb))
{
if (operand_equal_for_phi_arg_p (arg_true, smaller)
&& operand_equal_for_phi_arg_p (arg_false, larger))
{
/* Case
if (smaller < larger)
rslt = smaller;
else
rslt = larger; */
minmax = MIN_EXPR;
}
else if (operand_equal_for_phi_arg_p (arg_false, smaller)
&& operand_equal_for_phi_arg_p (arg_true, larger))
minmax = MAX_EXPR;
else
return false;
}
else
{
/* Recognize the following case, assuming d <= u:
if (a <= u)
b = MAX (a, d);
x = PHI <b, u>
This is equivalent to
b = MAX (a, d);
x = MIN (b, u); */
tree assign = last_and_only_stmt (middle_bb);
tree lhs, rhs, op0, op1, bound;
if (!assign
|| TREE_CODE (assign) != MODIFY_EXPR)
return false;
lhs = TREE_OPERAND (assign, 0);
rhs = TREE_OPERAND (assign, 1);
ass_code = TREE_CODE (rhs);
if (ass_code != MAX_EXPR && ass_code != MIN_EXPR)
return false;
op0 = TREE_OPERAND (rhs, 0);
op1 = TREE_OPERAND (rhs, 1);
if (true_edge->src == middle_bb)
{
/* We got here if the condition is true, i.e., SMALLER < LARGER. */
if (!operand_equal_for_phi_arg_p (lhs, arg_true))
return false;
if (operand_equal_for_phi_arg_p (arg_false, larger))
{
/* Case
if (smaller < larger)
{
r' = MAX_EXPR (smaller, bound)
}
r = PHI <r', larger> --> to be turned to MIN_EXPR. */
if (ass_code != MAX_EXPR)
return false;
minmax = MIN_EXPR;
if (operand_equal_for_phi_arg_p (op0, smaller))
bound = op1;
else if (operand_equal_for_phi_arg_p (op1, smaller))
bound = op0;
else
return false;
/* We need BOUND <= LARGER. */
if (!integer_nonzerop (fold (build2 (LE_EXPR, boolean_type_node,
bound, larger))))
return false;
}
else if (operand_equal_for_phi_arg_p (arg_false, smaller))
{
/* Case
if (smaller < larger)
{
r' = MIN_EXPR (larger, bound)
}
r = PHI <r', smaller> --> to be turned to MAX_EXPR. */
if (ass_code != MIN_EXPR)
return false;
minmax = MAX_EXPR;
if (operand_equal_for_phi_arg_p (op0, larger))
bound = op1;
else if (operand_equal_for_phi_arg_p (op1, larger))
bound = op0;
else
return false;
/* We need BOUND >= SMALLER. */
if (!integer_nonzerop (fold (build2 (GE_EXPR, boolean_type_node,
bound, smaller))))
return false;
}
else
return false;
}
else
{
/* We got here if the condition is false, i.e., SMALLER > LARGER. */
if (!operand_equal_for_phi_arg_p (lhs, arg_false))
return false;
if (operand_equal_for_phi_arg_p (arg_true, larger))
{
/* Case
if (smaller > larger)
{
r' = MIN_EXPR (smaller, bound)
}
r = PHI <r', larger> --> to be turned to MAX_EXPR. */
if (ass_code != MIN_EXPR)
return false;
minmax = MAX_EXPR;
if (operand_equal_for_phi_arg_p (op0, smaller))
bound = op1;
else if (operand_equal_for_phi_arg_p (op1, smaller))
bound = op0;
else
return false;
/* We need BOUND >= LARGER. */
if (!integer_nonzerop (fold (build2 (GE_EXPR, boolean_type_node,
bound, larger))))
return false;
}
else if (operand_equal_for_phi_arg_p (arg_true, smaller))
{
/* Case
if (smaller > larger)
{
r' = MAX_EXPR (larger, bound)
}
r = PHI <r', smaller> --> to be turned to MIN_EXPR. */
if (ass_code != MAX_EXPR)
return false;
minmax = MIN_EXPR;
if (operand_equal_for_phi_arg_p (op0, larger))
bound = op1;
else if (operand_equal_for_phi_arg_p (op1, larger))
bound = op0;
else
return false;
/* We need BOUND <= SMALLER. */
if (!integer_nonzerop (fold (build2 (LE_EXPR, boolean_type_node,
bound, smaller))))
return false;
}
else
return false;
}
/* Move the statement from the middle block. */
bsi = bsi_last (cond_bb);
bsi_from = bsi_last (middle_bb);
bsi_move_before (&bsi_from, &bsi);
}
/* Emit the statement to compute min/max. */
result = duplicate_ssa_name (PHI_RESULT (phi), NULL);
new = build2 (MODIFY_EXPR, type, result,
build2 (minmax, type, arg0, arg1));
SSA_NAME_DEF_STMT (result) = new;
bsi = bsi_last (cond_bb);
bsi_insert_before (&bsi, new, BSI_NEW_STMT);
replace_phi_edge_with_variable (cond_bb, phi_bb, e1, phi, result);
return true;
}
/* The function absolute_replacement does the main work of doing the absolute
replacement. Return true if the replacement is done. Otherwise return
false.
@ -497,9 +834,9 @@ abs_replacement (basic_block cond_bb, basic_block middle_bb,
tree new, cond;
block_stmt_iterator bsi;
edge true_edge, false_edge;
tree assign = NULL;
tree assign;
edge e;
tree rhs = NULL, lhs = NULL;
tree rhs, lhs;
bool negate;
enum tree_code cond_code;
@ -510,57 +847,31 @@ abs_replacement (basic_block cond_bb, basic_block middle_bb,
/* OTHER_BLOCK must have only one executable statement which must have the
form arg0 = -arg1 or arg1 = -arg0. */
bsi = bsi_start (middle_bb);
while (!bsi_end_p (bsi))
{
tree stmt = bsi_stmt (bsi);
/* Empty statements and labels are uninteresting. */
if (TREE_CODE (stmt) == LABEL_EXPR
|| IS_EMPTY_STMT (stmt))
{
bsi_next (&bsi);
continue;
}
/* If we found the assignment, but it was not the only executable
statement in OTHER_BLOCK, then we can not optimize. */
if (assign)
return false;
/* If we got here, then we have found the first executable statement
in OTHER_BLOCK. If it is anything other than arg = -arg1 or
arg1 = -arg0, then we can not optimize. */
if (TREE_CODE (stmt) == MODIFY_EXPR)
{
lhs = TREE_OPERAND (stmt, 0);
rhs = TREE_OPERAND (stmt, 1);
if (TREE_CODE (rhs) == NEGATE_EXPR)
{
rhs = TREE_OPERAND (rhs, 0);
/* The assignment has to be arg0 = -arg1 or arg1 = -arg0. */
if ((lhs == arg0 && rhs == arg1)
|| (lhs == arg1 && rhs == arg0))
{
assign = stmt;
bsi_next (&bsi);
}
else
return false;
}
else
return false;
}
else
return false;
}
assign = last_and_only_stmt (middle_bb);
/* If we did not find the proper negation assignment, then we can not
optimize. */
if (assign == NULL)
return false;
/* If we got here, then we have found the only executable statement
in OTHER_BLOCK. If it is anything other than arg = -arg1 or
arg1 = -arg0, then we can not optimize. */
if (TREE_CODE (assign) != MODIFY_EXPR)
return false;
lhs = TREE_OPERAND (assign, 0);
rhs = TREE_OPERAND (assign, 1);
if (TREE_CODE (rhs) != NEGATE_EXPR)
return false;
rhs = TREE_OPERAND (rhs, 0);
/* The assignment has to be arg0 = -arg1 or arg1 = -arg0. */
if (!(lhs == arg0 && rhs == arg1)
&& !(lhs == arg1 && rhs == arg0))
return false;
cond = COND_EXPR_COND (last_stmt (cond_bb));
result = PHI_RESULT (phi);
@ -607,8 +918,8 @@ abs_replacement (basic_block cond_bb, basic_block middle_bb,
lhs = result;
/* Build the modify expression with abs expression. */
new = build (MODIFY_EXPR, TREE_TYPE (lhs),
lhs, build1 (ABS_EXPR, TREE_TYPE (lhs), rhs));
new = build2 (MODIFY_EXPR, TREE_TYPE (lhs),
lhs, build1 (ABS_EXPR, TREE_TYPE (lhs), rhs));
bsi = bsi_last (cond_bb);
bsi_insert_before (&bsi, new, BSI_NEW_STMT);
@ -618,8 +929,8 @@ abs_replacement (basic_block cond_bb, basic_block middle_bb,
/* Get the right BSI. We want to insert after the recently
added ABS_EXPR statement (which we know is the first statement
in the block. */
new = build (MODIFY_EXPR, TREE_TYPE (result),
result, build1 (NEGATE_EXPR, TREE_TYPE (lhs), lhs));
new = build2 (MODIFY_EXPR, TREE_TYPE (result),
result, build1 (NEGATE_EXPR, TREE_TYPE (lhs), lhs));
bsi_insert_after (&bsi, new, BSI_NEW_STMT);
}