#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "errors.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "flags.h"
#include "tm_p.h"
#include "basic-block.h"
#include "timevar.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-pass.h"
#include "tree-dump.h"
#include "langhooks.h"
static void tree_ssa_phiopt (void);
static bool conditional_replacement (basic_block, tree, tree, tree);
static bool value_replacement (basic_block, tree, tree, tree);
static bool abs_replacement (basic_block, tree, tree, tree);
static void replace_phi_with_stmt (block_stmt_iterator, basic_block,
basic_block, tree, tree);
static bool candidate_bb_for_phi_optimization (basic_block,
basic_block *,
basic_block *);
static void
tree_ssa_phiopt (void)
{
basic_block bb;
bool removed_phis = false;
FOR_EACH_BB (bb)
{
tree arg0, arg1, phi;
phi = phi_nodes (bb);
if (phi && PHI_CHAIN (phi) == NULL
&& PHI_NUM_ARGS (phi) == 2)
{
arg0 = PHI_ARG_DEF (phi, 0);
arg1 = PHI_ARG_DEF (phi, 1);
if (conditional_replacement (bb, phi, arg0, arg1)
|| value_replacement (bb, phi, arg0, arg1)
|| abs_replacement (bb, phi, arg0, arg1))
{
removed_phis = true;
}
}
}
}
bool
empty_block_p (basic_block bb)
{
block_stmt_iterator bsi;
bsi = bsi_start (bb);
while (!bsi_end_p (bsi)
&& (TREE_CODE (bsi_stmt (bsi)) == LABEL_EXPR
|| IS_EMPTY_STMT (bsi_stmt (bsi))))
bsi_next (&bsi);
if (!bsi_end_p (bsi))
return false;
return true;
}
static bool
candidate_bb_for_phi_optimization (basic_block bb,
basic_block *cond_block_p,
basic_block *other_block_p)
{
tree last0, last1;
basic_block cond_block, other_block;
last0 = last_stmt (EDGE_PRED (bb, 0)->src);
last1 = last_stmt (EDGE_PRED (bb, 1)->src);
if (last0 && TREE_CODE (last0) == COND_EXPR)
{
cond_block = EDGE_PRED (bb, 0)->src;
other_block = EDGE_PRED (bb, 1)->src;
}
else if (last1 && TREE_CODE (last1) == COND_EXPR)
{
other_block = EDGE_PRED (bb, 0)->src;
cond_block = EDGE_PRED (bb, 1)->src;
}
else
return false;
if (EDGE_COUNT (cond_block->succs) != 2
|| (EDGE_SUCC (cond_block, 0)->flags & EDGE_ABNORMAL) != 0
|| (EDGE_SUCC (cond_block, 1)->flags & EDGE_ABNORMAL) != 0)
return false;
if (EDGE_COUNT (other_block->preds) != 1
|| EDGE_PRED (other_block, 0)->src != cond_block
|| EDGE_COUNT (other_block->succs) != 1
|| EDGE_SUCC (other_block, 0)->dest != bb
|| phi_nodes (other_block))
return false;
*cond_block_p = cond_block;
*other_block_p = other_block;
return true;
}
static void
replace_phi_with_stmt (block_stmt_iterator bsi, basic_block bb,
basic_block cond_block, tree phi, tree new)
{
basic_block block_to_remove;
bsi_insert_after (&bsi, new, BSI_NEW_STMT);
SSA_NAME_DEF_STMT (PHI_RESULT (phi)) = new;
release_phi_node (phi);
bb_ann (bb)->phi_nodes = NULL;
if (EDGE_SUCC (cond_block, 0)->dest == bb)
{
EDGE_SUCC (cond_block, 0)->flags |= EDGE_FALLTHRU;
EDGE_SUCC (cond_block, 0)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
block_to_remove = EDGE_SUCC (cond_block, 1)->dest;
}
else
{
EDGE_SUCC (cond_block, 1)->flags |= EDGE_FALLTHRU;
EDGE_SUCC (cond_block, 1)->flags
&= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
block_to_remove = EDGE_SUCC (cond_block, 0)->dest;
}
delete_basic_block (block_to_remove);
bsi = bsi_last (cond_block);
bsi_remove (&bsi);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
"COND_EXPR in block %d and PHI in block %d converted to straightline code.\n",
cond_block->index,
bb->index);
}
static bool
conditional_replacement (basic_block bb, tree phi, tree arg0, tree arg1)
{
tree result;
tree old_result = NULL;
basic_block other_block = NULL;
basic_block cond_block = NULL;
tree new, cond;
block_stmt_iterator bsi;
edge true_edge, false_edge;
tree new_var = NULL;
if ((integer_zerop (arg0) && integer_onep (arg1))
|| (integer_zerop (arg1) && integer_onep (arg0)))
;
else
return false;
if (!candidate_bb_for_phi_optimization (bb, &cond_block, &other_block)
|| !empty_block_p (other_block))
return false;
cond = COND_EXPR_COND (last_stmt (cond_block));
result = PHI_RESULT (phi);
if (TREE_CODE (cond) != SSA_NAME
&& !lang_hooks.types_compatible_p (TREE_TYPE (cond), TREE_TYPE (result)))
{
new_var = make_rename_temp (TREE_TYPE (cond), NULL);
old_result = cond;
cond = new_var;
}
if (!lang_hooks.types_compatible_p (TREE_TYPE (cond), TREE_TYPE (result)))
cond = fold_convert (TREE_TYPE (result), cond);
extract_true_false_edges_from_block (cond_block, &true_edge, &false_edge);
bsi = bsi_after_labels (bb);
if (old_result)
{
tree new1;
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 = build (MODIFY_EXPR, TREE_TYPE (old_result), new_var, new1);
bsi_insert_after (&bsi, new1, BSI_NEW_STMT);
}
if ((PHI_ARG_EDGE (phi, 0) == true_edge && integer_onep (arg0))
|| (PHI_ARG_EDGE (phi, 0) == false_edge && integer_zerop (arg0))
|| (PHI_ARG_EDGE (phi, 1) == true_edge && integer_onep (arg1))
|| (PHI_ARG_EDGE (phi, 1) == false_edge && integer_zerop (arg1)))
{
new = build (MODIFY_EXPR, TREE_TYPE (PHI_RESULT (phi)),
PHI_RESULT (phi), cond);
}
else
{
tree cond1 = invert_truthvalue (cond);
cond = cond1;
if (TREE_CODE (cond) == COND_EXPR)
return false;
if (is_gimple_cast (cond)
&& !is_gimple_val (TREE_OPERAND (cond, 0)))
{
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);
bsi_insert_after (&bsi, new, BSI_NEW_STMT);
cond = fold_convert (TREE_TYPE (result), new_var_1);
}
if (TREE_CODE (cond) == TRUTH_NOT_EXPR
&& !is_gimple_val (TREE_OPERAND (cond, 0)))
return false;
new = build (MODIFY_EXPR, TREE_TYPE (PHI_RESULT (phi)),
PHI_RESULT (phi), cond);
}
replace_phi_with_stmt (bsi, bb, cond_block, phi, new);
return true;
}
static bool
value_replacement (basic_block bb, tree phi, tree arg0, tree arg1)
{
tree result;
basic_block other_block = NULL;
basic_block cond_block = NULL;
tree new, cond;
edge true_edge, false_edge;
if (HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg1))))
return false;
if (!candidate_bb_for_phi_optimization (bb, &cond_block, &other_block)
|| !empty_block_p (other_block))
return false;
cond = COND_EXPR_COND (last_stmt (cond_block));
result = PHI_RESULT (phi);
if (TREE_CODE (cond) != NE_EXPR && TREE_CODE (cond) != EQ_EXPR)
return false;
extract_true_false_edges_from_block (cond_block, &true_edge, &false_edge);
if ((operand_equal_for_phi_arg_p (arg0, TREE_OPERAND (cond, 0))
&& operand_equal_for_phi_arg_p (arg1, TREE_OPERAND (cond, 1)))
|| (operand_equal_for_phi_arg_p (arg1, TREE_OPERAND (cond, 0))
&& operand_equal_for_phi_arg_p (arg0, TREE_OPERAND (cond, 1))))
{
edge e;
tree arg;
e = (TREE_CODE (cond) == NE_EXPR ? true_edge : false_edge);
if (e->dest == other_block)
e = EDGE_SUCC (e->dest, 0);
if (PHI_ARG_EDGE (phi, 0) == e)
arg = arg0;
else
arg = arg1;
new = build (MODIFY_EXPR, TREE_TYPE (result), result, arg);
replace_phi_with_stmt (bsi_after_labels (bb), bb, cond_block, phi, new);
return true;
}
return false;
}
static bool
abs_replacement (basic_block bb, tree phi, tree arg0, tree arg1)
{
tree result;
basic_block other_block = NULL;
basic_block cond_block = NULL;
tree new, cond;
block_stmt_iterator bsi;
edge true_edge, false_edge;
tree assign = NULL;
edge e;
tree rhs = NULL, lhs = NULL;
bool negate;
enum tree_code cond_code;
if (HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg1))))
return false;
if (!candidate_bb_for_phi_optimization (bb, &cond_block, &other_block))
return false;
bsi = bsi_start (other_block);
while (!bsi_end_p (bsi))
{
tree stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) == LABEL_EXPR
|| IS_EMPTY_STMT (stmt))
{
bsi_next (&bsi);
continue;
}
if (assign)
return false;
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);
if ((lhs == arg0 && rhs == arg1)
|| (lhs == arg1 && rhs == arg0))
{
assign = stmt;
bsi_next (&bsi);
}
else
return false;
}
else
return false;
}
else
return false;
}
if (assign == NULL)
return false;
cond = COND_EXPR_COND (last_stmt (cond_block));
result = PHI_RESULT (phi);
cond_code = TREE_CODE (cond);
if (cond_code != GT_EXPR && cond_code != GE_EXPR
&& cond_code != LT_EXPR && cond_code != LE_EXPR)
return false;
if (TREE_OPERAND (cond, 0) != rhs)
return false;
if (FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (cond, 1)))
? real_zerop (TREE_OPERAND (cond, 1))
: integer_zerop (TREE_OPERAND (cond, 1)))
;
else
return false;
extract_true_false_edges_from_block (cond_block, &true_edge, &false_edge);
if (cond_code == GT_EXPR || cond_code == GE_EXPR)
e = true_edge;
else
e = false_edge;
if (e->dest == other_block)
negate = true;
else
negate = false;
if (negate)
lhs = make_rename_temp (TREE_TYPE (result), NULL);
else
lhs = result;
new = build (MODIFY_EXPR, TREE_TYPE (lhs),
lhs, build1 (ABS_EXPR, TREE_TYPE (lhs), rhs));
replace_phi_with_stmt (bsi_after_labels (bb), bb, cond_block, phi, new);
if (negate)
{
bsi = bsi_start (bb);
bsi_next (&bsi);
new = build (MODIFY_EXPR, TREE_TYPE (result),
result, build1 (NEGATE_EXPR, TREE_TYPE (lhs), lhs));
bsi_insert_after (&bsi, new, BSI_NEW_STMT);
SSA_NAME_DEF_STMT (result) = new;
}
return true;
}
static bool
gate_phiopt (void)
{
return 1;
}
struct tree_opt_pass pass_phiopt =
{
"phiopt",
gate_phiopt,
tree_ssa_phiopt,
NULL,
NULL,
0,
TV_TREE_PHIOPT,
PROP_cfg | PROP_ssa | PROP_alias,
0,
0,
0,
TODO_cleanup_cfg | TODO_dump_func | TODO_ggc_collect
| TODO_verify_ssa | TODO_rename_vars
| TODO_verify_flow,
0
};