/* Loop transformation code generation
- Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
Contributed by Daniel Berlin <dberlin@dberlin.org>
This file is part of GCC.
#include "vec.h"
#include "lambda.h"
#include "vecprim.h"
+#include "pointer-set.h"
/* This loop nest code generation is based on non-singular matrix
math.
static lambda_lattice lambda_lattice_compute_base (lambda_loopnest,
struct obstack *);
-static tree find_induction_var_from_exit_cond (struct loop *);
static bool can_convert_to_perfect_nest (struct loop *);
/* Create a new lambda body vector. */
VEC(tree,heap) * outerinductionvars,
VEC(tree,heap) ** lboundvars,
VEC(tree,heap) ** uboundvars,
- VEC(int,heap) ** steps,
+ VEC(int,heap) ** steps,
struct obstack * lambda_obstack)
{
- tree phi;
- tree exit_cond;
+ gimple phi;
+ gimple exit_cond;
tree access_fn, inductionvar;
tree step;
lambda_loop lloop = NULL;
lambda_linear_expression lbound, ubound;
- tree test;
+ tree test_lhs, test_rhs;
int stepint;
int extra = 0;
tree lboundvar, uboundvar, uboundresult;
return NULL;
}
- test = TREE_OPERAND (exit_cond, 0);
-
- if (SSA_NAME_DEF_STMT (inductionvar) == NULL_TREE)
+ if (SSA_NAME_DEF_STMT (inductionvar) == NULL)
{
if (dump_file && (dump_flags & TDF_DETAILS))
}
phi = SSA_NAME_DEF_STMT (inductionvar);
- if (TREE_CODE (phi) != PHI_NODE)
+ if (gimple_code (phi) != GIMPLE_PHI)
{
- phi = SINGLE_SSA_TREE_OPERAND (phi, SSA_OP_USE);
- if (!phi)
+ tree op = SINGLE_SSA_TREE_OPERAND (phi, SSA_OP_USE);
+ if (!op)
{
if (dump_file && (dump_flags & TDF_DETAILS))
return NULL;
}
- phi = SSA_NAME_DEF_STMT (phi);
- if (TREE_CODE (phi) != PHI_NODE)
+ phi = SSA_NAME_DEF_STMT (op);
+ if (gimple_code (phi) != GIMPLE_PHI)
{
-
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
"Unable to convert loop: Cannot find PHI node for induction variable\n");
return NULL;
}
-
}
/* The induction variable name/version we want to put in the array is the
/* Only want phis for induction vars, which will have two
arguments. */
- if (PHI_NUM_ARGS (phi) != 2)
+ if (gimple_phi_num_args (phi) != 2)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
/* Another induction variable check. One argument's source should be
in the loop, one outside the loop. */
- if (flow_bb_inside_loop_p (loop, PHI_ARG_EDGE (phi, 0)->src)
- && flow_bb_inside_loop_p (loop, PHI_ARG_EDGE (phi, 1)->src))
+ if (flow_bb_inside_loop_p (loop, gimple_phi_arg_edge (phi, 0)->src)
+ && flow_bb_inside_loop_p (loop, gimple_phi_arg_edge (phi, 1)->src))
{
if (dump_file && (dump_flags & TDF_DETAILS))
return NULL;
}
- if (flow_bb_inside_loop_p (loop, PHI_ARG_EDGE (phi, 0)->src))
+ if (flow_bb_inside_loop_p (loop, gimple_phi_arg_edge (phi, 0)->src))
{
lboundvar = PHI_ARG_DEF (phi, 1);
lbound = gcc_tree_to_linear_expression (depth, lboundvar,
}
/* One part of the test may be a loop invariant tree. */
VEC_reserve (tree, heap, *invariants, 1);
- if (TREE_CODE (TREE_OPERAND (test, 1)) == SSA_NAME
- && invariant_in_loop_and_outer_loops (loop, TREE_OPERAND (test, 1)))
- VEC_quick_push (tree, *invariants, TREE_OPERAND (test, 1));
- else if (TREE_CODE (TREE_OPERAND (test, 0)) == SSA_NAME
- && invariant_in_loop_and_outer_loops (loop, TREE_OPERAND (test, 0)))
- VEC_quick_push (tree, *invariants, TREE_OPERAND (test, 0));
+ test_lhs = gimple_cond_lhs (exit_cond);
+ test_rhs = gimple_cond_rhs (exit_cond);
+
+ if (TREE_CODE (test_rhs) == SSA_NAME
+ && invariant_in_loop_and_outer_loops (loop, test_rhs))
+ VEC_quick_push (tree, *invariants, test_rhs);
+ else if (TREE_CODE (test_lhs) == SSA_NAME
+ && invariant_in_loop_and_outer_loops (loop, test_lhs))
+ VEC_quick_push (tree, *invariants, test_lhs);
/* The non-induction variable part of the test is the upper bound variable.
*/
- if (TREE_OPERAND (test, 0) == inductionvar)
- uboundvar = TREE_OPERAND (test, 1);
+ if (test_lhs == inductionvar)
+ uboundvar = test_rhs;
else
- uboundvar = TREE_OPERAND (test, 0);
+ uboundvar = test_lhs;
-
/* We only size the vectors assuming we have, at max, 2 times as many
invariants as we do loops (one for each bound).
This is just an arbitrary number, but it has to be matched against the
/* We might have some leftover. */
- if (TREE_CODE (test) == LT_EXPR)
+ if (gimple_cond_code (exit_cond) == LT_EXPR)
extra = -1 * stepint;
- else if (TREE_CODE (test) == NE_EXPR)
+ else if (gimple_cond_code (exit_cond) == NE_EXPR)
extra = -1 * stepint;
- else if (TREE_CODE (test) == GT_EXPR)
+ else if (gimple_cond_code (exit_cond) == GT_EXPR)
extra = -1 * stepint;
- else if (TREE_CODE (test) == EQ_EXPR)
+ else if (gimple_cond_code (exit_cond) == EQ_EXPR)
extra = 1 * stepint;
ubound = gcc_tree_to_linear_expression (depth, uboundvar,
/* Given a LOOP, find the induction variable it is testing against in the exit
condition. Return the induction variable if found, NULL otherwise. */
-static tree
+tree
find_induction_var_from_exit_cond (struct loop *loop)
{
- tree expr = get_loop_exit_condition (loop);
+ gimple expr = get_loop_exit_condition (loop);
tree ivarop;
- tree test;
- if (expr == NULL_TREE)
- return NULL_TREE;
- if (TREE_CODE (expr) != COND_EXPR)
+ tree test_lhs, test_rhs;
+ if (expr == NULL)
return NULL_TREE;
- test = TREE_OPERAND (expr, 0);
- if (!COMPARISON_CLASS_P (test))
+ if (gimple_code (expr) != GIMPLE_COND)
return NULL_TREE;
+ test_lhs = gimple_cond_lhs (expr);
+ test_rhs = gimple_cond_rhs (expr);
/* Find the side that is invariant in this loop. The ivar must be the other
side. */
- if (expr_invariant_in_loop_p (loop, TREE_OPERAND (test, 0)))
- ivarop = TREE_OPERAND (test, 1);
- else if (expr_invariant_in_loop_p (loop, TREE_OPERAND (test, 1)))
- ivarop = TREE_OPERAND (test, 0);
+ if (expr_invariant_in_loop_p (loop, test_lhs))
+ ivarop = test_rhs;
+ else if (expr_invariant_in_loop_p (loop, test_rhs))
+ ivarop = test_lhs;
else
return NULL_TREE;
static tree
lbv_to_gcc_expression (lambda_body_vector lbv,
tree type, VEC(tree,heap) *induction_vars,
- tree *stmts_to_insert)
+ gimple_seq *stmts_to_insert)
{
int k;
tree resvar;
tree type,
VEC(tree,heap) *induction_vars,
VEC(tree,heap) *invariants,
- enum tree_code wrap, tree *stmts_to_insert)
+ enum tree_code wrap, gimple_seq *stmts_to_insert)
{
int k;
tree resvar;
/* Remove the induction variable defined at IV_STMT. */
void
-remove_iv (tree iv_stmt)
+remove_iv (gimple iv_stmt)
{
- if (TREE_CODE (iv_stmt) == PHI_NODE)
+ gimple_stmt_iterator si = gsi_for_stmt (iv_stmt);
+
+ if (gimple_code (iv_stmt) == GIMPLE_PHI)
{
- int i;
+ unsigned i;
- for (i = 0; i < PHI_NUM_ARGS (iv_stmt); i++)
+ for (i = 0; i < gimple_phi_num_args (iv_stmt); i++)
{
- tree stmt;
+ gimple stmt;
imm_use_iterator imm_iter;
- tree arg = PHI_ARG_DEF (iv_stmt, i);
+ tree arg = gimple_phi_arg_def (iv_stmt, i);
bool used = false;
if (TREE_CODE (arg) != SSA_NAME)
remove_iv (SSA_NAME_DEF_STMT (arg));
}
- remove_phi_node (iv_stmt, NULL_TREE, true);
+ remove_phi_node (&si, true);
}
else
{
- block_stmt_iterator bsi = bsi_for_stmt (iv_stmt);
-
- bsi_remove (&bsi, true);
+ gsi_remove (&si, true);
release_defs (iv_stmt);
}
}
-
/* Transform a lambda loopnest NEW_LOOPNEST, which had TRANSFORM applied to
it, back into gcc code. This changes the
loops, their induction variables, and their bodies, so that they
lambda_loopnest_to_gcc_loopnest (struct loop *old_loopnest,
VEC(tree,heap) *old_ivs,
VEC(tree,heap) *invariants,
- VEC(tree,heap) **remove_ivs,
+ VEC(gimple,heap) **remove_ivs,
lambda_loopnest new_loopnest,
lambda_trans_matrix transform,
struct obstack * lambda_obstack)
{
struct loop *temp;
size_t i = 0;
+ unsigned j;
size_t depth = 0;
VEC(tree,heap) *new_ivs = NULL;
tree oldiv;
-
- block_stmt_iterator bsi;
+ gimple_stmt_iterator bsi;
+
+ transform = lambda_trans_matrix_inverse (transform);
if (dump_file)
{
- transform = lambda_trans_matrix_inverse (transform);
fprintf (dump_file, "Inverse of transformation matrix:\n");
print_lambda_trans_matrix (dump_file, transform);
}
lambda_loop newloop;
basic_block bb;
edge exit;
- tree ivvar, ivvarinced, exitcond, stmts;
+ tree ivvar, ivvarinced;
+ gimple exitcond;
+ gimple_seq stmts;
enum tree_code testtype;
tree newupperbound, newlowerbound;
lambda_linear_expression offset;
tree type;
bool insert_after;
- tree inc_stmt;
+ gimple inc_stmt;
oldiv = VEC_index (tree, old_ivs, i);
type = TREE_TYPE (oldiv);
/* Now build the new lower bounds, and insert the statements
necessary to generate it on the loop preheader. */
+ stmts = NULL;
newlowerbound = lle_to_gcc_expression (LL_LOWER_BOUND (newloop),
LL_LINEAR_OFFSET (newloop),
type,
if (stmts)
{
- bsi_insert_on_edge (loop_preheader_edge (temp), stmts);
- bsi_commit_edge_inserts ();
+ gsi_insert_seq_on_edge (loop_preheader_edge (temp), stmts);
+ gsi_commit_edge_inserts ();
}
/* Build the new upper bound and insert its statements in the
basic block of the exit condition */
+ stmts = NULL;
newupperbound = lle_to_gcc_expression (LL_UPPER_BOUND (newloop),
LL_LINEAR_OFFSET (newloop),
type,
invariants, MIN_EXPR, &stmts);
exit = single_exit (temp);
exitcond = get_loop_exit_condition (temp);
- bb = bb_for_stmt (exitcond);
- bsi = bsi_after_labels (bb);
+ bb = gimple_bb (exitcond);
+ bsi = gsi_after_labels (bb);
if (stmts)
- bsi_insert_before (&bsi, stmts, BSI_NEW_STMT);
+ gsi_insert_seq_before (&bsi, stmts, GSI_NEW_STMT);
/* Create the new iv. */
dominate the block containing the exit condition.
So we simply create our own incremented iv to use in the new exit
test, and let redundancy elimination sort it out. */
- inc_stmt = build2 (PLUS_EXPR, type,
- ivvar, build_int_cst (type, LL_STEP (newloop)));
- inc_stmt = build_gimple_modify_stmt (SSA_NAME_VAR (ivvar), inc_stmt);
+ inc_stmt = gimple_build_assign_with_ops (PLUS_EXPR, SSA_NAME_VAR (ivvar),
+ ivvar,
+ build_int_cst (type, LL_STEP (newloop)));
+
ivvarinced = make_ssa_name (SSA_NAME_VAR (ivvar), inc_stmt);
- GIMPLE_STMT_OPERAND (inc_stmt, 0) = ivvarinced;
- bsi = bsi_for_stmt (exitcond);
- bsi_insert_before (&bsi, inc_stmt, BSI_SAME_STMT);
+ gimple_assign_set_lhs (inc_stmt, ivvarinced);
+ bsi = gsi_for_stmt (exitcond);
+ gsi_insert_before (&bsi, inc_stmt, GSI_SAME_STMT);
/* Replace the exit condition with the new upper bound
comparison. */
if (exit->flags & EDGE_FALSE_VALUE)
testtype = swap_tree_comparison (testtype);
- COND_EXPR_COND (exitcond) = build2 (testtype,
- boolean_type_node,
- newupperbound, ivvarinced);
+ gimple_cond_set_condition (exitcond, testtype, newupperbound, ivvarinced);
update_stmt (exitcond);
VEC_replace (tree, new_ivs, i, ivvar);
imm_use_iterator imm_iter;
use_operand_p use_p;
tree oldiv_def;
- tree oldiv_stmt = SSA_NAME_DEF_STMT (oldiv);
- tree stmt;
+ gimple oldiv_stmt = SSA_NAME_DEF_STMT (oldiv);
+ gimple stmt;
- if (TREE_CODE (oldiv_stmt) == PHI_NODE)
+ if (gimple_code (oldiv_stmt) == GIMPLE_PHI)
oldiv_def = PHI_RESULT (oldiv_stmt);
else
oldiv_def = SINGLE_SSA_TREE_OPERAND (oldiv_stmt, SSA_OP_DEF);
FOR_EACH_IMM_USE_STMT (stmt, imm_iter, oldiv_def)
{
- tree newiv, stmts;
+ tree newiv;
+ gimple_seq stmts;
lambda_body_vector lbv, newlbv;
- gcc_assert (TREE_CODE (stmt) != PHI_NODE);
-
/* Compute the new expression for the induction
variable. */
depth = VEC_length (tree, new_ivs);
newlbv = lambda_body_vector_compute_new (transform, lbv,
lambda_obstack);
+ stmts = NULL;
newiv = lbv_to_gcc_expression (newlbv, TREE_TYPE (oldiv),
new_ivs, &stmts);
- if (stmts)
+
+ if (stmts && gimple_code (stmt) != GIMPLE_PHI)
{
- bsi = bsi_for_stmt (stmt);
- bsi_insert_before (&bsi, stmts, BSI_SAME_STMT);
+ bsi = gsi_for_stmt (stmt);
+ gsi_insert_seq_before (&bsi, stmts, GSI_SAME_STMT);
}
FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter)
propagate_value (use_p, newiv);
+
+ if (stmts && gimple_code (stmt) == GIMPLE_PHI)
+ for (j = 0; j < gimple_phi_num_args (stmt); j++)
+ if (gimple_phi_arg_def (stmt, j) == newiv)
+ gsi_insert_seq_on_edge (gimple_phi_arg_edge (stmt, j), stmts);
+
update_stmt (stmt);
}
/* Remove the now unused induction variable. */
- VEC_safe_push (tree, heap, *remove_ivs, oldiv_stmt);
+ VEC_safe_push (gimple, heap, *remove_ivs, oldiv_stmt);
}
VEC_free (tree, heap, new_ivs);
}
determining if we have a perfect loop nest. */
static bool
-not_interesting_stmt (tree stmt)
+not_interesting_stmt (gimple stmt)
{
/* Note that COND_EXPR's aren't interesting because if they were exiting the
loop, we would have already failed the number of exits tests. */
- if (TREE_CODE (stmt) == LABEL_EXPR
- || TREE_CODE (stmt) == GOTO_EXPR
- || TREE_CODE (stmt) == COND_EXPR)
+ if (gimple_code (stmt) == GIMPLE_LABEL
+ || gimple_code (stmt) == GIMPLE_GOTO
+ || gimple_code (stmt) == GIMPLE_COND)
return true;
return false;
}
/* Return TRUE if PHI uses DEF for it's in-the-loop edge for LOOP. */
static bool
-phi_loop_edge_uses_def (struct loop *loop, tree phi, tree def)
+phi_loop_edge_uses_def (struct loop *loop, gimple phi, tree def)
{
- int i;
- for (i = 0; i < PHI_NUM_ARGS (phi); i++)
- if (flow_bb_inside_loop_p (loop, PHI_ARG_EDGE (phi, i)->src))
+ unsigned i;
+ for (i = 0; i < gimple_phi_num_args (phi); i++)
+ if (flow_bb_inside_loop_p (loop, gimple_phi_arg_edge (phi, i)->src))
if (PHI_ARG_DEF (phi, i) == def)
return true;
return false;
/* Return TRUE if STMT is a use of PHI_RESULT. */
static bool
-stmt_uses_phi_result (tree stmt, tree phi_result)
+stmt_uses_phi_result (gimple stmt, tree phi_result)
{
tree use = SINGLE_SSA_TREE_OPERAND (stmt, SSA_OP_USE);
i_3 = PHI (0, i_29); */
static bool
-stmt_is_bumper_for_loop (struct loop *loop, tree stmt)
+stmt_is_bumper_for_loop (struct loop *loop, gimple stmt)
{
- tree use;
+ gimple use;
tree def;
imm_use_iterator iter;
use_operand_p use_p;
FOR_EACH_IMM_USE_FAST (use_p, iter, def)
{
use = USE_STMT (use_p);
- if (TREE_CODE (use) == PHI_NODE)
+ if (gimple_code (use) == GIMPLE_PHI)
{
if (phi_loop_edge_uses_def (loop, use, def))
if (stmt_uses_phi_result (stmt, PHI_RESULT (use)))
{
basic_block *bbs;
size_t i;
- tree exit_cond;
+ gimple exit_cond;
+ /* Loops at depth 0 are perfect nests. */
if (!loop->inner)
return true;
+
bbs = get_loop_body (loop);
exit_cond = get_loop_exit_condition (loop);
+
for (i = 0; i < loop->num_nodes; i++)
{
if (bbs[i]->loop_father == loop)
{
- block_stmt_iterator bsi;
- for (bsi = bsi_start (bbs[i]); !bsi_end_p (bsi); bsi_next (&bsi))
+ gimple_stmt_iterator bsi;
+
+ for (bsi = gsi_start_bb (bbs[i]); !gsi_end_p (bsi); gsi_next (&bsi))
{
- tree stmt = bsi_stmt (bsi);
+ gimple stmt = gsi_stmt (bsi);
+
+ if (gimple_code (stmt) == GIMPLE_COND
+ && exit_cond != stmt)
+ goto non_perfectly_nested;
+
if (stmt == exit_cond
|| not_interesting_stmt (stmt)
|| stmt_is_bumper_for_loop (loop, stmt))
continue;
+
+ non_perfectly_nested:
free (bbs);
return false;
}
}
}
+
free (bbs);
- /* See if the inner loops are perfectly nested as well. */
- if (loop->inner)
- return perfect_nest_p (loop->inner);
- return true;
+
+ return perfect_nest_p (loop->inner);
}
/* Replace the USES of X in STMT, or uses with the same step as X with Y.
of body basic block. */
static void
-replace_uses_equiv_to_x_with_y (struct loop *loop, tree stmt, tree x,
+replace_uses_equiv_to_x_with_y (struct loop *loop, gimple stmt, tree x,
int xstep, tree y, tree yinit,
htab_t replacements,
- block_stmt_iterator *firstbsi)
+ gimple_stmt_iterator *firstbsi)
{
ssa_op_iter iter;
use_operand_p use_p;
{
tree use = USE_FROM_PTR (use_p);
tree step = NULL_TREE;
- tree scev, init, val, var, setstmt;
+ tree scev, init, val, var;
+ gimple setstmt;
struct tree_map *h, in;
void **loc;
which sets Y. */
var = create_tmp_var (TREE_TYPE (use), "perfecttmp");
add_referenced_var (var);
- val = force_gimple_operand_bsi (firstbsi, val, false, NULL,
- true, BSI_SAME_STMT);
- setstmt = build_gimple_modify_stmt (var, val);
+ val = force_gimple_operand_gsi (firstbsi, val, false, NULL,
+ true, GSI_SAME_STMT);
+ setstmt = gimple_build_assign (var, val);
var = make_ssa_name (var, setstmt);
- GIMPLE_STMT_OPERAND (setstmt, 0) = var;
- bsi_insert_before (firstbsi, setstmt, BSI_SAME_STMT);
+ gimple_assign_set_lhs (setstmt, var);
+ gsi_insert_before (firstbsi, setstmt, GSI_SAME_STMT);
update_stmt (setstmt);
SET_USE (use_p, var);
h = GGC_NEW (struct tree_map);
/* Return true if STMT is an exit PHI for LOOP */
static bool
-exit_phi_for_loop_p (struct loop *loop, tree stmt)
+exit_phi_for_loop_p (struct loop *loop, gimple stmt)
{
-
- if (TREE_CODE (stmt) != PHI_NODE
- || PHI_NUM_ARGS (stmt) != 1
- || bb_for_stmt (stmt) != single_exit (loop)->dest)
+ if (gimple_code (stmt) != GIMPLE_PHI
+ || gimple_phi_num_args (stmt) != 1
+ || gimple_bb (stmt) != single_exit (loop)->dest)
return false;
return true;
copying it to the beginning of that loop and changing the uses. */
static bool
-can_put_in_inner_loop (struct loop *inner, tree stmt)
+can_put_in_inner_loop (struct loop *inner, gimple stmt)
{
imm_use_iterator imm_iter;
use_operand_p use_p;
- gcc_assert (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT);
- if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS)
- || !expr_invariant_in_loop_p (inner, GIMPLE_STMT_OPERAND (stmt, 1)))
+ gcc_assert (is_gimple_assign (stmt));
+ if (gimple_vuse (stmt)
+ || !stmt_invariant_in_loop_p (inner, stmt))
return false;
- FOR_EACH_IMM_USE_FAST (use_p, imm_iter, GIMPLE_STMT_OPERAND (stmt, 0))
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, gimple_assign_lhs (stmt))
{
if (!exit_phi_for_loop_p (inner, USE_STMT (use_p)))
{
- basic_block immbb = bb_for_stmt (USE_STMT (use_p));
+ basic_block immbb = gimple_bb (USE_STMT (use_p));
if (!flow_bb_inside_loop_p (inner, immbb))
return false;
}
/* Return true if STMT can be put *after* the inner loop of LOOP. */
+
static bool
-can_put_after_inner_loop (struct loop *loop, tree stmt)
+can_put_after_inner_loop (struct loop *loop, gimple stmt)
{
imm_use_iterator imm_iter;
use_operand_p use_p;
- if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS))
+ if (gimple_vuse (stmt))
return false;
- FOR_EACH_IMM_USE_FAST (use_p, imm_iter, GIMPLE_STMT_OPERAND (stmt, 0))
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, gimple_assign_lhs (stmt))
{
if (!exit_phi_for_loop_p (loop, USE_STMT (use_p)))
{
- basic_block immbb = bb_for_stmt (USE_STMT (use_p));
+ basic_block immbb = gimple_bb (USE_STMT (use_p));
if (!dominated_by_p (CDI_DOMINATORS,
immbb,
return true;
}
+/* Return true when the induction variable IV is simple enough to be
+ re-synthesized. */
+
+static bool
+can_duplicate_iv (tree iv, struct loop *loop)
+{
+ tree scev = instantiate_parameters
+ (loop, analyze_scalar_evolution (loop, iv));
+
+ if (!automatically_generated_chrec_p (scev))
+ {
+ tree step = evolution_part_in_loop_num (scev, loop->num);
+
+ if (step && step != chrec_dont_know && TREE_CODE (step) == INTEGER_CST)
+ return true;
+ }
+
+ return false;
+}
+
+/* If this is a scalar operation that can be put back into the inner
+ loop, or after the inner loop, through copying, then do so. This
+ works on the theory that any amount of scalar code we have to
+ reduplicate into or after the loops is less expensive that the win
+ we get from rearranging the memory walk the loop is doing so that
+ it has better cache behavior. */
+
+static bool
+cannot_convert_modify_to_perfect_nest (gimple stmt, struct loop *loop)
+{
+ use_operand_p use_a, use_b;
+ imm_use_iterator imm_iter;
+ ssa_op_iter op_iter, op_iter1;
+ tree op0 = gimple_assign_lhs (stmt);
+
+ /* The statement should not define a variable used in the inner
+ loop. */
+ if (TREE_CODE (op0) == SSA_NAME
+ && !can_duplicate_iv (op0, loop))
+ FOR_EACH_IMM_USE_FAST (use_a, imm_iter, op0)
+ if (gimple_bb (USE_STMT (use_a))->loop_father == loop->inner)
+ return true;
+
+ FOR_EACH_SSA_USE_OPERAND (use_a, stmt, op_iter, SSA_OP_USE)
+ {
+ gimple node;
+ tree op = USE_FROM_PTR (use_a);
+
+ /* The variables should not be used in both loops. */
+ if (!can_duplicate_iv (op, loop))
+ FOR_EACH_IMM_USE_FAST (use_b, imm_iter, op)
+ if (gimple_bb (USE_STMT (use_b))->loop_father == loop->inner)
+ return true;
+
+ /* The statement should not use the value of a scalar that was
+ modified in the loop. */
+ node = SSA_NAME_DEF_STMT (op);
+ if (gimple_code (node) == GIMPLE_PHI)
+ FOR_EACH_PHI_ARG (use_b, node, op_iter1, SSA_OP_USE)
+ {
+ tree arg = USE_FROM_PTR (use_b);
+
+ if (TREE_CODE (arg) == SSA_NAME)
+ {
+ gimple arg_stmt = SSA_NAME_DEF_STMT (arg);
+
+ if (gimple_bb (arg_stmt)
+ && (gimple_bb (arg_stmt)->loop_father == loop->inner))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+/* Return true when BB contains statements that can harm the transform
+ to a perfect loop nest. */
+
+static bool
+cannot_convert_bb_to_perfect_nest (basic_block bb, struct loop *loop)
+{
+ gimple_stmt_iterator bsi;
+ gimple exit_condition = get_loop_exit_condition (loop);
+
+ for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
+ {
+ gimple stmt = gsi_stmt (bsi);
+
+ if (stmt == exit_condition
+ || not_interesting_stmt (stmt)
+ || stmt_is_bumper_for_loop (loop, stmt))
+ continue;
+
+ if (is_gimple_assign (stmt))
+ {
+ if (cannot_convert_modify_to_perfect_nest (stmt, loop))
+ return true;
+
+ if (can_duplicate_iv (gimple_assign_lhs (stmt), loop))
+ continue;
+
+ if (can_put_in_inner_loop (loop->inner, stmt)
+ || can_put_after_inner_loop (loop, stmt))
+ continue;
+ }
+
+ /* If the bb of a statement we care about isn't dominated by the
+ header of the inner loop, then we can't handle this case
+ right now. This test ensures that the statement comes
+ completely *after* the inner loop. */
+ if (!dominated_by_p (CDI_DOMINATORS,
+ gimple_bb (stmt),
+ loop->inner->header))
+ return true;
+ }
+
+ return false;
+}
/* Return TRUE if LOOP is an imperfect nest that we can convert to a
can_convert_to_perfect_nest (struct loop *loop)
{
basic_block *bbs;
- tree exit_condition, phi;
size_t i;
- block_stmt_iterator bsi;
- basic_block exitdest;
+ gimple_stmt_iterator si;
/* Can't handle triply nested+ loops yet. */
if (!loop->inner || loop->inner->inner)
return false;
bbs = get_loop_body (loop);
- exit_condition = get_loop_exit_condition (loop);
for (i = 0; i < loop->num_nodes; i++)
- {
- if (bbs[i]->loop_father == loop)
- {
- for (bsi = bsi_start (bbs[i]); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- tree stmt = bsi_stmt (bsi);
-
- if (stmt == exit_condition
- || not_interesting_stmt (stmt)
- || stmt_is_bumper_for_loop (loop, stmt))
- continue;
-
- /* If this is a scalar operation that can be put back
- into the inner loop, or after the inner loop, through
- copying, then do so. This works on the theory that
- any amount of scalar code we have to reduplicate
- into or after the loops is less expensive that the
- win we get from rearranging the memory walk
- the loop is doing so that it has better
- cache behavior. */
- if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
- {
- use_operand_p use_a, use_b;
- imm_use_iterator imm_iter;
- ssa_op_iter op_iter, op_iter1;
- tree op0 = GIMPLE_STMT_OPERAND (stmt, 0);
- tree scev = instantiate_parameters
- (loop, analyze_scalar_evolution (loop, op0));
-
- /* If the IV is simple, it can be duplicated. */
- if (!automatically_generated_chrec_p (scev))
- {
- tree step = evolution_part_in_loop_num (scev, loop->num);
- if (step && step != chrec_dont_know
- && TREE_CODE (step) == INTEGER_CST)
- continue;
- }
-
- /* The statement should not define a variable used
- in the inner loop. */
- if (TREE_CODE (op0) == SSA_NAME)
- FOR_EACH_IMM_USE_FAST (use_a, imm_iter, op0)
- if (bb_for_stmt (USE_STMT (use_a))->loop_father
- == loop->inner)
- goto fail;
-
- FOR_EACH_SSA_USE_OPERAND (use_a, stmt, op_iter, SSA_OP_USE)
- {
- tree node, op = USE_FROM_PTR (use_a);
-
- /* The variables should not be used in both loops. */
- FOR_EACH_IMM_USE_FAST (use_b, imm_iter, op)
- if (bb_for_stmt (USE_STMT (use_b))->loop_father
- == loop->inner)
- goto fail;
-
- /* The statement should not use the value of a
- scalar that was modified in the loop. */
- node = SSA_NAME_DEF_STMT (op);
- if (TREE_CODE (node) == PHI_NODE)
- FOR_EACH_PHI_ARG (use_b, node, op_iter1, SSA_OP_USE)
- {
- tree arg = USE_FROM_PTR (use_b);
-
- if (TREE_CODE (arg) == SSA_NAME)
- {
- tree arg_stmt = SSA_NAME_DEF_STMT (arg);
-
- if (bb_for_stmt (arg_stmt)
- && (bb_for_stmt (arg_stmt)->loop_father
- == loop->inner))
- goto fail;
- }
- }
- }
-
- if (can_put_in_inner_loop (loop->inner, stmt)
- || can_put_after_inner_loop (loop, stmt))
- continue;
- }
-
- /* Otherwise, if the bb of a statement we care about isn't
- dominated by the header of the inner loop, then we can't
- handle this case right now. This test ensures that the
- statement comes completely *after* the inner loop. */
- if (!dominated_by_p (CDI_DOMINATORS,
- bb_for_stmt (stmt),
- loop->inner->header))
- goto fail;
- }
- }
- }
+ if (bbs[i]->loop_father == loop
+ && cannot_convert_bb_to_perfect_nest (bbs[i], loop))
+ goto fail;
/* We also need to make sure the loop exit only has simple copy phis in it,
- otherwise we don't know how to transform it into a perfect nest right
- now. */
- exitdest = single_exit (loop)->dest;
-
- for (phi = phi_nodes (exitdest); phi; phi = PHI_CHAIN (phi))
- if (PHI_NUM_ARGS (phi) != 1)
+ otherwise we don't know how to transform it into a perfect nest. */
+ for (si = gsi_start_phis (single_exit (loop)->dest);
+ !gsi_end_p (si);
+ gsi_next (&si))
+ if (gimple_phi_num_args (gsi_stmt (si)) != 1)
goto fail;
free (bbs);
VEC(tree,heap) *loopivs)
{
basic_block *bbs;
- tree exit_condition;
- tree cond_stmt;
+ gimple exit_condition;
+ gimple cond_stmt;
basic_block preheaderbb, headerbb, bodybb, latchbb, olddest;
int i;
- block_stmt_iterator bsi, firstbsi;
+ gimple_stmt_iterator bsi, firstbsi;
bool insert_after;
edge e;
struct loop *newloop;
- tree phi;
+ gimple phi;
tree uboundvar;
- tree stmt;
+ gimple stmt;
tree oldivvar, ivvar, ivvarinced;
VEC(tree,heap) *phis = NULL;
htab_t replacements = NULL;
headerbb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
/* Push the exit phi nodes that we are moving. */
- for (phi = phi_nodes (olddest); phi; phi = PHI_CHAIN (phi))
+ for (bsi = gsi_start_phis (olddest); !gsi_end_p (bsi); gsi_next (&bsi))
{
+ phi = gsi_stmt (bsi);
VEC_reserve (tree, heap, phis, 2);
VEC_quick_push (tree, phis, PHI_RESULT (phi));
VEC_quick_push (tree, phis, PHI_ARG_DEF (phi, 0));
e = redirect_edge_and_branch (single_succ_edge (preheaderbb), headerbb);
/* Remove the exit phis from the old basic block. */
- while (phi_nodes (olddest) != NULL)
- remove_phi_node (phi_nodes (olddest), NULL, false);
+ for (bsi = gsi_start_phis (olddest); !gsi_end_p (bsi); )
+ remove_phi_node (&bsi, false);
/* and add them back to the new basic block. */
while (VEC_length (tree, phis) != 0)
bodybb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
latchbb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
make_edge (headerbb, bodybb, EDGE_FALLTHRU);
- cond_stmt = build3 (COND_EXPR, void_type_node,
- build2 (NE_EXPR, boolean_type_node,
- integer_one_node,
- integer_zero_node),
- NULL_TREE, NULL_TREE);
- bsi = bsi_start (bodybb);
- bsi_insert_after (&bsi, cond_stmt, BSI_NEW_STMT);
+ cond_stmt = gimple_build_cond (NE_EXPR, integer_one_node, integer_zero_node,
+ NULL_TREE, NULL_TREE);
+ bsi = gsi_start_bb (bodybb);
+ gsi_insert_after (&bsi, cond_stmt, GSI_NEW_STMT);
e = make_edge (bodybb, olddest, EDGE_FALSE_VALUE);
make_edge (bodybb, latchbb, EDGE_TRUE_VALUE);
make_edge (latchbb, headerbb, EDGE_FALLTHRU);
it to one just in case. */
exit_condition = get_loop_exit_condition (newloop);
- uboundvar = create_tmp_var (integer_type_node, "uboundvar");
+ uboundvar = create_tmp_var (TREE_TYPE (VEC_index (tree, ubounds, 0)),
+ "uboundvar");
add_referenced_var (uboundvar);
- stmt = build_gimple_modify_stmt (uboundvar, VEC_index (tree, ubounds, 0));
+ stmt = gimple_build_assign (uboundvar, VEC_index (tree, ubounds, 0));
uboundvar = make_ssa_name (uboundvar, stmt);
- GIMPLE_STMT_OPERAND (stmt, 0) = uboundvar;
+ gimple_assign_set_lhs (stmt, uboundvar);
if (insert_after)
- bsi_insert_after (&bsi, stmt, BSI_SAME_STMT);
+ gsi_insert_after (&bsi, stmt, GSI_SAME_STMT);
else
- bsi_insert_before (&bsi, stmt, BSI_SAME_STMT);
+ gsi_insert_before (&bsi, stmt, GSI_SAME_STMT);
update_stmt (stmt);
- COND_EXPR_COND (exit_condition) = build2 (GE_EXPR,
- boolean_type_node,
- uboundvar,
- ivvarinced);
+ gimple_cond_set_condition (exit_condition, GE_EXPR, uboundvar, ivvarinced);
update_stmt (exit_condition);
replacements = htab_create_ggc (20, tree_map_hash,
tree_map_eq, NULL);
/* Now move the statements, and replace the induction variable in the moved
statements with the correct loop induction variable. */
oldivvar = VEC_index (tree, loopivs, 0);
- firstbsi = bsi_start (bodybb);
+ firstbsi = gsi_start_bb (bodybb);
for (i = loop->num_nodes - 1; i >= 0 ; i--)
{
- block_stmt_iterator tobsi = bsi_last (bodybb);
+ gimple_stmt_iterator tobsi = gsi_last_bb (bodybb);
if (bbs[i]->loop_father == loop)
{
/* If this is true, we are *before* the inner loop.
if (dominated_by_p (CDI_DOMINATORS, loop->inner->header, bbs[i]))
{
- block_stmt_iterator header_bsi
- = bsi_after_labels (loop->inner->header);
+ gimple_stmt_iterator header_bsi
+ = gsi_after_labels (loop->inner->header);
- for (bsi = bsi_start (bbs[i]); !bsi_end_p (bsi);)
+ for (bsi = gsi_start_bb (bbs[i]); !gsi_end_p (bsi);)
{
- tree stmt = bsi_stmt (bsi);
+ gimple stmt = gsi_stmt (bsi);
if (stmt == exit_condition
|| not_interesting_stmt (stmt)
|| stmt_is_bumper_for_loop (loop, stmt))
{
- bsi_next (&bsi);
+ gsi_next (&bsi);
continue;
}
- bsi_move_before (&bsi, &header_bsi);
+ gsi_move_before (&bsi, &header_bsi);
}
}
else
/* Note that the bsi only needs to be explicitly incremented
when we don't move something, since it is automatically
incremented when we do. */
- for (bsi = bsi_start (bbs[i]); !bsi_end_p (bsi);)
+ for (bsi = gsi_start_bb (bbs[i]); !gsi_end_p (bsi);)
{
- ssa_op_iter i;
- tree n, stmt = bsi_stmt (bsi);
+ gimple stmt = gsi_stmt (bsi);
if (stmt == exit_condition
|| not_interesting_stmt (stmt)
|| stmt_is_bumper_for_loop (loop, stmt))
{
- bsi_next (&bsi);
+ gsi_next (&bsi);
continue;
}
(loop, stmt, oldivvar, VEC_index (int, steps, 0), ivvar,
VEC_index (tree, lbounds, 0), replacements, &firstbsi);
- bsi_move_before (&bsi, &tobsi);
-
+ gsi_move_before (&bsi, &tobsi);
+
/* If the statement has any virtual operands, they may
need to be rewired because the original loop may
still reference them. */
- FOR_EACH_SSA_TREE_OPERAND (n, stmt, i, SSA_OP_ALL_VIRTUALS)
- mark_sym_for_renaming (SSA_NAME_VAR (n));
+ if (gimple_vuse (stmt))
+ mark_sym_for_renaming (gimple_vop (cfun));
}
}
gcc_assert (LTM_COLSIZE (trans) == nb_loops
&& LTM_ROWSIZE (trans) == nb_loops);
- /* When there is an unknown relation in the dependence_relations, we
- know that it is no worth looking at this loop nest: give up. */
+ /* When there are no dependences, the transformation is correct. */
+ if (VEC_length (ddr_p, dependence_relations) == 0)
+ return true;
+
ddr = VEC_index (ddr_p, dependence_relations, 0);
if (ddr == NULL)
return true;
+
+ /* When there is an unknown relation in the dependence_relations, we
+ know that it is no worth looking at this loop nest: give up. */
if (DDR_ARE_DEPENDENT (ddr) == chrec_dont_know)
return false;
}
return true;
}
+
+
+/* Collects parameters from affine function ACCESS_FUNCTION, and push
+ them in PARAMETERS. */
+
+static void
+lambda_collect_parameters_from_af (tree access_function,
+ struct pointer_set_t *param_set,
+ VEC (tree, heap) **parameters)
+{
+ if (access_function == NULL)
+ return;
+
+ if (TREE_CODE (access_function) == SSA_NAME
+ && pointer_set_contains (param_set, access_function) == 0)
+ {
+ pointer_set_insert (param_set, access_function);
+ VEC_safe_push (tree, heap, *parameters, access_function);
+ }
+ else
+ {
+ int i, num_operands = tree_operand_length (access_function);
+
+ for (i = 0; i < num_operands; i++)
+ lambda_collect_parameters_from_af (TREE_OPERAND (access_function, i),
+ param_set, parameters);
+ }
+}
+
+/* Collects parameters from DATAREFS, and push them in PARAMETERS. */
+
+void
+lambda_collect_parameters (VEC (data_reference_p, heap) *datarefs,
+ VEC (tree, heap) **parameters)
+{
+ unsigned i, j;
+ struct pointer_set_t *parameter_set = pointer_set_create ();
+ data_reference_p data_reference;
+
+ for (i = 0; VEC_iterate (data_reference_p, datarefs, i, data_reference); i++)
+ for (j = 0; j < DR_NUM_DIMENSIONS (data_reference); j++)
+ lambda_collect_parameters_from_af (DR_ACCESS_FN (data_reference, j),
+ parameter_set, parameters);
+ pointer_set_destroy (parameter_set);
+}
+
+/* Translates BASE_EXPR to vector CY. AM is needed for inferring
+ indexing positions in the data access vector. CST is the analyzed
+ integer constant. */
+
+static bool
+av_for_af_base (tree base_expr, lambda_vector cy, struct access_matrix *am,
+ int cst)
+{
+ bool result = true;
+
+ switch (TREE_CODE (base_expr))
+ {
+ case INTEGER_CST:
+ /* Constant part. */
+ cy[AM_CONST_COLUMN_INDEX (am)] += int_cst_value (base_expr) * cst;
+ return true;
+
+ case SSA_NAME:
+ {
+ int param_index =
+ access_matrix_get_index_for_parameter (base_expr, am);
+
+ if (param_index >= 0)
+ {
+ cy[param_index] = cst + cy[param_index];
+ return true;
+ }
+
+ return false;
+ }
+
+ case PLUS_EXPR:
+ return av_for_af_base (TREE_OPERAND (base_expr, 0), cy, am, cst)
+ && av_for_af_base (TREE_OPERAND (base_expr, 1), cy, am, cst);
+
+ case MINUS_EXPR:
+ return av_for_af_base (TREE_OPERAND (base_expr, 0), cy, am, cst)
+ && av_for_af_base (TREE_OPERAND (base_expr, 1), cy, am, -1 * cst);
+
+ case MULT_EXPR:
+ if (TREE_CODE (TREE_OPERAND (base_expr, 0)) == INTEGER_CST)
+ result = av_for_af_base (TREE_OPERAND (base_expr, 1),
+ cy, am, cst *
+ int_cst_value (TREE_OPERAND (base_expr, 0)));
+ else if (TREE_CODE (TREE_OPERAND (base_expr, 1)) == INTEGER_CST)
+ result = av_for_af_base (TREE_OPERAND (base_expr, 0),
+ cy, am, cst *
+ int_cst_value (TREE_OPERAND (base_expr, 1)));
+ else
+ result = false;
+
+ return result;
+
+ case NEGATE_EXPR:
+ return av_for_af_base (TREE_OPERAND (base_expr, 0), cy, am, -1 * cst);
+
+ default:
+ return false;
+ }
+
+ return result;
+}
+
+/* Translates ACCESS_FUN to vector CY. AM is needed for inferring
+ indexing positions in the data access vector. */
+
+static bool
+av_for_af (tree access_fun, lambda_vector cy, struct access_matrix *am)
+{
+ switch (TREE_CODE (access_fun))
+ {
+ case POLYNOMIAL_CHREC:
+ {
+ tree left = CHREC_LEFT (access_fun);
+ tree right = CHREC_RIGHT (access_fun);
+ unsigned var;
+
+ if (TREE_CODE (right) != INTEGER_CST)
+ return false;
+
+ var = am_vector_index_for_loop (am, CHREC_VARIABLE (access_fun));
+ cy[var] = int_cst_value (right);
+
+ if (TREE_CODE (left) == POLYNOMIAL_CHREC)
+ return av_for_af (left, cy, am);
+ else
+ return av_for_af_base (left, cy, am, 1);
+ }
+
+ case INTEGER_CST:
+ /* Constant part. */
+ return av_for_af_base (access_fun, cy, am, 1);
+
+ default:
+ return false;
+ }
+}
+
+/* Initializes the access matrix for DATA_REFERENCE. */
+
+static bool
+build_access_matrix (data_reference_p data_reference,
+ VEC (tree, heap) *parameters, VEC (loop_p, heap) *nest)
+{
+ struct access_matrix *am = GGC_NEW (struct access_matrix);
+ unsigned i, ndim = DR_NUM_DIMENSIONS (data_reference);
+ unsigned nivs = VEC_length (loop_p, nest);
+ unsigned lambda_nb_columns;
+
+ AM_LOOP_NEST (am) = nest;
+ AM_NB_INDUCTION_VARS (am) = nivs;
+ AM_PARAMETERS (am) = parameters;
+
+ lambda_nb_columns = AM_NB_COLUMNS (am);
+ AM_MATRIX (am) = VEC_alloc (lambda_vector, gc, ndim);
+
+ for (i = 0; i < ndim; i++)
+ {
+ lambda_vector access_vector = lambda_vector_new (lambda_nb_columns);
+ tree access_function = DR_ACCESS_FN (data_reference, i);
+
+ if (!av_for_af (access_function, access_vector, am))
+ return false;
+
+ VEC_quick_push (lambda_vector, AM_MATRIX (am), access_vector);
+ }
+
+ DR_ACCESS_MATRIX (data_reference) = am;
+ return true;
+}
+
+/* Returns false when one of the access matrices cannot be built. */
+
+bool
+lambda_compute_access_matrices (VEC (data_reference_p, heap) *datarefs,
+ VEC (tree, heap) *parameters,
+ VEC (loop_p, heap) *nest)
+{
+ data_reference_p dataref;
+ unsigned ix;
+
+ for (ix = 0; VEC_iterate (data_reference_p, datarefs, ix, dataref); ix++)
+ if (!build_access_matrix (dataref, parameters, nest))
+ return false;
+
+ return true;
+}