#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "errors.h"
#include "tree.h"
#include "c-common.h"
#include "flags.h"
#include "timevar.h"
#include "varray.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-dump.h"
#include "cfgloop.h"
#include "tree-fold-const.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "tree-dg.h"
#include "target.h"
static tree tree_if_convert_stmt (tree, tree, block_stmt_iterator *);
static void add_to_predicate_list (basic_block, tree);
static void clean_predicate_lists (struct loop *loop, basic_block *);
static bool is_appropriate_for_if_conv (struct loop *, bool);
static tree make_ifcvt_temp_variable (tree, tree, block_stmt_iterator *, bool);
static bool bb_with_exit_edge (basic_block);
static void collapse_blocks (struct loop *, basic_block *);
static void make_cond_modify_expr (tree, block_stmt_iterator *);
static tree
make_ifcvt_temp_variable (tree type, tree exp, block_stmt_iterator *bsi, bool before)
{
const char *name = "__ifcvt";
tree var, stmt, new_name;
if (is_gimple_val (exp))
return exp;
var = create_tmp_var (type, name);
add_referenced_tmp_var (var);
stmt = build (MODIFY_EXPR, type, var, exp);
new_name = make_ssa_name (var, stmt);
TREE_OPERAND (stmt, 0) = new_name;
SSA_NAME_DEF_STMT (new_name) = stmt;
if (before)
bsi_insert_before (bsi, stmt, BSI_SAME_STMT);
else
bsi_insert_after (bsi, stmt, BSI_SAME_STMT);
return new_name;
}
static void
add_to_predicate_list (basic_block bb, tree new_cond)
{
tree cond = bb->aux;
if (cond)
{
if (TREE_CODE (new_cond) == TRUTH_NOT_EXPR
&& cond == TREE_OPERAND (new_cond, 0))
cond = boolean_true_node;
else if (TREE_CODE (cond) == TRUTH_NOT_EXPR
&& new_cond == TREE_OPERAND (cond, 0))
cond = boolean_true_node;
else if (new_cond == boolean_true_node)
cond = boolean_true_node;
else if (cond != boolean_true_node)
cond = build (TRUTH_OR_EXPR, boolean_type_node,
unshare_expr (cond), new_cond);
}
else
cond = new_cond;
bb->aux = cond;
}
static void
add_to_dst_predicate_list (tree t, tree cond)
{
basic_block bb;
#ifdef ENABLE_CHECKING
if (TREE_CODE (t) != GOTO_EXPR)
abort ();
#endif
bb = label_to_block (TREE_OPERAND (t, 0));
add_to_predicate_list (bb, cond);
}
static void
make_cond_modify_expr (tree t, block_stmt_iterator *bsi)
{
basic_block bb;
tree cond, op0, op1;
tree new_cond, new_op1, arg0, arg1;
#ifdef ENABLE_CHECKING
if (TREE_CODE (t) != MODIFY_EXPR)
abort ();
#endif
bb = bb_for_stmt (t);
cond = bb->aux;
op0 = TREE_OPERAND (t, 0);
op1 = TREE_OPERAND (t, 1);
if (!cond || cond == boolean_true_node)
return;
new_op1 = make_ifcvt_temp_variable (TREE_TYPE (op1), op1, bsi, true);
if (TREE_CODE (cond) == TRUTH_NOT_EXPR)
{
cond = TREE_OPERAND (cond, 0);
arg0 = build_empty_stmt ();
arg1 = new_op1;
}
else
{
arg0 = new_op1;
arg1 = build_empty_stmt ();
}
new_cond = make_ifcvt_temp_variable (boolean_type_node,
unshare_expr (cond),
bsi, true);
TREE_OPERAND (t, 1) = build (COND_EXPR, TREE_TYPE (op0),
unshare_expr (new_cond), arg0, arg1);
TREE_TYPE (t) = TREE_TYPE (op0);
modify_stmt (t);
}
static void
replace_phi_with_cond_modify_expr (tree phi)
{
tree new_stmt;
basic_block bb;
block_stmt_iterator bsi;
tree rhs, cond;
tree arg_0, arg_1;
tree cond_0, cond_1;
edge e_0, e_1;
#ifdef ENABLE_CHECKING
if (TREE_CODE (phi) != PHI_NODE)
abort ();
#endif
bb = bb_for_stmt (phi);
bsi = bsi_start (bb);
new_stmt = NULL_TREE;
cond = NULL_TREE;
arg_0 = NULL_TREE;
arg_1 = NULL_TREE;
if (PHI_NUM_ARGS (phi) != 2)
abort ();
e_0 = PHI_ARG_EDGE (phi, 0);
e_1 = PHI_ARG_EDGE (phi, 1);
cond_0 = e_0->src->aux;
cond_1 = e_1->src->aux;
if (TREE_CODE (cond_0) == TRUTH_NOT_EXPR)
{
cond = cond_1;
arg_0 = PHI_ARG_DEF (phi, 1);
arg_1 = PHI_ARG_DEF (phi, 0);
}
else if (TREE_CODE (cond_0) != TRUTH_NOT_EXPR)
{
cond = cond_0;
arg_0 = PHI_ARG_DEF (phi, 0);
arg_1 = PHI_ARG_DEF (phi, 1);
}
else
{
abort ();
}
cond = make_ifcvt_temp_variable (TREE_TYPE (cond), unshare_expr (cond), &bsi, false);
if (TREE_CODE (cond) == VAR_DECL)
bitmap_set_bit (vars_to_rename, var_ann (cond)->uid);
rhs = build (COND_EXPR, TREE_TYPE (PHI_RESULT (phi)),
unshare_expr (cond), unshare_expr (arg_0),
unshare_expr (arg_1));
new_stmt = build (MODIFY_EXPR, TREE_TYPE (PHI_RESULT (phi)),
unshare_expr (PHI_RESULT (phi)), rhs);
SSA_NAME_DEF_STMT (PHI_RESULT (phi)) = new_stmt;
set_bb_for_stmt (new_stmt, bb);
bsi_insert_after (&bsi, new_stmt, BSI_SAME_STMT);
modify_stmt (new_stmt);
}
static tree
tree_if_convert_stmt (tree t, tree cond, block_stmt_iterator *bsi)
{
switch (TREE_CODE (t))
{
case LABEL_EXPR:
break;
case MODIFY_EXPR:
make_cond_modify_expr (t, bsi);
break;
case GOTO_EXPR:
add_to_predicate_list (bb_for_stmt (TREE_OPERAND (t, 1)), cond);
bsi_remove (bsi);
cond = NULL_TREE;
break;
case COND_EXPR:
if (bb_with_exit_edge (bb_for_stmt (t)))
{
cond = NULL_TREE;
break;
}
{
tree dst1, dst2, c;
c = TREE_OPERAND (t, 0);
dst1 = TREE_OPERAND (t, 1);
dst2 = TREE_OPERAND (t, 2);
c = make_ifcvt_temp_variable (TREE_TYPE (c), unshare_expr (c), bsi, true);
if (dst1)
{
tree new_cond;
if (cond == boolean_true_node || !cond)
new_cond = unshare_expr (c);
else
new_cond =
make_ifcvt_temp_variable (boolean_type_node,
build (TRUTH_AND_EXPR, boolean_type_node,
unshare_expr (cond), c),
bsi, true);
add_to_dst_predicate_list (dst1, new_cond);
}
if (dst2)
{
tree c2 = build1 (TRUTH_NOT_EXPR,
boolean_type_node,
unshare_expr (c));
tree new_cond;
if (cond == boolean_true_node || !cond)
new_cond = c2;
else
new_cond =
make_ifcvt_temp_variable (boolean_type_node,
build (TRUTH_AND_EXPR, boolean_type_node,
unshare_expr (cond), c2),
bsi, true);
add_to_dst_predicate_list (dst2, new_cond);
}
bsi_remove (bsi);
cond = NULL_TREE;
}
break;
default:
abort ();
break;
}
return cond;
}
static bool
bb_with_exit_edge (basic_block bb)
{
edge e;
bool exit_edge_found = false;
for (e = bb->succ; e && !exit_edge_found ; e = e->succ_next)
if (e->flags & EDGE_LOOP_EXIT)
exit_edge_found = true;
return exit_edge_found;
}
static bool
is_appropriate_for_if_conv (struct loop *loop, bool for_vectorizer)
{
basic_block bb;
basic_block *bbs;
block_stmt_iterator itr;
unsigned int i;
edge e;
if (!loop || loop->inner)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "not inner most loop\n");
return false;
}
if (loop->num_nodes <= 2)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "less thant 2 basic blocks\n");
return false;
}
if (loop->num_exits > 1)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "multiple exits\n");
return false;
}
if (for_vectorizer &&
(!targetm.vect.support_vector_compare_p ()
|| !targetm.vect.support_vector_select_p ()))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "target does not support vector compare/select\n");
return false;
}
for (e = loop->header->succ; e; e = e->succ_next)
if ( e->flags & EDGE_LOOP_EXIT)
return false;
compute_immediate_uses (TDFA_USE_OPS, NULL);
compute_immediate_uses (TDFA_USE_VOPS, NULL);
calculate_dominance_info (CDI_DOMINATORS);
calculate_dominance_info (CDI_POST_DOMINATORS);
bbs = get_loop_body_in_dom_order(loop);
for (i = 0; i < loop->num_nodes; i++)
{
edge e;
bb = bbs[i];
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "----------[%d]-------------\n", bb->index);
for (itr = bsi_start (bb); !bsi_end_p (itr); bsi_next (&itr))
{
tree t = bsi_stmt (itr);
switch (TREE_CODE (t))
{
case LABEL_EXPR:
break;
case MODIFY_EXPR:
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "-------------------------\n");
print_generic_stmt (dump_file, t, TDF_SLIM);
}
if (movement_possibility (t) == MOVE_IMPOSSIBLE)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "stmt is movable. Don't take risk\n");
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
{
int j;
dataflow_t df = get_immediate_uses (t);
int num_uses = num_immediate_uses (df);
for (j = 0; j < num_uses; j++)
{
tree use = immediate_use (df, j);
if (TREE_CODE (use) == PHI_NODE)
{
basic_block use_bb = bb_for_stmt (use);
basic_block t_bb = bb_for_stmt (t);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "t_bb = %d, use_bb = %d (%d)\n", t_bb->index,
use_bb->index,
flow_bb_inside_loop_p (loop, use_bb) ? 1 : 0);
if (dominated_by_p (CDI_DOMINATORS, t_bb, use_bb))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,"t_bb is dominated by use_bb\n");
}
else if (dominated_by_p (CDI_POST_DOMINATORS, t_bb, use_bb)
&& !dominated_by_p (CDI_DOMINATORS, use_bb, t_bb))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file,"t_bb is post dominated by use_bb AND");
fprintf (dump_file,"use_bb is dominated by t_bb\n");
}
}
else if (dominated_by_p (CDI_DOMINATORS, use_bb, t_bb))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file,"use_bb is dominated by t_bb");
fprintf (dump_file,"do not know how to handle this case");
}
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
else if (bb != loop->header && PHI_NUM_ARGS (use) != 2)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "More than two phi node args.\n");
free_dominance_info(CDI_POST_DOMINATORS);
return false;
}
else
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "dominance relationship between t_bb ");
fprintf (dump_file, "and use_bb difficult to handle");
}
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
}
}
}
break;
case GOTO_EXPR:
case COND_EXPR:
break;
default:
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "don't know what to do\n");
print_generic_stmt (dump_file, t, TDF_SLIM);
}
free_dominance_info (CDI_POST_DOMINATORS);
return false;
break;
}
}
{
tree phi;
for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi))
{
tree result = SSA_NAME_VAR (PHI_RESULT (phi));
var_ann_t ann = var_ann (result);
if (bb_with_exit_edge (bb))
{
tree d = PHI_ARG_DEF (phi, 0);
if (TREE_CODE (d) == SSA_NAME)
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "phi node where 1st argument is not constant,");
fprintf (dump_file, "in Exit block. Enough!\n");
}
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
}
if (ann
&& (ann->mem_tag_kind == TYPE_TAG || ann->mem_tag_kind == NAME_TAG))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,"PHIs with memory tags\n");
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
if (bb != loop->header && PHI_NUM_ARGS (phi) != 2)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "More than two phi node args.\n");
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
}
}
for (e = bb->succ; e; e = e->succ_next)
if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH | EDGE_ABNORMAL))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,"Difficult to handle edges\n");
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
}
if (dump_file)
fprintf (dump_file,"Applying if-conversion\n");
free (bbs);
free_dominance_info (CDI_POST_DOMINATORS);
return true;
}
static void
clean_predicate_lists (struct loop *loop, basic_block *bbs)
{
unsigned int i;
for (i = 0; i < loop->num_nodes; i++)
bbs[i]->aux = NULL;
}
static void
collapse_blocks (struct loop *loop, basic_block *bbs)
{
basic_block bb, exit_bb;
unsigned int orig_loop_num_nodes = loop->num_nodes;
unsigned int i;
for (i = 1; i < orig_loop_num_nodes; i++)
{
tree phi;
bb = bbs[i];
if (bb == loop->header || bb == loop->latch)
continue;
phi = phi_nodes (bb);
while (phi)
{
tree next = TREE_CHAIN (phi);
replace_phi_with_cond_modify_expr (phi);
remove_phi_node (phi, NULL_TREE, bb);
phi = next;
}
}
while (loop->header->succ != NULL)
ssa_remove_edge (loop->header->succ);
exit_bb = NULL;
for (i = 1; i < orig_loop_num_nodes; i++)
{
edge e;
block_stmt_iterator bsi;
tree_stmt_iterator last;
bb = bbs[i];
if (bb == loop->latch)
continue;
for (e = bb->succ; e && !exit_bb; e = e->succ_next)
if (e->flags & EDGE_LOOP_EXIT)
exit_bb = bb;
if (bb == exit_bb)
{
edge new_e;
new_e = make_edge (bbs[0], bb, EDGE_FALLTHRU);
set_immediate_dominator (CDI_DOMINATORS, bb, bbs[0]);
if (exit_bb != loop->latch)
{
for (e = bb->succ; e; e = e->succ_next)
if (!(e->flags & EDGE_LOOP_EXIT))
{
redirect_edge_and_branch (e, loop->latch);
set_immediate_dominator (CDI_DOMINATORS, loop->latch, bb);
}
}
continue;
}
while (bb->succ != NULL)
ssa_remove_edge (bb->succ);
while (bb->pred != NULL)
ssa_remove_edge (bb->pred);
for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
{
if (TREE_CODE (bsi_stmt (bsi)) == LABEL_EXPR)
bsi_remove (&bsi);
else
{
set_bb_for_stmt (bsi_stmt (bsi), loop->header);
bsi_next (&bsi);
}
}
last = tsi_last (loop->header->stmt_list);
tsi_link_after (&last, bb->stmt_list, TSI_NEW_STMT);
bb->stmt_list = NULL;
if (dom_computed[CDI_DOMINATORS])
delete_from_dominance_info (CDI_DOMINATORS, bb);
if (dom_computed[CDI_POST_DOMINATORS])
delete_from_dominance_info (CDI_POST_DOMINATORS, bb);
remove_bb_from_loops (bb);
expunge_block (bb);
}
}
bool
tree_if_conversion (struct loop *loop, bool for_vectorizer)
{
basic_block bb;
basic_block *bbs;
block_stmt_iterator itr;
tree cond;
unsigned int i;
flow_loop_scan (loop, LOOP_ALL);
if (!is_appropriate_for_if_conv (loop, for_vectorizer))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,"-------------------------\n");
return false;
}
cond = boolean_true_node;
bbs = get_loop_body_in_bfs_order (loop);
for (i = 0; i < loop->num_nodes; i++)
{
bb = bbs [i];
cond = bb->aux;
for (itr = bsi_start (bb); !bsi_end_p (itr); )
{
tree t = bsi_stmt (itr);
cond = tree_if_convert_stmt (t, cond, &itr);
if (!bsi_end_p (itr))
bsi_next (&itr);
}
if (bb->succ && !bb->succ->succ_next)
{
basic_block bb_n = bb->succ->dest;
if (cond != NULL_TREE)
add_to_predicate_list (bb_n, cond);
cond = NULL_TREE;
}
}
collapse_blocks (loop, bbs);
rewrite_into_ssa (false);
rewrite_into_loop_closed_ssa ();
bitmap_clear (vars_to_rename);
clean_predicate_lists (loop, bbs);
free (bbs);
free_df ();
return true;
}