Commit 2abae5f1 by Sebastian Pop

New Graphite files.

2009-07-30  Sebastian Pop  <sebastian.pop@amd.com>

	* ChangeLog.graphite: New.
	* graphite-blocking.c: New.
	* graphite-clast-to-gimple.c: New.
	* graphite-clast-to-gimple.h: New.
	* graphite-dependences.c: New.
	* graphite-dependences.h: New.
	* graphite-interchange.c: New.
	* graphite-poly.c: New.
	* graphite-poly.h: New.
	* graphite-ppl.c: New.
	* graphite-ppl.h: New.
	* graphite-scop-detection.c: New.
	* graphite-scop-detection.h: New.
	* graphite-sese-to-poly.c: New.
	* graphite-sese-to-poly.h: New.
	* sese.c: New.
	* sese.h: New.

From-SVN: r150300
parent e7c705bb
2009-07-30 Sebastian Pop <sebastian.pop@amd.com>
* ChangeLog.graphite: New.
* graphite-blocking.c: New.
* graphite-clast-to-gimple.c: New.
* graphite-clast-to-gimple.h: New.
* graphite-dependences.c: New.
* graphite-dependences.h: New.
* graphite-interchange.c: New.
* graphite-poly.c: New.
* graphite-poly.h: New.
* graphite-ppl.c: New.
* graphite-ppl.h: New.
* graphite-scop-detection.c: New.
* graphite-scop-detection.h: New.
* graphite-sese-to-poly.c: New.
* graphite-sese-to-poly.h: New.
* sese.c: New.
* sese.h: New.
2009-07-30 Sebastian Pop <sebastian.pop@amd.com>
* tree-chrec.c (evolution_function_right_is_integer_cst): New.
* tree-chrec.h (evolution_function_right_is_integer_cst): Declared.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Heuristics and transform for loop blocking and strip mining on
polyhedral representation.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Pranav Garg <pranav.garg2107@gmail.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "output.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "params.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "sese.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
/* Strip mines with a factor STRIDE the loop around PBB at depth
LOOP_DEPTH. The following example comes from the wiki page:
http://gcc.gnu.org/wiki/Graphite/Strip_mine
The strip mine of a loop with a tile of 64 can be obtained with a
scattering function as follows:
$ cat ./albert_strip_mine.cloog
# language: C
c
# parameter {n | n >= 0}
1 3
# n 1
1 1 0
1
n
1 # Number of statements:
1
# {i | 0 <= i <= n}
2 4
# i n 1
1 1 0 0
1 -1 1 0
0 0 0
1
i
1 # Scattering functions
3 6
# NEW OLD i n 1
1 -64 0 1 0 0
1 64 0 -1 0 63
0 0 1 -1 0 0
1
NEW OLD
#the output of CLooG is like this:
#$ cloog ./albert_strip_mine.cloog
# for (NEW=0;NEW<=floord(n,64);NEW++) {
# for (OLD=max(64*NEW,0);OLD<=min(64*NEW+63,n);OLD++) {
# S1(i = OLD) ;
# }
# }
*/
static bool
pbb_strip_mine_loop_depth (poly_bb_p pbb, int loop_depth, int stride)
{
ppl_dimension_type iter, dim;
ppl_Polyhedron_t res = PBB_TRANSFORMED_SCATTERING (pbb);
ppl_dimension_type strip = psct_scattering_dim_for_loop_depth (pbb,
loop_depth);
psct_add_scattering_dimension (pbb, strip);
iter = psct_iterator_dim (pbb, loop_depth);
ppl_Polyhedron_space_dimension (res, &dim);
/* Lower bound of the striped loop. */
{
ppl_Constraint_t new_cstr;
ppl_Linear_Expression_t expr;
ppl_new_Linear_Expression_with_dimension (&expr, dim);
ppl_set_coef (expr, strip, -1 * stride);
ppl_set_coef (expr, iter, 1);
ppl_new_Constraint (&new_cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_delete_Linear_Expression (expr);
ppl_Polyhedron_add_constraint (res, new_cstr);
ppl_delete_Constraint (new_cstr);
}
/* Upper bound of the striped loop. */
{
ppl_Constraint_t new_cstr;
ppl_Linear_Expression_t expr;
ppl_new_Linear_Expression_with_dimension (&expr, dim);
ppl_set_coef (expr, strip, stride);
ppl_set_coef (expr, iter, -1);
ppl_set_inhomogeneous (expr, stride - 1);
ppl_new_Constraint (&new_cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_delete_Linear_Expression (expr);
ppl_Polyhedron_add_constraint (res, new_cstr);
ppl_delete_Constraint (new_cstr);
}
return true;
}
/* Returns true when strip mining with STRIDE of the loop around PBB
at depth LOOP_DEPTH is profitable. */
static bool
pbb_strip_mine_profitable_p (poly_bb_p pbb,
graphite_dim_t loop_depth,
int stride)
{
Value niter, strip_stride;
bool res;
value_init (strip_stride);
value_init (niter);
value_set_si (strip_stride, stride);
pbb_number_of_iterations (pbb, loop_depth, niter);
res = value_gt (niter, strip_stride);
value_clear (strip_stride);
value_clear (niter);
return res;
}
/* Strip mines all the loops around PBB. Nothing profitable in all this:
this is just a driver function. */
static bool
pbb_do_strip_mine (poly_bb_p pbb)
{
graphite_dim_t loop_depth;
int stride = 64;
bool transform_done = false;
for (loop_depth = 0; loop_depth < pbb_dim_iter_domain (pbb); loop_depth++)
if (pbb_strip_mine_profitable_p (pbb, loop_depth, stride))
transform_done |= pbb_strip_mine_loop_depth (pbb, loop_depth, stride);
return transform_done;
}
/* Strip mines all the loops in SCOP. Nothing profitable in all this:
this is just a driver function. */
bool
scop_do_strip_mine (scop_p scop)
{
poly_bb_p pbb;
int i;
bool transform_done = false;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
transform_done |= pbb_do_strip_mine (pbb);
return transform_done;
}
#endif
/* Translation of CLAST (CLooG AST) to Gimple.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "sese.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
#include "graphite-scop-detection.h"
#include "graphite-clast-to-gimple.h"
#include "graphite-dependences.h"
/* Verifies properties that GRAPHITE should maintain during translation. */
static inline void
graphite_verify (void)
{
#ifdef ENABLE_CHECKING
verify_loop_structure ();
verify_dominators (CDI_DOMINATORS);
verify_dominators (CDI_POST_DOMINATORS);
verify_ssa (false);
verify_loop_closed_ssa ();
#endif
}
/* For a given loop DEPTH in the loop nest of the original black box
PBB, return the old induction variable associated to that loop. */
static inline tree
pbb_to_depth_to_oldiv (poly_bb_p pbb, int depth)
{
gimple_bb_p gbb = PBB_BLACK_BOX (pbb);
sese region = SCOP_REGION (PBB_SCOP (pbb));
loop_p loop = gbb_loop_at_index (gbb, region, depth);
return (tree) loop->aux;
}
/* For a given scattering dimension, return the new induction variable
associated to it. */
static inline tree
newivs_to_depth_to_newiv (VEC (tree, heap) *newivs, int depth)
{
return VEC_index (tree, newivs, depth);
}
/* Returns the tree variable from the name NAME that was given in
Cloog representation. */
static tree
clast_name_to_gcc (const char *name, sese region, VEC (tree, heap) *newivs,
htab_t newivs_index)
{
int index;
VEC (tree, heap) *params = SESE_PARAMS (region);
htab_t params_index = SESE_PARAMS_INDEX (region);
if (params && params_index)
{
index = clast_name_to_index (name, params_index);
if (index >= 0)
return VEC_index (tree, params, index);
}
gcc_assert (newivs && newivs_index);
index = clast_name_to_index (name, newivs_index);
gcc_assert (index >= 0);
return newivs_to_depth_to_newiv (newivs, index);
}
/* Returns the maximal precision type for expressions E1 and E2. */
static inline tree
max_precision_type (tree e1, tree e2)
{
tree type1 = TREE_TYPE (e1);
tree type2 = TREE_TYPE (e2);
return TYPE_PRECISION (type1) > TYPE_PRECISION (type2) ? type1 : type2;
}
static tree
clast_to_gcc_expression (tree, struct clast_expr *, sese, VEC (tree, heap) *,
htab_t);
/* Converts a Cloog reduction expression R with reduction operation OP
to a GCC expression tree of type TYPE. */
static tree
clast_to_gcc_expression_red (tree type, enum tree_code op,
struct clast_reduction *r,
sese region, VEC (tree, heap) *newivs,
htab_t newivs_index)
{
int i;
tree res = clast_to_gcc_expression (type, r->elts[0], region, newivs,
newivs_index);
tree operand_type = (op == POINTER_PLUS_EXPR) ? sizetype : type;
for (i = 1; i < r->n; i++)
{
tree t = clast_to_gcc_expression (operand_type, r->elts[i], region,
newivs, newivs_index);
res = fold_build2 (op, type, res, t);
}
return res;
}
/* Converts a Cloog AST expression E back to a GCC expression tree of
type TYPE. */
static tree
clast_to_gcc_expression (tree type, struct clast_expr *e,
sese region, VEC (tree, heap) *newivs,
htab_t newivs_index)
{
switch (e->type)
{
case expr_term:
{
struct clast_term *t = (struct clast_term *) e;
if (t->var)
{
if (value_one_p (t->val))
{
tree name = clast_name_to_gcc (t->var, region, newivs,
newivs_index);
return fold_convert (type, name);
}
else if (value_mone_p (t->val))
{
tree name = clast_name_to_gcc (t->var, region, newivs,
newivs_index);
name = fold_convert (type, name);
return fold_build1 (NEGATE_EXPR, type, name);
}
else
{
tree name = clast_name_to_gcc (t->var, region, newivs,
newivs_index);
tree cst = gmp_cst_to_tree (type, t->val);
name = fold_convert (type, name);
return fold_build2 (MULT_EXPR, type, cst, name);
}
}
else
return gmp_cst_to_tree (type, t->val);
}
case expr_red:
{
struct clast_reduction *r = (struct clast_reduction *) e;
switch (r->type)
{
case clast_red_sum:
return clast_to_gcc_expression_red
(type, POINTER_TYPE_P (type) ? POINTER_PLUS_EXPR : PLUS_EXPR,
r, region, newivs, newivs_index);
case clast_red_min:
return clast_to_gcc_expression_red (type, MIN_EXPR, r, region,
newivs, newivs_index);
case clast_red_max:
return clast_to_gcc_expression_red (type, MAX_EXPR, r, region,
newivs, newivs_index);
default:
gcc_unreachable ();
}
break;
}
case expr_bin:
{
struct clast_binary *b = (struct clast_binary *) e;
struct clast_expr *lhs = (struct clast_expr *) b->LHS;
tree tl = clast_to_gcc_expression (type, lhs, region, newivs,
newivs_index);
tree tr = gmp_cst_to_tree (type, b->RHS);
switch (b->type)
{
case clast_bin_fdiv:
return fold_build2 (FLOOR_DIV_EXPR, type, tl, tr);
case clast_bin_cdiv:
return fold_build2 (CEIL_DIV_EXPR, type, tl, tr);
case clast_bin_div:
return fold_build2 (EXACT_DIV_EXPR, type, tl, tr);
case clast_bin_mod:
return fold_build2 (TRUNC_MOD_EXPR, type, tl, tr);
default:
gcc_unreachable ();
}
}
default:
gcc_unreachable ();
}
return NULL_TREE;
}
/* Returns the type for the expression E. */
static tree
gcc_type_for_clast_expr (struct clast_expr *e,
sese region, VEC (tree, heap) *newivs,
htab_t newivs_index)
{
switch (e->type)
{
case expr_term:
{
struct clast_term *t = (struct clast_term *) e;
if (t->var)
return TREE_TYPE (clast_name_to_gcc (t->var, region, newivs,
newivs_index));
else
return NULL_TREE;
}
case expr_red:
{
struct clast_reduction *r = (struct clast_reduction *) e;
if (r->n == 1)
return gcc_type_for_clast_expr (r->elts[0], region, newivs,
newivs_index);
else
{
int i;
for (i = 0; i < r->n; i++)
{
tree type = gcc_type_for_clast_expr (r->elts[i], region,
newivs, newivs_index);
if (type)
return type;
}
return NULL_TREE;
}
}
case expr_bin:
{
struct clast_binary *b = (struct clast_binary *) e;
struct clast_expr *lhs = (struct clast_expr *) b->LHS;
return gcc_type_for_clast_expr (lhs, region, newivs,
newivs_index);
}
default:
gcc_unreachable ();
}
return NULL_TREE;
}
/* Returns the type for the equation CLEQ. */
static tree
gcc_type_for_clast_eq (struct clast_equation *cleq,
sese region, VEC (tree, heap) *newivs,
htab_t newivs_index)
{
tree type = gcc_type_for_clast_expr (cleq->LHS, region, newivs,
newivs_index);
if (type)
return type;
return gcc_type_for_clast_expr (cleq->RHS, region, newivs, newivs_index);
}
/* Translates a clast equation CLEQ to a tree. */
static tree
graphite_translate_clast_equation (sese region,
struct clast_equation *cleq,
VEC (tree, heap) *newivs,
htab_t newivs_index)
{
enum tree_code comp;
tree type = gcc_type_for_clast_eq (cleq, region, newivs, newivs_index);
tree lhs = clast_to_gcc_expression (type, cleq->LHS, region, newivs,
newivs_index);
tree rhs = clast_to_gcc_expression (type, cleq->RHS, region, newivs,
newivs_index);
if (cleq->sign == 0)
comp = EQ_EXPR;
else if (cleq->sign > 0)
comp = GE_EXPR;
else
comp = LE_EXPR;
return fold_build2 (comp, boolean_type_node, lhs, rhs);
}
/* Creates the test for the condition in STMT. */
static tree
graphite_create_guard_cond_expr (sese region, struct clast_guard *stmt,
VEC (tree, heap) *newivs,
htab_t newivs_index)
{
tree cond = NULL;
int i;
for (i = 0; i < stmt->n; i++)
{
tree eq = graphite_translate_clast_equation (region, &stmt->eq[i],
newivs, newivs_index);
if (cond)
cond = fold_build2 (TRUTH_AND_EXPR, TREE_TYPE (eq), cond, eq);
else
cond = eq;
}
return cond;
}
/* Creates a new if region corresponding to Cloog's guard. */
static edge
graphite_create_new_guard (sese region, edge entry_edge,
struct clast_guard *stmt,
VEC (tree, heap) *newivs,
htab_t newivs_index)
{
tree cond_expr = graphite_create_guard_cond_expr (region, stmt, newivs,
newivs_index);
edge exit_edge = create_empty_if_region_on_edge (entry_edge, cond_expr);
return exit_edge;
}
/* Walks a CLAST and returns the first statement in the body of a
loop. */
static struct clast_user_stmt *
clast_get_body_of_loop (struct clast_stmt *stmt)
{
if (!stmt
|| CLAST_STMT_IS_A (stmt, stmt_user))
return (struct clast_user_stmt *) stmt;
if (CLAST_STMT_IS_A (stmt, stmt_for))
return clast_get_body_of_loop (((struct clast_for *) stmt)->body);
if (CLAST_STMT_IS_A (stmt, stmt_guard))
return clast_get_body_of_loop (((struct clast_guard *) stmt)->then);
if (CLAST_STMT_IS_A (stmt, stmt_block))
return clast_get_body_of_loop (((struct clast_block *) stmt)->body);
gcc_unreachable ();
}
/* Given a CLOOG_IV, returns the type that it should have in GCC land.
If the information is not available, i.e. in the case one of the
transforms created the loop, just return integer_type_node. */
static tree
gcc_type_for_cloog_iv (const char *cloog_iv, gimple_bb_p gbb)
{
struct ivtype_map_elt_s tmp;
PTR *slot;
tmp.cloog_iv = cloog_iv;
slot = htab_find_slot (GBB_CLOOG_IV_TYPES (gbb), &tmp, NO_INSERT);
if (slot && *slot)
return ((ivtype_map_elt) *slot)->type;
return integer_type_node;
}
/* Returns the induction variable for the loop that gets translated to
STMT. */
static tree
gcc_type_for_iv_of_clast_loop (struct clast_for *stmt_for)
{
struct clast_stmt *stmt = (struct clast_stmt *) stmt_for;
struct clast_user_stmt *body = clast_get_body_of_loop (stmt);
const char *cloog_iv = stmt_for->iterator;
CloogStatement *cs = body->statement;
poly_bb_p pbb = (poly_bb_p) cloog_statement_usr (cs);
return gcc_type_for_cloog_iv (cloog_iv, PBB_BLACK_BOX (pbb));
}
/* Creates a new LOOP corresponding to Cloog's STMT. Inserts an
induction variable for the new LOOP. New LOOP is attached to CFG
starting at ENTRY_EDGE. LOOP is inserted into the loop tree and
becomes the child loop of the OUTER_LOOP. NEWIVS_INDEX binds
CLooG's scattering name to the induction variable created for the
loop of STMT. The new induction variable is inserted in the NEWIVS
vector. */
static struct loop *
graphite_create_new_loop (sese region, edge entry_edge,
struct clast_for *stmt,
loop_p outer, VEC (tree, heap) **newivs,
htab_t newivs_index)
{
tree type = gcc_type_for_iv_of_clast_loop (stmt);
tree lb = clast_to_gcc_expression (type, stmt->LB, region, *newivs,
newivs_index);
tree ub = clast_to_gcc_expression (type, stmt->UB, region, *newivs,
newivs_index);
tree stride = gmp_cst_to_tree (type, stmt->stride);
tree ivvar = create_tmp_var (type, "graphite_IV");
tree iv, iv_after_increment;
loop_p loop = create_empty_loop_on_edge
(entry_edge, lb, stride, ub, ivvar, &iv, &iv_after_increment,
outer ? outer : entry_edge->src->loop_father);
add_referenced_var (ivvar);
save_clast_name_index (newivs_index, stmt->iterator,
VEC_length (tree, *newivs));
VEC_safe_push (tree, heap, *newivs, iv);
return loop;
}
/* Inserts in MAP a tuple (OLD_NAME, NEW_NAME) for the induction
variables of the loops around GBB in SESE. */
static void
build_iv_mapping (htab_t map, sese region,
VEC (tree, heap) *newivs, htab_t newivs_index,
struct clast_user_stmt *user_stmt)
{
struct clast_stmt *t;
int index = 0;
CloogStatement *cs = user_stmt->statement;
poly_bb_p pbb = (poly_bb_p) cloog_statement_usr (cs);
for (t = user_stmt->substitutions; t; t = t->next, index++)
{
struct clast_expr *expr = (struct clast_expr *)
((struct clast_assignment *)t)->RHS;
tree type = gcc_type_for_clast_expr (expr, region, newivs,
newivs_index);
tree old_name = pbb_to_depth_to_oldiv (pbb, index);
tree e = clast_to_gcc_expression (type, expr, region, newivs,
newivs_index);
set_rename (map, old_name, e);
}
}
/* Helper function for htab_traverse. */
static int
copy_renames (void **slot, void *s)
{
struct rename_map_elt_s *entry = (struct rename_map_elt_s *) *slot;
htab_t res = (htab_t) s;
tree old_name = entry->old_name;
tree expr = entry->expr;
struct rename_map_elt_s tmp;
PTR *x;
tmp.old_name = old_name;
x = htab_find_slot (res, &tmp, INSERT);
if (!*x)
*x = new_rename_map_elt (old_name, expr);
return 1;
}
/* Construct bb_pbb_def with BB and PBB. */
static bb_pbb_def *
new_bb_pbb_def (basic_block bb, poly_bb_p pbb)
{
bb_pbb_def *bb_pbb_p;
bb_pbb_p = XNEW (bb_pbb_def);
bb_pbb_p->bb = bb;
bb_pbb_p->pbb = pbb;
return bb_pbb_p;
}
/* Mark BB with it's relevant PBB via hashing table BB_PBB_MAPPING. */
static void
mark_bb_with_pbb (poly_bb_p pbb, basic_block bb, htab_t bb_pbb_mapping)
{
bb_pbb_def tmp;
PTR *x;
tmp.bb = bb;
x = htab_find_slot (bb_pbb_mapping, &tmp, INSERT);
if (!*x)
*x = new_bb_pbb_def (bb, pbb);
}
/* Returns the scattering dimension for STMTFOR.
FIXME: This is a hackish solution to locate the scattering
dimension in newly created loops. Here the hackish solush
assume that the stmt_for->iterator is always something like:
scat_1 , scat_3 etc., where after "scat_" is loop level in
scattering dimension.
*/
static int get_stmtfor_depth (struct clast_for *stmtfor)
{
const char * iterator = stmtfor->iterator;
const char * depth;
depth = strchr (iterator, '_');
if (!strncmp (iterator, "scat_", 5))
return atoi (depth+1);
gcc_unreachable();
}
/* Translates a CLAST statement STMT to GCC representation in the
context of a SESE.
- NEXT_E is the edge where new generated code should be attached.
- CONTEXT_LOOP is the loop in which the generated code will be placed
- RENAME_MAP contains a set of tuples of new names associated to
the original variables names.
- BB_PBB_MAPPING is is a basic_block and it's related poly_bb_p mapping.
*/
static edge
translate_clast (sese region, struct loop *context_loop,
struct clast_stmt *stmt, edge next_e,
htab_t rename_map, VEC (tree, heap) **newivs,
htab_t newivs_index, htab_t bb_pbb_mapping)
{
if (!stmt)
return next_e;
if (CLAST_STMT_IS_A (stmt, stmt_root))
return translate_clast (region, context_loop, stmt->next, next_e,
rename_map, newivs, newivs_index, bb_pbb_mapping);
if (CLAST_STMT_IS_A (stmt, stmt_user))
{
gimple_bb_p gbb;
basic_block new_bb;
CloogStatement *cs = ((struct clast_user_stmt *) stmt)->statement;
poly_bb_p pbb = (poly_bb_p) cloog_statement_usr (cs);
gbb = PBB_BLACK_BOX (pbb);
if (GBB_BB (gbb) == ENTRY_BLOCK_PTR)
return next_e;
build_iv_mapping (rename_map, region, *newivs, newivs_index,
(struct clast_user_stmt *) stmt);
next_e = copy_bb_and_scalar_dependences (GBB_BB (gbb), region,
next_e, rename_map);
new_bb = next_e->src;
mark_bb_with_pbb (pbb, new_bb, bb_pbb_mapping);
recompute_all_dominators ();
update_ssa (TODO_update_ssa);
graphite_verify ();
return translate_clast (region, context_loop, stmt->next, next_e,
rename_map, newivs, newivs_index,
bb_pbb_mapping);
}
if (CLAST_STMT_IS_A (stmt, stmt_for))
{
struct clast_for *stmtfor = (struct clast_for *)stmt;
struct loop *loop
= graphite_create_new_loop (region, next_e, stmtfor,
context_loop, newivs, newivs_index);
edge last_e = single_exit (loop);
edge to_body = single_succ_edge (loop->header);
basic_block after = to_body->dest;
loop->aux = XNEW (int);
/* Pass scattering level information of the new loop by LOOP->AUX. */
*((int *)(loop->aux)) = get_stmtfor_depth (stmtfor);
/* Create a basic block for loop close phi nodes. */
last_e = single_succ_edge (split_edge (last_e));
/* Translate the body of the loop. */
next_e = translate_clast
(region, loop, ((struct clast_for *) stmt)->body,
single_succ_edge (loop->header), rename_map, newivs,
newivs_index, bb_pbb_mapping);
redirect_edge_succ_nodup (next_e, after);
set_immediate_dominator (CDI_DOMINATORS, next_e->dest, next_e->src);
/* Remove from rename_map all the tuples containing variables
defined in loop's body. */
insert_loop_close_phis (rename_map, loop);
recompute_all_dominators ();
graphite_verify ();
return translate_clast (region, context_loop, stmt->next, last_e,
rename_map, newivs, newivs_index,
bb_pbb_mapping);
}
if (CLAST_STMT_IS_A (stmt, stmt_guard))
{
edge last_e = graphite_create_new_guard (region, next_e,
((struct clast_guard *) stmt),
*newivs, newivs_index);
edge true_e = get_true_edge_from_guard_bb (next_e->dest);
edge false_e = get_false_edge_from_guard_bb (next_e->dest);
edge exit_true_e = single_succ_edge (true_e->dest);
edge exit_false_e = single_succ_edge (false_e->dest);
htab_t before_guard = htab_create (10, rename_map_elt_info,
eq_rename_map_elts, free);
htab_traverse (rename_map, copy_renames, before_guard);
next_e = translate_clast (region, context_loop,
((struct clast_guard *) stmt)->then,
true_e, rename_map, newivs, newivs_index,
bb_pbb_mapping);
insert_guard_phis (last_e->src, exit_true_e, exit_false_e,
before_guard, rename_map);
htab_delete (before_guard);
recompute_all_dominators ();
graphite_verify ();
return translate_clast (region, context_loop, stmt->next, last_e,
rename_map, newivs, newivs_index,
bb_pbb_mapping);
}
if (CLAST_STMT_IS_A (stmt, stmt_block))
{
next_e = translate_clast (region, context_loop,
((struct clast_block *) stmt)->body,
next_e, rename_map, newivs, newivs_index,
bb_pbb_mapping);
recompute_all_dominators ();
graphite_verify ();
return translate_clast (region, context_loop, stmt->next, next_e,
rename_map, newivs, newivs_index,
bb_pbb_mapping);
}
gcc_unreachable ();
}
/* Returns the first cloog name used in EXPR. */
static const char *
find_cloog_iv_in_expr (struct clast_expr *expr)
{
struct clast_term *term = (struct clast_term *) expr;
if (expr->type == expr_term
&& !term->var)
return NULL;
if (expr->type == expr_term)
return term->var;
if (expr->type == expr_red)
{
int i;
struct clast_reduction *red = (struct clast_reduction *) expr;
for (i = 0; i < red->n; i++)
{
const char *res = find_cloog_iv_in_expr ((red)->elts[i]);
if (res)
return res;
}
}
return NULL;
}
/* Build for a clast_user_stmt USER_STMT a map between the CLAST
induction variables and the corresponding GCC old induction
variables. This information is stored on each GRAPHITE_BB. */
static void
compute_cloog_iv_types_1 (poly_bb_p pbb, struct clast_user_stmt *user_stmt)
{
gimple_bb_p gbb = PBB_BLACK_BOX (pbb);
struct clast_stmt *t;
int index = 0;
for (t = user_stmt->substitutions; t; t = t->next, index++)
{
PTR *slot;
struct ivtype_map_elt_s tmp;
struct clast_expr *expr = (struct clast_expr *)
((struct clast_assignment *)t)->RHS;
/* Create an entry (clast_var, type). */
tmp.cloog_iv = find_cloog_iv_in_expr (expr);
if (!tmp.cloog_iv)
continue;
slot = htab_find_slot (GBB_CLOOG_IV_TYPES (gbb), &tmp, INSERT);
if (!*slot)
{
tree oldiv = pbb_to_depth_to_oldiv (pbb, index);
tree type = oldiv ? TREE_TYPE (oldiv) : integer_type_node;
*slot = new_ivtype_map_elt (tmp.cloog_iv, type);
}
}
}
/* Walk the CLAST tree starting from STMT and build for each
clast_user_stmt a map between the CLAST induction variables and the
corresponding GCC old induction variables. This information is
stored on each GRAPHITE_BB. */
static void
compute_cloog_iv_types (struct clast_stmt *stmt)
{
if (!stmt)
return;
if (CLAST_STMT_IS_A (stmt, stmt_root))
goto next;
if (CLAST_STMT_IS_A (stmt, stmt_user))
{
CloogStatement *cs = ((struct clast_user_stmt *) stmt)->statement;
poly_bb_p pbb = (poly_bb_p) cloog_statement_usr (cs);
gimple_bb_p gbb = PBB_BLACK_BOX (pbb);
if (!GBB_CLOOG_IV_TYPES (gbb))
GBB_CLOOG_IV_TYPES (gbb) = htab_create (10, ivtype_map_elt_info,
eq_ivtype_map_elts, free);
compute_cloog_iv_types_1 (pbb, (struct clast_user_stmt *) stmt);
goto next;
}
if (CLAST_STMT_IS_A (stmt, stmt_for))
{
struct clast_stmt *s = ((struct clast_for *) stmt)->body;
compute_cloog_iv_types (s);
goto next;
}
if (CLAST_STMT_IS_A (stmt, stmt_guard))
{
struct clast_stmt *s = ((struct clast_guard *) stmt)->then;
compute_cloog_iv_types (s);
goto next;
}
if (CLAST_STMT_IS_A (stmt, stmt_block))
{
struct clast_stmt *s = ((struct clast_block *) stmt)->body;
compute_cloog_iv_types (s);
goto next;
}
gcc_unreachable ();
next:
compute_cloog_iv_types (stmt->next);
}
/* Free the SCATTERING domain list. */
static void
free_scattering (CloogDomainList *scattering)
{
while (scattering)
{
CloogDomain *dom = cloog_domain (scattering);
CloogDomainList *next = cloog_next_domain (scattering);
cloog_domain_free (dom);
free (scattering);
scattering = next;
}
}
/* Initialize Cloog's parameter names from the names used in GIMPLE.
Initialize Cloog's iterator names, using 'graphite_iterator_%d'
from 0 to scop_nb_loops (scop). */
static void
initialize_cloog_names (scop_p scop, CloogProgram *prog)
{
sese region = SCOP_REGION (scop);
int i;
int nb_iterators = scop_max_loop_depth (scop);
int nb_scattering = cloog_program_nb_scattdims (prog);
char **iterators = XNEWVEC (char *, nb_iterators * 2);
char **scattering = XNEWVEC (char *, nb_scattering);
cloog_program_set_names (prog, cloog_names_malloc ());
cloog_names_set_nb_parameters (cloog_program_names (prog),
VEC_length (tree, SESE_PARAMS (region)));
cloog_names_set_parameters (cloog_program_names (prog),
SESE_PARAMS_NAMES (region));
for (i = 0; i < nb_iterators; i++)
{
int len = 4 + 16;
iterators[i] = XNEWVEC (char, len);
snprintf (iterators[i], len, "git_%d", i);
}
cloog_names_set_nb_iterators (cloog_program_names (prog),
nb_iterators);
cloog_names_set_iterators (cloog_program_names (prog),
iterators);
for (i = 0; i < nb_scattering; i++)
{
int len = 5 + 16;
scattering[i] = XNEWVEC (char, len);
snprintf (scattering[i], len, "scat_%d", i);
}
cloog_names_set_nb_scattering (cloog_program_names (prog),
nb_scattering);
cloog_names_set_scattering (cloog_program_names (prog),
scattering);
}
/* Build cloog program for SCoP. */
static void
build_cloog_prog (scop_p scop, CloogProgram *prog)
{
int i;
int max_nb_loops = scop_max_loop_depth (scop);
poly_bb_p pbb;
CloogLoop *loop_list = NULL;
CloogBlockList *block_list = NULL;
CloogDomainList *scattering = NULL;
int nbs = 2 * max_nb_loops + 1;
int *scaldims;
cloog_program_set_context
(prog, new_Cloog_Domain_from_ppl_Pointset_Powerset (SCOP_CONTEXT (scop)));
nbs = unify_scattering_dimensions (scop);
scaldims = (int *) xmalloc (nbs * (sizeof (int)));
cloog_program_set_nb_scattdims (prog, nbs);
initialize_cloog_names (scop, prog);
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
{
CloogStatement *stmt;
CloogBlock *block;
/* Dead code elimination: when the domain of a PBB is empty,
don't generate code for the PBB. */
if (ppl_Pointset_Powerset_C_Polyhedron_is_empty (PBB_DOMAIN (pbb)))
continue;
/* Build the new statement and its block. */
stmt = cloog_statement_alloc (GBB_BB (PBB_BLACK_BOX (pbb))->index);
block = cloog_block_alloc (stmt, 0, NULL, pbb_dim_iter_domain (pbb));
cloog_statement_set_usr (stmt, pbb);
/* Build loop list. */
{
CloogLoop *new_loop_list = cloog_loop_malloc ();
cloog_loop_set_next (new_loop_list, loop_list);
cloog_loop_set_domain
(new_loop_list,
new_Cloog_Domain_from_ppl_Pointset_Powerset (PBB_DOMAIN (pbb)));
cloog_loop_set_block (new_loop_list, block);
loop_list = new_loop_list;
}
/* Build block list. */
{
CloogBlockList *new_block_list = cloog_block_list_malloc ();
cloog_block_list_set_next (new_block_list, block_list);
cloog_block_list_set_block (new_block_list, block);
block_list = new_block_list;
}
/* Build scattering list. */
{
/* XXX: Replace with cloog_domain_list_alloc(), when available. */
CloogDomainList *new_scattering
= (CloogDomainList *) xmalloc (sizeof (CloogDomainList));
ppl_Polyhedron_t scat;
CloogDomain *dom;
scat = PBB_TRANSFORMED_SCATTERING (pbb);
dom = new_Cloog_Domain_from_ppl_Polyhedron (scat);
cloog_set_next_domain (new_scattering, scattering);
cloog_set_domain (new_scattering, dom);
scattering = new_scattering;
}
}
cloog_program_set_loop (prog, loop_list);
cloog_program_set_blocklist (prog, block_list);
for (i = 0; i < nbs; i++)
scaldims[i] = 0 ;
cloog_program_set_scaldims (prog, scaldims);
/* Extract scalar dimensions to simplify the code generation problem. */
cloog_program_extract_scalars (prog, scattering);
/* Apply scattering. */
cloog_program_scatter (prog, scattering);
free_scattering (scattering);
/* Iterators corresponding to scalar dimensions have to be extracted. */
cloog_names_scalarize (cloog_program_names (prog), nbs,
cloog_program_scaldims (prog));
/* Free blocklist. */
{
CloogBlockList *next = cloog_program_blocklist (prog);
while (next)
{
CloogBlockList *toDelete = next;
next = cloog_block_list_next (next);
cloog_block_list_set_next (toDelete, NULL);
cloog_block_list_set_block (toDelete, NULL);
cloog_block_list_free (toDelete);
}
cloog_program_set_blocklist (prog, NULL);
}
}
/* Return the options that will be used in GLOOG. */
static CloogOptions *
set_cloog_options (void)
{
CloogOptions *options = cloog_options_malloc ();
/* Change cloog output language to C. If we do use FORTRAN instead, cloog
will stop e.g. with "ERROR: unbounded loops not allowed in FORTRAN.", if
we pass an incomplete program to cloog. */
options->language = LANGUAGE_C;
/* Enable complex equality spreading: removes dummy statements
(assignments) in the generated code which repeats the
substitution equations for statements. This is useless for
GLooG. */
options->esp = 1;
/* Enable C pretty-printing mode: normalizes the substitution
equations for statements. */
options->cpp = 1;
/* Allow cloog to build strides with a stride width different to one.
This example has stride = 4:
for (i = 0; i < 20; i += 4)
A */
options->strides = 1;
/* Disable optimizations and make cloog generate source code closer to the
input. This is useful for debugging, but later we want the optimized
code.
XXX: We can not disable optimizations, as loop blocking is not working
without them. */
if (0)
{
options->f = -1;
options->l = INT_MAX;
}
return options;
}
/* Prints STMT to STDERR. */
void
print_clast_stmt (FILE *file, struct clast_stmt *stmt)
{
CloogOptions *options = set_cloog_options ();
pprint (file, stmt, 0, options);
cloog_options_free (options);
}
/* Prints STMT to STDERR. */
void
debug_clast_stmt (struct clast_stmt *stmt)
{
print_clast_stmt (stderr, stmt);
}
/* Translate SCOP to a CLooG program and clast. These two
representations should be freed together: a clast cannot be used
without a program. */
cloog_prog_clast
scop_to_clast (scop_p scop)
{
CloogOptions *options = set_cloog_options ();
cloog_prog_clast pc;
/* Connect new cloog prog generation to graphite. */
pc.prog = cloog_program_malloc ();
build_cloog_prog (scop, pc.prog);
pc.prog = cloog_program_generate (pc.prog, options);
pc.stmt = cloog_clast_create (pc.prog, options);
cloog_options_free (options);
return pc;
}
/* Prints to FILE the code generated by CLooG for SCOP. */
void
print_generated_program (FILE *file, scop_p scop)
{
CloogOptions *options = set_cloog_options ();
cloog_prog_clast pc = scop_to_clast (scop);
fprintf (file, " (prog: \n");
cloog_program_print (file, pc.prog);
fprintf (file, " )\n");
fprintf (file, " (clast: \n");
pprint (file, pc.stmt, 0, options);
fprintf (file, " )\n");
cloog_options_free (options);
cloog_clast_free (pc.stmt);
cloog_program_free (pc.prog);
}
/* Prints to STDERR the code generated by CLooG for SCOP. */
void
debug_generated_program (scop_p scop)
{
print_generated_program (stderr, scop);
}
/* A LOOP is in normal form for Graphite when it contains only one
scalar phi node that defines the main induction variable of the
loop, only one increment of the IV, and only one exit condition. */
static void
graphite_loop_normal_form (loop_p loop)
{
struct tree_niter_desc niter;
tree nit;
gimple_seq stmts;
edge exit = single_dom_exit (loop);
bool known_niter = number_of_iterations_exit (loop, exit, &niter, false);
/* At this point we should know the number of iterations, */
gcc_assert (known_niter);
nit = force_gimple_operand (unshare_expr (niter.niter), &stmts, true,
NULL_TREE);
if (stmts)
gsi_insert_seq_on_edge_immediate (loop_preheader_edge (loop), stmts);
loop->aux = canonicalize_loop_ivs (loop, &nit);
}
/* Converts REGION to loop normal form: one induction variable per loop. */
static void
build_graphite_loop_normal_form (sese region)
{
int i;
loop_p loop;
for (i = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), i, loop); i++)
graphite_loop_normal_form (loop);
}
/* GIMPLE Loop Generator: generates loops from STMT in GIMPLE form for
the given SCOP. Return true if code generation succeeded.
BB_PBB_MAPPING is a basic_block and it's related poly_bb_p mapping.
*/
bool
gloog (scop_p scop, htab_t bb_pbb_mapping)
{
edge new_scop_exit_edge = NULL;
VEC (tree, heap) *newivs = VEC_alloc (tree, heap, 10);
loop_p context_loop;
sese region = SCOP_REGION (scop);
ifsese if_region = NULL;
htab_t rename_map, newivs_index;
cloog_prog_clast pc = scop_to_clast (scop);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "\nCLAST generated by CLooG: \n");
print_clast_stmt (dump_file, pc.stmt);
fprintf (dump_file, "\n");
}
build_graphite_loop_normal_form (region);
recompute_all_dominators ();
graphite_verify ();
if_region = move_sese_in_condition (region);
sese_insert_phis_for_liveouts (region,
if_region->region->exit->src,
if_region->false_region->exit,
if_region->true_region->exit);
recompute_all_dominators ();
graphite_verify ();
context_loop = SESE_ENTRY (region)->src->loop_father;
compute_cloog_iv_types (pc.stmt);
rename_map = htab_create (10, rename_map_elt_info, eq_rename_map_elts, free);
newivs_index = htab_create (10, clast_name_index_elt_info,
eq_clast_name_indexes, free);
new_scop_exit_edge = translate_clast (region, context_loop, pc.stmt,
if_region->true_region->entry,
rename_map, &newivs, newivs_index,
bb_pbb_mapping);
sese_reset_aux_in_loops (region);
graphite_verify ();
sese_adjust_liveout_phis (region, rename_map,
if_region->region->exit->src,
if_region->false_region->exit,
if_region->true_region->exit);
recompute_all_dominators ();
graphite_verify ();
htab_delete (rename_map);
htab_delete (newivs_index);
VEC_free (tree, heap, newivs);
cloog_clast_free (pc.stmt);
cloog_program_free (pc.prog);
return true;
}
/* Find BB's related poly_bb_p in hash table BB_PBB_MAPPING. */
static poly_bb_p
find_pbb_via_hash (htab_t bb_pbb_mapping, basic_block bb)
{
bb_pbb_def tmp;
PTR *slot;
tmp.bb = bb;
slot = htab_find_slot (bb_pbb_mapping, &tmp, NO_INSERT);
if (slot && *slot)
return ((bb_pbb_def *) *slot)->pbb;
return NULL;
}
/* Free loop->aux in newly created loops by translate_clast. */
void
free_aux_in_new_loops (void)
{
loop_p loop;
loop_iterator li;
FOR_EACH_LOOP (li, loop, 0)
{
if (!loop->aux)
continue;
free(loop->aux);
loop->aux = NULL;
}
}
/* Check data dependency in LOOP. BB_PBB_MAPPING is a basic_block and
it's related poly_bb_p mapping.
*/
static bool
dependency_in_loop_p (loop_p loop, htab_t bb_pbb_mapping)
{
unsigned i,j;
int level = 0;
basic_block *bbs = get_loop_body_in_dom_order (loop);
level = *((int *)(loop->aux));
for (i = 0; i < loop->num_nodes; i++)
{
poly_bb_p pbb1 = find_pbb_via_hash (bb_pbb_mapping, bbs[i]);
if (pbb1 == NULL)
continue;
for (j = 0; j < loop->num_nodes; j++)
{
poly_bb_p pbb2 = find_pbb_via_hash (bb_pbb_mapping, bbs[j]);
if (pbb2 == NULL)
continue;
if (dependency_between_pbbs_p (pbb1, pbb2, level))
{
free (bbs);
return true;
}
}
}
free (bbs);
return false;
}
/* Mark loop as parallel if data dependency does not exist.
BB_PBB_MAPPING is a basic_block and it's related poly_bb_p mapping.
*/
void mark_loops_parallel (htab_t bb_pbb_mapping)
{
loop_p loop;
loop_iterator li;
int num_no_dependency = 0;
FOR_EACH_LOOP (li, loop, 0)
{
if (!loop->aux)
continue;
if (!dependency_in_loop_p (loop, bb_pbb_mapping))
{
loop->can_be_parallel = true;
num_no_dependency++;
}
}
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "\n%d loops carried no dependency.\n",
num_no_dependency);
}
}
#endif
/* Translation of CLAST (CLooG AST) to Gimple.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_GRAPHITE_CLAST_TO_GIMPLE_H
#define GCC_GRAPHITE_CLAST_TO_GIMPLE_H
/* Data structure for CLooG program representation. */
typedef struct cloog_prog_clast {
CloogProgram *prog;
struct clast_stmt *stmt;
} cloog_prog_clast;
/* Stores BB's related PBB. */
typedef struct bb_pbb_def
{
basic_block bb;
poly_bb_p pbb;
}bb_pbb_def;
extern bool gloog (scop_p, htab_t);
extern cloog_prog_clast scop_to_clast (scop_p);
extern void debug_clast_stmt (struct clast_stmt *);
extern void print_clast_stmt (FILE *, struct clast_stmt *);
extern void debug_clast_name_indexes (htab_t);
extern void mark_loops_parallel (htab_t);
extern void free_aux_in_new_loops (void);
/* Hash function for data base element BB_PBB. */
static inline hashval_t
bb_pbb_map_hash (const void *bb_pbb)
{
return (hashval_t)(((const bb_pbb_def *)bb_pbb)->bb->index);
}
/* Compare data base element BB_PBB1 and BB_PBB2. */
static inline int
eq_bb_pbb_map (const void *bb_pbb1, const void *bb_pbb2)
{
const bb_pbb_def *bp1 = (const bb_pbb_def *) bb_pbb1;
const bb_pbb_def *bp2 = (const bb_pbb_def *) bb_pbb2;
return (bp1->bb->index == bp2->bb->index);
}
#endif
/* Data dependence analysis for Graphite.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Konrad Trifunovic <konrad.trifunovic@inria.fr>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "pointer-set.h"
#include "gimple.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "sese.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
#include "graphite-dependences.h"
/* Creates a new polyhedral data reference pair and
returns it. Parameter SOURCE denotes a source data reference
while parameter SINK denotes a sink data reference. Both
SOURCE and SINK define a pair of references, thus they
define an edge in DDG (Data Dependence Graph). */
static poly_dr_pair_p
new_poly_dr_pair (poly_dr_p source,
poly_dr_p sink,
ppl_Pointset_Powerset_C_Polyhedron_t ddp)
{
poly_dr_pair_p pdrpp;
pdrpp = XNEW (struct poly_dr_pair);
pdrpp->source = source;
pdrpp->sink = sink;
pdrpp->ddp = ddp;
return pdrpp;
}
/* Comparison function for poly_dr_pair hash table. */
int
eq_poly_dr_pair_p (const void *pdrpp1, const void *pdrpp2)
{
const struct poly_dr_pair *p1 = (const struct poly_dr_pair *) pdrpp1;
const struct poly_dr_pair *p2 = (const struct poly_dr_pair *) pdrpp2;
return (p1->source == p2->source
&& p1->sink == p2->sink);
}
/* Hash function for poly_dr_pair hashtable. */
hashval_t
hash_poly_dr_pair_p (const void *pdrpp)
{
const struct poly_dr_pair *p = (const struct poly_dr_pair *) pdrpp;
return (hashval_t) ((long) p->source + (long) p->sink);
}
/* Returns a polyhedron of dimension DIM.
Maps the dimensions [0, ..., cut - 1] of polyhedron P to OFFSET0
and the dimensions [cut, ..., nb_dim] to DIM - GDIM. */
static ppl_Pointset_Powerset_C_Polyhedron_t
map_into_dep_poly (graphite_dim_t dim, graphite_dim_t gdim,
ppl_Pointset_Powerset_C_Polyhedron_t p,
graphite_dim_t cut,
graphite_dim_t offset)
{
ppl_Pointset_Powerset_C_Polyhedron_t res;
ppl_new_Pointset_Powerset_C_Polyhedron_from_Pointset_Powerset_C_Polyhedron
(&res, p);
ppl_insert_dimensions_pointset (res, 0, offset);
ppl_insert_dimensions_pointset (res, offset + cut,
dim - offset - cut - gdim);
return res;
}
/* Swap [cut0, ..., cut1] to the end of DR: "a CUT0 b CUT1 c" is
transformed into "a CUT0 c CUT1' b"
Add NB0 zeros before "a": "00...0 a CUT0 c CUT1' b"
Add NB1 zeros between "a" and "c": "00...0 a 00...0 c CUT1' b"
Add DIM - NB0 - NB1 - PDIM zeros between "c" and "b":
"00...0 a 00...0 c 00...0 b". */
static ppl_Pointset_Powerset_C_Polyhedron_t
map_dr_into_dep_poly (graphite_dim_t dim,
ppl_Pointset_Powerset_C_Polyhedron_t dr,
graphite_dim_t cut0, graphite_dim_t cut1,
graphite_dim_t nb0, graphite_dim_t nb1)
{
ppl_dimension_type pdim;
ppl_dimension_type *map;
ppl_Pointset_Powerset_C_Polyhedron_t res;
ppl_dimension_type i;
ppl_new_Pointset_Powerset_C_Polyhedron_from_Pointset_Powerset_C_Polyhedron
(&res, dr);
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (res, &pdim);
map = (ppl_dimension_type *) XNEWVEC (ppl_dimension_type, pdim);
/* First mapping: move 'g' vector to right position. */
for (i = 0; i < cut0; i++)
map[i] = i;
for (i = cut0; i < cut1; i++)
map[i] = pdim - cut1 + i;
for (i = cut1; i < pdim; i++)
map[i] = cut0 + i - cut1;
ppl_Pointset_Powerset_C_Polyhedron_map_space_dimensions (res, map, pdim);
free (map);
/* After swapping 's' and 'g' vectors, we have to update a new cut. */
cut1 = pdim - cut1 + cut0;
ppl_insert_dimensions_pointset (res, 0, nb0);
ppl_insert_dimensions_pointset (res, nb0 + cut0, nb1);
ppl_insert_dimensions_pointset (res, nb0 + nb1 + cut1,
dim - nb0 - nb1 - pdim);
return res;
}
/* Builds a constraints of the form "POS1 - POS2 CSTR_TYPE C" */
static ppl_Constraint_t
build_pairwise_constraint (graphite_dim_t dim,
graphite_dim_t pos1, graphite_dim_t pos2,
int c, enum ppl_enum_Constraint_Type cstr_type)
{
ppl_Linear_Expression_t expr;
ppl_Constraint_t cstr;
ppl_Coefficient_t coef;
Value v, v_op, v_c;
value_init (v);
value_init (v_op);
value_init (v_c);
value_set_si (v, 1);
value_set_si (v_op, -1);
value_set_si (v_c, c);
ppl_new_Coefficient (&coef);
ppl_new_Linear_Expression_with_dimension (&expr, dim);
ppl_assign_Coefficient_from_mpz_t (coef, v);
ppl_Linear_Expression_add_to_coefficient (expr, pos1, coef);
ppl_assign_Coefficient_from_mpz_t (coef, v_op);
ppl_Linear_Expression_add_to_coefficient (expr, pos2, coef);
ppl_assign_Coefficient_from_mpz_t (coef, v_c);
ppl_Linear_Expression_add_to_inhomogeneous (expr, coef);
ppl_new_Constraint (&cstr, expr, cstr_type);
ppl_delete_Linear_Expression (expr);
ppl_delete_Coefficient (coef);
value_clear (v);
value_clear (v_op);
value_clear (v_c);
return cstr;
}
/* Builds subscript equality constraints. */
static ppl_Pointset_Powerset_C_Polyhedron_t
dr_equality_constraints (graphite_dim_t dim,
graphite_dim_t pos, graphite_dim_t nb_subscripts)
{
ppl_Polyhedron_t subscript_equalities;
ppl_Pointset_Powerset_C_Polyhedron_t res;
Value v, v_op;
graphite_dim_t i;
value_init (v);
value_init (v_op);
value_set_si (v, 1);
value_set_si (v_op, -1);
ppl_new_C_Polyhedron_from_space_dimension (&subscript_equalities, dim, 0);
for (i = 0; i < nb_subscripts; i++)
{
ppl_Linear_Expression_t expr;
ppl_Constraint_t cstr;
ppl_Coefficient_t coef;
ppl_new_Coefficient (&coef);
ppl_new_Linear_Expression_with_dimension (&expr, dim);
ppl_assign_Coefficient_from_mpz_t (coef, v);
ppl_Linear_Expression_add_to_coefficient (expr, pos + i, coef);
ppl_assign_Coefficient_from_mpz_t (coef, v_op);
ppl_Linear_Expression_add_to_coefficient (expr, pos + i + nb_subscripts,
coef);
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_EQUAL);
ppl_Polyhedron_add_constraint (subscript_equalities, cstr);
ppl_delete_Linear_Expression (expr);
ppl_delete_Constraint (cstr);
ppl_delete_Coefficient (coef);
}
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron
(&res, subscript_equalities);
value_clear (v);
value_clear (v_op);
ppl_delete_Polyhedron (subscript_equalities);
return res;
}
/* Builds scheduling equality constraints. */
static ppl_Pointset_Powerset_C_Polyhedron_t
build_pairwise_scheduling_equality (graphite_dim_t dim,
graphite_dim_t pos, graphite_dim_t offset)
{
ppl_Pointset_Powerset_C_Polyhedron_t res;
ppl_Polyhedron_t equalities;
ppl_Constraint_t cstr;
ppl_new_C_Polyhedron_from_space_dimension (&equalities, dim, 0);
cstr = build_pairwise_constraint (dim, pos, pos + offset, 0,
PPL_CONSTRAINT_TYPE_EQUAL);
ppl_Polyhedron_add_constraint (equalities, cstr);
ppl_delete_Constraint (cstr);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron (&res, equalities);
ppl_delete_Polyhedron (equalities);
return res;
}
/* Builds scheduling inequality constraints. */
static ppl_Pointset_Powerset_C_Polyhedron_t
build_pairwise_scheduling_inequality (graphite_dim_t dim,
graphite_dim_t pos,
graphite_dim_t offset,
bool direction)
{
ppl_Pointset_Powerset_C_Polyhedron_t res;
ppl_Polyhedron_t equalities;
ppl_Constraint_t cstr;
ppl_new_C_Polyhedron_from_space_dimension (&equalities, dim, 0);
if (direction)
cstr = build_pairwise_constraint (dim, pos, pos + offset, -1,
PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
else
cstr = build_pairwise_constraint (dim, pos, pos + offset, 1,
PPL_CONSTRAINT_TYPE_LESS_OR_EQUAL);
ppl_Polyhedron_add_constraint (equalities, cstr);
ppl_delete_Constraint (cstr);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron (&res, equalities);
ppl_delete_Polyhedron (equalities);
return res;
}
/* Returns true when adding the lexicographical constraints at level I
to the RES dependence polyhedron returns an empty polyhedron. */
static bool
lexicographically_gt_p (ppl_Pointset_Powerset_C_Polyhedron_t res,
graphite_dim_t dim,
graphite_dim_t offset,
bool direction, graphite_dim_t i)
{
ppl_Pointset_Powerset_C_Polyhedron_t ineq;
bool empty_p;
ineq = build_pairwise_scheduling_inequality (dim, i, offset,
direction);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (ineq, res);
empty_p = ppl_Pointset_Powerset_C_Polyhedron_is_empty (ineq);
if (!empty_p)
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, ineq);
ppl_delete_Pointset_Powerset_C_Polyhedron (ineq);
return !empty_p;
}
/* Build the precedence constraints for the lexicographical comparison
of time vectors RES following the lexicographical order. */
static void
build_lexicographically_gt_constraint (ppl_Pointset_Powerset_C_Polyhedron_t *res,
graphite_dim_t dim,
graphite_dim_t tdim1,
graphite_dim_t offset,
bool direction)
{
graphite_dim_t i;
if (lexicographically_gt_p (*res, dim, offset, direction, 0))
return;
for (i = 0; i < tdim1 - 1; i++)
{
ppl_Pointset_Powerset_C_Polyhedron_t sceq;
sceq = build_pairwise_scheduling_equality (dim, i, offset);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (*res, sceq);
ppl_delete_Pointset_Powerset_C_Polyhedron (sceq);
if (lexicographically_gt_p (*res, dim, offset, direction, i + 1))
return;
}
if (i == tdim1 - 1)
{
ppl_delete_Pointset_Powerset_C_Polyhedron (*res);
ppl_new_Pointset_Powerset_C_Polyhedron_from_space_dimension (res, dim, 1);
}
}
/* Build the dependence polyhedron for data references PDR1 and PDR2. */
static ppl_Pointset_Powerset_C_Polyhedron_t
dependence_polyhedron_1 (poly_bb_p pbb1, poly_bb_p pbb2,
ppl_Pointset_Powerset_C_Polyhedron_t d1,
ppl_Pointset_Powerset_C_Polyhedron_t d2,
poly_dr_p pdr1, poly_dr_p pdr2,
ppl_Polyhedron_t s1, ppl_Polyhedron_t s2,
bool direction,
bool original_scattering_p)
{
scop_p scop = PBB_SCOP (pbb1);
graphite_dim_t tdim1 = original_scattering_p ?
pbb_nb_scattering_orig (pbb1) : pbb_nb_scattering_transform (pbb1);
graphite_dim_t tdim2 = original_scattering_p ?
pbb_nb_scattering_orig (pbb2) : pbb_nb_scattering_transform (pbb2);
graphite_dim_t ddim1 = pbb_dim_iter_domain (pbb1);
graphite_dim_t ddim2 = pbb_dim_iter_domain (pbb2);
graphite_dim_t sdim1 = pdr_nb_subscripts (pdr1) + 1;
graphite_dim_t gdim = scop_nb_params (scop);
graphite_dim_t dim1 = pdr_dim (pdr1);
graphite_dim_t dim2 = pdr_dim (pdr2);
graphite_dim_t dim = tdim1 + tdim2 + dim1 + dim2;
ppl_Pointset_Powerset_C_Polyhedron_t res;
ppl_Pointset_Powerset_C_Polyhedron_t id1, id2, isc1, isc2, idr1, idr2;
ppl_Pointset_Powerset_C_Polyhedron_t sc1, sc2, dreq;
gcc_assert (PBB_SCOP (pbb1) == PBB_SCOP (pbb2));
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron (&sc1, s1);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron (&sc2, s2);
id1 = map_into_dep_poly (dim, gdim, d1, ddim1, tdim1);
id2 = map_into_dep_poly (dim, gdim, d2, ddim2, tdim1 + ddim1 + tdim2);
isc1 = map_into_dep_poly (dim, gdim, sc1, ddim1 + tdim1, 0);
isc2 = map_into_dep_poly (dim, gdim, sc2, ddim2 + tdim2, tdim1 + ddim1);
idr1 = map_dr_into_dep_poly (dim, PDR_ACCESSES (pdr1), ddim1, ddim1 + gdim,
tdim1, tdim2 + ddim2);
idr2 = map_dr_into_dep_poly (dim, PDR_ACCESSES (pdr2), ddim2, ddim2 + gdim,
tdim1 + ddim1 + tdim2, sdim1);
/* Now add the subscript equalities. */
dreq = dr_equality_constraints (dim, tdim1 + ddim1 + tdim2 + ddim2, sdim1);
ppl_new_Pointset_Powerset_C_Polyhedron_from_space_dimension (&res, dim, 0);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, id1);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, id2);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, isc1);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, isc2);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, idr1);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, idr2);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (res, dreq);
ppl_delete_Pointset_Powerset_C_Polyhedron (id1);
ppl_delete_Pointset_Powerset_C_Polyhedron (id2);
ppl_delete_Pointset_Powerset_C_Polyhedron (sc1);
ppl_delete_Pointset_Powerset_C_Polyhedron (sc2);
ppl_delete_Pointset_Powerset_C_Polyhedron (isc1);
ppl_delete_Pointset_Powerset_C_Polyhedron (isc2);
ppl_delete_Pointset_Powerset_C_Polyhedron (idr1);
ppl_delete_Pointset_Powerset_C_Polyhedron (idr2);
ppl_delete_Pointset_Powerset_C_Polyhedron (dreq);
if (!ppl_Pointset_Powerset_C_Polyhedron_is_empty (res))
build_lexicographically_gt_constraint (&res, dim, MIN (tdim1, tdim2),
tdim1 + ddim1, direction);
return res;
}
/* Build the dependence polyhedron for data references PDR1 and PDR2.
If possible use already cached information. */
static ppl_Pointset_Powerset_C_Polyhedron_t
dependence_polyhedron (poly_bb_p pbb1, poly_bb_p pbb2,
ppl_Pointset_Powerset_C_Polyhedron_t d1,
ppl_Pointset_Powerset_C_Polyhedron_t d2,
poly_dr_p pdr1, poly_dr_p pdr2,
ppl_Polyhedron_t s1, ppl_Polyhedron_t s2,
bool direction,
bool original_scattering_p)
{
poly_dr_pair tmp;
PTR *x = NULL;
ppl_Pointset_Powerset_C_Polyhedron_t res;
if (original_scattering_p)
{
tmp.source = pdr1;
tmp.sink = pdr2;
x = htab_find_slot (SCOP_ORIGINAL_PDR_PAIRS (PBB_SCOP (pbb1)),
&tmp, INSERT);
if (x && *x)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nddp cache: hit.\n");
return ((poly_dr_pair *)*x)->ddp;
}
else if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nddp cache: miss.\n");
}
res = dependence_polyhedron_1 (pbb1, pbb2, d1, d2, pdr1, pdr2,
s1, s2, direction, original_scattering_p);
if (original_scattering_p)
{
gcc_assert (x && *x == NULL);
*x = new_poly_dr_pair (pdr1, pdr2, res);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nddp cache: add element.\n");
}
return res;
}
/* Returns true when the PBB_TRANSFORMED_SCATTERING functions of PBB1
and PBB2 respect the data dependences of PBB_ORIGINAL_SCATTERING
functions. */
static bool
graphite_legal_transform_dr (poly_bb_p pbb1, poly_bb_p pbb2,
poly_dr_p pdr1, poly_dr_p pdr2)
{
ppl_Pointset_Powerset_C_Polyhedron_t d1 = PBB_DOMAIN (pbb1);
ppl_Pointset_Powerset_C_Polyhedron_t d2 = PBB_DOMAIN (pbb2);
ppl_Polyhedron_t so1 = PBB_ORIGINAL_SCATTERING (pbb1);
ppl_Polyhedron_t so2 = PBB_ORIGINAL_SCATTERING (pbb2);
ppl_Pointset_Powerset_C_Polyhedron_t po;
graphite_dim_t sdim1 = pdr_nb_subscripts (pdr1) + 1;
graphite_dim_t sdim2 = pdr_nb_subscripts (pdr2) + 1;
if (sdim1 != sdim2)
return true;
po = dependence_polyhedron (pbb1, pbb2, d1, d2, pdr1, pdr2, so1, so2,
true, true);
if (ppl_Pointset_Powerset_C_Polyhedron_is_empty (po))
return true;
else
{
ppl_Polyhedron_t st1 = PBB_TRANSFORMED_SCATTERING (pbb1);
ppl_Polyhedron_t st2 = PBB_TRANSFORMED_SCATTERING (pbb2);
ppl_Pointset_Powerset_C_Polyhedron_t pt;
graphite_dim_t ddim1 = pbb_dim_iter_domain (pbb1);
graphite_dim_t otdim1 = pbb_nb_scattering_orig (pbb1);
graphite_dim_t otdim2 = pbb_nb_scattering_orig (pbb2);
graphite_dim_t ttdim1 = pbb_nb_scattering_transform (pbb1);
graphite_dim_t ttdim2 = pbb_nb_scattering_transform (pbb2);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nloop carries dependency.\n");
pt = dependence_polyhedron (pbb1, pbb2, d1, d2, pdr1, pdr2, st1, st2,
false, false);
/* Extend PO and PT to have the same dimensions. */
ppl_insert_dimensions_pointset (po, otdim1, ttdim1);
ppl_insert_dimensions_pointset (po, otdim1 + ttdim1 + ddim1 + otdim2,
ttdim2);
ppl_insert_dimensions_pointset (pt, 0, otdim1);
ppl_insert_dimensions_pointset (pt, otdim1 + ttdim1 + ddim1, otdim2);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (po, pt);
return ppl_Pointset_Powerset_C_Polyhedron_is_empty (po);
}
}
/* Iterates over the data references of PBB1 and PBB2 and detect
whether the transformed schedule is correct. */
static bool
graphite_legal_transform_bb (poly_bb_p pbb1, poly_bb_p pbb2)
{
int i, j;
poly_dr_p pdr1, pdr2;
for (i = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb1), i, pdr1); i++)
for (j = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb2), j, pdr2); j++)
if (!graphite_legal_transform_dr (pbb1, pbb2, pdr1, pdr2))
return false;
return true;
}
/* Iterates over the SCOP and detect whether the transformed schedule
is correct. */
bool
graphite_legal_transform (scop_p scop)
{
int i, j;
poly_bb_p pbb1, pbb2;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb1); i++)
for (j = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), j, pbb2); j++)
if (!graphite_legal_transform_bb (pbb1, pbb2))
return false;
return true;
}
/* Remove all the dimensions except alias information at dimension
ALIAS_DIM. */
static void
build_alias_set_powerset (ppl_Pointset_Powerset_C_Polyhedron_t alias_powerset,
ppl_dimension_type alias_dim)
{
ppl_dimension_type *ds;
ppl_dimension_type access_dim;
unsigned i, pos = 0;
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (alias_powerset,
&access_dim);
ds = XNEWVEC (ppl_dimension_type, access_dim-1);
for (i = 0; i < access_dim; i++)
{
if (i == alias_dim)
continue;
ds[pos] = i;
pos++;
}
ppl_Pointset_Powerset_C_Polyhedron_remove_space_dimensions (alias_powerset,
ds,
access_dim - 1);
free (ds);
}
/* Return true when PDR1 and PDR2 may alias. */
static bool
poly_drs_may_alias_p (poly_dr_p pdr1, poly_dr_p pdr2)
{
ppl_Pointset_Powerset_C_Polyhedron_t alias_powerset1, alias_powerset2;
ppl_Pointset_Powerset_C_Polyhedron_t accesses1 = PDR_ACCESSES (pdr1);
ppl_Pointset_Powerset_C_Polyhedron_t accesses2 = PDR_ACCESSES (pdr2);
ppl_dimension_type alias_dim1 = pdr_alias_set_dim (pdr1);
ppl_dimension_type alias_dim2 = pdr_alias_set_dim (pdr2);
int empty_p;
ppl_new_Pointset_Powerset_C_Polyhedron_from_Pointset_Powerset_C_Polyhedron
(&alias_powerset1, accesses1);
ppl_new_Pointset_Powerset_C_Polyhedron_from_Pointset_Powerset_C_Polyhedron
(&alias_powerset2, accesses2);
build_alias_set_powerset (alias_powerset1, alias_dim1);
build_alias_set_powerset (alias_powerset2, alias_dim2);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign
(alias_powerset1, alias_powerset2);
empty_p = ppl_Pointset_Powerset_C_Polyhedron_is_empty (alias_powerset1);
ppl_delete_Pointset_Powerset_C_Polyhedron (alias_powerset1);
ppl_delete_Pointset_Powerset_C_Polyhedron (alias_powerset2);
return !empty_p;
}
/* Returns TRUE when the dependence polyhedron between PDR1 and
PDR2 represents a loop carried dependence at level LEVEL. Otherwise
return FALSE. */
static bool
graphite_carried_dependence_level_k (poly_dr_p pdr1, poly_dr_p pdr2,
int level)
{
poly_bb_p pbb1 = PDR_PBB (pdr1);
poly_bb_p pbb2 = PDR_PBB (pdr2);
ppl_Pointset_Powerset_C_Polyhedron_t d1 = PBB_DOMAIN (pbb1);
ppl_Pointset_Powerset_C_Polyhedron_t d2 = PBB_DOMAIN (pbb2);
ppl_Polyhedron_t so1 = PBB_TRANSFORMED_SCATTERING (pbb1);
ppl_Polyhedron_t so2 = PBB_TRANSFORMED_SCATTERING (pbb2);
ppl_Pointset_Powerset_C_Polyhedron_t po;
ppl_Pointset_Powerset_C_Polyhedron_t eqpp;
graphite_dim_t sdim1 = pdr_nb_subscripts (pdr1) + 1;
graphite_dim_t sdim2 = pdr_nb_subscripts (pdr2) + 1;
graphite_dim_t tdim1 = pbb_nb_scattering_transform (pbb1);
graphite_dim_t ddim1 = pbb_dim_iter_domain (pbb1);
ppl_dimension_type dim;
bool empty_p;
if ((PDR_TYPE (pdr1) == PDR_READ && PDR_TYPE (pdr2) == PDR_READ)
|| !poly_drs_may_alias_p (pdr1, pdr2))
return false;
if (sdim1 != sdim2)
return true;
po = dependence_polyhedron (pbb1, pbb2, d1, d2, pdr1, pdr2, so1, so2,
true, false);
if (ppl_Pointset_Powerset_C_Polyhedron_is_empty (po))
{
ppl_delete_Pointset_Powerset_C_Polyhedron (po);
return false;
}
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (po, &dim);
eqpp = build_pairwise_scheduling_inequality (dim, level, tdim1 + ddim1, 1);
ppl_Pointset_Powerset_C_Polyhedron_intersection_assign (eqpp, po);
empty_p = ppl_Pointset_Powerset_C_Polyhedron_is_empty (eqpp);
ppl_delete_Pointset_Powerset_C_Polyhedron (po);
ppl_delete_Pointset_Powerset_C_Polyhedron (eqpp);
return !empty_p;
}
/* Check data dependency between PBB1 and PBB2 at level LEVEL. */
bool
dependency_between_pbbs_p (poly_bb_p pbb1, poly_bb_p pbb2, int level)
{
int i, j;
poly_dr_p pdr1, pdr2;
for (i = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb1), i, pdr1); i++)
for (j = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb2), j, pdr2); j++)
if (graphite_carried_dependence_level_k (pdr1, pdr2, level))
return true;
return false;
}
#endif
/* Graphite polyhedral representation.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Konrad Trifunovic <konrad.trifunovic@gmail.com>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_GRAPHITE_DEPENDENCES_H
#define GCC_GRAPHITE_DEPENDENCES_H
extern bool graphite_legal_transform (scop_p);
extern bool dependency_between_pbbs_p (poly_bb_p, poly_bb_p, int);
typedef struct poly_dr_pair *poly_dr_pair_p;
typedef struct poly_dr_pair
{
/* Source polyhedral data reference of the dependence. */
poly_dr_p source;
/* Sink data reference of the dependence. */
poly_dr_p sink;
/* Data dependence polyhedron descibing dependence
between SOURCE and SINK data references. */
ppl_Pointset_Powerset_C_Polyhedron_t ddp;
}poly_dr_pair;
#define PDRP_SOURCE(PDRP) (PDR->source)
#define PDRP_SINK(PDRP) (PDR->sink)
#define PDRP_DDP(PDRP) (PDR->ddp)
extern int eq_poly_dr_pair_p (const void *, const void *);
extern hashval_t hash_poly_dr_pair_p (const void *);
#endif
/* Interchange heuristics and transform for loop interchange on
polyhedral representation.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Harsha Jagasia <harsha.jagasia@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "output.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "params.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "sese.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
/* Returns the subscript dimension defined by CSTR in PDR. */
static ppl_dimension_type
compute_subscript (poly_dr_p pdr, ppl_const_Constraint_t cstr)
{
graphite_dim_t i;
ppl_Linear_Expression_t expr;
ppl_Coefficient_t coef;
Value val;
value_init (val);
ppl_new_Coefficient (&coef);
for (i = 0; i < pdr_nb_subscripts (pdr); i++)
{
ppl_dimension_type sub_dim = pdr_subscript_dim (pdr, i);
ppl_new_Linear_Expression_from_Constraint (&expr, cstr);
ppl_Linear_Expression_coefficient (expr, sub_dim, coef);
ppl_delete_Linear_Expression (expr);
ppl_Coefficient_to_mpz_t (coef, val);
if (value_notzero_p (val))
{
gcc_assert (value_one_p (val)
|| value_mone_p (val));
value_clear (val);
ppl_delete_Coefficient (coef);
return sub_dim;
}
}
gcc_unreachable ();
return 0;
}
static void
compute_array_size_cstr (ppl_dimension_type sub_dim, Value res,
ppl_const_Constraint_t cstr)
{
ppl_Linear_Expression_t expr;
ppl_Coefficient_t coef;
Value val;
value_init (val);
ppl_new_Coefficient (&coef);
ppl_new_Linear_Expression_from_Constraint (&expr, cstr);
ppl_Linear_Expression_coefficient (expr, sub_dim, coef);
ppl_Coefficient_to_mpz_t (coef, val);
value_set_si (res, 0);
if (value_notzero_p (val))
{
gcc_assert (value_one_p (val) || value_mone_p (val));
ppl_Linear_Expression_inhomogeneous_term (expr, coef);
ppl_Coefficient_to_mpz_t (coef, res);
value_absolute (res, res);
}
value_clear (val);
ppl_delete_Coefficient (coef);
ppl_delete_Linear_Expression (expr);
}
/* Returns in ARRAY_SIZE the size in bytes of the array PDR for the
subscript at dimension SUB_DIM. */
static void
compute_array_size_poly (poly_dr_p pdr, ppl_dimension_type sub_dim, Value array_size,
ppl_const_Polyhedron_t ph)
{
ppl_const_Constraint_System_t pcs;
ppl_Constraint_System_const_iterator_t cit, cend;
ppl_const_Constraint_t cstr;
Value val;
Value res;
if (sub_dim >= pdr_subscript_dim (pdr, pdr_nb_subscripts (pdr)))
{
value_set_si (array_size, 1);
return;
}
value_init (val);
value_init (res);
value_set_si (res, 0);
ppl_Polyhedron_get_constraints (ph, &pcs);
ppl_new_Constraint_System_const_iterator (&cit);
ppl_new_Constraint_System_const_iterator (&cend);
for (ppl_Constraint_System_begin (pcs, cit),
ppl_Constraint_System_end (pcs, cend);
!ppl_Constraint_System_const_iterator_equal_test (cit, cend);
ppl_Constraint_System_const_iterator_increment (cit))
{
ppl_Constraint_System_const_iterator_dereference (cit, &cstr);
if (ppl_Constraint_type (cstr) == PPL_CONSTRAINT_TYPE_EQUAL)
continue;
compute_array_size_cstr (sub_dim, val, cstr);
value_max (res, res, val);
}
compute_array_size_poly (pdr, sub_dim + 1, val, ph);
value_multiply (array_size, res, val);
value_clear (res);
value_clear (val);
}
/* Initializes ARRAY_SIZE, the size in bytes of the array for the
subscript at dimension SUB_DIM in PDR. */
static void
compute_array_size (poly_dr_p pdr, ppl_dimension_type sub_dim, Value array_size)
{
ppl_Pointset_Powerset_C_Polyhedron_t data_container = PDR_DATA_CONTAINER (pdr);
ppl_Pointset_Powerset_C_Polyhedron_iterator_t it, end;
Value val;
value_set_si (array_size, 1);
if (sub_dim >= pdr_subscript_dim (pdr, pdr_nb_subscripts (pdr)))
return;
value_init (val);
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&it);
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&end);
for (ppl_Pointset_Powerset_C_Polyhedron_iterator_begin (data_container, it),
ppl_Pointset_Powerset_C_Polyhedron_iterator_end (data_container, end);
!ppl_Pointset_Powerset_C_Polyhedron_iterator_equal_test (it, end);
ppl_Pointset_Powerset_C_Polyhedron_iterator_increment (it))
{
ppl_const_Polyhedron_t ph;
ppl_Pointset_Powerset_C_Polyhedron_iterator_dereference (it, &ph);
compute_array_size_poly (pdr, sub_dim, val, ph);
value_max (array_size, array_size, val);
}
value_clear (val);
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (it);
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (end);
}
/* Computes ACCESS_STRIDES, the sum of all the strides of PDR at
LOOP_DEPTH. */
static void
gather_access_strides_poly (poly_dr_p pdr, ppl_const_Polyhedron_t ph,
ppl_dimension_type loop_dim, Value res)
{
ppl_const_Constraint_System_t pcs;
ppl_Constraint_System_const_iterator_t cit, cend;
ppl_const_Constraint_t cstr;
ppl_Linear_Expression_t expr;
ppl_Coefficient_t coef;
Value stride;
Value array_size;
value_init (array_size);
value_init (stride);
ppl_new_Coefficient (&coef);
value_set_si (res, 0);
ppl_Polyhedron_get_constraints (ph, &pcs);
ppl_new_Constraint_System_const_iterator (&cit);
ppl_new_Constraint_System_const_iterator (&cend);
for (ppl_Constraint_System_begin (pcs, cit),
ppl_Constraint_System_end (pcs, cend);
!ppl_Constraint_System_const_iterator_equal_test (cit, cend);
ppl_Constraint_System_const_iterator_increment (cit))
{
ppl_Constraint_System_const_iterator_dereference (cit, &cstr);
ppl_new_Linear_Expression_from_Constraint (&expr, cstr);
ppl_Linear_Expression_coefficient (expr, loop_dim, coef);
ppl_delete_Linear_Expression (expr);
ppl_Coefficient_to_mpz_t (coef, stride);
if (value_zero_p (stride))
continue;
value_absolute (stride, stride);
compute_array_size (pdr, compute_subscript (pdr, cstr), array_size);
value_multiply (stride, stride, array_size);
value_addto (res, res, stride);
}
value_clear (array_size);
value_clear (stride);
ppl_delete_Coefficient (coef);
ppl_delete_Constraint_System_const_iterator (cit);
ppl_delete_Constraint_System_const_iterator (cend);
}
/* Computes ACCESS_STRIDES, the sum of all the strides of PDR at
LOOP_DEPTH. */
static void
gather_access_strides (poly_dr_p pdr, graphite_dim_t loop_depth,
Value access_strides)
{
ppl_dimension_type loop_dim = pdr_iterator_dim (pdr, loop_depth);
ppl_Pointset_Powerset_C_Polyhedron_t accesses = PDR_ACCESSES (pdr);
ppl_Pointset_Powerset_C_Polyhedron_iterator_t it, end;
Value res;
value_init (res);
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&it);
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&end);
for (ppl_Pointset_Powerset_C_Polyhedron_iterator_begin (accesses, it),
ppl_Pointset_Powerset_C_Polyhedron_iterator_end (accesses, end);
!ppl_Pointset_Powerset_C_Polyhedron_iterator_equal_test (it, end);
ppl_Pointset_Powerset_C_Polyhedron_iterator_increment (it))
{
ppl_const_Polyhedron_t ph;
ppl_Pointset_Powerset_C_Polyhedron_iterator_dereference (it, &ph);
gather_access_strides_poly (pdr, ph, loop_dim, res);
value_addto (access_strides, access_strides, res);
}
value_clear (res);
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (it);
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (end);
}
/* Returns true when it is profitable to interchange loop at depth1
and loop at depth2 with depth1 < depth2 for the polyhedral black
box PBB. */
static bool
pbb_interchange_profitable_p (graphite_dim_t depth1, graphite_dim_t depth2, poly_bb_p pbb)
{
int i;
poly_dr_p pdr;
Value access_strides1, access_strides2;
bool res;
gcc_assert (depth1 < depth2);
value_init (access_strides1);
value_init (access_strides2);
value_set_si (access_strides1, 0);
value_set_si (access_strides2, 0);
for (i = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb), i, pdr); i++)
{
gather_access_strides (pdr, depth1, access_strides1);
gather_access_strides (pdr, depth2, access_strides2);
}
res = value_lt (access_strides1, access_strides2);
value_clear (access_strides1);
value_clear (access_strides2);
return res;
}
/* Interchanges the loops at DEPTH1 and DEPTH2 of the original
scattering and assigns the resulting polyhedron to the transformed
scattering. */
static void
pbb_interchange_loop_depths (graphite_dim_t depth1, graphite_dim_t depth2, poly_bb_p pbb)
{
ppl_dimension_type i, dim;
ppl_dimension_type *map;
ppl_Polyhedron_t poly = PBB_TRANSFORMED_SCATTERING (pbb);
ppl_dimension_type dim1 = psct_iterator_dim (pbb, depth1);
ppl_dimension_type dim2 = psct_iterator_dim (pbb, depth2);
ppl_Polyhedron_space_dimension (poly, &dim);
map = (ppl_dimension_type *) XNEWVEC (ppl_dimension_type, dim);
for (i = 0; i < dim; i++)
map[i] = i;
map[dim1] = dim2;
map[dim2] = dim1;
ppl_Polyhedron_map_space_dimensions (poly, map, dim);
free (map);
}
/* Interchanges all the loop depths that are considered profitable for PBB. */
static bool
pbb_do_interchange (poly_bb_p pbb, scop_p scop)
{
graphite_dim_t i, j;
bool transform_done = false;
for (i = 0; i < pbb_dim_iter_domain (pbb); i++)
for (j = i + 1; j < pbb_dim_iter_domain (pbb); j++)
if (pbb_interchange_profitable_p (i, j, pbb))
{
pbb_interchange_loop_depths (i, j, pbb);
if (graphite_legal_transform (scop))
{
transform_done = true;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
"PBB %d: loops at depths %d and %d will be interchanged.\n",
GBB_BB (PBB_BLACK_BOX (pbb))->index, (int) i, (int) j);
}
else
/* Undo the transform. */
pbb_interchange_loop_depths (j, i, pbb);
}
return transform_done;
}
/* Interchanges all the loop depths that are considered profitable for SCOP. */
bool
scop_do_interchange (scop_p scop)
{
int i;
poly_bb_p pbb;
bool transform_done = false;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
transform_done |= pbb_do_interchange (pbb, scop);
return transform_done;
}
#endif
/* Graphite polyhedral representation.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Tobias Grosser <grosser@fim.uni-passau.de>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "output.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "params.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "sese.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
#include "graphite-dependences.h"
/* Return the maximal loop depth in SCOP. */
int
scop_max_loop_depth (scop_p scop)
{
int i;
poly_bb_p pbb;
int max_nb_loops = 0;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
{
int nb_loops = pbb_dim_iter_domain (pbb);
if (max_nb_loops < nb_loops)
max_nb_loops = nb_loops;
}
return max_nb_loops;
}
/* Extend the scattering matrix of PBB to MAX_SCATTERING scattering
dimensions. */
static void
extend_scattering (poly_bb_p pbb, int max_scattering)
{
ppl_dimension_type nb_old_dims, nb_new_dims;
int nb_added_dims, i;
ppl_Coefficient_t coef;
Value one;
nb_added_dims = max_scattering - pbb_nb_scattering_transform (pbb);
value_init (one);
value_set_si (one, 1);
ppl_new_Coefficient (&coef);
ppl_assign_Coefficient_from_mpz_t (coef, one);
gcc_assert (nb_added_dims >= 0);
nb_old_dims = pbb_nb_scattering_transform (pbb) + pbb_dim_iter_domain (pbb)
+ scop_nb_params (PBB_SCOP (pbb));
nb_new_dims = nb_old_dims + nb_added_dims;
ppl_insert_dimensions (PBB_TRANSFORMED_SCATTERING (pbb),
pbb_nb_scattering_transform (pbb), nb_added_dims);
PBB_NB_SCATTERING_TRANSFORM (pbb) += nb_added_dims;
/* Add identity matrix for the added dimensions. */
for (i = max_scattering - nb_added_dims; i < max_scattering; i++)
{
ppl_Constraint_t cstr;
ppl_Linear_Expression_t expr;
ppl_new_Linear_Expression_with_dimension (&expr, nb_new_dims);
ppl_Linear_Expression_add_to_coefficient (expr, i, coef);
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_EQUAL);
ppl_Polyhedron_add_constraint (PBB_TRANSFORMED_SCATTERING (pbb), cstr);
ppl_delete_Constraint (cstr);
ppl_delete_Linear_Expression (expr);
}
ppl_delete_Coefficient (coef);
value_clear (one);
}
/* All scattering matrices in SCOP will have the same number of scattering
dimensions. */
int
unify_scattering_dimensions (scop_p scop)
{
int i;
poly_bb_p pbb;
graphite_dim_t max_scattering = 0;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
max_scattering = MAX (pbb_nb_scattering_transform (pbb), max_scattering);
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
extend_scattering (pbb, max_scattering);
return max_scattering;
}
/* Prints to FILE the scattering function of PBB. */
void
print_scattering_function (FILE *file, poly_bb_p pbb)
{
graphite_dim_t i;
if (!PBB_TRANSFORMED_SCATTERING (pbb))
return;
fprintf (file, "scattering bb_%d (\n", GBB_BB (PBB_BLACK_BOX (pbb))->index);
fprintf (file, "# eq");
for (i = 0; i < pbb_nb_scattering_transform (pbb); i++)
fprintf (file, " s%d", (int) i);
for (i = 0; i < pbb_nb_local_vars (pbb); i++)
fprintf (file, " lv%d", (int) i);
for (i = 0; i < pbb_dim_iter_domain (pbb); i++)
fprintf (file, " i%d", (int) i);
for (i = 0; i < pbb_nb_params (pbb); i++)
fprintf (file, " p%d", (int) i);
fprintf (file, " cst\n");
ppl_print_polyhedron_matrix (file, PBB_TRANSFORMED_SCATTERING (pbb));
fprintf (file, ")\n");
}
/* Prints to FILE the iteration domain of PBB. */
void
print_iteration_domain (FILE *file, poly_bb_p pbb)
{
print_pbb_domain (file, pbb);
}
/* Prints to FILE the scattering functions of every PBB of SCOP. */
void
print_scattering_functions (FILE *file, scop_p scop)
{
int i;
poly_bb_p pbb;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
print_scattering_function (file, pbb);
}
/* Prints to FILE the iteration domains of every PBB of SCOP. */
void
print_iteration_domains (FILE *file, scop_p scop)
{
int i;
poly_bb_p pbb;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
print_iteration_domain (file, pbb);
}
/* Prints to STDERR the scattering function of PBB. */
void
debug_scattering_function (poly_bb_p pbb)
{
print_scattering_function (stderr, pbb);
}
/* Prints to STDERR the iteration domain of PBB. */
void
debug_iteration_domain (poly_bb_p pbb)
{
print_iteration_domain (stderr, pbb);
}
/* Prints to STDERR the scattering functions of every PBB of SCOP. */
void
debug_scattering_functions (scop_p scop)
{
print_scattering_functions (stderr, scop);
}
/* Prints to STDERR the iteration domains of every PBB of SCOP. */
void
debug_iteration_domains (scop_p scop)
{
print_iteration_domains (stderr, scop);
}
/* Apply graphite transformations to all the basic blocks of SCOP. */
bool
apply_poly_transforms (scop_p scop)
{
bool transform_done = false;
gcc_assert (graphite_legal_transform (scop));
/* Generate code even if we did not apply any real transformation.
This also allows to check the performance for the identity
transformation: GIMPLE -> GRAPHITE -> GIMPLE
Keep in mind that CLooG optimizes in control, so the loop structure
may change, even if we only use -fgraphite-identity. */
if (flag_graphite_identity)
transform_done = true;
if (flag_graphite_force_parallel)
transform_done = true;
if (flag_loop_block)
gcc_unreachable (); /* Not yet supported. */
if (flag_loop_strip_mine)
{
transform_done |= scop_do_strip_mine (scop);
gcc_assert (graphite_legal_transform (scop));
}
if (flag_loop_interchange)
{
transform_done |= scop_do_interchange (scop);
gcc_assert (graphite_legal_transform (scop));
}
return transform_done;
}
/* Create a new polyhedral data reference and add it to PBB. It is defined by
its ACCESSES, its TYPE*/
void
new_poly_dr (poly_bb_p pbb,
ppl_Pointset_Powerset_C_Polyhedron_t accesses,
ppl_Pointset_Powerset_C_Polyhedron_t data_container,
enum POLY_DR_TYPE type, void *cdr)
{
poly_dr_p pdr = XNEW (struct poly_dr);
PDR_PBB (pdr) = pbb;
PDR_ACCESSES (pdr) = accesses;
PDR_DATA_CONTAINER (pdr) = data_container;
PDR_TYPE (pdr) = type;
PDR_CDR (pdr) = cdr;
VEC_safe_push (poly_dr_p, heap, PBB_DRS (pbb), pdr);
}
/* Free polyhedral data reference PDR. */
void
free_poly_dr (poly_dr_p pdr)
{
ppl_delete_Pointset_Powerset_C_Polyhedron (PDR_ACCESSES (pdr));
ppl_delete_Pointset_Powerset_C_Polyhedron (PDR_DATA_CONTAINER (pdr));
XDELETE (pdr);
}
/* Create a new polyhedral black box. */
void
new_poly_bb (scop_p scop, void *black_box)
{
poly_bb_p pbb = XNEW (struct poly_bb);
PBB_DOMAIN (pbb) = NULL;
PBB_SCOP (pbb) = scop;
pbb_set_black_box (pbb, black_box);
PBB_TRANSFORMED_SCATTERING (pbb) = NULL;
PBB_ORIGINAL_SCATTERING (pbb) = NULL;
PBB_DRS (pbb) = VEC_alloc (poly_dr_p, heap, 3);
PBB_NB_SCATTERING_TRANSFORM (pbb) = 0;
PBB_NB_LOCAL_VARIABLES (pbb) = 0;
VEC_safe_push (poly_bb_p, heap, SCOP_BBS (scop), pbb);
}
/* Free polyhedral black box. */
void
free_poly_bb (poly_bb_p pbb)
{
int i;
poly_dr_p pdr;
ppl_delete_Pointset_Powerset_C_Polyhedron (PBB_DOMAIN (pbb));
if (PBB_TRANSFORMED_SCATTERING (pbb))
ppl_delete_Polyhedron (PBB_TRANSFORMED_SCATTERING (pbb));
if (PBB_ORIGINAL_SCATTERING (pbb))
ppl_delete_Polyhedron (PBB_ORIGINAL_SCATTERING (pbb));
if (PBB_DRS (pbb))
for (i = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb), i, pdr); i++)
free_poly_dr (pdr);
VEC_free (poly_dr_p, heap, PBB_DRS (pbb));
XDELETE (pbb);
}
static void
print_pdr_access_layout (FILE *file, poly_dr_p pdr)
{
graphite_dim_t i;
fprintf (file, "# eq");
for (i = 0; i < pdr_dim_iter_domain (pdr); i++)
fprintf (file, " i%d", (int) i);
for (i = 0; i < pdr_nb_params (pdr); i++)
fprintf (file, " p%d", (int) i);
fprintf (file, " alias");
for (i = 0; i < pdr_nb_subscripts (pdr); i++)
fprintf (file, " sub%d", (int) i);
fprintf (file, " cst\n");
}
/* Prints to FILE the polyhedral data reference PDR. */
void
print_pdr (FILE *file, poly_dr_p pdr)
{
fprintf (file, "pdr (");
switch (PDR_TYPE (pdr))
{
case PDR_READ:
fprintf (file, "read \n");
break;
case PDR_WRITE:
fprintf (file, "write \n");
break;
case PDR_MAY_WRITE:
fprintf (file, "may_write \n");
break;
default:
gcc_unreachable ();
}
dump_data_reference (file, (data_reference_p) PDR_CDR (pdr));
fprintf (file, "data accesses (\n");
print_pdr_access_layout (file, pdr);
ppl_print_powerset_matrix (file, PDR_ACCESSES (pdr));
fprintf (file, ")\n");
fprintf (file, "data container (\n");
print_pdr_access_layout (file, pdr);
ppl_print_powerset_matrix (file, PDR_DATA_CONTAINER (pdr));
fprintf (file, ")\n");
fprintf (file, ")\n");
}
/* Prints to STDERR the polyhedral data reference PDR. */
void
debug_pdr (poly_dr_p pdr)
{
print_pdr (stderr, pdr);
}
/* Creates a new SCOP containing REGION. */
scop_p
new_scop (void *region)
{
scop_p scop = XNEW (struct scop);
SCOP_DEP_GRAPH (scop) = NULL;
SCOP_CONTEXT (scop) = NULL;
scop_set_region (scop, region);
SCOP_BBS (scop) = VEC_alloc (poly_bb_p, heap, 3);
SCOP_ORIGINAL_PDR_PAIRS (scop) = htab_create (10, hash_poly_dr_pair_p,
eq_poly_dr_pair_p, free);
return scop;
}
/* Deletes SCOP. */
void
free_scop (scop_p scop)
{
int i;
poly_bb_p pbb;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
free_poly_bb (pbb);
VEC_free (poly_bb_p, heap, SCOP_BBS (scop));
if (SCOP_CONTEXT (scop))
ppl_delete_Pointset_Powerset_C_Polyhedron (SCOP_CONTEXT (scop));
htab_delete (SCOP_ORIGINAL_PDR_PAIRS (scop));
XDELETE (scop);
}
/* Print to FILE the domain of PBB. */
void
print_pbb_domain (FILE *file, poly_bb_p pbb)
{
graphite_dim_t i;
gimple_bb_p gbb = PBB_BLACK_BOX (pbb);
if (!PBB_DOMAIN (pbb))
return;
fprintf (file, "domains bb_%d (\n", GBB_BB (gbb)->index);
fprintf (file, "# eq");
for (i = 0; i < pbb_dim_iter_domain (pbb); i++)
fprintf (file, " i%d", (int) i);
for (i = 0; i < pbb_nb_params (pbb); i++)
fprintf (file, " p%d", (int) i);
fprintf (file, " cst\n");
if (PBB_DOMAIN (pbb))
ppl_print_powerset_matrix (file, PBB_DOMAIN (pbb));
fprintf (file, ")\n");
}
/* Dump the cases of a graphite basic block GBB on FILE. */
static void
dump_gbb_cases (FILE *file, gimple_bb_p gbb)
{
int i;
gimple stmt;
VEC (gimple, heap) *cases;
if (!gbb)
return;
cases = GBB_CONDITION_CASES (gbb);
if (VEC_empty (gimple, cases))
return;
fprintf (file, "cases bb_%d (", GBB_BB (gbb)->index);
for (i = 0; VEC_iterate (gimple, cases, i, stmt); i++)
print_gimple_stmt (file, stmt, 0, 0);
fprintf (file, ")\n");
}
/* Dump conditions of a graphite basic block GBB on FILE. */
static void
dump_gbb_conditions (FILE *file, gimple_bb_p gbb)
{
int i;
gimple stmt;
VEC (gimple, heap) *conditions;
if (!gbb)
return;
conditions = GBB_CONDITIONS (gbb);
if (VEC_empty (gimple, conditions))
return;
fprintf (file, "conditions bb_%d (", GBB_BB (gbb)->index);
for (i = 0; VEC_iterate (gimple, conditions, i, stmt); i++)
print_gimple_stmt (file, stmt, 0, 0);
fprintf (file, ")\n");
}
/* Print to FILE all the data references of PBB. */
void
print_pdrs (FILE *file, poly_bb_p pbb)
{
int i;
poly_dr_p pdr;
for (i = 0; VEC_iterate (poly_dr_p, PBB_DRS (pbb), i, pdr); i++)
print_pdr (file, pdr);
}
/* Print to STDERR all the data references of PBB. */
void
debug_pdrs (poly_bb_p pbb)
{
print_pdrs (stderr, pbb);
}
/* Print to FILE the domain and scattering function of PBB. */
void
print_pbb (FILE *file, poly_bb_p pbb)
{
dump_gbb_conditions (file, PBB_BLACK_BOX (pbb));
dump_gbb_cases (file, PBB_BLACK_BOX (pbb));
print_pdrs (file, pbb);
print_pbb_domain (file, pbb);
print_scattering_function (file, pbb);
}
/* Print to FILE the parameters of SCOP. */
void
print_scop_params (FILE *file, scop_p scop)
{
int i;
tree t;
fprintf (file, "parameters (\n");
for (i = 0; VEC_iterate (tree, SESE_PARAMS (SCOP_REGION (scop)), i, t); i++)
{
fprintf (file, "p_%d -> ", i);
print_generic_expr (file, t, 0);
fprintf (file, "\n");
}
fprintf (file, ")\n");
}
/* Print to FILE the context of SCoP. */
void
print_scop_context (FILE *file, scop_p scop)
{
graphite_dim_t i;
fprintf (file, "context (\n");
fprintf (file, "# eq");
for (i = 0; i < scop_nb_params (scop); i++)
fprintf (file, " p%d", (int) i);
fprintf (file, " cst\n");
if (SCOP_CONTEXT (scop))
ppl_print_powerset_matrix (file, SCOP_CONTEXT (scop));
fprintf (file, ")\n");
}
/* Print to FILE the SCOP. */
void
print_scop (FILE *file, scop_p scop)
{
int i;
poly_bb_p pbb;
print_scop_params (file, scop);
print_scop_context (file, scop);
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
print_pbb (file, pbb);
}
/* Print to STDERR the domain of PBB. */
void
debug_pbb_domain (poly_bb_p pbb)
{
print_pbb_domain (stderr, pbb);
}
/* Print to FILE the domain and scattering function of PBB. */
void
debug_pbb (poly_bb_p pbb)
{
print_pbb (stderr, pbb);
}
/* Print to STDERR the context of SCOP. */
void
debug_scop_context (scop_p scop)
{
print_scop_context (stderr, scop);
}
/* Print to STDERR the SCOP. */
void
debug_scop (scop_p scop)
{
print_scop (stderr, scop);
}
/* Print to STDERR the parameters of SCOP. */
void
debug_scop_params (scop_p scop)
{
print_scop_params (stderr, scop);
}
/* The dimension in the transformed scattering polyhedron of PBB
containing the scattering iterator for the loop at depth LOOP_DEPTH. */
ppl_dimension_type
psct_scattering_dim_for_loop_depth (poly_bb_p pbb, graphite_dim_t loop_depth)
{
ppl_const_Constraint_System_t pcs;
ppl_Constraint_System_const_iterator_t cit, cend;
ppl_const_Constraint_t cstr;
ppl_Polyhedron_t ph = PBB_TRANSFORMED_SCATTERING (pbb);
ppl_dimension_type iter = psct_iterator_dim (pbb, loop_depth);
ppl_Linear_Expression_t expr;
ppl_Coefficient_t coef;
Value val;
graphite_dim_t i;
value_init (val);
ppl_new_Coefficient (&coef);
ppl_Polyhedron_get_constraints (ph, &pcs);
ppl_new_Constraint_System_const_iterator (&cit);
ppl_new_Constraint_System_const_iterator (&cend);
for (ppl_Constraint_System_begin (pcs, cit),
ppl_Constraint_System_end (pcs, cend);
!ppl_Constraint_System_const_iterator_equal_test (cit, cend);
ppl_Constraint_System_const_iterator_increment (cit))
{
ppl_Constraint_System_const_iterator_dereference (cit, &cstr);
ppl_new_Linear_Expression_from_Constraint (&expr, cstr);
ppl_Linear_Expression_coefficient (expr, iter, coef);
ppl_Coefficient_to_mpz_t (coef, val);
if (value_zero_p (val))
{
ppl_delete_Linear_Expression (expr);
continue;
}
for (i = 0; i < pbb_nb_scattering_transform (pbb); i++)
{
ppl_dimension_type scatter = psct_scattering_dim (pbb, i);
ppl_Linear_Expression_coefficient (expr, scatter, coef);
ppl_Coefficient_to_mpz_t (coef, val);
if (value_notzero_p (val))
{
value_clear (val);
ppl_delete_Linear_Expression (expr);
ppl_delete_Coefficient (coef);
ppl_delete_Constraint_System_const_iterator (cit);
ppl_delete_Constraint_System_const_iterator (cend);
return scatter;
}
}
}
gcc_unreachable ();
}
/* Returns the number of iterations NITER of the loop around PBB at
depth LOOP_DEPTH. */
void
pbb_number_of_iterations (poly_bb_p pbb,
graphite_dim_t loop_depth,
Value niter)
{
ppl_dimension_type loop_iter = pbb_iterator_dim (pbb, loop_depth);
ppl_Linear_Expression_t le;
ppl_Coefficient_t num, denom;
Value dv;
int maximum;
ppl_dimension_type dim;
value_init (dv);
ppl_new_Coefficient (&num);
ppl_new_Coefficient (&denom);
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (PBB_DOMAIN (pbb), &dim);
ppl_new_Linear_Expression_with_dimension (&le, dim);
ppl_set_coef (le, loop_iter, 1);
ppl_Pointset_Powerset_C_Polyhedron_maximize (PBB_DOMAIN (pbb), le,
num, denom, &maximum);
if (maximum == 1)
{
ppl_Coefficient_to_mpz_t (num, niter);
ppl_Coefficient_to_mpz_t (denom, dv);
value_division (niter, niter, dv);
}
else
value_set_si (niter, -1);
value_clear (dv);
ppl_delete_Linear_Expression (le);
ppl_delete_Coefficient (num);
ppl_delete_Coefficient (denom);
}
#endif
/* Graphite polyhedral representation.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Tobias Grosser <grosser@fim.uni-passau.de>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_GRAPHITE_POLY_H
#define GCC_GRAPHITE_POLY_H
typedef struct poly_dr *poly_dr_p;
DEF_VEC_P(poly_dr_p);
DEF_VEC_ALLOC_P (poly_dr_p, heap);
typedef struct poly_bb *poly_bb_p;
DEF_VEC_P(poly_bb_p);
DEF_VEC_ALLOC_P (poly_bb_p, heap);
typedef struct scop *scop_p;
DEF_VEC_P(scop_p);
DEF_VEC_ALLOC_P (scop_p, heap);
typedef ppl_dimension_type graphite_dim_t;
static inline graphite_dim_t pbb_dim_iter_domain (const struct poly_bb *);
static inline graphite_dim_t pbb_nb_params (const struct poly_bb *);
static inline graphite_dim_t scop_nb_params (scop_p);
/* A data reference can write or read some memory or we
just know it may write some memory. */
enum POLY_DR_TYPE
{
PDR_READ,
/* PDR_MAY_READs are represented using PDR_READS. This does not limit the
expressiveness. */
PDR_WRITE,
PDR_MAY_WRITE
};
struct poly_dr
{
/* A pointer to compiler's data reference description. */
void *compiler_dr;
/* A pointer to the PBB that contains this data reference. */
poly_bb_p pbb;
enum POLY_DR_TYPE type;
/* The access polyhedron contains the polyhedral space this data
reference will access.
The polyhedron contains these dimensions:
- The alias set (a):
Every memory access is classified in at least one alias set.
- The subscripts (s_0, ..., s_n):
The memory is accessed using zero or more subscript dimensions.
- The iteration domain (variables and parameters)
Do not hardcode the dimensions. Use the following accessor functions:
- pdr_alias_set_dim
- pdr_subscript_dim
- pdr_iterator_dim
- pdr_parameter_dim
Example:
| int A[1335][123];
| int *p = malloc ();
|
| k = ...
| for i
| {
| if (unknown_function ())
| p = A;
| ... = p[?][?];
| for j
| A[i][j+b] = m;
| }
The data access A[i][j+k] in alias set "5" is described like this:
| i j k a s0 s1 1
| 0 0 0 1 0 0 -5 = 0
|-1 0 0 0 1 0 0 = 0
| 0 -1 -1 0 0 1 0 = 0
The constraints on the data container A[1335][123] are:
| i j k a s0 s1 1
| 0 0 0 0 1 0 0 >= 0
| 0 0 0 0 0 1 0 >= 0
| 0 0 0 0 -1 0 1335 >= 0
| 0 0 0 0 0 -1 123 >= 0
The pointer "*p" in alias set "5" and "7" is described as a union of
polyhedron:
| i k a s0 1
| 0 0 1 0 -5 = 0
| 0 0 0 1 0 >= 0
"or"
| i k a s0 1
| 0 0 1 0 -7 = 0
| 0 0 0 1 0 >= 0
"*p" accesses all of the object allocated with 'malloc'.
The scalar data access "m" is represented as an array with zero subscript
dimensions.
| i j k a 1
| 0 0 0 -1 15 = 0 */
ppl_Pointset_Powerset_C_Polyhedron_t accesses;
ppl_Pointset_Powerset_C_Polyhedron_t data_container;
};
#define PDR_CDR(PDR) (PDR->compiler_dr)
#define PDR_PBB(PDR) (PDR->pbb)
#define PDR_TYPE(PDR) (PDR->type)
#define PDR_ACCESSES(PDR) (PDR->accesses)
#define PDR_DATA_CONTAINER(PDR) (PDR->data_container)
void new_poly_dr (poly_bb_p, ppl_Pointset_Powerset_C_Polyhedron_t,
ppl_Pointset_Powerset_C_Polyhedron_t,
enum POLY_DR_TYPE, void *);
void free_poly_dr (poly_dr_p);
void debug_pdr (poly_dr_p);
void print_pdr (FILE *, poly_dr_p);
static inline scop_p pdr_scop (poly_dr_p pdr);
/* The number of subscripts of the PDR. */
static inline graphite_dim_t
pdr_nb_subscripts (poly_dr_p pdr)
{
poly_bb_p pbb = PDR_PBB (pdr);
ppl_dimension_type dim;
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (PDR_ACCESSES (pdr),
&dim);
return dim - pbb_dim_iter_domain (pbb) - pbb_nb_params (pbb) - 1;
}
/* The dimension of the iteration domain of the scop of PDR. */
static inline ppl_dimension_type
pdr_dim_iter_domain (poly_dr_p pdr)
{
return pbb_dim_iter_domain (PDR_PBB (pdr));
}
/* The number of parameters of the scop of PDR. */
static inline ppl_dimension_type
pdr_nb_params (poly_dr_p pdr)
{
return scop_nb_params (pdr_scop (pdr));
}
/* The dimension of the accesses polyhedron of PDR. */
static inline graphite_dim_t
pdr_dim (poly_dr_p pdr)
{
graphite_dim_t alias_nb_dimensions = 1;
return pbb_dim_iter_domain (PDR_PBB (pdr)) + alias_nb_dimensions
+ pdr_nb_subscripts (pdr) + scop_nb_params (pdr_scop (pdr));
}
/* The dimension of the alias set in PDR. */
static inline ppl_dimension_type
pdr_alias_set_dim (poly_dr_p pdr)
{
poly_bb_p pbb = PDR_PBB (pdr);
return pbb_dim_iter_domain (pbb) + pbb_nb_params (pbb);
}
/* The dimension in PDR containing subscript S. */
static inline ppl_dimension_type
pdr_subscript_dim (poly_dr_p pdr, graphite_dim_t s)
{
poly_bb_p pbb = PDR_PBB (pdr);
return pbb_dim_iter_domain (pbb) + pbb_nb_params (pbb) + 1 + s;
}
/* The dimension in PDR containing the loop iterator ITER. */
static inline ppl_dimension_type
pdr_iterator_dim (poly_dr_p pdr ATTRIBUTE_UNUSED, graphite_dim_t iter)
{
return iter;
}
/* The dimension in PDR containing parameter PARAM. */
static inline ppl_dimension_type
pdr_parameter_dim (poly_dr_p pdr, graphite_dim_t param)
{
poly_bb_p pbb = PDR_PBB (pdr);
return pbb_dim_iter_domain (pbb) + param;
}
/* POLY_BB represents a blackbox in the polyhedral model. */
struct poly_bb
{
void *black_box;
scop_p scop;
/* The iteration domain of this bb.
Example:
for (i = a - 7*b + 8; i <= 3*a + 13*b + 20; i++)
for (j = 2; j <= 2*i + 5; j++)
for (k = 0; k <= 5; k++)
S (i,j,k)
Loop iterators: i, j, k
Parameters: a, b
| i >= a - 7b + 8
| i <= 3a + 13b + 20
| j >= 2
| j <= 2i + 5
| k >= 0
| k <= 5
The number of variables in the DOMAIN may change and is not
related to the number of loops in the original code. */
ppl_Pointset_Powerset_C_Polyhedron_t domain;
/* The data references we access. */
VEC (poly_dr_p, heap) *drs;
/* The scattering function containing the transformations. */
ppl_Polyhedron_t transformed_scattering;
/* The original scattering function. */
ppl_Polyhedron_t original_scattering;
/* The number of local variables. */
int nb_local_variables;
/* The number of scattering dimensions in the TRANSFORMED scattering. */
int nb_scattering_transform;
};
#define PBB_BLACK_BOX(PBB) ((gimple_bb_p) PBB->black_box)
#define PBB_SCOP(PBB) (PBB->scop)
#define PBB_DOMAIN(PBB) (PBB->domain)
#define PBB_DRS(PBB) (PBB->drs)
#define PBB_TRANSFORMED_SCATTERING(PBB) (PBB->transformed_scattering)
#define PBB_ORIGINAL_SCATTERING(PBB) (PBB->original_scattering)
#define PBB_NB_LOCAL_VARIABLES(PBB) (PBB->nb_local_variables)
#define PBB_NB_SCATTERING_TRANSFORM(PBB) (PBB->nb_scattering_transform)
extern void new_poly_bb (scop_p, void *);
extern void free_poly_bb (poly_bb_p);
extern void debug_loop_vec (poly_bb_p);
extern void schedule_to_scattering (poly_bb_p, int);
extern void print_pbb_domain (FILE *, poly_bb_p);
extern void print_pbb (FILE *, poly_bb_p);
extern void print_scop_context (FILE *, scop_p);
extern void print_scop (FILE *, scop_p);
extern void debug_pbb_domain (poly_bb_p);
extern void debug_pbb (poly_bb_p);
extern void print_pdrs (FILE *, poly_bb_p);
extern void debug_pdrs (poly_bb_p);
extern void debug_scop_context (scop_p);
extern void debug_scop (scop_p);
extern void print_scop_params (FILE *, scop_p);
extern void debug_scop_params (scop_p);
extern void print_iteration_domain (FILE *, poly_bb_p);
extern void print_iteration_domains (FILE *, scop_p);
extern void debug_iteration_domain (poly_bb_p);
extern void debug_iteration_domains (scop_p);
extern bool scop_do_interchange (scop_p);
extern bool scop_do_strip_mine (scop_p);
extern void pbb_number_of_iterations (poly_bb_p, graphite_dim_t, Value);
/* The scop that contains the PDR. */
static inline scop_p pdr_scop (poly_dr_p pdr)
{
return PBB_SCOP (PDR_PBB (pdr));
}
/* Set black box of PBB to BLACKBOX. */
static inline void
pbb_set_black_box (poly_bb_p pbb, void *black_box)
{
pbb->black_box = black_box;
}
/* The number of loops around PBB: the dimension of the iteration
domain. */
static inline graphite_dim_t
pbb_dim_iter_domain (const struct poly_bb *pbb)
{
scop_p scop = PBB_SCOP (pbb);
ppl_dimension_type dim;
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (PBB_DOMAIN (pbb), &dim);
return dim - scop_nb_params (scop);
}
/* The number of params defined in PBB. */
static inline graphite_dim_t
pbb_nb_params (const struct poly_bb *pbb)
{
scop_p scop = PBB_SCOP (pbb);
return scop_nb_params (scop);
}
/* The number of scattering dimensions in the SCATTERING polyhedron
of a PBB for a given SCOP. */
static inline graphite_dim_t
pbb_nb_scattering_orig (const struct poly_bb *pbb)
{
return 2 * pbb_dim_iter_domain (pbb) + 1;
}
/* The number of scattering dimensions in PBB. */
static inline graphite_dim_t
pbb_nb_scattering_transform (const struct poly_bb *pbb)
{
return PBB_NB_SCATTERING_TRANSFORM (pbb);
}
/* Returns the number of local variables used in the transformed
scattering polyhedron of PBB. */
static inline graphite_dim_t
pbb_nb_local_vars (const struct poly_bb *pbb)
{
/* For now we do not have any local variables, as we do not do strip
mining for example. */
return PBB_NB_LOCAL_VARIABLES (pbb);
}
/* The dimension in the domain of PBB containing the iterator ITER. */
static inline ppl_dimension_type
pbb_iterator_dim (poly_bb_p pbb ATTRIBUTE_UNUSED, graphite_dim_t iter)
{
return iter;
}
/* The dimension in the domain of PBB containing the iterator ITER. */
static inline ppl_dimension_type
pbb_parameter_dim (poly_bb_p pbb, graphite_dim_t param)
{
return param
+ pbb_dim_iter_domain (pbb);
}
/* The dimension in the original scattering polyhedron of PBB
containing the scattering iterator SCATTER. */
static inline ppl_dimension_type
psco_scattering_dim (poly_bb_p pbb ATTRIBUTE_UNUSED, graphite_dim_t scatter)
{
gcc_assert (scatter < pbb_nb_scattering_orig (pbb));
return scatter;
}
/* The dimension in the transformed scattering polyhedron of PBB
containing the scattering iterator SCATTER. */
static inline ppl_dimension_type
psct_scattering_dim (poly_bb_p pbb ATTRIBUTE_UNUSED, graphite_dim_t scatter)
{
gcc_assert (scatter <= pbb_nb_scattering_transform (pbb));
return scatter;
}
ppl_dimension_type psct_scattering_dim_for_loop_depth (poly_bb_p,
graphite_dim_t);
/* The dimension in the transformed scattering polyhedron of PBB of
the local variable LV. */
static inline ppl_dimension_type
psct_local_var_dim (poly_bb_p pbb, graphite_dim_t lv)
{
gcc_assert (lv <= pbb_nb_local_vars (pbb));
return lv + pbb_nb_scattering_transform (pbb);
}
/* The dimension in the original scattering polyhedron of PBB
containing the loop iterator ITER. */
static inline ppl_dimension_type
psco_iterator_dim (poly_bb_p pbb, graphite_dim_t iter)
{
gcc_assert (iter < pbb_dim_iter_domain (pbb));
return iter + pbb_nb_scattering_orig (pbb);
}
/* The dimension in the transformed scattering polyhedron of PBB
containing the loop iterator ITER. */
static inline ppl_dimension_type
psct_iterator_dim (poly_bb_p pbb, graphite_dim_t iter)
{
gcc_assert (iter < pbb_dim_iter_domain (pbb));
return iter
+ pbb_nb_scattering_transform (pbb)
+ pbb_nb_local_vars (pbb);
}
/* The dimension in the original scattering polyhedron of PBB
containing parameter PARAM. */
static inline ppl_dimension_type
psco_parameter_dim (poly_bb_p pbb, graphite_dim_t param)
{
gcc_assert (param < pbb_nb_params (pbb));
return param
+ pbb_nb_scattering_orig (pbb)
+ pbb_dim_iter_domain (pbb);
}
/* The dimension in the transformed scattering polyhedron of PBB
containing parameter PARAM. */
static inline ppl_dimension_type
psct_parameter_dim (poly_bb_p pbb, graphite_dim_t param)
{
gcc_assert (param < pbb_nb_params (pbb));
return param
+ pbb_nb_scattering_transform (pbb)
+ pbb_nb_local_vars (pbb)
+ pbb_dim_iter_domain (pbb);
}
/* Adds to the transformed scattering polyhedron of PBB a new local
variable and returns its index. */
static inline graphite_dim_t
psct_add_local_variable (poly_bb_p pbb)
{
graphite_dim_t nlv = pbb_nb_local_vars (pbb);
ppl_dimension_type lv_column = psct_local_var_dim (pbb, nlv);
ppl_insert_dimensions (PBB_TRANSFORMED_SCATTERING (pbb), lv_column, 1);
PBB_NB_LOCAL_VARIABLES (pbb) += 1;
return nlv;
}
/* Adds a dimension to the transformed scattering polyhedron of PBB at
INDEX. */
static inline void
psct_add_scattering_dimension (poly_bb_p pbb, ppl_dimension_type index)
{
gcc_assert (index < pbb_nb_scattering_transform (pbb));
ppl_insert_dimensions (PBB_TRANSFORMED_SCATTERING (pbb), index, 1);
PBB_NB_SCATTERING_TRANSFORM (pbb) += 1;
}
/* A SCOP is a Static Control Part of the program, simple enough to be
represented in polyhedral form. */
struct scop
{
/* A SCOP is defined as a SESE region. */
void *region;
/* Number of parameters in SCoP. */
graphite_dim_t nb_params;
/* All the basic blocks in this scop that contain memory references
and that will be represented as statements in the polyhedral
representation. */
VEC (poly_bb_p, heap) *bbs;
/* Data dependence graph for this SCoP. */
struct graph *dep_graph;
/* The context describes known restrictions concerning the parameters
and relations in between the parameters.
void f (int8_t a, uint_16_t b) {
c = 2 a + b;
...
}
Here we can add these restrictions to the context:
-128 >= a >= 127
0 >= b >= 65,535
c = 2a + b */
ppl_Pointset_Powerset_C_Polyhedron_t context;
/* A hashtable of the original pairs of dependent data references.
For each pair of dependent data references, the dependence
polyhedron is stored also. */
htab_t original_pdr_pairs;
};
#define SCOP_BBS(S) (S->bbs)
#define SCOP_REGION(S) ((sese) S->region)
#define SCOP_DEP_GRAPH(S) (S->dep_graph)
#define SCOP_CONTEXT(S) (S->context)
#define SCOP_ORIGINAL_PDR_PAIRS(S) (S->original_pdr_pairs)
extern scop_p new_scop (void *);
extern void free_scop (scop_p);
extern void free_scops (VEC (scop_p, heap) *);
extern void print_generated_program (FILE *, scop_p);
extern void debug_generated_program (scop_p);
extern void print_scattering_function (FILE *, poly_bb_p);
extern void print_scattering_functions (FILE *, scop_p);
extern void debug_scattering_function (poly_bb_p);
extern void debug_scattering_functions (scop_p);
extern int scop_max_loop_depth (scop_p);
extern int unify_scattering_dimensions (scop_p);
extern bool apply_poly_transforms (scop_p);
extern bool graphite_legal_transform (scop_p);
/* Set the region of SCOP to REGION. */
static inline void
scop_set_region (scop_p scop, void *region)
{
scop->region = region;
}
/* Returns the number of parameters for SCOP. */
static inline graphite_dim_t
scop_nb_params (scop_p scop)
{
return scop->nb_params;
}
/* Set the number of params of SCOP to NB_PARAMS. */
static inline void
scop_set_nb_params (scop_p scop, graphite_dim_t nb_params)
{
scop->nb_params = nb_params;
}
#endif
/* Gimple Represented as Polyhedra.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com>
and Tobias Grosser <grosser@fim.uni-passau.de>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#ifdef HAVE_cloog
#include "ppl_c.h"
#include "cloog/cloog.h"
#include "graphite-ppl.h"
/* Translates row ROW of the CloogMatrix MATRIX to a PPL Constraint. */
static ppl_Constraint_t
cloog_matrix_to_ppl_constraint (CloogMatrix *matrix, int row)
{
int j;
ppl_Constraint_t cstr;
ppl_Coefficient_t coef;
ppl_Linear_Expression_t expr;
ppl_dimension_type dim = matrix->NbColumns - 2;
ppl_new_Coefficient (&coef);
ppl_new_Linear_Expression_with_dimension (&expr, dim);
for (j = 1; j < matrix->NbColumns - 1; j++)
{
ppl_assign_Coefficient_from_mpz_t (coef, matrix->p[row][j]);
ppl_Linear_Expression_add_to_coefficient (expr, j - 1, coef);
}
ppl_assign_Coefficient_from_mpz_t (coef,
matrix->p[row][matrix->NbColumns - 1]);
ppl_Linear_Expression_add_to_inhomogeneous (expr, coef);
ppl_delete_Coefficient (coef);
if (value_zero_p (matrix->p[row][0]))
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_EQUAL);
else
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_delete_Linear_Expression (expr);
return cstr;
}
/* Creates a PPL constraint system from MATRIX. */
static void
new_Constraint_System_from_Cloog_Matrix (ppl_Constraint_System_t *pcs,
CloogMatrix *matrix)
{
int i;
ppl_new_Constraint_System (pcs);
for (i = 0; i < matrix->NbRows; i++)
{
ppl_Constraint_t c = cloog_matrix_to_ppl_constraint (matrix, i);
ppl_Constraint_System_insert_Constraint (*pcs, c);
ppl_delete_Constraint (c);
}
}
/* Creates a PPL Polyhedron from MATRIX. */
void
new_C_Polyhedron_from_Cloog_Matrix (ppl_Polyhedron_t *ph,
CloogMatrix *matrix)
{
ppl_Constraint_System_t cs;
new_Constraint_System_from_Cloog_Matrix (&cs, matrix);
ppl_new_C_Polyhedron_recycle_Constraint_System (ph, cs);
}
/* Counts the number of constraints in PCS. */
static int
ppl_Constrain_System_number_of_constraints (ppl_const_Constraint_System_t pcs)
{
ppl_Constraint_System_const_iterator_t cit, end;
int num = 0;
ppl_new_Constraint_System_const_iterator (&cit);
ppl_new_Constraint_System_const_iterator (&end);
for (ppl_Constraint_System_begin (pcs, cit),
ppl_Constraint_System_end (pcs, end);
!ppl_Constraint_System_const_iterator_equal_test (cit, end);
ppl_Constraint_System_const_iterator_increment (cit))
num++;
ppl_delete_Constraint_System_const_iterator (cit);
ppl_delete_Constraint_System_const_iterator (end);
return num;
}
static void
oppose_constraint (CloogMatrix *m, int row)
{
int k;
/* Do not oppose the first column: it is the eq/ineq one. */
for (k = 1; k < m->NbColumns; k++)
value_oppose (m->p[row][k], m->p[row][k]);
}
/* Inserts constraint CSTR at row ROW of matrix M. */
void
insert_constraint_into_matrix (CloogMatrix *m, int row,
ppl_const_Constraint_t cstr)
{
ppl_Coefficient_t c;
ppl_dimension_type i, dim, nb_cols = m->NbColumns;
ppl_Constraint_space_dimension (cstr, &dim);
ppl_new_Coefficient (&c);
for (i = 0; i < dim; i++)
{
ppl_Constraint_coefficient (cstr, i, c);
ppl_Coefficient_to_mpz_t (c, m->p[row][i + 1]);
}
for (i = dim; i < nb_cols - 1; i++)
value_set_si (m->p[row][i + 1], 0);
ppl_Constraint_inhomogeneous_term (cstr, c);
ppl_Coefficient_to_mpz_t (c, m->p[row][nb_cols - 1]);
value_set_si (m->p[row][0], 1);
switch (ppl_Constraint_type (cstr))
{
case PPL_CONSTRAINT_TYPE_LESS_THAN:
oppose_constraint (m, row);
case PPL_CONSTRAINT_TYPE_GREATER_THAN:
value_sub_int (m->p[row][nb_cols - 1],
m->p[row][nb_cols - 1], 1);
break;
case PPL_CONSTRAINT_TYPE_LESS_OR_EQUAL:
oppose_constraint (m, row);
case PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL:
break;
case PPL_CONSTRAINT_TYPE_EQUAL:
value_set_si (m->p[row][0], 0);
break;
default:
/* Not yet implemented. */
gcc_unreachable();
}
ppl_delete_Coefficient (c);
}
/* Creates a CloogMatrix from constraint system PCS. */
static CloogMatrix *
new_Cloog_Matrix_from_ppl_Constraint_System (ppl_const_Constraint_System_t pcs)
{
CloogMatrix *matrix;
ppl_Constraint_System_const_iterator_t cit, end;
ppl_dimension_type dim;
int rows;
int row = 0;
rows = ppl_Constrain_System_number_of_constraints (pcs);
ppl_Constraint_System_space_dimension (pcs, &dim);
matrix = cloog_matrix_alloc (rows, dim + 2);
ppl_new_Constraint_System_const_iterator (&cit);
ppl_new_Constraint_System_const_iterator (&end);
for (ppl_Constraint_System_begin (pcs, cit),
ppl_Constraint_System_end (pcs, end);
!ppl_Constraint_System_const_iterator_equal_test (cit, end);
ppl_Constraint_System_const_iterator_increment (cit))
{
ppl_const_Constraint_t c;
ppl_Constraint_System_const_iterator_dereference (cit, &c);
insert_constraint_into_matrix (matrix, row, c);
row++;
}
ppl_delete_Constraint_System_const_iterator (cit);
ppl_delete_Constraint_System_const_iterator (end);
return matrix;
}
/* Creates a CloogMatrix from polyhedron PH. */
CloogMatrix *
new_Cloog_Matrix_from_ppl_Polyhedron (ppl_const_Polyhedron_t ph)
{
ppl_const_Constraint_System_t pcs;
CloogMatrix *res;
ppl_Polyhedron_get_constraints (ph, &pcs);
res = new_Cloog_Matrix_from_ppl_Constraint_System (pcs);
return res;
}
/* Creates a CloogDomain from polyhedron PH. */
CloogDomain *
new_Cloog_Domain_from_ppl_Polyhedron (ppl_const_Polyhedron_t ph)
{
CloogMatrix *mat = new_Cloog_Matrix_from_ppl_Polyhedron (ph);
CloogDomain *res = cloog_domain_matrix2domain (mat);
cloog_matrix_free (mat);
return res;
}
/* Creates a CloogDomain from a pointset powerset PS. */
CloogDomain *
new_Cloog_Domain_from_ppl_Pointset_Powerset (
ppl_Pointset_Powerset_C_Polyhedron_t ps)
{
CloogDomain *res = NULL;
ppl_Pointset_Powerset_C_Polyhedron_iterator_t it, end;
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&it);
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&end);
for (ppl_Pointset_Powerset_C_Polyhedron_iterator_begin (ps, it),
ppl_Pointset_Powerset_C_Polyhedron_iterator_end (ps, end);
!ppl_Pointset_Powerset_C_Polyhedron_iterator_equal_test (it, end);
ppl_Pointset_Powerset_C_Polyhedron_iterator_increment (it))
{
ppl_const_Polyhedron_t ph;
CloogDomain *tmp;
ppl_Pointset_Powerset_C_Polyhedron_iterator_dereference (it, &ph);
tmp = new_Cloog_Domain_from_ppl_Polyhedron (ph);
if (res == NULL)
res = tmp;
else
res = cloog_domain_union (res, tmp);
}
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (it);
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (end);
gcc_assert (res != NULL);
return res;
}
/* Set the inhomogeneous term of E to X. */
void
ppl_set_inhomogeneous_gmp (ppl_Linear_Expression_t e, Value x)
{
Value v0, v1;
ppl_Coefficient_t c;
value_init (v0);
value_init (v1);
ppl_new_Coefficient (&c);
ppl_Linear_Expression_inhomogeneous_term (e, c);
ppl_Coefficient_to_mpz_t (c, v1);
value_oppose (v1, v1);
value_assign (v0, x);
value_addto (v0, v0, v1);
ppl_assign_Coefficient_from_mpz_t (c, v0);
ppl_Linear_Expression_add_to_inhomogeneous (e, c);
value_clear (v0);
value_clear (v1);
ppl_delete_Coefficient (c);
}
/* Set E[I] to X. */
void
ppl_set_coef_gmp (ppl_Linear_Expression_t e, ppl_dimension_type i, Value x)
{
Value v0, v1;
ppl_Coefficient_t c;
value_init (v0);
value_init (v1);
ppl_new_Coefficient (&c);
ppl_Linear_Expression_coefficient (e, i, c);
ppl_Coefficient_to_mpz_t (c, v1);
value_oppose (v1, v1);
value_assign (v0, x);
value_addto (v0, v0, v1);
ppl_assign_Coefficient_from_mpz_t (c, v0);
ppl_Linear_Expression_add_to_coefficient (e, i, c);
value_clear (v0);
value_clear (v1);
ppl_delete_Coefficient (c);
}
/* Insert after X NB_NEW_DIMS empty dimensions into PH.
With x = 3 and nb_new_dims = 4
| d0 d1 d2 d3 d4
is transformed to
| d0 d1 d2 x0 x1 x2 x3 d3 d4
| map = {0, 1, 2, 7, 8, 3, 4, 5, 6}
*/
void
ppl_insert_dimensions_pointset (ppl_Pointset_Powerset_C_Polyhedron_t ph, int x,
int nb_new_dims)
{
ppl_dimension_type i, dim;
ppl_dimension_type *map;
ppl_dimension_type x_ppl, nb_new_dims_ppl;
x_ppl = (ppl_dimension_type) x;
nb_new_dims_ppl = (ppl_dimension_type) nb_new_dims;
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (ph, &dim);
ppl_Pointset_Powerset_C_Polyhedron_add_space_dimensions_and_embed (ph, nb_new_dims);
map = (ppl_dimension_type *) XNEWVEC (ppl_dimension_type, dim + nb_new_dims);
for (i = 0; i < x_ppl; i++)
map[i] = i;
for (i = x_ppl; i < x_ppl + nb_new_dims_ppl; i++)
map[dim + i - x_ppl] = i;
for (i = x_ppl + nb_new_dims_ppl; i < dim + nb_new_dims_ppl; i++)
map[i - nb_new_dims_ppl] = i;
ppl_Pointset_Powerset_C_Polyhedron_map_space_dimensions (ph, map, dim + nb_new_dims);
free (map);
}
/* Insert after X NB_NEW_DIMS empty dimensions into PH.
With x = 3 and nb_new_dims = 4
| d0 d1 d2 d3 d4
is transformed to
| d0 d1 d2 x0 x1 x2 x3 d3 d4
| map = {0, 1, 2, 7, 8, 3, 4, 5, 6}
*/
void
ppl_insert_dimensions (ppl_Polyhedron_t ph, int x,
int nb_new_dims)
{
ppl_dimension_type i, dim;
ppl_dimension_type *map;
ppl_dimension_type x_ppl, nb_new_dims_ppl;
x_ppl = (ppl_dimension_type) x;
nb_new_dims_ppl = (ppl_dimension_type) nb_new_dims;
ppl_Polyhedron_space_dimension (ph, &dim);
ppl_Polyhedron_add_space_dimensions_and_embed (ph, nb_new_dims);
map = (ppl_dimension_type *) XNEWVEC (ppl_dimension_type, dim + nb_new_dims);
for (i = 0; i < x_ppl; i++)
map[i] = i;
for (i = x_ppl; i < x_ppl + nb_new_dims_ppl; i++)
map[dim + i - x_ppl] = i;
for (i = x_ppl + nb_new_dims_ppl; i < dim + nb_new_dims_ppl; i++)
map[i - nb_new_dims_ppl] = i;
ppl_Polyhedron_map_space_dimensions (ph, map, dim + nb_new_dims);
free (map);
}
/* Based on the original polyhedron PH, returns a new polyhedron with
an extra dimension placed at position LOOP + 1 that slices the
dimension LOOP into strips of size STRIDE. */
ppl_Polyhedron_t
ppl_strip_loop (ppl_Polyhedron_t ph, ppl_dimension_type loop, int stride)
{
ppl_const_Constraint_System_t pcs;
ppl_Constraint_System_const_iterator_t cit, end;
ppl_const_Constraint_t cstr;
ppl_Linear_Expression_t expr;
int v;
ppl_dimension_type dim;
ppl_Polyhedron_t res;
ppl_Coefficient_t c;
Value val;
value_init (val);
ppl_new_Coefficient (&c);
ppl_Polyhedron_space_dimension (ph, &dim);
ppl_Polyhedron_get_constraints (ph, &pcs);
/* Start from a copy of the constraints. */
ppl_new_C_Polyhedron_from_space_dimension (&res, dim + 1, 0);
ppl_Polyhedron_add_constraints (res, pcs);
/* Add an empty dimension for the strip loop. */
ppl_insert_dimensions (res, loop, 1);
/* Identify the constraints that define the lower and upper bounds
of the strip-mined loop, and add them to the strip loop. */
{
ppl_Polyhedron_t tmp;
ppl_new_C_Polyhedron_from_space_dimension (&tmp, dim + 1, 0);
ppl_new_Constraint_System_const_iterator (&cit);
ppl_new_Constraint_System_const_iterator (&end);
for (ppl_Constraint_System_begin (pcs, cit),
ppl_Constraint_System_end (pcs, end);
!ppl_Constraint_System_const_iterator_equal_test (cit, end);
ppl_Constraint_System_const_iterator_increment (cit))
{
ppl_Constraint_System_const_iterator_dereference (cit, &cstr);
ppl_new_Linear_Expression_from_Constraint (&expr, cstr);
ppl_Linear_Expression_coefficient (expr, loop, c);
ppl_delete_Linear_Expression (expr);
ppl_Coefficient_to_mpz_t (c, val);
v = value_get_si (val);
if (0 < v || v < 0)
ppl_Polyhedron_add_constraint (tmp, cstr);
}
ppl_delete_Constraint_System_const_iterator (cit);
ppl_delete_Constraint_System_const_iterator (end);
ppl_insert_dimensions (tmp, loop + 1, 1);
ppl_Polyhedron_get_constraints (tmp, &pcs);
ppl_Polyhedron_add_constraints (res, pcs);
ppl_delete_Polyhedron (tmp);
}
/* Lower bound of a tile starts at "stride * outer_iv". */
{
ppl_Constraint_t new_cstr;
ppl_new_Linear_Expression_with_dimension (&expr, dim + 1);
ppl_set_coef (expr, loop + 1, 1);
ppl_set_coef (expr, loop, -1 * stride);
ppl_new_Constraint (&new_cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_delete_Linear_Expression (expr);
ppl_Polyhedron_add_constraint (res, new_cstr);
ppl_delete_Constraint (new_cstr);
}
/* Upper bound of a tile stops at "stride * outer_iv + stride - 1",
or at the old upper bound that is not modified. */
{
ppl_Constraint_t new_cstr;
ppl_new_Linear_Expression_with_dimension (&expr, dim + 1);
ppl_set_coef (expr, loop + 1, -1);
ppl_set_coef (expr, loop, stride);
ppl_set_inhomogeneous (expr, stride - 1);
ppl_new_Constraint (&new_cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_delete_Linear_Expression (expr);
ppl_Polyhedron_add_constraint (res, new_cstr);
ppl_delete_Constraint (new_cstr);
}
value_clear (val);
ppl_delete_Coefficient (c);
return res;
}
/* Lexicographically compares two linear expressions A and B and
returns negative when A < B, 0 when A == B and positive when A > B. */
int
ppl_lexico_compare_linear_expressions (ppl_Linear_Expression_t a,
ppl_Linear_Expression_t b)
{
ppl_dimension_type min_length, length1, length2;
ppl_dimension_type i;
ppl_Coefficient_t c;
int res;
Value va, vb;
ppl_Linear_Expression_space_dimension (a, &length1);
ppl_Linear_Expression_space_dimension (b, &length2);
ppl_new_Coefficient (&c);
value_init (va);
value_init (vb);
if (length1 < length2)
min_length = length1;
else
min_length = length2;
for (i = 0; i < min_length; i++)
{
ppl_Linear_Expression_coefficient (a, i, c);
ppl_Coefficient_to_mpz_t (c, va);
ppl_Linear_Expression_coefficient (b, i, c);
ppl_Coefficient_to_mpz_t (c, vb);
res = value_compare (va, vb);
if (res == 0)
continue;
value_clear (va);
value_clear (vb);
ppl_delete_Coefficient (c);
return res;
}
value_clear (va);
value_clear (vb);
ppl_delete_Coefficient (c);
return length1 - length2;
}
/* Print to FILE the polyhedron PH under its PolyLib matrix form. */
void
ppl_print_polyhedron_matrix (FILE *file, ppl_const_Polyhedron_t ph)
{
CloogMatrix *mat = new_Cloog_Matrix_from_ppl_Polyhedron (ph);
cloog_matrix_print (file, mat);
cloog_matrix_free (mat);
}
/* Print to FILE the powerset PS in its PolyLib matrix form. */
void
ppl_print_powerset_matrix (FILE *file,
ppl_Pointset_Powerset_C_Polyhedron_t ps)
{
ppl_Pointset_Powerset_C_Polyhedron_iterator_t it, end;
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&it);
ppl_new_Pointset_Powerset_C_Polyhedron_iterator (&end);
for (ppl_Pointset_Powerset_C_Polyhedron_iterator_begin (ps, it),
ppl_Pointset_Powerset_C_Polyhedron_iterator_end (ps, end);
!ppl_Pointset_Powerset_C_Polyhedron_iterator_equal_test (it, end);
ppl_Pointset_Powerset_C_Polyhedron_iterator_increment (it))
{
ppl_const_Polyhedron_t ph;
ppl_Pointset_Powerset_C_Polyhedron_iterator_dereference (it, &ph);
ppl_print_polyhedron_matrix (file, ph);
}
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (it);
ppl_delete_Pointset_Powerset_C_Polyhedron_iterator (end);
}
/* Print to STDERR the polyhedron PH under its PolyLib matrix form. */
void
debug_ppl_polyhedron_matrix (ppl_Polyhedron_t ph)
{
ppl_print_polyhedron_matrix (stderr, ph);
}
/* Print to STDERR the powerset PS in its PolyLib matrix form. */
void
debug_ppl_powerset_matrix (ppl_Pointset_Powerset_C_Polyhedron_t ps)
{
ppl_print_powerset_matrix (stderr, ps);
}
/* Read from FILE a polyhedron under PolyLib matrix form and return a
PPL polyhedron object. */
void
ppl_read_polyhedron_matrix (ppl_Polyhedron_t *ph, FILE *file)
{
CloogMatrix *mat = cloog_matrix_read (file);
new_C_Polyhedron_from_Cloog_Matrix (ph, mat);
cloog_matrix_free (mat);
}
#endif
/* Gimple Represented as Polyhedra.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@inria.fr>
and Tobias Grosser <grosser@fim.uni-passau.de>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_GRAPHITE_PPL_H
#define GCC_GRAPHITE_PPL_H
#include "double-int.h"
#include "tree.h"
CloogMatrix *new_Cloog_Matrix_from_ppl_Polyhedron (ppl_const_Polyhedron_t);
CloogDomain *new_Cloog_Domain_from_ppl_Polyhedron (ppl_const_Polyhedron_t);
CloogDomain * new_Cloog_Domain_from_ppl_Pointset_Powerset (
ppl_Pointset_Powerset_C_Polyhedron_t);
void new_C_Polyhedron_from_Cloog_Matrix (ppl_Polyhedron_t *, CloogMatrix *);
void insert_constraint_into_matrix (CloogMatrix *, int, ppl_const_Constraint_t);
ppl_Polyhedron_t ppl_strip_loop (ppl_Polyhedron_t, ppl_dimension_type, int);
int ppl_lexico_compare_linear_expressions (ppl_Linear_Expression_t,
ppl_Linear_Expression_t);
void ppl_print_polyhedron_matrix (FILE *, ppl_const_Polyhedron_t);
void ppl_print_powerset_matrix (FILE *, ppl_Pointset_Powerset_C_Polyhedron_t);
void debug_ppl_polyhedron_matrix (ppl_Polyhedron_t);
void debug_ppl_powerset_matrix (ppl_Pointset_Powerset_C_Polyhedron_t);
void ppl_read_polyhedron_matrix (ppl_Polyhedron_t *, FILE *);
void ppl_insert_dimensions (ppl_Polyhedron_t, int, int);
void ppl_insert_dimensions_pointset (ppl_Pointset_Powerset_C_Polyhedron_t, int,
int);
void ppl_set_inhomogeneous_gmp (ppl_Linear_Expression_t, Value);
void ppl_set_coef_gmp (ppl_Linear_Expression_t, ppl_dimension_type, Value);
/* Assigns to RES the value of the INTEGER_CST T. */
static inline void
tree_int_to_gmp (tree t, Value res)
{
double_int di = tree_to_double_int (t);
mpz_set_double_int (res, di, TYPE_UNSIGNED (TREE_TYPE (t)));
}
/* Converts a GMP constant VAL to a tree and returns it. */
static inline tree
gmp_cst_to_tree (tree type, Value val)
{
tree t = type ? type : integer_type_node;
Value tmp;
double_int di;
value_init (tmp);
value_assign (tmp, val);
di = mpz_get_double_int (t, tmp, true);
value_clear (tmp);
return double_int_to_tree (t, di);
}
/* Set the inhomogeneous term of E to the integer X. */
static inline void
ppl_set_inhomogeneous (ppl_Linear_Expression_t e, int x)
{
Value v;
value_init (v);
value_set_si (v, x);
ppl_set_inhomogeneous_gmp (e, v);
value_clear (v);
}
/* Set the inhomogeneous term of E to the tree X. */
static inline void
ppl_set_inhomogeneous_tree (ppl_Linear_Expression_t e, tree x)
{
Value v;
value_init (v);
tree_int_to_gmp (x, v);
ppl_set_inhomogeneous_gmp (e, v);
value_clear (v);
}
/* Set E[I] to integer X. */
static inline void
ppl_set_coef (ppl_Linear_Expression_t e, ppl_dimension_type i, int x)
{
Value v;
value_init (v);
value_set_si (v, x);
ppl_set_coef_gmp (e, i, v);
value_clear (v);
}
/* Set E[I] to tree X. */
static inline void
ppl_set_coef_tree (ppl_Linear_Expression_t e, ppl_dimension_type i, tree x)
{
Value v;
value_init (v);
tree_int_to_gmp (x, v);
ppl_set_coef_gmp (e, i, v);
value_clear (v);
}
/* Sets RES to the max of V1 and V2. */
static inline void
value_max (Value res, Value v1, Value v2)
{
if (value_compare (v1, v2) < 0)
value_assign (res, v2);
value_assign (res, v1);
}
#endif
/* Detection of Static Control Parts (SCoP) for Graphite.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Tobias Grosser <grosser@fim.uni-passau.de>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "sese.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
#include "graphite-scop-detection.h"
/* The type of the analyzed basic block. */
typedef enum gbb_type {
GBB_UNKNOWN,
GBB_LOOP_SING_EXIT_HEADER,
GBB_LOOP_MULT_EXIT_HEADER,
GBB_LOOP_EXIT,
GBB_COND_HEADER,
GBB_SIMPLE,
GBB_LAST
} gbb_type;
/* Detect the type of BB. Loop headers are only marked, if they are
new. This means their loop_father is different to LAST_LOOP.
Otherwise they are treated like any other bb and their type can be
any other type. */
static gbb_type
get_bb_type (basic_block bb, struct loop *last_loop)
{
VEC (basic_block, heap) *dom;
int nb_dom, nb_suc;
struct loop *loop = bb->loop_father;
/* Check, if we entry into a new loop. */
if (loop != last_loop)
{
if (single_exit (loop) != NULL)
return GBB_LOOP_SING_EXIT_HEADER;
else if (loop->num != 0)
return GBB_LOOP_MULT_EXIT_HEADER;
else
return GBB_COND_HEADER;
}
dom = get_dominated_by (CDI_DOMINATORS, bb);
nb_dom = VEC_length (basic_block, dom);
VEC_free (basic_block, heap, dom);
if (nb_dom == 0)
return GBB_LAST;
nb_suc = VEC_length (edge, bb->succs);
if (nb_dom == 1 && nb_suc == 1)
return GBB_SIMPLE;
return GBB_COND_HEADER;
}
/* A SCoP detection region, defined using bbs as borders.
All control flow touching this region, comes in passing basic_block
ENTRY and leaves passing basic_block EXIT. By using bbs instead of
edges for the borders we are able to represent also regions that do
not have a single entry or exit edge.
But as they have a single entry basic_block and a single exit
basic_block, we are able to generate for every sd_region a single
entry and exit edge.
1 2
\ /
3 <- entry
|
4
/ \ This region contains: {3, 4, 5, 6, 7, 8}
5 6
| |
7 8
\ /
9 <- exit */
typedef struct sd_region_p
{
/* The entry bb dominates all bbs in the sd_region. It is part of
the region. */
basic_block entry;
/* The exit bb postdominates all bbs in the sd_region, but is not
part of the region. */
basic_block exit;
} sd_region;
DEF_VEC_O(sd_region);
DEF_VEC_ALLOC_O(sd_region, heap);
/* Moves the scops from SOURCE to TARGET and clean up SOURCE. */
static void
move_sd_regions (VEC (sd_region, heap) **source,
VEC (sd_region, heap) **target)
{
sd_region *s;
int i;
for (i = 0; VEC_iterate (sd_region, *source, i, s); i++)
VEC_safe_push (sd_region, heap, *target, s);
VEC_free (sd_region, heap, *source);
}
/* Something like "n * m" is not allowed. */
static bool
graphite_can_represent_init (tree e)
{
switch (TREE_CODE (e))
{
case POLYNOMIAL_CHREC:
return graphite_can_represent_init (CHREC_LEFT (e))
&& graphite_can_represent_init (CHREC_RIGHT (e));
case MULT_EXPR:
if (chrec_contains_symbols (TREE_OPERAND (e, 0)))
return host_integerp (TREE_OPERAND (e, 1), 0);
else
return host_integerp (TREE_OPERAND (e, 0), 0);
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
case MINUS_EXPR:
return graphite_can_represent_init (TREE_OPERAND (e, 0))
&& graphite_can_represent_init (TREE_OPERAND (e, 1));
case NEGATE_EXPR:
case BIT_NOT_EXPR:
CASE_CONVERT:
case NON_LVALUE_EXPR:
return graphite_can_represent_init (TREE_OPERAND (e, 0));
default:
break;
}
return true;
}
/* Return true when SCEV can be represented in the polyhedral model.
An expression can be represented, if it can be expressed as an
affine expression. For loops (i, j) and parameters (m, n) all
affine expressions are of the form:
x1 * i + x2 * j + x3 * m + x4 * n + x5 * 1 where x1..x5 element of Z
1 i + 20 j + (-2) m + 25
Something like "i * n" or "n * m" is not allowed.
OUTERMOST_LOOP defines the outermost loop that can variate. */
static bool
graphite_can_represent_scev (tree scev, int outermost_loop)
{
if (chrec_contains_undetermined (scev))
return false;
if (TREE_CODE (scev) == POLYNOMIAL_CHREC
/* Check for constant strides. With a non constant stride of
'n' we would have a value of 'iv * n'. */
&& (!evolution_function_right_is_integer_cst (scev)
/* Check the initial value: 'n * m' cannot be represented. */
|| !graphite_can_represent_init (scev)))
return false;
/* Only affine functions can be represented. */
if (!scev_is_linear_expression (scev))
return false;
return evolution_function_is_invariant_p (scev, outermost_loop)
|| evolution_function_is_affine_multivariate_p (scev, outermost_loop);
}
/* Return true when EXPR can be represented in the polyhedral model.
This means an expression can be represented, if it is linear with
respect to the loops and the strides are non parametric.
LOOP is the place where the expr will be evaluated and OUTERMOST_LOOP
defindes the outermost loop that can variate. SCOP_ENTRY defines the
entry of the region we analyse. */
static bool
graphite_can_represent_expr (basic_block scop_entry, loop_p loop,
loop_p outermost_loop, tree expr)
{
tree scev = analyze_scalar_evolution (loop, expr);
scev = instantiate_scev (scop_entry, loop, scev);
return graphite_can_represent_scev (scev, outermost_loop->num);
}
/* Return false if the tree_code of the operand OP or any of its operands
is component_ref. */
static bool
exclude_component_ref (tree op)
{
int i;
int len;
if (!op)
return true;
if (TREE_CODE (op) == COMPONENT_REF)
return false;
len = TREE_OPERAND_LENGTH (op);
for (i = 0; i < len; ++i)
if (!exclude_component_ref (TREE_OPERAND (op, i)))
return false;
return true;
}
/* Return true if the data references of STMT can be represented by
Graphite. */
static bool
stmt_has_simple_data_refs_p (loop_p outermost_loop, gimple stmt)
{
data_reference_p dr;
unsigned i;
int j;
bool res = true;
int loop = outermost_loop->num;
VEC (data_reference_p, heap) *drs = VEC_alloc (data_reference_p, heap, 5);
graphite_find_data_references_in_stmt (outermost_loop, stmt, &drs);
for (j = 0; VEC_iterate (data_reference_p, drs, j, dr); j++)
for (i = 0; i < DR_NUM_DIMENSIONS (dr); i++)
if (!graphite_can_represent_scev (DR_ACCESS_FN (dr, i), loop))
{
res = false;
goto done;
}
done:
free_data_refs (drs);
return res;
}
/* Return true if we can create an affine data-ref for OP in STMT
in regards to OUTERMOST_LOOP. */
static bool
stmt_simple_memref_p (loop_p outermost_loop, gimple stmt, tree op)
{
data_reference_p dr;
unsigned int i;
VEC(tree,heap) *fns;
tree t;
bool res = true;
dr = create_data_ref (outermost_loop, op, stmt, true);
fns = DR_ACCESS_FNS (dr);
for (i = 0; VEC_iterate (tree, fns, i, t); i++)
if (!graphite_can_represent_scev (t, outermost_loop->num))
{
res = false;
break;
}
free_data_ref (dr);
return res;
}
/* Return true if the operand OP used in STMT is simple in regards to
OUTERMOST_LOOP. */
static bool
is_simple_operand (loop_p outermost_loop, gimple stmt, tree op)
{
/* It is not a simple operand when it is a declaration, */
if (DECL_P (op))
return false;
/* or a structure, */
if (AGGREGATE_TYPE_P (TREE_TYPE (op)))
return false;
/* or a memory access that cannot be analyzed by the data reference
analysis. */
if (handled_component_p (op) || INDIRECT_REF_P (op))
if (!stmt_simple_memref_p (outermost_loop, stmt, op))
return false;
return exclude_component_ref (op);
}
/* Return true only when STMT is simple enough for being handled by
Graphite. This depends on SCOP_ENTRY, as the parameters are
initialized relatively to this basic block, the linear functions
are initialized to OUTERMOST_LOOP and BB is the place where we try
to evaluate the STMT. */
static bool
stmt_simple_for_scop_p (basic_block scop_entry, loop_p outermost_loop,
gimple stmt, basic_block bb)
{
loop_p loop = bb->loop_father;
gcc_assert (scop_entry);
/* GIMPLE_ASM and GIMPLE_CALL may embed arbitrary side effects.
Calls have side-effects, except those to const or pure
functions. */
if (gimple_has_volatile_ops (stmt)
|| (gimple_code (stmt) == GIMPLE_CALL
&& !(gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE)))
|| (gimple_code (stmt) == GIMPLE_ASM))
return false;
if (!stmt_has_simple_data_refs_p (outermost_loop, stmt))
return false;
switch (gimple_code (stmt))
{
case GIMPLE_RETURN:
case GIMPLE_LABEL:
return true;
case GIMPLE_COND:
{
tree op;
ssa_op_iter op_iter;
enum tree_code code = gimple_cond_code (stmt);
/* We can handle all binary comparisons. Inequalities are
also supported as they can be represented with union of
polyhedra. */
if (!(code == LT_EXPR
|| code == GT_EXPR
|| code == LE_EXPR
|| code == GE_EXPR
|| code == EQ_EXPR
|| code == NE_EXPR))
return false;
FOR_EACH_SSA_TREE_OPERAND (op, stmt, op_iter, SSA_OP_ALL_USES)
if (!graphite_can_represent_expr (scop_entry, loop, outermost_loop,
op)
/* We can not handle REAL_TYPE. Failed for pr39260. */
|| TREE_CODE (TREE_TYPE (op)) == REAL_TYPE)
return false;
return true;
}
case GIMPLE_ASSIGN:
{
enum tree_code code = gimple_assign_rhs_code (stmt);
switch (get_gimple_rhs_class (code))
{
case GIMPLE_UNARY_RHS:
case GIMPLE_SINGLE_RHS:
return (is_simple_operand (outermost_loop, stmt,
gimple_assign_lhs (stmt))
&& is_simple_operand (outermost_loop, stmt,
gimple_assign_rhs1 (stmt)));
case GIMPLE_BINARY_RHS:
return (is_simple_operand (outermost_loop, stmt,
gimple_assign_lhs (stmt))
&& is_simple_operand (outermost_loop, stmt,
gimple_assign_rhs1 (stmt))
&& is_simple_operand (outermost_loop, stmt,
gimple_assign_rhs2 (stmt)));
case GIMPLE_INVALID_RHS:
default:
gcc_unreachable ();
}
}
case GIMPLE_CALL:
{
size_t i;
size_t n = gimple_call_num_args (stmt);
tree lhs = gimple_call_lhs (stmt);
if (lhs && !is_simple_operand (outermost_loop, stmt, lhs))
return false;
for (i = 0; i < n; i++)
if (!is_simple_operand (outermost_loop, stmt,
gimple_call_arg (stmt, i)))
return false;
return true;
}
default:
/* These nodes cut a new scope. */
return false;
}
return false;
}
/* Returns the statement of BB that contains a harmful operation: that
can be a function call with side effects, the induction variables
are not linear with respect to SCOP_ENTRY, etc. The current open
scop should end before this statement. The evaluation is limited using
OUTERMOST_LOOP as outermost loop that may change. */
static gimple
harmful_stmt_in_bb (basic_block scop_entry, loop_p outer_loop, basic_block bb)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
if (!stmt_simple_for_scop_p (scop_entry, outer_loop, gsi_stmt (gsi), bb))
return gsi_stmt (gsi);
return NULL;
}
/* Return true when it is not possible to represent LOOP in the
polyhedral representation. This is evaluated taking SCOP_ENTRY and
OUTERMOST_LOOP in mind. */
static bool
graphite_can_represent_loop (basic_block scop_entry, loop_p outermost_loop,
loop_p loop)
{
tree niter = number_of_latch_executions (loop);
/* Number of iterations unknown. */
if (chrec_contains_undetermined (niter))
return false;
/* Number of iterations not affine. */
if (!graphite_can_represent_expr (scop_entry, loop, outermost_loop, niter))
return false;
return true;
}
/* Store information needed by scopdet_* functions. */
struct scopdet_info
{
/* Exit of the open scop would stop if the current BB is harmful. */
basic_block exit;
/* Where the next scop would start if the current BB is harmful. */
basic_block next;
/* The bb or one of its children contains open loop exits. That means
loop exit nodes that are not surrounded by a loop dominated by bb. */
bool exits;
/* The bb or one of its children contains only structures we can handle. */
bool difficult;
};
static struct scopdet_info build_scops_1 (basic_block, loop_p,
VEC (sd_region, heap) **, loop_p);
/* Calculates BB infos. If bb is difficult we add valid SCoPs dominated by BB
to SCOPS. TYPE is the gbb_type of BB. */
static struct scopdet_info
scopdet_basic_block_info (basic_block bb, loop_p outermost_loop,
VEC (sd_region, heap) **scops, gbb_type type)
{
loop_p loop = bb->loop_father;
struct scopdet_info result;
gimple stmt;
/* XXX: ENTRY_BLOCK_PTR could be optimized in later steps. */
basic_block entry_block = ENTRY_BLOCK_PTR;
stmt = harmful_stmt_in_bb (entry_block, outermost_loop, bb);
result.difficult = (stmt != NULL);
result.exit = NULL;
switch (type)
{
case GBB_LAST:
result.next = NULL;
result.exits = false;
/* Mark bbs terminating a SESE region difficult, if they start
a condition. */
if (!single_succ_p (bb))
result.difficult = true;
else
result.exit = single_succ (bb);
break;
case GBB_SIMPLE:
result.next = single_succ (bb);
result.exits = false;
result.exit = single_succ (bb);
break;
case GBB_LOOP_SING_EXIT_HEADER:
{
VEC (sd_region, heap) *regions = VEC_alloc (sd_region, heap, 3);
struct scopdet_info sinfo;
edge exit_e = single_exit (loop);
sinfo = build_scops_1 (bb, outermost_loop, &regions, loop);
if (!graphite_can_represent_loop (entry_block, outermost_loop, loop))
result.difficult = true;
result.difficult |= sinfo.difficult;
/* Try again with another loop level. */
if (result.difficult
&& loop_depth (outermost_loop) + 1 == loop_depth (loop))
{
outermost_loop = loop;
VEC_free (sd_region, heap, regions);
regions = VEC_alloc (sd_region, heap, 3);
sinfo = scopdet_basic_block_info (bb, outermost_loop, scops, type);
result = sinfo;
result.difficult = true;
if (sinfo.difficult)
move_sd_regions (&regions, scops);
else
{
sd_region open_scop;
open_scop.entry = bb;
open_scop.exit = exit_e->dest;
VEC_safe_push (sd_region, heap, *scops, &open_scop);
VEC_free (sd_region, heap, regions);
}
}
else
{
result.exit = exit_e->dest;
result.next = exit_e->dest;
/* If we do not dominate result.next, remove it. It's either
the EXIT_BLOCK_PTR, or another bb dominates it and will
call the scop detection for this bb. */
if (!dominated_by_p (CDI_DOMINATORS, result.next, bb))
result.next = NULL;
if (exit_e->src->loop_father != loop)
result.next = NULL;
result.exits = false;
if (result.difficult)
move_sd_regions (&regions, scops);
else
VEC_free (sd_region, heap, regions);
}
break;
}
case GBB_LOOP_MULT_EXIT_HEADER:
{
/* XXX: For now we just do not join loops with multiple exits. If the
exits lead to the same bb it may be possible to join the loop. */
VEC (sd_region, heap) *regions = VEC_alloc (sd_region, heap, 3);
VEC (edge, heap) *exits = get_loop_exit_edges (loop);
edge e;
int i;
build_scops_1 (bb, loop, &regions, loop);
/* Scan the code dominated by this loop. This means all bbs, that are
are dominated by a bb in this loop, but are not part of this loop.
The easiest case:
- The loop exit destination is dominated by the exit sources.
TODO: We miss here the more complex cases:
- The exit destinations are dominated by another bb inside
the loop.
- The loop dominates bbs, that are not exit destinations. */
for (i = 0; VEC_iterate (edge, exits, i, e); i++)
if (e->src->loop_father == loop
&& dominated_by_p (CDI_DOMINATORS, e->dest, e->src))
{
if (loop_outer (outermost_loop))
outermost_loop = loop_outer (outermost_loop);
/* Pass loop_outer to recognize e->dest as loop header in
build_scops_1. */
if (e->dest->loop_father->header == e->dest)
build_scops_1 (e->dest, outermost_loop, &regions,
loop_outer (e->dest->loop_father));
else
build_scops_1 (e->dest, outermost_loop, &regions,
e->dest->loop_father);
}
result.next = NULL;
result.exit = NULL;
result.difficult = true;
result.exits = false;
move_sd_regions (&regions, scops);
VEC_free (edge, heap, exits);
break;
}
case GBB_COND_HEADER:
{
VEC (sd_region, heap) *regions = VEC_alloc (sd_region, heap, 3);
struct scopdet_info sinfo;
VEC (basic_block, heap) *dominated;
int i;
basic_block dom_bb;
basic_block last_exit = NULL;
edge e;
result.exits = false;
/* First check the successors of BB, and check if it is
possible to join the different branches. */
for (i = 0; VEC_iterate (edge, bb->succs, i, e); i++)
{
/* Ignore loop exits. They will be handled after the loop
body. */
if (is_loop_exit (loop, e->dest))
{
result.exits = true;
continue;
}
/* Do not follow edges that lead to the end of the
conditions block. For example, in
| 0
| /|\
| 1 2 |
| | | |
| 3 4 |
| \|/
| 6
the edge from 0 => 6. Only check if all paths lead to
the same node 6. */
if (!single_pred_p (e->dest))
{
/* Check, if edge leads directly to the end of this
condition. */
if (!last_exit)
last_exit = e->dest;
if (e->dest != last_exit)
result.difficult = true;
continue;
}
if (!dominated_by_p (CDI_DOMINATORS, e->dest, bb))
{
result.difficult = true;
continue;
}
sinfo = build_scops_1 (e->dest, outermost_loop, &regions, loop);
result.exits |= sinfo.exits;
result.difficult |= sinfo.difficult;
/* Checks, if all branches end at the same point.
If that is true, the condition stays joinable.
Have a look at the example above. */
if (sinfo.exit)
{
if (!last_exit)
last_exit = sinfo.exit;
if (sinfo.exit != last_exit)
result.difficult = true;
}
else
result.difficult = true;
}
if (!last_exit)
result.difficult = true;
/* Join the branches of the condition if possible. */
if (!result.exits && !result.difficult)
{
/* Only return a next pointer if we dominate this pointer.
Otherwise it will be handled by the bb dominating it. */
if (dominated_by_p (CDI_DOMINATORS, last_exit, bb)
&& last_exit != bb)
result.next = last_exit;
else
result.next = NULL;
result.exit = last_exit;
VEC_free (sd_region, heap, regions);
break;
}
/* Scan remaining bbs dominated by BB. */
dominated = get_dominated_by (CDI_DOMINATORS, bb);
for (i = 0; VEC_iterate (basic_block, dominated, i, dom_bb); i++)
{
/* Ignore loop exits: they will be handled after the loop body. */
if (loop_depth (find_common_loop (loop, dom_bb->loop_father))
< loop_depth (loop))
{
result.exits = true;
continue;
}
/* Ignore the bbs processed above. */
if (single_pred_p (dom_bb) && single_pred (dom_bb) == bb)
continue;
if (loop_depth (loop) > loop_depth (dom_bb->loop_father))
sinfo = build_scops_1 (dom_bb, outermost_loop, &regions,
loop_outer (loop));
else
sinfo = build_scops_1 (dom_bb, outermost_loop, &regions, loop);
result.exits |= sinfo.exits;
result.difficult = true;
result.exit = NULL;
}
VEC_free (basic_block, heap, dominated);
result.next = NULL;
move_sd_regions (&regions, scops);
break;
}
default:
gcc_unreachable ();
}
return result;
}
/* Starting from CURRENT we walk the dominance tree and add new sd_regions to
SCOPS. The analyse if a sd_region can be handled is based on the value
of OUTERMOST_LOOP. Only loops inside OUTERMOST loops may change. LOOP
is the loop in which CURRENT is handled.
TODO: These functions got a little bit big. They definitely should be cleaned
up. */
static struct scopdet_info
build_scops_1 (basic_block current, loop_p outermost_loop,
VEC (sd_region, heap) **scops, loop_p loop)
{
bool in_scop = false;
sd_region open_scop;
struct scopdet_info sinfo;
/* Initialize result. */
struct scopdet_info result;
result.exits = false;
result.difficult = false;
result.next = NULL;
result.exit = NULL;
open_scop.entry = NULL;
open_scop.exit = NULL;
sinfo.exit = NULL;
/* Loop over the dominance tree. If we meet a difficult bb, close
the current SCoP. Loop and condition header start a new layer,
and can only be added if all bbs in deeper layers are simple. */
while (current != NULL)
{
sinfo = scopdet_basic_block_info (current, outermost_loop, scops,
get_bb_type (current, loop));
if (!in_scop && !(sinfo.exits || sinfo.difficult))
{
open_scop.entry = current;
open_scop.exit = NULL;
in_scop = true;
}
else if (in_scop && (sinfo.exits || sinfo.difficult))
{
open_scop.exit = current;
VEC_safe_push (sd_region, heap, *scops, &open_scop);
in_scop = false;
}
result.difficult |= sinfo.difficult;
result.exits |= sinfo.exits;
current = sinfo.next;
}
/* Try to close open_scop, if we are still in an open SCoP. */
if (in_scop)
{
open_scop.exit = sinfo.exit;
gcc_assert (open_scop.exit);
VEC_safe_push (sd_region, heap, *scops, &open_scop);
}
result.exit = sinfo.exit;
return result;
}
/* Checks if a bb is contained in REGION. */
static bool
bb_in_sd_region (basic_block bb, sd_region *region)
{
return bb_in_region (bb, region->entry, region->exit);
}
/* Returns the single entry edge of REGION, if it does not exits NULL. */
static edge
find_single_entry_edge (sd_region *region)
{
edge e;
edge_iterator ei;
edge entry = NULL;
FOR_EACH_EDGE (e, ei, region->entry->preds)
if (!bb_in_sd_region (e->src, region))
{
if (entry)
{
entry = NULL;
break;
}
else
entry = e;
}
return entry;
}
/* Returns the single exit edge of REGION, if it does not exits NULL. */
static edge
find_single_exit_edge (sd_region *region)
{
edge e;
edge_iterator ei;
edge exit = NULL;
FOR_EACH_EDGE (e, ei, region->exit->preds)
if (bb_in_sd_region (e->src, region))
{
if (exit)
{
exit = NULL;
break;
}
else
exit = e;
}
return exit;
}
/* Create a single entry edge for REGION. */
static void
create_single_entry_edge (sd_region *region)
{
if (find_single_entry_edge (region))
return;
/* There are multiple predecessors for bb_3
| 1 2
| | /
| |/
| 3 <- entry
| |\
| | |
| 4 ^
| | |
| |/
| 5
There are two edges (1->3, 2->3), that point from outside into the region,
and another one (5->3), a loop latch, lead to bb_3.
We split bb_3.
| 1 2
| | /
| |/
|3.0
| |\ (3.0 -> 3.1) = single entry edge
|3.1 | <- entry
| | |
| | |
| 4 ^
| | |
| |/
| 5
If the loop is part of the SCoP, we have to redirect the loop latches.
| 1 2
| | /
| |/
|3.0
| | (3.0 -> 3.1) = entry edge
|3.1 <- entry
| |\
| | |
| 4 ^
| | |
| |/
| 5 */
if (region->entry->loop_father->header != region->entry
|| dominated_by_p (CDI_DOMINATORS,
loop_latch_edge (region->entry->loop_father)->src,
region->exit))
{
edge forwarder = split_block_after_labels (region->entry);
region->entry = forwarder->dest;
}
else
/* This case is never executed, as the loop headers seem always to have a
single edge pointing from outside into the loop. */
gcc_unreachable ();
#ifdef ENABLE_CHECKING
gcc_assert (find_single_entry_edge (region));
#endif
}
/* Check if the sd_region, mentioned in EDGE, has no exit bb. */
static bool
sd_region_without_exit (edge e)
{
sd_region *r = (sd_region *) e->aux;
if (r)
return r->exit == NULL;
else
return false;
}
/* Create a single exit edge for REGION. */
static void
create_single_exit_edge (sd_region *region)
{
edge e;
edge_iterator ei;
edge forwarder = NULL;
basic_block exit;
if (find_single_exit_edge (region))
return;
/* We create a forwarder bb (5) for all edges leaving this region
(3->5, 4->5). All other edges leading to the same bb, are moved
to a new bb (6). If these edges where part of another region (2->5)
we update the region->exit pointer, of this region.
To identify which edge belongs to which region we depend on the e->aux
pointer in every edge. It points to the region of the edge or to NULL,
if the edge is not part of any region.
1 2 3 4 1->5 no region, 2->5 region->exit = 5,
\| |/ 3->5 region->exit = NULL, 4->5 region->exit = NULL
5 <- exit
changes to
1 2 3 4 1->6 no region, 2->6 region->exit = 6,
| | \/ 3->5 no region, 4->5 no region,
| | 5
\| / 5->6 region->exit = 6
6
Now there is only a single exit edge (5->6). */
exit = region->exit;
region->exit = NULL;
forwarder = make_forwarder_block (exit, &sd_region_without_exit, NULL);
/* Unmark the edges, that are no longer exit edges. */
FOR_EACH_EDGE (e, ei, forwarder->src->preds)
if (e->aux)
e->aux = NULL;
/* Mark the new exit edge. */
single_succ_edge (forwarder->src)->aux = region;
/* Update the exit bb of all regions, where exit edges lead to
forwarder->dest. */
FOR_EACH_EDGE (e, ei, forwarder->dest->preds)
if (e->aux)
((sd_region *) e->aux)->exit = forwarder->dest;
#ifdef ENABLE_CHECKING
gcc_assert (find_single_exit_edge (region));
#endif
}
/* Unmark the exit edges of all REGIONS.
See comment in "create_single_exit_edge". */
static void
unmark_exit_edges (VEC (sd_region, heap) *regions)
{
int i;
sd_region *s;
edge e;
edge_iterator ei;
for (i = 0; VEC_iterate (sd_region, regions, i, s); i++)
FOR_EACH_EDGE (e, ei, s->exit->preds)
e->aux = NULL;
}
/* Mark the exit edges of all REGIONS.
See comment in "create_single_exit_edge". */
static void
mark_exit_edges (VEC (sd_region, heap) *regions)
{
int i;
sd_region *s;
edge e;
edge_iterator ei;
for (i = 0; VEC_iterate (sd_region, regions, i, s); i++)
FOR_EACH_EDGE (e, ei, s->exit->preds)
if (bb_in_sd_region (e->src, s))
e->aux = s;
}
/* Create for all scop regions a single entry and a single exit edge. */
static void
create_sese_edges (VEC (sd_region, heap) *regions)
{
int i;
sd_region *s;
for (i = 0; VEC_iterate (sd_region, regions, i, s); i++)
create_single_entry_edge (s);
mark_exit_edges (regions);
for (i = 0; VEC_iterate (sd_region, regions, i, s); i++)
create_single_exit_edge (s);
unmark_exit_edges (regions);
fix_loop_structure (NULL);
#ifdef ENABLE_CHECKING
verify_loop_structure ();
verify_dominators (CDI_DOMINATORS);
verify_ssa (false);
#endif
}
/* Create graphite SCoPs from an array of scop detection REGIONS. */
static void
build_graphite_scops (VEC (sd_region, heap) *regions,
VEC (scop_p, heap) **scops)
{
int i;
sd_region *s;
for (i = 0; VEC_iterate (sd_region, regions, i, s); i++)
{
edge entry = find_single_entry_edge (s);
edge exit = find_single_exit_edge (s);
scop_p scop = new_scop (new_sese (entry, exit));
VEC_safe_push (scop_p, heap, *scops, scop);
/* Are there overlapping SCoPs? */
#ifdef ENABLE_CHECKING
{
int j;
sd_region *s2;
for (j = 0; VEC_iterate (sd_region, regions, j, s2); j++)
if (s != s2)
gcc_assert (!bb_in_sd_region (s->entry, s2));
}
#endif
}
}
/* Returns true when BB contains only close phi nodes. */
static bool
contains_only_close_phi_nodes (basic_block bb)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
if (gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
return false;
return true;
}
/* Print statistics for SCOP to FILE. */
static void
print_graphite_scop_statistics (FILE* file, scop_p scop)
{
long n_bbs = 0;
long n_loops = 0;
long n_stmts = 0;
long n_conditions = 0;
long n_p_bbs = 0;
long n_p_loops = 0;
long n_p_stmts = 0;
long n_p_conditions = 0;
basic_block bb;
FOR_ALL_BB (bb)
{
gimple_stmt_iterator psi;
loop_p loop = bb->loop_father;
if (!bb_in_sese_p (bb, SCOP_REGION (scop)))
continue;
n_bbs++;
n_p_bbs += bb->count;
if (VEC_length (edge, bb->succs) > 1)
{
n_conditions++;
n_p_conditions += bb->count;
}
for (psi = gsi_start_bb (bb); !gsi_end_p (psi); gsi_next (&psi))
{
n_stmts++;
n_p_stmts += bb->count;
}
if (loop->header == bb && loop_in_sese_p (loop, SCOP_REGION (scop)))
{
n_loops++;
n_p_loops += bb->count;
}
}
fprintf (file, "\nBefore limit_scops SCoP statistics (");
fprintf (file, "BBS:%ld, ", n_bbs);
fprintf (file, "LOOPS:%ld, ", n_loops);
fprintf (file, "CONDITIONS:%ld, ", n_conditions);
fprintf (file, "STMTS:%ld)\n", n_stmts);
fprintf (file, "\nBefore limit_scops SCoP profiling statistics (");
fprintf (file, "BBS:%ld, ", n_p_bbs);
fprintf (file, "LOOPS:%ld, ", n_p_loops);
fprintf (file, "CONDITIONS:%ld, ", n_p_conditions);
fprintf (file, "STMTS:%ld)\n", n_p_stmts);
}
/* Print statistics for SCOPS to FILE. */
static void
print_graphite_statistics (FILE* file, VEC (scop_p, heap) *scops)
{
int i;
scop_p scop;
for (i = 0; VEC_iterate (scop_p, scops, i, scop); i++)
print_graphite_scop_statistics (file, scop);
}
/* Version of free_scops special cased for limit_scops. */
static void
free_scops_1 (VEC (scop_p, heap) **scops)
{
int i;
scop_p scop;
for (i = 0; VEC_iterate (scop_p, *scops, i, scop); i++)
{
sese region = SCOP_REGION (scop);
free (SESE_PARAMS_NAMES (region));
SESE_PARAMS_NAMES (region) = 0;
}
free_scops (*scops);
}
/* We limit all SCoPs to SCoPs, that are completely surrounded by a loop.
Example:
for (i |
{ |
for (j | SCoP 1
for (k |
} |
* SCoP frontier, as this line is not surrounded by any loop. *
for (l | SCoP 2
This is necessary as scalar evolution and parameter detection need a
outermost loop to initialize parameters correctly.
TODO: FIX scalar evolution and parameter detection to allow more flexible
SCoP frontiers. */
static void
limit_scops (VEC (scop_p, heap) **scops)
{
VEC (sd_region, heap) *regions = VEC_alloc (sd_region, heap, 3);
int i;
scop_p scop;
for (i = 0; VEC_iterate (scop_p, *scops, i, scop); i++)
{
int j;
loop_p loop;
sese region = SCOP_REGION (scop);
build_scop_bbs (scop);
build_sese_loop_nests (region);
for (j = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), j, loop); j++)
if (!loop_in_sese_p (loop_outer (loop), region)
&& single_exit (loop))
{
sd_region open_scop;
open_scop.entry = loop->header;
open_scop.exit = single_exit (loop)->dest;
/* This is a hack on top of the limit_scops hack. The
limit_scops hack should disappear all together. */
if (single_succ_p (open_scop.exit)
&& contains_only_close_phi_nodes (open_scop.exit))
open_scop.exit = single_succ_edge (open_scop.exit)->dest;
VEC_safe_push (sd_region, heap, regions, &open_scop);
}
}
free_scops_1 (scops);
*scops = VEC_alloc (scop_p, heap, 3);
create_sese_edges (regions);
build_graphite_scops (regions, scops);
VEC_free (sd_region, heap, regions);
}
/* Transforms LOOP to the canonical loop closed SSA form. */
static void
canonicalize_loop_closed_ssa (loop_p loop)
{
edge e = single_exit (loop);
basic_block bb;
if (!e || e->flags & EDGE_ABNORMAL)
return;
bb = e->dest;
if (VEC_length (edge, bb->preds) == 1)
split_block_after_labels (bb);
else
{
gimple_stmt_iterator psi;
basic_block close = split_edge (e);
e = single_succ_edge (close);
for (psi = gsi_start_phis (bb); !gsi_end_p (psi); gsi_next (&psi))
{
gimple phi = gsi_stmt (psi);
unsigned i;
for (i = 0; i < gimple_phi_num_args (phi); i++)
if (gimple_phi_arg_edge (phi, i) == e)
{
tree res, arg = gimple_phi_arg_def (phi, i);
use_operand_p use_p;
gimple close_phi;
if (TREE_CODE (arg) != SSA_NAME)
continue;
close_phi = create_phi_node (arg, close);
res = create_new_def_for (gimple_phi_result (close_phi),
close_phi,
gimple_phi_result_ptr (close_phi));
add_phi_arg (close_phi, arg,
gimple_phi_arg_edge (close_phi, 0),
UNKNOWN_LOCATION);
use_p = gimple_phi_arg_imm_use_ptr (phi, i);
replace_exp (use_p, res);
update_stmt (phi);
}
}
}
}
/* Converts the current loop closed SSA form to a canonical form
expected by the Graphite code generation.
The loop closed SSA form has the following invariant: a variable
defined in a loop that is used outside the loop appears only in the
phi nodes in the destination of the loop exit. These phi nodes are
called close phi nodes.
The canonical loop closed SSA form contains the extra invariants:
- when the loop contains only one exit, the close phi nodes contain
only one argument. That implies that the basic block that contains
the close phi nodes has only one predecessor, that is a basic block
in the loop.
- the basic block containing the close phi nodes does not contain
other statements.
*/
static void
canonicalize_loop_closed_ssa_form (void)
{
loop_iterator li;
loop_p loop;
#ifdef ENABLE_CHECKING
verify_loop_closed_ssa ();
#endif
FOR_EACH_LOOP (li, loop, 0)
canonicalize_loop_closed_ssa (loop);
rewrite_into_loop_closed_ssa (NULL, TODO_update_ssa);
update_ssa (TODO_update_ssa);
#ifdef ENABLE_CHECKING
verify_loop_closed_ssa ();
#endif
}
/* Find Static Control Parts (SCoP) in the current function and pushes
them to SCOPS. */
void
build_scops (VEC (scop_p, heap) **scops)
{
struct loop *loop = current_loops->tree_root;
VEC (sd_region, heap) *regions = VEC_alloc (sd_region, heap, 3);
canonicalize_loop_closed_ssa_form ();
build_scops_1 (single_succ (ENTRY_BLOCK_PTR), ENTRY_BLOCK_PTR->loop_father,
&regions, loop);
create_sese_edges (regions);
build_graphite_scops (regions, scops);
if (dump_file && (dump_flags & TDF_DETAILS))
print_graphite_statistics (dump_file, *scops);
limit_scops (scops);
VEC_free (sd_region, heap, regions);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nnumber of SCoPs: %d\n",
VEC_length (scop_p, *scops));
}
/* Pretty print all SCoPs in DOT format and mark them with different colors.
If there are not enough colors, paint later SCoPs gray.
Special nodes:
- "*" after the node number: entry of a SCoP,
- "#" after the node number: exit of a SCoP,
- "()" entry or exit not part of SCoP. */
static void
dot_all_scops_1 (FILE *file, VEC (scop_p, heap) *scops)
{
basic_block bb;
edge e;
edge_iterator ei;
scop_p scop;
const char* color;
int i;
/* Disable debugging while printing graph. */
int tmp_dump_flags = dump_flags;
dump_flags = 0;
fprintf (file, "digraph all {\n");
FOR_ALL_BB (bb)
{
int part_of_scop = false;
/* Use HTML for every bb label. So we are able to print bbs
which are part of two different SCoPs, with two different
background colors. */
fprintf (file, "%d [label=<\n <TABLE BORDER=\"0\" CELLBORDER=\"1\" ",
bb->index);
fprintf (file, "CELLSPACING=\"0\">\n");
/* Select color for SCoP. */
for (i = 0; VEC_iterate (scop_p, scops, i, scop); i++)
{
sese region = SCOP_REGION (scop);
if (bb_in_sese_p (bb, region)
|| (SESE_EXIT_BB (region) == bb)
|| (SESE_ENTRY_BB (region) == bb))
{
switch (i % 17)
{
case 0: /* red */
color = "#e41a1c";
break;
case 1: /* blue */
color = "#377eb8";
break;
case 2: /* green */
color = "#4daf4a";
break;
case 3: /* purple */
color = "#984ea3";
break;
case 4: /* orange */
color = "#ff7f00";
break;
case 5: /* yellow */
color = "#ffff33";
break;
case 6: /* brown */
color = "#a65628";
break;
case 7: /* rose */
color = "#f781bf";
break;
case 8:
color = "#8dd3c7";
break;
case 9:
color = "#ffffb3";
break;
case 10:
color = "#bebada";
break;
case 11:
color = "#fb8072";
break;
case 12:
color = "#80b1d3";
break;
case 13:
color = "#fdb462";
break;
case 14:
color = "#b3de69";
break;
case 15:
color = "#fccde5";
break;
case 16:
color = "#bc80bd";
break;
default: /* gray */
color = "#999999";
}
fprintf (file, " <TR><TD WIDTH=\"50\" BGCOLOR=\"%s\">", color);
if (!bb_in_sese_p (bb, region))
fprintf (file, " (");
if (bb == SESE_ENTRY_BB (region)
&& bb == SESE_EXIT_BB (region))
fprintf (file, " %d*# ", bb->index);
else if (bb == SESE_ENTRY_BB (region))
fprintf (file, " %d* ", bb->index);
else if (bb == SESE_EXIT_BB (region))
fprintf (file, " %d# ", bb->index);
else
fprintf (file, " %d ", bb->index);
if (!bb_in_sese_p (bb,region))
fprintf (file, ")");
fprintf (file, "</TD></TR>\n");
part_of_scop = true;
}
}
if (!part_of_scop)
{
fprintf (file, " <TR><TD WIDTH=\"50\" BGCOLOR=\"#ffffff\">");
fprintf (file, " %d </TD></TR>\n", bb->index);
}
fprintf (file, " </TABLE>>, shape=box, style=\"setlinewidth(0)\"]\n");
}
FOR_ALL_BB (bb)
{
FOR_EACH_EDGE (e, ei, bb->succs)
fprintf (file, "%d -> %d;\n", bb->index, e->dest->index);
}
fputs ("}\n\n", file);
/* Enable debugging again. */
dump_flags = tmp_dump_flags;
}
/* Display all SCoPs using dotty. */
void
dot_all_scops (VEC (scop_p, heap) *scops)
{
/* When debugging, enable the following code. This cannot be used
in production compilers because it calls "system". */
#if 0
int x;
FILE *stream = fopen ("/tmp/allscops.dot", "w");
gcc_assert (stream);
dot_all_scops_1 (stream, scops);
fclose (stream);
x = system ("dotty /tmp/allscops.dot");
#else
dot_all_scops_1 (stderr, scops);
#endif
}
/* Display all SCoPs using dotty. */
void
dot_scop (scop_p scop)
{
VEC (scop_p, heap) *scops = NULL;
if (scop)
VEC_safe_push (scop_p, heap, scops, scop);
/* When debugging, enable the following code. This cannot be used
in production compilers because it calls "system". */
#if 0
{
int x;
FILE *stream = fopen ("/tmp/allscops.dot", "w");
gcc_assert (stream);
dot_all_scops_1 (stream, scops);
fclose (stream);
x = system ("dotty /tmp/allscops.dot");
}
#else
dot_all_scops_1 (stderr, scops);
#endif
VEC_free (scop_p, heap, scops);
}
#endif
/* Detection of Static Control Parts (SCoP) for Graphite.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com> and
Tobias Grosser <grosser@fim.uni-passau.de>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
extern void build_scops (VEC (scop_p, heap) **);
extern void build_scop_bbs (scop_p);
extern int nb_reductions_in_loop (loop_p);
extern void dot_all_scops (VEC (scop_p, heap) *);
extern void dot_scop (scop_p);
/* Conversion of SESE regions to Polyhedra.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "sese.h"
#ifdef HAVE_cloog
#include "cloog/cloog.h"
#include "ppl_c.h"
#include "graphite-ppl.h"
#include "graphite.h"
#include "graphite-poly.h"
#include "graphite-scop-detection.h"
#include "graphite-clast-to-gimple.h"
#include "graphite-sese-to-poly.h"
/* Check if VAR is used in a phi node, that is no loop header. */
static bool
var_used_in_not_loop_header_phi_node (tree var)
{
imm_use_iterator imm_iter;
gimple stmt;
bool result = false;
FOR_EACH_IMM_USE_STMT (stmt, imm_iter, var)
{
basic_block bb = gimple_bb (stmt);
if (gimple_code (stmt) == GIMPLE_PHI
&& bb->loop_father->header != bb)
result = true;
}
return result;
}
/* Returns the index of the phi argument corresponding to the initial
value in the loop. */
static size_t
loop_entry_phi_arg (gimple phi)
{
loop_p loop = gimple_bb (phi)->loop_father;
size_t 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))
return i;
gcc_unreachable ();
return 0;
}
/* Removes a simple copy phi node "RES = phi (INIT, RES)" at position
PSI by inserting on the loop ENTRY edge assignment "RES = INIT". */
static void
remove_simple_copy_phi (gimple_stmt_iterator *psi)
{
gimple phi = gsi_stmt (*psi);
tree res = gimple_phi_result (phi);
size_t entry = loop_entry_phi_arg (phi);
tree init = gimple_phi_arg_def (phi, entry);
gimple stmt = gimple_build_assign (res, init);
edge e = gimple_phi_arg_edge (phi, entry);
remove_phi_node (psi, false);
gsi_insert_on_edge_immediate (e, stmt);
SSA_NAME_DEF_STMT (res) = stmt;
}
/* Removes an invariant phi node at position PSI by inserting on the
loop ENTRY edge the assignment RES = INIT. */
static void
remove_invariant_phi (sese region, gimple_stmt_iterator *psi)
{
gimple phi = gsi_stmt (*psi);
loop_p loop = loop_containing_stmt (phi);
tree res = gimple_phi_result (phi);
tree scev = scalar_evolution_in_region (region, loop, res);
size_t entry = loop_entry_phi_arg (phi);
edge e = gimple_phi_arg_edge (phi, entry);
tree var;
gimple stmt;
gimple_seq stmts;
gimple_stmt_iterator gsi;
if (tree_contains_chrecs (scev, NULL))
scev = gimple_phi_arg_def (phi, entry);
var = force_gimple_operand (scev, &stmts, true, NULL_TREE);
stmt = gimple_build_assign (res, var);
remove_phi_node (psi, false);
if (!stmts)
stmts = gimple_seq_alloc ();
gsi = gsi_last (stmts);
gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
gsi_insert_seq_on_edge (e, stmts);
gsi_commit_edge_inserts ();
SSA_NAME_DEF_STMT (res) = stmt;
}
/* Returns true when the phi node at PSI is of the form "a = phi (a, x)". */
static inline bool
simple_copy_phi_p (gimple phi)
{
tree res;
if (gimple_phi_num_args (phi) != 2)
return false;
res = gimple_phi_result (phi);
return (res == gimple_phi_arg_def (phi, 0)
|| res == gimple_phi_arg_def (phi, 1));
}
/* Returns true when the phi node at position PSI is a reduction phi
node in REGION. Otherwise moves the pointer PSI to the next phi to
be considered. */
static bool
reduction_phi_p (sese region, gimple_stmt_iterator *psi)
{
loop_p loop;
tree scev;
affine_iv iv;
gimple phi = gsi_stmt (*psi);
tree res = gimple_phi_result (phi);
if (!is_gimple_reg (res))
{
gsi_next (psi);
return false;
}
loop = loop_containing_stmt (phi);
if (simple_copy_phi_p (phi))
{
/* FIXME: PRE introduces phi nodes like these, for an example,
see id-5.f in the fortran graphite testsuite:
# prephitmp.85_265 = PHI <prephitmp.85_258(33), prephitmp.85_265(18)>
*/
remove_simple_copy_phi (psi);
return false;
}
/* Main induction variables with constant strides in LOOP are not
reductions. */
if (simple_iv (loop, loop, res, &iv, true))
{
gsi_next (psi);
return false;
}
scev = scalar_evolution_in_region (region, loop, res);
if (chrec_contains_undetermined (scev))
return true;
if (evolution_function_is_invariant_p (scev, loop->num))
{
remove_invariant_phi (region, psi);
return false;
}
/* All the other cases are considered reductions. */
return true;
}
/* Returns true when BB will be represented in graphite. Return false
for the basic blocks that contain code eliminated in the code
generation pass: i.e. induction variables and exit conditions. */
static bool
graphite_stmt_p (sese region, basic_block bb,
VEC (data_reference_p, heap) *drs)
{
gimple_stmt_iterator gsi;
loop_p loop = bb->loop_father;
if (VEC_length (data_reference_p, drs) > 0)
return true;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple stmt = gsi_stmt (gsi);
switch (gimple_code (stmt))
{
/* Control flow expressions can be ignored, as they are
represented in the iteration domains and will be
regenerated by graphite. */
case GIMPLE_COND:
case GIMPLE_GOTO:
case GIMPLE_SWITCH:
break;
case GIMPLE_ASSIGN:
{
tree var = gimple_assign_lhs (stmt);
/* We need these bbs to be able to construct the phi nodes. */
if (var_used_in_not_loop_header_phi_node (var))
return true;
var = scalar_evolution_in_region (region, loop, var);
if (chrec_contains_undetermined (var))
return true;
break;
}
default:
return true;
}
}
return false;
}
/* Store the GRAPHITE representation of BB. */
static gimple_bb_p
new_gimple_bb (basic_block bb, VEC (data_reference_p, heap) *drs)
{
struct gimple_bb *gbb;
gbb = XNEW (struct gimple_bb);
bb->aux = gbb;
GBB_BB (gbb) = bb;
GBB_DATA_REFS (gbb) = drs;
GBB_CONDITIONS (gbb) = NULL;
GBB_CONDITION_CASES (gbb) = NULL;
GBB_CLOOG_IV_TYPES (gbb) = NULL;
return gbb;
}
/* Frees GBB. */
static void
free_gimple_bb (struct gimple_bb *gbb)
{
if (GBB_CLOOG_IV_TYPES (gbb))
htab_delete (GBB_CLOOG_IV_TYPES (gbb));
free_data_refs (GBB_DATA_REFS (gbb));
VEC_free (gimple, heap, GBB_CONDITIONS (gbb));
VEC_free (gimple, heap, GBB_CONDITION_CASES (gbb));
GBB_BB (gbb)->aux = 0;
XDELETE (gbb);
}
/* Deletes all gimple bbs in SCOP. */
static void
remove_gbbs_in_scop (scop_p scop)
{
int i;
poly_bb_p pbb;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
free_gimple_bb (PBB_BLACK_BOX (pbb));
}
/* Deletes all scops in SCOPS. */
void
free_scops (VEC (scop_p, heap) *scops)
{
int i;
scop_p scop;
for (i = 0; VEC_iterate (scop_p, scops, i, scop); i++)
{
remove_gbbs_in_scop (scop);
free_sese (SCOP_REGION (scop));
free_scop (scop);
}
VEC_free (scop_p, heap, scops);
}
/* Generates a polyhedral black box only if the bb contains interesting
information. */
static void
try_generate_gimple_bb (scop_p scop, basic_block bb)
{
VEC (data_reference_p, heap) *drs = VEC_alloc (data_reference_p, heap, 5);
loop_p nest = outermost_loop_in_sese (SCOP_REGION (scop), bb);
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
graphite_find_data_references_in_stmt (nest, gsi_stmt (gsi), &drs);
if (!graphite_stmt_p (SCOP_REGION (scop), bb, drs))
free_data_refs (drs);
else
new_poly_bb (scop, new_gimple_bb (bb, drs));
}
/* Returns true if all predecessors of BB, that are not dominated by BB, are
marked in MAP. The predecessors dominated by BB are loop latches and will
be handled after BB. */
static bool
all_non_dominated_preds_marked_p (basic_block bb, sbitmap map)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->preds)
if (!TEST_BIT (map, e->src->index)
&& !dominated_by_p (CDI_DOMINATORS, e->src, bb))
return false;
return true;
}
/* Compare the depth of two basic_block's P1 and P2. */
static int
compare_bb_depths (const void *p1, const void *p2)
{
const_basic_block const bb1 = *(const_basic_block const*)p1;
const_basic_block const bb2 = *(const_basic_block const*)p2;
int d1 = loop_depth (bb1->loop_father);
int d2 = loop_depth (bb2->loop_father);
if (d1 < d2)
return 1;
if (d1 > d2)
return -1;
return 0;
}
/* Sort the basic blocks from DOM such that the first are the ones at
a deepest loop level. */
static void
graphite_sort_dominated_info (VEC (basic_block, heap) *dom)
{
size_t len = VEC_length (basic_block, dom);
qsort (VEC_address (basic_block, dom), len, sizeof (basic_block),
compare_bb_depths);
}
/* Recursive helper function for build_scops_bbs. */
static void
build_scop_bbs_1 (scop_p scop, sbitmap visited, basic_block bb)
{
sese region = SCOP_REGION (scop);
VEC (basic_block, heap) *dom;
if (TEST_BIT (visited, bb->index)
|| !bb_in_sese_p (bb, region))
return;
try_generate_gimple_bb (scop, bb);
SET_BIT (visited, bb->index);
dom = get_dominated_by (CDI_DOMINATORS, bb);
if (dom == NULL)
return;
graphite_sort_dominated_info (dom);
while (!VEC_empty (basic_block, dom))
{
int i;
basic_block dom_bb;
for (i = 0; VEC_iterate (basic_block, dom, i, dom_bb); i++)
if (all_non_dominated_preds_marked_p (dom_bb, visited))
{
build_scop_bbs_1 (scop, visited, dom_bb);
VEC_unordered_remove (basic_block, dom, i);
break;
}
}
VEC_free (basic_block, heap, dom);
}
/* Gather the basic blocks belonging to the SCOP. */
void
build_scop_bbs (scop_p scop)
{
sbitmap visited = sbitmap_alloc (last_basic_block);
sese region = SCOP_REGION (scop);
sbitmap_zero (visited);
build_scop_bbs_1 (scop, visited, SESE_ENTRY_BB (region));
sbitmap_free (visited);
}
/* Converts the STATIC_SCHEDULE of PBB into a scattering polyhedron.
We generate SCATTERING_DIMENSIONS scattering dimensions.
CLooG 0.15.0 and previous versions require, that all
scattering functions of one CloogProgram have the same number of
scattering dimensions, therefore we allow to specify it. This
should be removed in future versions of CLooG.
The scattering polyhedron consists of these dimensions: scattering,
loop_iterators, parameters.
Example:
| scattering_dimensions = 5
| used_scattering_dimensions = 3
| nb_iterators = 1
| scop_nb_params = 2
|
| Schedule:
| i
| 4 5
|
| Scattering polyhedron:
|
| scattering: {s1, s2, s3, s4, s5}
| loop_iterators: {i}
| parameters: {p1, p2}
|
| s1 s2 s3 s4 s5 i p1 p2 1
| 1 0 0 0 0 0 0 0 -4 = 0
| 0 1 0 0 0 -1 0 0 0 = 0
| 0 0 1 0 0 0 0 0 -5 = 0 */
static void
build_pbb_scattering_polyhedrons (ppl_Linear_Expression_t static_schedule,
poly_bb_p pbb, int scattering_dimensions)
{
int i;
scop_p scop = PBB_SCOP (pbb);
int nb_iterators = pbb_dim_iter_domain (pbb);
int used_scattering_dimensions = nb_iterators * 2 + 1;
int nb_params = scop_nb_params (scop);
ppl_Coefficient_t c;
ppl_dimension_type dim = scattering_dimensions + nb_iterators + nb_params;
Value v;
gcc_assert (scattering_dimensions >= used_scattering_dimensions);
value_init (v);
ppl_new_Coefficient (&c);
ppl_new_C_Polyhedron_from_space_dimension
(&PBB_TRANSFORMED_SCATTERING (pbb), dim, 0);
PBB_NB_SCATTERING_TRANSFORM (pbb) = scattering_dimensions;
for (i = 0; i < scattering_dimensions; i++)
{
ppl_Constraint_t cstr;
ppl_Linear_Expression_t expr;
ppl_new_Linear_Expression_with_dimension (&expr, dim);
value_set_si (v, 1);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_coefficient (expr, i, c);
/* Textual order inside this loop. */
if ((i % 2) == 0)
{
ppl_Linear_Expression_coefficient (static_schedule, i / 2, c);
ppl_Coefficient_to_mpz_t (c, v);
value_oppose (v, v);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_inhomogeneous (expr, c);
}
/* Iterations of this loop. */
else /* if ((i % 2) == 1) */
{
int loop = (i - 1) / 2;
value_set_si (v, -1);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_coefficient
(expr, scattering_dimensions + loop, c);
}
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_EQUAL);
ppl_Polyhedron_add_constraint (PBB_TRANSFORMED_SCATTERING (pbb), cstr);
ppl_delete_Linear_Expression (expr);
ppl_delete_Constraint (cstr);
}
value_clear (v);
ppl_delete_Coefficient (c);
ppl_new_C_Polyhedron_from_C_Polyhedron (&PBB_ORIGINAL_SCATTERING (pbb),
PBB_TRANSFORMED_SCATTERING (pbb));
}
/* Build for BB the static schedule.
The static schedule is a Dewey numbering of the abstract syntax
tree: http://en.wikipedia.org/wiki/Dewey_Decimal_Classification
The following example informally defines the static schedule:
A
for (i: ...)
{
for (j: ...)
{
B
C
}
for (k: ...)
{
D
E
}
}
F
Static schedules for A to F:
DEPTH
0 1 2
A 0
B 1 0 0
C 1 0 1
D 1 1 0
E 1 1 1
F 2
*/
static void
build_scop_scattering (scop_p scop)
{
int i;
poly_bb_p pbb;
gimple_bb_p previous_gbb = NULL;
ppl_Linear_Expression_t static_schedule;
ppl_Coefficient_t c;
Value v;
value_init (v);
ppl_new_Coefficient (&c);
ppl_new_Linear_Expression (&static_schedule);
/* We have to start schedules at 0 on the first component and
because we cannot compare_prefix_loops against a previous loop,
prefix will be equal to zero, and that index will be
incremented before copying. */
value_set_si (v, -1);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_coefficient (static_schedule, 0, c);
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
{
gimple_bb_p gbb = PBB_BLACK_BOX (pbb);
ppl_Linear_Expression_t common;
int prefix;
int nb_scat_dims = pbb_dim_iter_domain (pbb) * 2 + 1;
if (previous_gbb)
prefix = nb_common_loops (SCOP_REGION (scop), previous_gbb, gbb);
else
prefix = 0;
previous_gbb = gbb;
ppl_new_Linear_Expression_with_dimension (&common, prefix + 1);
ppl_assign_Linear_Expression_from_Linear_Expression (common,
static_schedule);
value_set_si (v, 1);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_coefficient (common, prefix, c);
ppl_assign_Linear_Expression_from_Linear_Expression (static_schedule,
common);
build_pbb_scattering_polyhedrons (common, pbb, nb_scat_dims);
ppl_delete_Linear_Expression (common);
}
value_clear (v);
ppl_delete_Coefficient (c);
ppl_delete_Linear_Expression (static_schedule);
}
/* Add the value K to the dimension D of the linear expression EXPR. */
static void
add_value_to_dim (ppl_dimension_type d, ppl_Linear_Expression_t expr,
Value k)
{
Value val;
ppl_Coefficient_t coef;
ppl_new_Coefficient (&coef);
ppl_Linear_Expression_coefficient (expr, d, coef);
value_init (val);
ppl_Coefficient_to_mpz_t (coef, val);
value_addto (val, val, k);
ppl_assign_Coefficient_from_mpz_t (coef, val);
ppl_Linear_Expression_add_to_coefficient (expr, d, coef);
value_clear (val);
ppl_delete_Coefficient (coef);
}
/* In the context of scop S, scan E, the right hand side of a scalar
evolution function in loop VAR, and translate it to a linear
expression EXPR. */
static void
scan_tree_for_params_right_scev (sese s, tree e, int var,
ppl_Linear_Expression_t expr)
{
if (expr)
{
loop_p loop = get_loop (var);
ppl_dimension_type l = sese_loop_depth (s, loop) - 1;
Value val;
/* Scalar evolutions should happen in the sese region. */
gcc_assert (sese_loop_depth (s, loop) > 0);
/* We can not deal with parametric strides like:
| p = parameter;
|
| for i:
| a [i * p] = ... */
gcc_assert (TREE_CODE (e) == INTEGER_CST);
value_init (val);
value_set_si (val, int_cst_value (e));
add_value_to_dim (l, expr, val);
value_clear (val);
}
}
/* Scan the integer constant CST, and add it to the inhomogeneous part of the
linear expression EXPR. K is the multiplier of the constant. */
static void
scan_tree_for_params_int (tree cst, ppl_Linear_Expression_t expr, Value k)
{
Value val;
ppl_Coefficient_t coef;
int v = int_cst_value (cst);
value_init (val);
value_set_si (val, 0);
/* Necessary to not get "-1 = 2^n - 1". */
if (v < 0)
value_sub_int (val, val, -v);
else
value_add_int (val, val, v);
value_multiply (val, val, k);
ppl_new_Coefficient (&coef);
ppl_assign_Coefficient_from_mpz_t (coef, val);
ppl_Linear_Expression_add_to_inhomogeneous (expr, coef);
value_clear (val);
ppl_delete_Coefficient (coef);
}
/* Saves in NV at index I a new name for variable P. */
static void
save_var_name (char **nv, int i, tree p)
{
const char *name = get_name (SSA_NAME_VAR (p));
if (name)
{
int len = strlen (name) + 16;
nv[i] = XNEWVEC (char, len);
snprintf (nv[i], len, "%s_%d", name, SSA_NAME_VERSION (p));
}
else
{
nv[i] = XNEWVEC (char, 16);
snprintf (nv[i], 2 + 16, "T_%d", SSA_NAME_VERSION (p));
}
}
/* When parameter NAME is in REGION, returns its index in SESE_PARAMS.
Otherwise returns -1. */
static inline int
parameter_index_in_region_1 (tree name, sese region)
{
int i;
tree p;
gcc_assert (TREE_CODE (name) == SSA_NAME);
for (i = 0; VEC_iterate (tree, SESE_PARAMS (region), i, p); i++)
if (p == name)
return i;
return -1;
}
/* When the parameter NAME is in REGION, returns its index in
SESE_PARAMS. Otherwise this function inserts NAME in SESE_PARAMS
and returns the index of NAME. */
static int
parameter_index_in_region (tree name, sese region)
{
int i;
gcc_assert (TREE_CODE (name) == SSA_NAME);
i = parameter_index_in_region_1 (name, region);
if (i != -1)
return i;
gcc_assert (SESE_ADD_PARAMS (region));
i = VEC_length (tree, SESE_PARAMS (region));
save_var_name (SESE_PARAMS_NAMES (region), i, name);
save_clast_name_index (SESE_PARAMS_INDEX (region),
SESE_PARAMS_NAMES (region)[i], i);
VEC_safe_push (tree, heap, SESE_PARAMS (region), name);
return i;
}
/* In the context of sese S, scan the expression E and translate it to
a linear expression C. When parsing a symbolic multiplication, K
represents the constant multiplier of an expression containing
parameters. */
static void
scan_tree_for_params (sese s, tree e, ppl_Linear_Expression_t c,
Value k)
{
if (e == chrec_dont_know)
return;
switch (TREE_CODE (e))
{
case POLYNOMIAL_CHREC:
scan_tree_for_params_right_scev (s, CHREC_RIGHT (e),
CHREC_VARIABLE (e), c);
scan_tree_for_params (s, CHREC_LEFT (e), c, k);
break;
case MULT_EXPR:
if (chrec_contains_symbols (TREE_OPERAND (e, 0)))
{
if (c)
{
Value val;
gcc_assert (host_integerp (TREE_OPERAND (e, 1), 0));
value_init (val);
value_set_si (val, int_cst_value (TREE_OPERAND (e, 1)));
value_multiply (val, val, k);
scan_tree_for_params (s, TREE_OPERAND (e, 0), c, val);
value_clear (val);
}
else
scan_tree_for_params (s, TREE_OPERAND (e, 0), c, k);
}
else
{
if (c)
{
Value val;
gcc_assert (host_integerp (TREE_OPERAND (e, 0), 0));
value_init (val);
value_set_si (val, int_cst_value (TREE_OPERAND (e, 0)));
value_multiply (val, val, k);
scan_tree_for_params (s, TREE_OPERAND (e, 1), c, val);
value_clear (val);
}
else
scan_tree_for_params (s, TREE_OPERAND (e, 1), c, k);
}
break;
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
scan_tree_for_params (s, TREE_OPERAND (e, 0), c, k);
scan_tree_for_params (s, TREE_OPERAND (e, 1), c, k);
break;
case MINUS_EXPR:
{
ppl_Linear_Expression_t tmp_expr = NULL;
if (c)
{
ppl_dimension_type dim;
ppl_Linear_Expression_space_dimension (c, &dim);
ppl_new_Linear_Expression_with_dimension (&tmp_expr, dim);
}
scan_tree_for_params (s, TREE_OPERAND (e, 0), c, k);
scan_tree_for_params (s, TREE_OPERAND (e, 1), tmp_expr, k);
if (c)
{
ppl_subtract_Linear_Expression_from_Linear_Expression (c,
tmp_expr);
ppl_delete_Linear_Expression (tmp_expr);
}
break;
}
case NEGATE_EXPR:
{
ppl_Linear_Expression_t tmp_expr = NULL;
if (c)
{
ppl_dimension_type dim;
ppl_Linear_Expression_space_dimension (c, &dim);
ppl_new_Linear_Expression_with_dimension (&tmp_expr, dim);
}
scan_tree_for_params (s, TREE_OPERAND (e, 0), tmp_expr, k);
if (c)
{
ppl_subtract_Linear_Expression_from_Linear_Expression (c,
tmp_expr);
ppl_delete_Linear_Expression (tmp_expr);
}
break;
}
case BIT_NOT_EXPR:
{
ppl_Linear_Expression_t tmp_expr = NULL;
if (c)
{
ppl_dimension_type dim;
ppl_Linear_Expression_space_dimension (c, &dim);
ppl_new_Linear_Expression_with_dimension (&tmp_expr, dim);
}
scan_tree_for_params (s, TREE_OPERAND (e, 0), tmp_expr, k);
if (c)
{
ppl_Coefficient_t coef;
Value minus_one;
ppl_subtract_Linear_Expression_from_Linear_Expression (c,
tmp_expr);
ppl_delete_Linear_Expression (tmp_expr);
value_init (minus_one);
value_set_si (minus_one, -1);
ppl_new_Coefficient_from_mpz_t (&coef, minus_one);
ppl_Linear_Expression_add_to_inhomogeneous (c, coef);
value_clear (minus_one);
ppl_delete_Coefficient (coef);
}
break;
}
case SSA_NAME:
{
ppl_dimension_type p = parameter_index_in_region (e, s);
if (c)
{
ppl_dimension_type dim;
ppl_Linear_Expression_space_dimension (c, &dim);
p += dim - sese_nb_params (s);
add_value_to_dim (p, c, k);
}
break;
}
case INTEGER_CST:
if (c)
scan_tree_for_params_int (e, c, k);
break;
CASE_CONVERT:
case NON_LVALUE_EXPR:
scan_tree_for_params (s, TREE_OPERAND (e, 0), c, k);
break;
default:
gcc_unreachable ();
break;
}
}
/* Data structure for idx_record_params. */
struct irp_data
{
struct loop *loop;
sese region;
};
/* For a data reference with an ARRAY_REF as its BASE, record the
parameters occurring in IDX. DTA is passed in as complementary
information, and is used by the automatic walker function. This
function is a callback for for_each_index. */
static bool
idx_record_params (tree base, tree *idx, void *dta)
{
struct irp_data *data = (struct irp_data *) dta;
if (TREE_CODE (base) != ARRAY_REF)
return true;
if (TREE_CODE (*idx) == SSA_NAME)
{
tree scev;
sese region = data->region;
struct loop *loop = data->loop;
Value one;
scev = scalar_evolution_in_region (region, loop, *idx);
value_init (one);
value_set_si (one, 1);
scan_tree_for_params (region, scev, NULL, one);
value_clear (one);
}
return true;
}
/* Find parameters with respect to REGION in BB. We are looking in memory
access functions, conditions and loop bounds. */
static void
find_params_in_bb (sese region, gimple_bb_p gbb)
{
int i;
data_reference_p dr;
gimple stmt;
loop_p loop = GBB_BB (gbb)->loop_father;
for (i = 0; VEC_iterate (data_reference_p, GBB_DATA_REFS (gbb), i, dr); i++)
{
struct irp_data irp;
irp.loop = loop;
irp.region = region;
for_each_index (&dr->ref, idx_record_params, &irp);
}
/* Find parameters in conditional statements. */
for (i = 0; VEC_iterate (gimple, GBB_CONDITIONS (gbb), i, stmt); i++)
{
Value one;
tree lhs = scalar_evolution_in_region (region, loop,
gimple_cond_lhs (stmt));
tree rhs = scalar_evolution_in_region (region, loop,
gimple_cond_rhs (stmt));
value_init (one);
value_set_si (one, 1);
scan_tree_for_params (region, lhs, NULL, one);
scan_tree_for_params (region, rhs, NULL, one);
value_clear (one);
}
}
/* Record the parameters used in the SCOP. A variable is a parameter
in a scop if it does not vary during the execution of that scop. */
static void
find_scop_parameters (scop_p scop)
{
poly_bb_p pbb;
unsigned i;
sese region = SCOP_REGION (scop);
struct loop *loop;
Value one;
value_init (one);
value_set_si (one, 1);
/* Find the parameters used in the loop bounds. */
for (i = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), i, loop); i++)
{
tree nb_iters = number_of_latch_executions (loop);
if (!chrec_contains_symbols (nb_iters))
continue;
nb_iters = scalar_evolution_in_region (region, loop, nb_iters);
scan_tree_for_params (region, nb_iters, NULL, one);
}
value_clear (one);
/* Find the parameters used in data accesses. */
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
find_params_in_bb (region, PBB_BLACK_BOX (pbb));
scop_set_nb_params (scop, sese_nb_params (region));
SESE_ADD_PARAMS (region) = false;
}
/* Returns a gimple_bb from BB. */
static inline gimple_bb_p
gbb_from_bb (basic_block bb)
{
return (gimple_bb_p) bb->aux;
}
/* Builds the constraint polyhedra for LOOP in SCOP. OUTER_PH gives
the constraints for the surrounding loops. */
static void
build_loop_iteration_domains (scop_p scop, struct loop *loop,
ppl_Polyhedron_t outer_ph, int nb)
{
int i;
ppl_Polyhedron_t ph;
tree nb_iters = number_of_latch_executions (loop);
ppl_dimension_type dim = nb + 1 + scop_nb_params (scop);
sese region = SCOP_REGION (scop);
{
ppl_const_Constraint_System_t pcs;
ppl_dimension_type *map
= (ppl_dimension_type *) XNEWVEC (ppl_dimension_type, dim);
ppl_new_C_Polyhedron_from_space_dimension (&ph, dim, 0);
ppl_Polyhedron_get_constraints (outer_ph, &pcs);
ppl_Polyhedron_add_constraints (ph, pcs);
for (i = 0; i < (int) nb; i++)
map[i] = i;
for (i = (int) nb; i < (int) dim - 1; i++)
map[i] = i + 1;
map[dim - 1] = nb;
ppl_Polyhedron_map_space_dimensions (ph, map, dim);
free (map);
}
/* 0 <= loop_i */
{
ppl_Constraint_t lb;
ppl_Linear_Expression_t lb_expr;
ppl_new_Linear_Expression_with_dimension (&lb_expr, dim);
ppl_set_coef (lb_expr, nb, 1);
ppl_new_Constraint (&lb, lb_expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_delete_Linear_Expression (lb_expr);
ppl_Polyhedron_add_constraint (ph, lb);
ppl_delete_Constraint (lb);
}
if (TREE_CODE (nb_iters) == INTEGER_CST)
{
ppl_Constraint_t ub;
ppl_Linear_Expression_t ub_expr;
ppl_new_Linear_Expression_with_dimension (&ub_expr, dim);
/* loop_i <= cst_nb_iters */
ppl_set_coef (ub_expr, nb, -1);
ppl_set_inhomogeneous_tree (ub_expr, nb_iters);
ppl_new_Constraint (&ub, ub_expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_Polyhedron_add_constraint (ph, ub);
ppl_delete_Linear_Expression (ub_expr);
ppl_delete_Constraint (ub);
}
else if (!chrec_contains_undetermined (nb_iters))
{
Value one;
ppl_Constraint_t ub;
ppl_Linear_Expression_t ub_expr;
value_init (one);
value_set_si (one, 1);
ppl_new_Linear_Expression_with_dimension (&ub_expr, dim);
nb_iters = scalar_evolution_in_region (region, loop, nb_iters);
scan_tree_for_params (SCOP_REGION (scop), nb_iters, ub_expr, one);
value_clear (one);
/* loop_i <= expr_nb_iters */
ppl_set_coef (ub_expr, nb, -1);
ppl_new_Constraint (&ub, ub_expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_Polyhedron_add_constraint (ph, ub);
ppl_delete_Linear_Expression (ub_expr);
ppl_delete_Constraint (ub);
}
else
gcc_unreachable ();
if (loop->inner && loop_in_sese_p (loop->inner, region))
build_loop_iteration_domains (scop, loop->inner, ph, nb + 1);
if (nb != 0
&& loop->next
&& loop_in_sese_p (loop->next, region))
build_loop_iteration_domains (scop, loop->next, outer_ph, nb);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron
((ppl_Pointset_Powerset_C_Polyhedron_t *) &loop->aux, ph);
ppl_delete_Polyhedron (ph);
}
/* Returns a linear expression for tree T evaluated in PBB. */
static ppl_Linear_Expression_t
create_linear_expr_from_tree (poly_bb_p pbb, tree t)
{
Value one;
ppl_Linear_Expression_t res;
ppl_dimension_type dim;
sese region = SCOP_REGION (PBB_SCOP (pbb));
loop_p loop = GBB_BB (PBB_BLACK_BOX (pbb))->loop_father;
dim = pbb_dim_iter_domain (pbb) + pbb_nb_params (pbb);
ppl_new_Linear_Expression_with_dimension (&res, dim);
t = scalar_evolution_in_region (region, loop, t);
gcc_assert (!automatically_generated_chrec_p (t));
value_init (one);
value_set_si (one, 1);
scan_tree_for_params (region, t, res, one);
value_clear (one);
return res;
}
/* Returns the ppl constraint type from the gimple tree code CODE. */
static enum ppl_enum_Constraint_Type
ppl_constraint_type_from_tree_code (enum tree_code code)
{
switch (code)
{
/* We do not support LT and GT to be able to work with C_Polyhedron.
As we work on integer polyhedron "a < b" can be expressed by
"a + 1 <= b". */
case LT_EXPR:
case GT_EXPR:
gcc_unreachable ();
case LE_EXPR:
return PPL_CONSTRAINT_TYPE_LESS_OR_EQUAL;
case GE_EXPR:
return PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL;
case EQ_EXPR:
return PPL_CONSTRAINT_TYPE_EQUAL;
default:
gcc_unreachable ();
}
}
/* Add conditional statement STMT to PS. It is evaluated in PBB and
CODE is used as the comparison operator. This allows us to invert the
condition or to handle inequalities. */
static void
add_condition_to_domain (ppl_Pointset_Powerset_C_Polyhedron_t ps, gimple stmt,
poly_bb_p pbb, enum tree_code code)
{
Value v;
ppl_Coefficient_t c;
ppl_Linear_Expression_t left, right;
ppl_Constraint_t cstr;
enum ppl_enum_Constraint_Type type;
left = create_linear_expr_from_tree (pbb, gimple_cond_lhs (stmt));
right = create_linear_expr_from_tree (pbb, gimple_cond_rhs (stmt));
/* If we have < or > expressions convert them to <= or >= by adding 1 to
the left or the right side of the expression. */
if (code == LT_EXPR)
{
value_init (v);
value_set_si (v, 1);
ppl_new_Coefficient (&c);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_inhomogeneous (left, c);
ppl_delete_Coefficient (c);
value_clear (v);
code = LE_EXPR;
}
else if (code == GT_EXPR)
{
value_init (v);
value_set_si (v, 1);
ppl_new_Coefficient (&c);
ppl_assign_Coefficient_from_mpz_t (c, v);
ppl_Linear_Expression_add_to_inhomogeneous (right, c);
ppl_delete_Coefficient (c);
value_clear (v);
code = GE_EXPR;
}
type = ppl_constraint_type_from_tree_code (code);
ppl_subtract_Linear_Expression_from_Linear_Expression (left, right);
ppl_new_Constraint (&cstr, left, type);
ppl_Pointset_Powerset_C_Polyhedron_add_constraint (ps, cstr);
ppl_delete_Constraint (cstr);
ppl_delete_Linear_Expression (left);
ppl_delete_Linear_Expression (right);
}
/* Add conditional statement STMT to pbb. CODE is used as the comparision
operator. This allows us to invert the condition or to handle
inequalities. */
static void
add_condition_to_pbb (poly_bb_p pbb, gimple stmt, enum tree_code code)
{
if (code == NE_EXPR)
{
ppl_Pointset_Powerset_C_Polyhedron_t left = PBB_DOMAIN (pbb);
ppl_Pointset_Powerset_C_Polyhedron_t right;
ppl_new_Pointset_Powerset_C_Polyhedron_from_Pointset_Powerset_C_Polyhedron
(&right, left);
add_condition_to_domain (left, stmt, pbb, LT_EXPR);
add_condition_to_domain (right, stmt, pbb, GT_EXPR);
ppl_Pointset_Powerset_C_Polyhedron_upper_bound_assign (left,
right);
ppl_delete_Pointset_Powerset_C_Polyhedron (right);
}
else
add_condition_to_domain (PBB_DOMAIN (pbb), stmt, pbb, code);
}
/* Add conditions to the domain of PBB. */
static void
add_conditions_to_domain (poly_bb_p pbb)
{
unsigned int i;
gimple stmt;
gimple_bb_p gbb = PBB_BLACK_BOX (pbb);
VEC (gimple, heap) *conditions = GBB_CONDITIONS (gbb);
if (VEC_empty (gimple, conditions))
return;
for (i = 0; VEC_iterate (gimple, conditions, i, stmt); i++)
switch (gimple_code (stmt))
{
case GIMPLE_COND:
{
enum tree_code code = gimple_cond_code (stmt);
/* The conditions for ELSE-branches are inverted. */
if (VEC_index (gimple, gbb->condition_cases, i) == NULL)
code = invert_tree_comparison (code, false);
add_condition_to_pbb (pbb, stmt, code);
break;
}
case GIMPLE_SWITCH:
/* Switch statements are not supported right now - fall throught. */
default:
gcc_unreachable ();
break;
}
}
/* Structure used to pass data to dom_walk. */
struct bsc
{
VEC (gimple, heap) **conditions, **cases;
sese region;
};
/* Returns non NULL when BB has a single predecessor and the last
statement of that predecessor is a COND_EXPR. */
static gimple
single_pred_cond (basic_block bb)
{
if (single_pred_p (bb))
{
edge e = single_pred_edge (bb);
basic_block pred = e->src;
gimple stmt = last_stmt (pred);
if (stmt && gimple_code (stmt) == GIMPLE_COND)
return stmt;
}
return NULL;
}
/* Call-back for dom_walk executed before visiting the dominated
blocks. */
static void
build_sese_conditions_before (struct dom_walk_data *dw_data,
basic_block bb)
{
struct bsc *data = (struct bsc *) dw_data->global_data;
VEC (gimple, heap) **conditions = data->conditions;
VEC (gimple, heap) **cases = data->cases;
gimple_bb_p gbb = gbb_from_bb (bb);
gimple stmt = single_pred_cond (bb);
if (!bb_in_sese_p (bb, data->region))
return;
if (stmt)
{
edge e = single_pred_edge (bb);
VEC_safe_push (gimple, heap, *conditions, stmt);
if (e->flags & EDGE_TRUE_VALUE)
VEC_safe_push (gimple, heap, *cases, stmt);
else
VEC_safe_push (gimple, heap, *cases, NULL);
}
if (gbb)
{
GBB_CONDITIONS (gbb) = VEC_copy (gimple, heap, *conditions);
GBB_CONDITION_CASES (gbb) = VEC_copy (gimple, heap, *cases);
}
}
/* Call-back for dom_walk executed after visiting the dominated
blocks. */
static void
build_sese_conditions_after (struct dom_walk_data *dw_data,
basic_block bb)
{
struct bsc *data = (struct bsc *) dw_data->global_data;
VEC (gimple, heap) **conditions = data->conditions;
VEC (gimple, heap) **cases = data->cases;
if (!bb_in_sese_p (bb, data->region))
return;
if (single_pred_cond (bb))
{
VEC_pop (gimple, *conditions);
VEC_pop (gimple, *cases);
}
}
/* Record all conditions in REGION. */
static void
build_sese_conditions (sese region)
{
struct dom_walk_data walk_data;
VEC (gimple, heap) *conditions = VEC_alloc (gimple, heap, 3);
VEC (gimple, heap) *cases = VEC_alloc (gimple, heap, 3);
struct bsc data;
data.conditions = &conditions;
data.cases = &cases;
data.region = region;
walk_data.dom_direction = CDI_DOMINATORS;
walk_data.initialize_block_local_data = NULL;
walk_data.before_dom_children = build_sese_conditions_before;
walk_data.after_dom_children = build_sese_conditions_after;
walk_data.global_data = &data;
walk_data.block_local_data_size = 0;
init_walk_dominator_tree (&walk_data);
walk_dominator_tree (&walk_data, SESE_ENTRY_BB (region));
fini_walk_dominator_tree (&walk_data);
VEC_free (gimple, heap, conditions);
VEC_free (gimple, heap, cases);
}
/* Traverses all the GBBs of the SCOP and add their constraints to the
iteration domains. */
static void
add_conditions_to_constraints (scop_p scop)
{
int i;
poly_bb_p pbb;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
add_conditions_to_domain (pbb);
}
/* Add constraints on the possible values of parameter P from the type
of P. */
static void
add_param_constraints (scop_p scop, ppl_Polyhedron_t context, graphite_dim_t p)
{
ppl_Constraint_t cstr;
ppl_Linear_Expression_t le;
tree parameter = VEC_index (tree, SESE_PARAMS (SCOP_REGION (scop)), p);
tree type = TREE_TYPE (parameter);
tree lb, ub;
/* Disabled until we fix CPU2006. */
return;
if (!INTEGRAL_TYPE_P (type))
return;
lb = TYPE_MIN_VALUE (type);
ub = TYPE_MAX_VALUE (type);
if (lb)
{
ppl_new_Linear_Expression_with_dimension (&le, scop_nb_params (scop));
ppl_set_coef (le, p, -1);
ppl_set_inhomogeneous_tree (le, lb);
ppl_new_Constraint (&cstr, le, PPL_CONSTRAINT_TYPE_LESS_OR_EQUAL);
ppl_Polyhedron_add_constraint (context, cstr);
ppl_delete_Linear_Expression (le);
ppl_delete_Constraint (cstr);
}
if (ub)
{
ppl_new_Linear_Expression_with_dimension (&le, scop_nb_params (scop));
ppl_set_coef (le, p, -1);
ppl_set_inhomogeneous_tree (le, ub);
ppl_new_Constraint (&cstr, le, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_Polyhedron_add_constraint (context, cstr);
ppl_delete_Linear_Expression (le);
ppl_delete_Constraint (cstr);
}
}
/* Build the context of the SCOP. The context usually contains extra
constraints that are added to the iteration domains that constrain
some parameters. */
static void
build_scop_context (scop_p scop)
{
ppl_Polyhedron_t context;
graphite_dim_t p, n = scop_nb_params (scop);
ppl_new_C_Polyhedron_from_space_dimension (&context, n, 0);
for (p = 0; p < n; p++)
add_param_constraints (scop, context, p);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron
(&SCOP_CONTEXT (scop), context);
ppl_delete_Polyhedron (context);
}
/* Build the iteration domains: the loops belonging to the current
SCOP, and that vary for the execution of the current basic block.
Returns false if there is no loop in SCOP. */
static void
build_scop_iteration_domain (scop_p scop)
{
struct loop *loop;
sese region = SCOP_REGION (scop);
int i;
ppl_Polyhedron_t ph;
poly_bb_p pbb;
ppl_new_C_Polyhedron_from_space_dimension (&ph, scop_nb_params (scop), 0);
for (i = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), i, loop); i++)
if (!loop_in_sese_p (loop_outer (loop), region))
build_loop_iteration_domains (scop, loop, ph, 0);
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
if (gbb_loop (PBB_BLACK_BOX (pbb))->aux)
ppl_new_Pointset_Powerset_C_Polyhedron_from_Pointset_Powerset_C_Polyhedron
(&PBB_DOMAIN (pbb), (ppl_const_Pointset_Powerset_C_Polyhedron_t)
gbb_loop (PBB_BLACK_BOX (pbb))->aux);
else
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron
(&PBB_DOMAIN (pbb), ph);
for (i = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), i, loop); i++)
if (loop->aux)
{
ppl_delete_Pointset_Powerset_C_Polyhedron
((ppl_Pointset_Powerset_C_Polyhedron_t) loop->aux);
loop->aux = NULL;
}
ppl_delete_Polyhedron (ph);
}
/* Add a constrain to the ACCESSES polyhedron for the alias set of
data reference DR. ACCESSP_NB_DIMS is the dimension of the
ACCESSES polyhedron, DOM_NB_DIMS is the dimension of the iteration
domain. */
static void
pdr_add_alias_set (ppl_Polyhedron_t accesses, data_reference_p dr,
ppl_dimension_type accessp_nb_dims,
ppl_dimension_type dom_nb_dims)
{
ppl_Linear_Expression_t alias;
ppl_Constraint_t cstr;
int alias_set_num = 0;
if (dr->aux != NULL)
{
alias_set_num = *((int *)(dr->aux));
free (dr->aux);
dr->aux = NULL;
}
ppl_new_Linear_Expression_with_dimension (&alias, accessp_nb_dims);
ppl_set_coef (alias, dom_nb_dims, 1);
ppl_set_inhomogeneous (alias, -alias_set_num);
ppl_new_Constraint (&cstr, alias, PPL_CONSTRAINT_TYPE_EQUAL);
ppl_Polyhedron_add_constraint (accesses, cstr);
ppl_delete_Linear_Expression (alias);
ppl_delete_Constraint (cstr);
}
/* Add to ACCESSES polyhedron equalities defining the access functions
to the memory. ACCESSP_NB_DIMS is the dimension of the ACCESSES
polyhedron, DOM_NB_DIMS is the dimension of the iteration domain.
PBB is the poly_bb_p that contains the data reference DR. */
static void
pdr_add_memory_accesses (ppl_Polyhedron_t accesses, data_reference_p dr,
ppl_dimension_type accessp_nb_dims,
ppl_dimension_type dom_nb_dims,
poly_bb_p pbb)
{
int i, nb_subscripts = DR_NUM_DIMENSIONS (dr);
Value v;
scop_p scop = PBB_SCOP (pbb);
sese region = SCOP_REGION (scop);
value_init (v);
for (i = 0; i < nb_subscripts; i++)
{
ppl_Linear_Expression_t fn, access;
ppl_Constraint_t cstr;
ppl_dimension_type subscript = dom_nb_dims + 1 + i;
tree afn = DR_ACCESS_FN (dr, nb_subscripts - 1 - i);
ppl_new_Linear_Expression_with_dimension (&fn, dom_nb_dims);
ppl_new_Linear_Expression_with_dimension (&access, accessp_nb_dims);
value_set_si (v, 1);
scan_tree_for_params (region, afn, fn, v);
ppl_assign_Linear_Expression_from_Linear_Expression (access, fn);
ppl_set_coef (access, subscript, -1);
ppl_new_Constraint (&cstr, access, PPL_CONSTRAINT_TYPE_EQUAL);
ppl_Polyhedron_add_constraint (accesses, cstr);
ppl_delete_Linear_Expression (fn);
ppl_delete_Linear_Expression (access);
ppl_delete_Constraint (cstr);
}
value_clear (v);
}
/* Add constrains representing the size of the accessed data to the
DATA_CONTAINER polyhedron. ACCESSP_NB_DIMS is the dimension of the
DATA_CONTAINER polyhedron, DOM_NB_DIMS is the dimension of the iteration
domain. */
static void
pdr_add_data_dimensions (ppl_Polyhedron_t data_container, data_reference_p dr,
ppl_dimension_type accessp_nb_dims,
ppl_dimension_type dom_nb_dims)
{
tree ref = DR_REF (dr);
int i, nb_subscripts = DR_NUM_DIMENSIONS (dr);
tree array_size;
HOST_WIDE_INT elt_size;
array_size = TYPE_SIZE (TREE_TYPE (ref));
if (array_size == NULL_TREE
|| TREE_CODE (array_size) != INTEGER_CST)
return;
elt_size = int_cst_value (array_size);
for (i = nb_subscripts - 1; i >= 0; i--)
{
ppl_Linear_Expression_t expr;
ppl_Constraint_t cstr;
ppl_dimension_type subscript = dom_nb_dims + 1 + i;
/* 0 <= subscript */
ppl_new_Linear_Expression_with_dimension (&expr, accessp_nb_dims);
ppl_set_coef (expr, subscript, 1);
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_Polyhedron_add_constraint (data_container, cstr);
ppl_delete_Linear_Expression (expr);
ppl_delete_Constraint (cstr);
ref = TREE_OPERAND (ref, 0);
array_size = TYPE_SIZE (TREE_TYPE (ref));
if (array_size == NULL_TREE
|| TREE_CODE (array_size) != INTEGER_CST)
break;
/* subscript <= array_size */
ppl_new_Linear_Expression_with_dimension (&expr, accessp_nb_dims);
ppl_set_coef (expr, subscript, -1);
if (elt_size)
ppl_set_inhomogeneous (expr, int_cst_value (array_size) / elt_size);
ppl_new_Constraint (&cstr, expr, PPL_CONSTRAINT_TYPE_GREATER_OR_EQUAL);
ppl_Polyhedron_add_constraint (data_container, cstr);
ppl_delete_Linear_Expression (expr);
ppl_delete_Constraint (cstr);
elt_size = int_cst_value (array_size);
}
}
/* Build data accesses for DR in PBB. */
static void
build_poly_dr (data_reference_p dr, poly_bb_p pbb)
{
ppl_Polyhedron_t accesses, data_container;
ppl_Pointset_Powerset_C_Polyhedron_t accesses_ps, data_container_ps;
ppl_dimension_type dom_nb_dims;
ppl_dimension_type accessp_nb_dims;
ppl_Pointset_Powerset_C_Polyhedron_space_dimension (PBB_DOMAIN (pbb),
&dom_nb_dims);
accessp_nb_dims = dom_nb_dims + 1 + DR_NUM_DIMENSIONS (dr);
ppl_new_C_Polyhedron_from_space_dimension (&accesses, accessp_nb_dims, 0);
ppl_new_C_Polyhedron_from_space_dimension (&data_container,
accessp_nb_dims, 0);
pdr_add_alias_set (accesses, dr, accessp_nb_dims, dom_nb_dims);
pdr_add_memory_accesses (accesses, dr, accessp_nb_dims, dom_nb_dims, pbb);
pdr_add_data_dimensions (data_container, dr, accessp_nb_dims, dom_nb_dims);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron (&accesses_ps,
accesses);
ppl_new_Pointset_Powerset_C_Polyhedron_from_C_Polyhedron (&data_container_ps,
data_container);
ppl_delete_Polyhedron (accesses);
ppl_delete_Polyhedron (data_container);
new_poly_dr (pbb, accesses_ps, data_container_ps,
DR_IS_READ (dr) ? PDR_READ : PDR_WRITE, dr);
}
/* Group each data reference in DRS with it's alias set num. */
static void
build_alias_set_for_drs (VEC (data_reference_p, heap) **drs)
{
int num_vertex = VEC_length (data_reference_p, *drs);
struct graph *g = new_graph (num_vertex);
data_reference_p dr1, dr2;
int i, j;
int num_component;
int *queue;
for (i = 0; VEC_iterate (data_reference_p, *drs, i, dr1); i++)
for (j = i+1; VEC_iterate (data_reference_p, *drs, j, dr2); j++)
if (dr_may_alias_p (dr1, dr2))
{
add_edge (g, i, j);
add_edge (g, j, i);
}
queue = XNEWVEC (int, num_vertex);
for (i = 0; i < num_vertex; i++)
queue[i] = i;
num_component = graphds_dfs (g, queue, num_vertex, NULL, true, NULL);
for (i = 0; i < g->n_vertices; i++)
{
data_reference_p dr = VEC_index (data_reference_p, *drs, i);
dr->aux = XNEW (int);
*((int *)(dr->aux)) = g->vertices[i].component + 1;
}
free (queue);
free_graph (g);
}
/* Build the data references for PBB. */
static void
build_pbb_drs (poly_bb_p pbb)
{
int j;
data_reference_p dr;
VEC (data_reference_p, heap) *gbb_drs = GBB_DATA_REFS (PBB_BLACK_BOX (pbb));
build_alias_set_for_drs (&gbb_drs);
for (j = 0; VEC_iterate (data_reference_p, gbb_drs, j, dr); j++)
build_poly_dr (dr, pbb);
}
/* Build data references in SCOP. */
static void
build_scop_drs (scop_p scop)
{
int i;
poly_bb_p pbb;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
build_pbb_drs (pbb);
}
/* Return a gsi at the position of the VAR definition. */
static gimple_stmt_iterator
gsi_for_ssa_name_def (tree var)
{
gimple stmt;
basic_block bb;
gimple_stmt_iterator gsi;
gimple_stmt_iterator psi;
gcc_assert (TREE_CODE (var) == SSA_NAME);
stmt = SSA_NAME_DEF_STMT (var);
bb = gimple_bb (stmt);
for (psi = gsi_start_phis (bb); !gsi_end_p (psi); gsi_next (&psi))
if (stmt == gsi_stmt (psi))
return gsi_after_labels (bb);
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
if (stmt == gsi_stmt (gsi))
{
gsi_next (&gsi);
return gsi;
}
gcc_unreachable ();
return gsi;
}
/* Insert the assignment "RES := VAR" just after the definition of VAR. */
static void
insert_out_of_ssa_copy (tree res, tree var)
{
gimple_stmt_iterator gsi = gsi_for_ssa_name_def (var);
gimple stmt;
gimple_seq stmts;
gimple_stmt_iterator si;
var = force_gimple_operand (var, &stmts, true, NULL_TREE);
stmt = gimple_build_assign (res, var);
if (!stmts)
stmts = gimple_seq_alloc ();
si = gsi_last (stmts);
gsi_insert_after (&si, stmt, GSI_NEW_STMT);
gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
}
/* Insert on edge E the assignment "RES := EXPR". */
static void
insert_out_of_ssa_copy_on_edge (edge e, tree res, tree expr)
{
gimple_stmt_iterator gsi;
gimple_seq stmts;
tree var = force_gimple_operand (expr, &stmts, true, NULL_TREE);
gimple stmt = gimple_build_assign (res, var);
if (!stmts)
stmts = gimple_seq_alloc ();
gsi = gsi_last (stmts);
gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
gsi_insert_seq_on_edge (e, stmts);
gsi_commit_edge_inserts ();
}
/* Creates a zero dimension array of the same type as VAR. */
static tree
create_zero_dim_array (tree var)
{
tree index_type = build_index_type (integer_zero_node);
tree elt_type = TREE_TYPE (var);
tree array_type = build_array_type (elt_type, index_type);
tree base = create_tmp_var (array_type, "Red");
add_referenced_var (base);
return build4 (ARRAY_REF, elt_type, base, integer_zero_node, NULL_TREE,
NULL_TREE);
}
/* Returns true when PHI is a loop close phi node. */
static bool
scalar_close_phi_node_p (gimple phi)
{
gcc_assert (gimple_code (phi) == GIMPLE_PHI);
if (!is_gimple_reg (gimple_phi_result (phi)))
return false;
return (gimple_phi_num_args (phi) == 1);
}
/* Rewrite out of SSA the reduction phi node at PSI by creating a zero
dimension array for it. */
static void
rewrite_close_phi_out_of_ssa (gimple_stmt_iterator *psi)
{
gimple phi = gsi_stmt (*psi);
tree res = gimple_phi_result (phi);
tree var = SSA_NAME_VAR (res);
tree zero_dim_array = create_zero_dim_array (var);
gimple_stmt_iterator gsi = gsi_after_labels (gimple_bb (phi));
gimple stmt = gimple_build_assign (res, zero_dim_array);
tree arg = gimple_phi_arg_def (phi, 0);
insert_out_of_ssa_copy (zero_dim_array, arg);
remove_phi_node (psi, false);
gsi_insert_before (&gsi, stmt, GSI_NEW_STMT);
SSA_NAME_DEF_STMT (res) = stmt;
}
/* Rewrite out of SSA the reduction phi node at PSI by creating a zero
dimension array for it. */
static void
rewrite_phi_out_of_ssa (gimple_stmt_iterator *psi)
{
size_t i;
gimple phi = gsi_stmt (*psi);
basic_block bb = gimple_bb (phi);
tree res = gimple_phi_result (phi);
tree var = SSA_NAME_VAR (res);
tree zero_dim_array = create_zero_dim_array (var);
gimple_stmt_iterator gsi;
gimple stmt;
gimple_seq stmts;
for (i = 0; i < gimple_phi_num_args (phi); i++)
{
tree arg = gimple_phi_arg_def (phi, i);
/* Try to avoid the insertion on edges as much as possible: this
would avoid the insertion of code on loop latch edges, making
the pattern matching of the vectorizer happy, or it would
avoid the insertion of useless basic blocks. Note that it is
incorrect to insert out of SSA copies close by their
definition when they are more than two loop levels apart:
for example, starting from a double nested loop
| a = ...
| loop_1
| loop_2
| b = phi (a, c)
| c = ...
| end_2
| end_1
the following transform is incorrect
| a = ...
| Red[0] = a
| loop_1
| loop_2
| b = Red[0]
| c = ...
| Red[0] = c
| end_2
| end_1
whereas inserting the copy on the incomming edge is correct
| a = ...
| loop_1
| Red[0] = a
| loop_2
| b = Red[0]
| c = ...
| Red[0] = c
| end_2
| end_1
*/
if (TREE_CODE (arg) == SSA_NAME
&& is_gimple_reg (arg)
&& gimple_bb (SSA_NAME_DEF_STMT (arg))
&& (flow_bb_inside_loop_p (bb->loop_father,
gimple_bb (SSA_NAME_DEF_STMT (arg)))
|| flow_bb_inside_loop_p (loop_outer (bb->loop_father),
gimple_bb (SSA_NAME_DEF_STMT (arg)))))
insert_out_of_ssa_copy (zero_dim_array, arg);
else
insert_out_of_ssa_copy_on_edge (gimple_phi_arg_edge (phi, i),
zero_dim_array, arg);
}
var = force_gimple_operand (zero_dim_array, &stmts, true, NULL_TREE);
if (!stmts)
stmts = gimple_seq_alloc ();
stmt = gimple_build_assign (res, var);
remove_phi_node (psi, false);
SSA_NAME_DEF_STMT (res) = stmt;
gsi = gsi_last (stmts);
gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
gsi = gsi_after_labels (bb);
gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
}
/* Rewrite out of SSA all the reduction phi nodes of SCOP. */
static void
rewrite_reductions_out_of_ssa (scop_p scop)
{
basic_block bb;
gimple_stmt_iterator psi;
sese region = SCOP_REGION (scop);
FOR_EACH_BB (bb)
if (bb_in_region (bb, SESE_ENTRY_BB (region), SESE_EXIT_BB (region)))
for (psi = gsi_start_phis (bb); !gsi_end_p (psi);)
{
if (scalar_close_phi_node_p (gsi_stmt (psi)))
rewrite_close_phi_out_of_ssa (&psi);
else if (reduction_phi_p (region, &psi))
rewrite_phi_out_of_ssa (&psi);
}
update_ssa (TODO_update_ssa);
#ifdef ENABLE_CHECKING
verify_ssa (false);
verify_loop_closed_ssa ();
#endif
}
/* Returns the number of pbbs that are in loops contained in SCOP. */
static int
nb_pbbs_in_loops (scop_p scop)
{
int i;
poly_bb_p pbb;
int res = 0;
for (i = 0; VEC_iterate (poly_bb_p, SCOP_BBS (scop), i, pbb); i++)
if (loop_in_sese_p (gbb_loop (PBB_BLACK_BOX (pbb)), SCOP_REGION (scop)))
res++;
return res;
}
/* Builds the polyhedral representation for a SESE region. */
bool
build_poly_scop (scop_p scop)
{
sese region = SCOP_REGION (scop);
rewrite_reductions_out_of_ssa (scop);
build_scop_bbs (scop);
/* FIXME: This restriction is needed to avoid a problem in CLooG.
Once CLooG is fixed, remove this guard. Anyways, it makes no
sense to optimize a scop containing only PBBs that do not belong
to any loops. */
if (nb_pbbs_in_loops (scop) == 0)
return false;
build_sese_loop_nests (region);
build_sese_conditions (region);
find_scop_parameters (scop);
build_scop_iteration_domain (scop);
build_scop_context (scop);
add_conditions_to_constraints (scop);
build_scop_scattering (scop);
build_scop_drs (scop);
return true;
}
/* Always return false. Exercise the scop_to_clast function. */
void
check_poly_representation (scop_p scop)
{
#ifdef ENABLE_CHECKING
cloog_prog_clast pc = scop_to_clast (scop);
cloog_clast_free (pc.stmt);
cloog_program_free (pc.prog);
#endif
}
#endif
/* Conversion of SESE regions to Polyhedra.
Copyright (C) 2009 Free Software Foundation, Inc.
Contributed by Sebastian Pop <sebastian.pop@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_GRAPHITE_SESE_TO_POLY_H
#define GCC_GRAPHITE_SESE_TO_POLY_H
bool build_poly_scop (scop_p);
void check_poly_representation (scop_p);
#endif
/* Single entry single exit control flow regions.
Copyright (C) 2008, 2009 Free Software Foundation, Inc.
Contributed by Jan Sjodin <jan.sjodin@amd.com> and
Sebastian Pop <sebastian.pop@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "toplev.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "domwalk.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "gimple.h"
#include "sese.h"
/* Print to stderr the element ELT. */
static void
debug_rename_elt (rename_map_elt elt)
{
fprintf (stderr, "(");
print_generic_expr (stderr, elt->old_name, 0);
fprintf (stderr, ", ");
print_generic_expr (stderr, elt->expr, 0);
fprintf (stderr, ")\n");
}
/* Helper function for debug_rename_map. */
static int
debug_rename_map_1 (void **slot, void *s ATTRIBUTE_UNUSED)
{
struct rename_map_elt_s *entry = (struct rename_map_elt_s *) *slot;
debug_rename_elt (entry);
return 1;
}
/* Print to stderr all the elements of MAP. */
void
debug_rename_map (htab_t map)
{
htab_traverse (map, debug_rename_map_1, NULL);
}
/* Computes a hash function for database element ELT. */
hashval_t
rename_map_elt_info (const void *elt)
{
return htab_hash_pointer (((const struct rename_map_elt_s *) elt)->old_name);
}
/* Compares database elements E1 and E2. */
int
eq_rename_map_elts (const void *e1, const void *e2)
{
const struct rename_map_elt_s *elt1 = (const struct rename_map_elt_s *) e1;
const struct rename_map_elt_s *elt2 = (const struct rename_map_elt_s *) e2;
return (elt1->old_name == elt2->old_name);
}
/* Print to stderr the element ELT. */
static void
debug_ivtype_elt (ivtype_map_elt elt)
{
fprintf (stderr, "(%s, ", elt->cloog_iv);
print_generic_expr (stderr, elt->type, 0);
fprintf (stderr, ")\n");
}
/* Helper function for debug_ivtype_map. */
static int
debug_ivtype_map_1 (void **slot, void *s ATTRIBUTE_UNUSED)
{
struct ivtype_map_elt_s *entry = (struct ivtype_map_elt_s *) *slot;
debug_ivtype_elt (entry);
return 1;
}
/* Print to stderr all the elements of MAP. */
void
debug_ivtype_map (htab_t map)
{
htab_traverse (map, debug_ivtype_map_1, NULL);
}
/* Computes a hash function for database element ELT. */
hashval_t
ivtype_map_elt_info (const void *elt)
{
return htab_hash_pointer (((const struct ivtype_map_elt_s *) elt)->cloog_iv);
}
/* Compares database elements E1 and E2. */
int
eq_ivtype_map_elts (const void *e1, const void *e2)
{
const struct ivtype_map_elt_s *elt1 = (const struct ivtype_map_elt_s *) e1;
const struct ivtype_map_elt_s *elt2 = (const struct ivtype_map_elt_s *) e2;
return (elt1->cloog_iv == elt2->cloog_iv);
}
/* Record LOOP as occuring in REGION. */
static void
sese_record_loop (sese region, loop_p loop)
{
if (sese_contains_loop (region, loop))
return;
bitmap_set_bit (SESE_LOOPS (region), loop->num);
VEC_safe_push (loop_p, heap, SESE_LOOP_NEST (region), loop);
}
/* Build the loop nests contained in REGION. Returns true when the
operation was successful. */
void
build_sese_loop_nests (sese region)
{
unsigned i;
basic_block bb;
struct loop *loop0, *loop1;
FOR_EACH_BB (bb)
if (bb_in_sese_p (bb, region))
{
struct loop *loop = bb->loop_father;
/* Only add loops if they are completely contained in the SCoP. */
if (loop->header == bb
&& bb_in_sese_p (loop->latch, region))
sese_record_loop (region, loop);
}
/* Make sure that the loops in the SESE_LOOP_NEST are ordered. It
can be the case that an inner loop is inserted before an outer
loop. To avoid this, semi-sort once. */
for (i = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), i, loop0); i++)
{
if (VEC_length (loop_p, SESE_LOOP_NEST (region)) == i + 1)
break;
loop1 = VEC_index (loop_p, SESE_LOOP_NEST (region), i + 1);
if (loop0->num > loop1->num)
{
VEC_replace (loop_p, SESE_LOOP_NEST (region), i, loop1);
VEC_replace (loop_p, SESE_LOOP_NEST (region), i + 1, loop0);
}
}
}
/* For a USE in BB, if BB is outside REGION, mark the USE in the
LIVEOUTS set. */
static void
sese_build_liveouts_use (sese region, bitmap liveouts, basic_block bb,
tree use)
{
unsigned ver;
basic_block def_bb;
if (TREE_CODE (use) != SSA_NAME)
return;
ver = SSA_NAME_VERSION (use);
def_bb = gimple_bb (SSA_NAME_DEF_STMT (use));
if (!def_bb
|| !bb_in_sese_p (def_bb, region)
|| bb_in_sese_p (bb, region))
return;
bitmap_set_bit (liveouts, ver);
}
/* Marks for rewrite all the SSA_NAMES defined in REGION and that are
used in BB that is outside of the REGION. */
static void
sese_build_liveouts_bb (sese region, bitmap liveouts, basic_block bb)
{
gimple_stmt_iterator bsi;
edge e;
edge_iterator ei;
ssa_op_iter iter;
use_operand_p use_p;
FOR_EACH_EDGE (e, ei, bb->succs)
for (bsi = gsi_start_phis (e->dest); !gsi_end_p (bsi); gsi_next (&bsi))
sese_build_liveouts_use (region, liveouts, bb,
PHI_ARG_DEF_FROM_EDGE (gsi_stmt (bsi), e));
for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
FOR_EACH_SSA_USE_OPERAND (use_p, gsi_stmt (bsi), iter, SSA_OP_ALL_USES)
sese_build_liveouts_use (region, liveouts, bb, USE_FROM_PTR (use_p));
}
/* Build the LIVEOUTS of REGION: the set of variables defined inside
and used outside the REGION. */
static void
sese_build_liveouts (sese region, bitmap liveouts)
{
basic_block bb;
FOR_EACH_BB (bb)
sese_build_liveouts_bb (region, liveouts, bb);
}
/* Builds a new SESE region from edges ENTRY and EXIT. */
sese
new_sese (edge entry, edge exit)
{
sese region = XNEW (struct sese_s);
SESE_ENTRY (region) = entry;
SESE_EXIT (region) = exit;
SESE_LOOPS (region) = BITMAP_ALLOC (NULL);
SESE_LOOP_NEST (region) = VEC_alloc (loop_p, heap, 3);
SESE_ADD_PARAMS (region) = true;
SESE_PARAMS (region) = VEC_alloc (tree, heap, 3);
SESE_PARAMS_INDEX (region) = htab_create (10, clast_name_index_elt_info,
eq_clast_name_indexes, free);
SESE_PARAMS_NAMES (region) = XNEWVEC (char *, num_ssa_names);
return region;
}
/* Deletes REGION. */
void
free_sese (sese region)
{
if (SESE_LOOPS (region))
SESE_LOOPS (region) = BITMAP_ALLOC (NULL);
VEC_free (tree, heap, SESE_PARAMS (region));
VEC_free (loop_p, heap, SESE_LOOP_NEST (region));
if (SESE_PARAMS_INDEX (region))
htab_delete (SESE_PARAMS_INDEX (region));
/* Do not free SESE_PARAMS_NAMES: CLooG does that. */
XDELETE (region);
}
/* Add exit phis for USE on EXIT. */
static void
sese_add_exit_phis_edge (basic_block exit, tree use, edge false_e, edge true_e)
{
gimple phi = create_phi_node (use, exit);
create_new_def_for (gimple_phi_result (phi), phi,
gimple_phi_result_ptr (phi));
add_phi_arg (phi, use, false_e, UNKNOWN_LOCATION);
add_phi_arg (phi, use, true_e, UNKNOWN_LOCATION);
}
/* Insert in the block BB phi nodes for variables defined in REGION
and used outside the REGION. The code generation moves REGION in
the else clause of an "if (1)" and generates code in the then
clause that is at this point empty:
| if (1)
| empty;
| else
| REGION;
*/
void
sese_insert_phis_for_liveouts (sese region, basic_block bb,
edge false_e, edge true_e)
{
unsigned i;
bitmap_iterator bi;
bitmap liveouts = BITMAP_ALLOC (NULL);
update_ssa (TODO_update_ssa);
sese_build_liveouts (region, liveouts);
EXECUTE_IF_SET_IN_BITMAP (liveouts, 0, i, bi)
sese_add_exit_phis_edge (bb, ssa_name (i), false_e, true_e);
BITMAP_FREE (liveouts);
update_ssa (TODO_update_ssa);
}
/* Get the definition of NAME before the SESE. Keep track of the
basic blocks that have been VISITED in a bitmap. */
static tree
get_vdef_before_sese (sese region, tree name, sbitmap visited)
{
unsigned i;
gimple def_stmt = SSA_NAME_DEF_STMT (name);
basic_block def_bb = gimple_bb (def_stmt);
if (!def_bb || !bb_in_sese_p (def_bb, region))
return name;
if (TEST_BIT (visited, def_bb->index))
return NULL_TREE;
SET_BIT (visited, def_bb->index);
switch (gimple_code (def_stmt))
{
case GIMPLE_PHI:
for (i = 0; i < gimple_phi_num_args (def_stmt); i++)
{
tree arg = gimple_phi_arg_def (def_stmt, i);
tree res = get_vdef_before_sese (region, arg, visited);
if (res)
return res;
}
return NULL_TREE;
default:
return NULL_TREE;
}
}
/* Adjust a virtual phi node PHI that is placed at the end of the
generated code for SCOP:
| if (1)
| generated code from REGION;
| else
| REGION;
The FALSE_E edge comes from the original code, TRUE_E edge comes
from the code generated for the SCOP. */
static void
sese_adjust_vphi (sese region, gimple phi, edge true_e)
{
unsigned i;
gcc_assert (gimple_phi_num_args (phi) == 2);
for (i = 0; i < gimple_phi_num_args (phi); i++)
if (gimple_phi_arg_edge (phi, i) == true_e)
{
tree true_arg, false_arg, before_scop_arg;
sbitmap visited;
true_arg = gimple_phi_arg_def (phi, i);
if (!SSA_NAME_IS_DEFAULT_DEF (true_arg))
return;
false_arg = gimple_phi_arg_def (phi, i == 0 ? 1 : 0);
if (SSA_NAME_IS_DEFAULT_DEF (false_arg))
return;
visited = sbitmap_alloc (last_basic_block);
sbitmap_zero (visited);
before_scop_arg = get_vdef_before_sese (region, false_arg, visited);
gcc_assert (before_scop_arg != NULL_TREE);
SET_PHI_ARG_DEF (phi, i, before_scop_arg);
sbitmap_free (visited);
}
}
/* Returns the name associated to OLD_NAME in MAP. */
static tree
get_rename (htab_t map, tree old_name)
{
struct rename_map_elt_s tmp;
PTR *slot;
tmp.old_name = old_name;
slot = htab_find_slot (map, &tmp, NO_INSERT);
if (slot && *slot)
return ((rename_map_elt) *slot)->expr;
return old_name;
}
/* Register in MAP the rename tuple (old_name, expr). */
void
set_rename (htab_t map, tree old_name, tree expr)
{
struct rename_map_elt_s tmp;
PTR *slot;
if (old_name == expr)
return;
tmp.old_name = old_name;
slot = htab_find_slot (map, &tmp, INSERT);
if (!slot)
return;
if (*slot)
free (*slot);
*slot = new_rename_map_elt (old_name, expr);
}
/* Adjusts the phi nodes in the block BB for variables defined in
SCOP_REGION and used outside the SCOP_REGION. The code generation
moves SCOP_REGION in the else clause of an "if (1)" and generates
code in the then clause:
| if (1)
| generated code from REGION;
| else
| REGION;
To adjust the phi nodes after the condition, the RENAME_MAP is
used. */
void
sese_adjust_liveout_phis (sese region, htab_t rename_map, basic_block bb,
edge false_e, edge true_e)
{
gimple_stmt_iterator si;
for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
{
unsigned i;
unsigned false_i = 0;
gimple phi = gsi_stmt (si);
if (!is_gimple_reg (PHI_RESULT (phi)))
{
sese_adjust_vphi (region, phi, true_e);
continue;
}
for (i = 0; i < gimple_phi_num_args (phi); i++)
if (gimple_phi_arg_edge (phi, i) == false_e)
{
false_i = i;
break;
}
for (i = 0; i < gimple_phi_num_args (phi); i++)
if (gimple_phi_arg_edge (phi, i) == true_e)
{
tree old_name = gimple_phi_arg_def (phi, false_i);
tree expr = get_rename (rename_map, old_name);
gimple_seq stmts;
gcc_assert (old_name != expr);
if (TREE_CODE (expr) != SSA_NAME
&& is_gimple_reg (old_name))
{
tree type = TREE_TYPE (old_name);
tree var = create_tmp_var (type, "var");
expr = build2 (MODIFY_EXPR, type, var, expr);
expr = force_gimple_operand (expr, &stmts, true, NULL);
gsi_insert_seq_on_edge_immediate (true_e, stmts);
}
SET_PHI_ARG_DEF (phi, i, expr);
}
}
}
/* Rename the SSA_NAMEs used in STMT and that appear in MAP. */
static void
rename_variables_in_stmt (gimple stmt, htab_t map, gimple_stmt_iterator *insert_gsi)
{
ssa_op_iter iter;
use_operand_p use_p;
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
{
tree use = USE_FROM_PTR (use_p);
tree expr = get_rename (map, use);
tree type_use = TREE_TYPE (use);
tree type_expr = TREE_TYPE (expr);
gimple_seq stmts;
if (use == expr)
continue;
if (type_use != type_expr
|| (TREE_CODE (expr) != SSA_NAME
&& is_gimple_reg (use)))
{
tree var = create_tmp_var (type_use, "var");
if (type_use != type_expr)
expr = fold_convert (type_use, expr);
expr = build2 (MODIFY_EXPR, type_use, var, expr);
expr = force_gimple_operand (expr, &stmts, true, NULL);
gsi_insert_seq_before (insert_gsi, stmts, GSI_SAME_STMT);
}
replace_exp (use_p, expr);
}
update_stmt (stmt);
}
/* Returns true if NAME is a parameter of SESE. */
static bool
is_parameter (sese region, tree name)
{
int i;
tree p;
for (i = 0; VEC_iterate (tree, SESE_PARAMS (region), i, p); i++)
if (p == name)
return true;
return false;
}
/* Returns true if NAME is an induction variable. */
static bool
is_iv (tree name)
{
return gimple_code (SSA_NAME_DEF_STMT (name)) == GIMPLE_PHI;
}
static void expand_scalar_variables_stmt (gimple, basic_block, sese,
htab_t, gimple_stmt_iterator *);
static tree
expand_scalar_variables_expr (tree, tree, enum tree_code, tree, basic_block,
sese, htab_t, gimple_stmt_iterator *);
static tree
expand_scalar_variables_call (gimple stmt, basic_block bb, sese region,
htab_t map, gimple_stmt_iterator *gsi)
{
int i, nargs = gimple_call_num_args (stmt);
VEC (tree, gc) *args = VEC_alloc (tree, gc, nargs);
tree fn_type = TREE_TYPE (gimple_call_fn (stmt));
tree fn = gimple_call_fndecl (stmt);
tree call_expr, var, lhs;
gimple call;
for (i = 0; i < nargs; i++)
{
tree arg = gimple_call_arg (stmt, i);
tree t = TREE_TYPE (arg);
var = create_tmp_var (t, "var");
arg = expand_scalar_variables_expr (t, arg, TREE_CODE (arg), NULL,
bb, region, map, gsi);
arg = build2 (MODIFY_EXPR, t, var, arg);
arg = force_gimple_operand_gsi (gsi, arg, true, NULL,
true, GSI_SAME_STMT);
VEC_quick_push (tree, args, arg);
}
lhs = gimple_call_lhs (stmt);
var = create_tmp_var (TREE_TYPE (lhs), "var");
call_expr = build_call_vec (fn_type, fn, args);
call = gimple_build_call_from_tree (call_expr);
var = make_ssa_name (var, call);
gimple_call_set_lhs (call, var);
gsi_insert_before (gsi, call, GSI_SAME_STMT);
return var;
}
/* Copies at GSI all the scalar computations on which the ssa_name OP0
depends on in the SESE: these are all the scalar variables used in
the definition of OP0, that are defined outside BB and still in the
SESE, i.e. not a parameter of the SESE. The expression that is
returned contains only induction variables from the generated code:
MAP contains the induction variables renaming mapping, and is used
to translate the names of induction variables. */
static tree
expand_scalar_variables_ssa_name (tree op0, basic_block bb,
sese region, htab_t map,
gimple_stmt_iterator *gsi)
{
gimple def_stmt;
tree new_op;
if (is_parameter (region, op0)
|| is_iv (op0))
return get_rename (map, op0);
def_stmt = SSA_NAME_DEF_STMT (op0);
/* Check whether we already have a rename for OP0. */
new_op = get_rename (map, op0);
if (new_op != op0
&& gimple_bb (SSA_NAME_DEF_STMT (new_op)) == bb)
return new_op;
if (gimple_bb (def_stmt) == bb)
{
/* If the defining statement is in the basic block already
we do not need to create a new expression for it, we
only need to ensure its operands are expanded. */
expand_scalar_variables_stmt (def_stmt, bb, region, map, gsi);
return new_op;
}
else
{
if (!gimple_bb (def_stmt)
|| !bb_in_sese_p (gimple_bb (def_stmt), region))
return new_op;
switch (gimple_code (def_stmt))
{
case GIMPLE_ASSIGN:
{
tree var0 = gimple_assign_rhs1 (def_stmt);
enum tree_code subcode = gimple_assign_rhs_code (def_stmt);
tree var1 = gimple_assign_rhs2 (def_stmt);
tree type = gimple_expr_type (def_stmt);
return expand_scalar_variables_expr (type, var0, subcode, var1, bb,
region, map, gsi);
}
case GIMPLE_CALL:
return expand_scalar_variables_call (def_stmt, bb, region, map, gsi);
default:
gcc_unreachable ();
return new_op;
}
}
}
/* Copies at GSI all the scalar computations on which the expression
OP0 CODE OP1 depends on in the SESE: these are all the scalar
variables used in OP0 and OP1, defined outside BB and still defined
in the SESE, i.e. not a parameter of the SESE. The expression that
is returned contains only induction variables from the generated
code: MAP contains the induction variables renaming mapping, and is
used to translate the names of induction variables. */
static tree
expand_scalar_variables_expr (tree type, tree op0, enum tree_code code,
tree op1, basic_block bb, sese region,
htab_t map, gimple_stmt_iterator *gsi)
{
if (TREE_CODE_CLASS (code) == tcc_constant
|| TREE_CODE_CLASS (code) == tcc_declaration)
return op0;
/* For data references we have to duplicate also its memory
indexing. */
if (TREE_CODE_CLASS (code) == tcc_reference)
{
switch (code)
{
case REALPART_EXPR:
case IMAGPART_EXPR:
{
tree op = TREE_OPERAND (op0, 0);
tree res = expand_scalar_variables_expr
(type, op, TREE_CODE (op), NULL, bb, region, map, gsi);
return build1 (code, type, res);
}
case INDIRECT_REF:
{
tree old_name = TREE_OPERAND (op0, 0);
tree expr = expand_scalar_variables_ssa_name
(old_name, bb, region, map, gsi);
if (TREE_CODE (expr) != SSA_NAME
&& is_gimple_reg (old_name))
{
tree type = TREE_TYPE (old_name);
tree var = create_tmp_var (type, "var");
expr = build2 (MODIFY_EXPR, type, var, expr);
expr = force_gimple_operand_gsi (gsi, expr, true, NULL,
true, GSI_SAME_STMT);
}
return fold_build1 (code, type, expr);
}
case ARRAY_REF:
{
tree op00 = TREE_OPERAND (op0, 0);
tree op01 = TREE_OPERAND (op0, 1);
tree op02 = TREE_OPERAND (op0, 2);
tree op03 = TREE_OPERAND (op0, 3);
tree base = expand_scalar_variables_expr
(TREE_TYPE (op00), op00, TREE_CODE (op00), NULL, bb, region,
map, gsi);
tree subscript = expand_scalar_variables_expr
(TREE_TYPE (op01), op01, TREE_CODE (op01), NULL, bb, region,
map, gsi);
return build4 (ARRAY_REF, type, base, subscript, op02, op03);
}
default:
/* The above cases should catch everything. */
gcc_unreachable ();
}
}
if (TREE_CODE_CLASS (code) == tcc_unary)
{
tree op0_type = TREE_TYPE (op0);
enum tree_code op0_code = TREE_CODE (op0);
tree op0_expr = expand_scalar_variables_expr (op0_type, op0, op0_code,
NULL, bb, region, map, gsi);
return fold_build1 (code, type, op0_expr);
}
if (TREE_CODE_CLASS (code) == tcc_binary
|| TREE_CODE_CLASS (code) == tcc_comparison)
{
tree op0_type = TREE_TYPE (op0);
enum tree_code op0_code = TREE_CODE (op0);
tree op0_expr = expand_scalar_variables_expr (op0_type, op0, op0_code,
NULL, bb, region, map, gsi);
tree op1_type = TREE_TYPE (op1);
enum tree_code op1_code = TREE_CODE (op1);
tree op1_expr = expand_scalar_variables_expr (op1_type, op1, op1_code,
NULL, bb, region, map, gsi);
return fold_build2 (code, type, op0_expr, op1_expr);
}
if (code == SSA_NAME)
return expand_scalar_variables_ssa_name (op0, bb, region, map, gsi);
if (code == ADDR_EXPR)
return op0;
gcc_unreachable ();
return NULL;
}
/* Copies at the beginning of BB all the scalar computations on which
STMT depends on in the SESE: these are all the scalar variables used
in STMT, defined outside BB and still defined in the SESE, i.e. not a
parameter of the SESE. The expression that is returned contains
only induction variables from the generated code: MAP contains the
induction variables renaming mapping, and is used to translate the
names of induction variables. */
static void
expand_scalar_variables_stmt (gimple stmt, basic_block bb, sese region,
htab_t map, gimple_stmt_iterator *gsi)
{
ssa_op_iter iter;
use_operand_p use_p;
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
{
tree use = USE_FROM_PTR (use_p);
tree type = TREE_TYPE (use);
enum tree_code code = TREE_CODE (use);
tree use_expr;
if (!is_gimple_reg (use))
continue;
/* Don't expand USE if we already have a rename for it. */
use_expr = get_rename (map, use);
if (use_expr != use)
continue;
use_expr = expand_scalar_variables_expr (type, use, code, NULL, bb,
region, map, gsi);
use_expr = fold_convert (type, use_expr);
if (use_expr == use)
continue;
if (TREE_CODE (use_expr) != SSA_NAME)
{
tree var = create_tmp_var (type, "var");
use_expr = build2 (MODIFY_EXPR, type, var, use_expr);
use_expr = force_gimple_operand_gsi (gsi, use_expr, true, NULL,
true, GSI_SAME_STMT);
}
replace_exp (use_p, use_expr);
}
update_stmt (stmt);
}
/* Copies at the beginning of BB all the scalar computations on which
BB depends on in the SESE: these are all the scalar variables used
in BB, defined outside BB and still defined in the SESE, i.e. not a
parameter of the SESE. The expression that is returned contains
only induction variables from the generated code: MAP contains the
induction variables renaming mapping, and is used to translate the
names of induction variables. */
static void
expand_scalar_variables (basic_block bb, sese region, htab_t map)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi);)
{
gimple stmt = gsi_stmt (gsi);
expand_scalar_variables_stmt (stmt, bb, region, map, &gsi);
gsi_next (&gsi);
}
}
/* Rename all the SSA_NAMEs from block BB according to the MAP. */
static void
rename_variables (basic_block bb, htab_t map)
{
gimple_stmt_iterator gsi;
gimple_stmt_iterator insert_gsi = gsi_start_bb (bb);
for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi))
rename_variables_in_stmt (gsi_stmt (gsi), map, &insert_gsi);
}
/* Remove condition from BB. */
static void
remove_condition (basic_block bb)
{
gimple last = last_stmt (bb);
if (last && gimple_code (last) == GIMPLE_COND)
{
gimple_stmt_iterator gsi = gsi_last_bb (bb);
gsi_remove (&gsi, true);
}
}
/* Returns the first successor edge of BB with EDGE_TRUE_VALUE flag set. */
edge
get_true_edge_from_guard_bb (basic_block bb)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_TRUE_VALUE)
return e;
gcc_unreachable ();
return NULL;
}
/* Returns the first successor edge of BB with EDGE_TRUE_VALUE flag cleared. */
edge
get_false_edge_from_guard_bb (basic_block bb)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->succs)
if (!(e->flags & EDGE_TRUE_VALUE))
return e;
gcc_unreachable ();
return NULL;
}
/* Returns true when NAME is defined in LOOP. */
static bool
defined_in_loop_p (tree name, loop_p loop)
{
gimple stmt = SSA_NAME_DEF_STMT (name);
return (gimple_bb (stmt)->loop_father == loop);
}
/* Returns the gimple statement that uses NAME outside the loop it is
defined in, returns NULL if there is no such loop close phi node.
An invariant of the loop closed SSA form is that the only use of a
variable, outside the loop it is defined in, is in the loop close
phi node that just follows the loop. */
static gimple
alive_after_loop (tree name)
{
use_operand_p use_p;
imm_use_iterator imm_iter;
loop_p loop = gimple_bb (SSA_NAME_DEF_STMT (name))->loop_father;
FOR_EACH_IMM_USE_FAST (use_p, imm_iter, name)
{
gimple stmt = USE_STMT (use_p);
if (gimple_code (stmt) == GIMPLE_PHI
&& gimple_bb (stmt)->loop_father != loop)
return stmt;
}
return NULL;
}
/* Return true if a close phi has not yet been inserted for the use of
variable NAME on the single exit of LOOP. */
static bool
close_phi_not_yet_inserted_p (loop_p loop, tree name)
{
gimple_stmt_iterator psi;
basic_block bb = single_exit (loop)->dest;
for (psi = gsi_start_phis (bb); !gsi_end_p (psi); gsi_next (&psi))
if (gimple_phi_arg_def (gsi_stmt (psi), 0) == name)
return false;
return true;
}
/* A structure for passing parameters to add_loop_exit_phis. */
typedef struct alep {
loop_p loop;
VEC (rename_map_elt, heap) *new_renames;
} *alep_p;
/* Helper function for htab_traverse in insert_loop_close_phis. */
static int
add_loop_exit_phis (void **slot, void *data)
{
struct rename_map_elt_s *entry;
alep_p a;
loop_p loop;
tree expr, new_name;
bool def_in_loop_p, used_outside_p, need_close_phi_p;
gimple old_close_phi;
if (!slot || !data)
return 1;
entry = (struct rename_map_elt_s *) *slot;
a = (alep_p) data;
loop = a->loop;
expr = entry->expr;
if (TREE_CODE (expr) != SSA_NAME)
return 1;
new_name = expr;
def_in_loop_p = defined_in_loop_p (new_name, loop);
old_close_phi = alive_after_loop (entry->old_name);
used_outside_p = (old_close_phi != NULL);
need_close_phi_p = (def_in_loop_p && used_outside_p
&& close_phi_not_yet_inserted_p (loop, new_name));
/* Insert a loop close phi node. */
if (need_close_phi_p)
{
basic_block bb = single_exit (loop)->dest;
gimple phi = create_phi_node (new_name, bb);
tree new_res = create_new_def_for (gimple_phi_result (phi), phi,
gimple_phi_result_ptr (phi));
add_phi_arg (phi, new_name, single_pred_edge (bb), UNKNOWN_LOCATION);
VEC_safe_push (rename_map_elt, heap, a->new_renames,
new_rename_map_elt (gimple_phi_result (old_close_phi),
new_res));
}
/* Remove the old rename from the map. */
if (def_in_loop_p && *slot)
{
free (*slot);
*slot = NULL;
}
return 1;
}
/* Traverses MAP and removes from it all the tuples (OLD, NEW) where
NEW is defined in LOOP. Inserts on the exit of LOOP the close phi
node "RES = phi (NEW)" corresponding to "OLD_RES = phi (OLD)" in
the original code. Inserts in MAP the tuple (OLD_RES, RES). */
void
insert_loop_close_phis (htab_t map, loop_p loop)
{
int i;
struct alep a;
rename_map_elt elt;
a.loop = loop;
a.new_renames = VEC_alloc (rename_map_elt, heap, 3);
update_ssa (TODO_update_ssa);
htab_traverse (map, add_loop_exit_phis, &a);
update_ssa (TODO_update_ssa);
for (i = 0; VEC_iterate (rename_map_elt, a.new_renames, i, elt); i++)
{
set_rename (map, elt->old_name, elt->expr);
free (elt);
}
VEC_free (rename_map_elt, heap, a.new_renames);
}
/* Helper structure for htab_traverse in insert_guard_phis. */
struct igp {
basic_block bb;
edge true_edge, false_edge;
htab_t before_guard;
};
/* Return the default name that is before the guard. */
static tree
default_before_guard (htab_t before_guard, tree old_name)
{
tree res = get_rename (before_guard, old_name);
if (res == old_name)
{
if (is_gimple_reg (res))
return fold_convert (TREE_TYPE (res), integer_zero_node);
return gimple_default_def (cfun, SSA_NAME_VAR (res));
}
return res;
}
/* Helper function for htab_traverse in insert_guard_phis. */
static int
add_guard_exit_phis (void **slot, void *s)
{
struct rename_map_elt_s *entry = (struct rename_map_elt_s *) *slot;
struct igp *i = (struct igp *) s;
basic_block bb = i->bb;
edge true_edge = i->true_edge;
edge false_edge = i->false_edge;
tree name1 = entry->expr;
tree name2 = default_before_guard (i->before_guard, entry->old_name);
gimple phi;
tree res;
gimple_seq stmts;
/* Nothing to be merged if the name before the guard is the same as
the one after. */
if (name1 == name2)
return 1;
if (TREE_TYPE (name1) != TREE_TYPE (name2))
name1 = fold_convert (TREE_TYPE (name2), name1);
if (TREE_CODE (name1) != SSA_NAME
&& (TREE_CODE (name2) != SSA_NAME
|| is_gimple_reg (name2)))
{
tree type = TREE_TYPE (name2);
tree var = create_tmp_var (type, "var");
name1 = build2 (MODIFY_EXPR, type, var, name1);
name1 = force_gimple_operand (name1, &stmts, true, NULL);
gsi_insert_seq_on_edge_immediate (true_edge, stmts);
}
phi = create_phi_node (entry->old_name, bb);
res = create_new_def_for (gimple_phi_result (phi), phi,
gimple_phi_result_ptr (phi));
add_phi_arg (phi, name1, true_edge, UNKNOWN_LOCATION);
add_phi_arg (phi, name2, false_edge, UNKNOWN_LOCATION);
entry->expr = res;
*slot = entry;
return 1;
}
/* Iterate over RENAME_MAP and get tuples of the form (OLD, NAME1).
If there is a correspondent tuple (OLD, NAME2) in BEFORE_GUARD,
with NAME1 different than NAME2, then insert in BB the phi node:
| RES = phi (NAME1 (on TRUE_EDGE), NAME2 (on FALSE_EDGE))"
if there is no tuple for OLD in BEFORE_GUARD, insert
| RES = phi (NAME1 (on TRUE_EDGE),
| DEFAULT_DEFINITION of NAME1 (on FALSE_EDGE))".
Finally register in RENAME_MAP the tuple (OLD, RES). */
void
insert_guard_phis (basic_block bb, edge true_edge, edge false_edge,
htab_t before_guard, htab_t rename_map)
{
struct igp i;
i.bb = bb;
i.true_edge = true_edge;
i.false_edge = false_edge;
i.before_guard = before_guard;
update_ssa (TODO_update_ssa);
htab_traverse (rename_map, add_guard_exit_phis, &i);
update_ssa (TODO_update_ssa);
}
/* Create a duplicate of the basic block BB. NOTE: This does not
preserve SSA form. */
static void
graphite_copy_stmts_from_block (basic_block bb, basic_block new_bb, htab_t map)
{
gimple_stmt_iterator gsi, gsi_tgt;
gsi_tgt = gsi_start_bb (new_bb);
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
def_operand_p def_p;
ssa_op_iter op_iter;
int region;
gimple stmt = gsi_stmt (gsi);
gimple copy;
if (gimple_code (stmt) == GIMPLE_LABEL)
continue;
/* Create a new copy of STMT and duplicate STMT's virtual
operands. */
copy = gimple_copy (stmt);
gsi_insert_after (&gsi_tgt, copy, GSI_NEW_STMT);
mark_sym_for_renaming (gimple_vop (cfun));
region = lookup_stmt_eh_region (stmt);
if (region >= 0)
add_stmt_to_eh_region (copy, region);
gimple_duplicate_stmt_histograms (cfun, copy, cfun, stmt);
/* Create new names for all the definitions created by COPY and
add replacement mappings for each new name. */
FOR_EACH_SSA_DEF_OPERAND (def_p, copy, op_iter, SSA_OP_ALL_DEFS)
{
tree old_name = DEF_FROM_PTR (def_p);
tree new_name = create_new_def_for (old_name, copy, def_p);
set_rename (map, old_name, new_name);
}
}
}
/* Copies BB and includes in the copied BB all the statements that can
be reached following the use-def chains from the memory accesses,
and returns the next edge following this new block. */
edge
copy_bb_and_scalar_dependences (basic_block bb, sese region,
edge next_e, htab_t map)
{
basic_block new_bb = split_edge (next_e);
next_e = single_succ_edge (new_bb);
graphite_copy_stmts_from_block (bb, new_bb, map);
remove_condition (new_bb);
remove_phi_nodes (new_bb);
expand_scalar_variables (new_bb, region, map);
rename_variables (new_bb, map);
return next_e;
}
/* Returns the outermost loop in SCOP that contains BB. */
struct loop *
outermost_loop_in_sese (sese region, basic_block bb)
{
struct loop *nest;
nest = bb->loop_father;
while (loop_outer (nest)
&& loop_in_sese_p (loop_outer (nest), region))
nest = loop_outer (nest);
return nest;
}
/* Sets the false region of an IF_REGION to REGION. */
void
if_region_set_false_region (ifsese if_region, sese region)
{
basic_block condition = if_region_get_condition_block (if_region);
edge false_edge = get_false_edge_from_guard_bb (condition);
basic_block dummy = false_edge->dest;
edge entry_region = SESE_ENTRY (region);
edge exit_region = SESE_EXIT (region);
basic_block before_region = entry_region->src;
basic_block last_in_region = exit_region->src;
void **slot = htab_find_slot_with_hash (current_loops->exits, exit_region,
htab_hash_pointer (exit_region),
NO_INSERT);
entry_region->flags = false_edge->flags;
false_edge->flags = exit_region->flags;
redirect_edge_pred (entry_region, condition);
redirect_edge_pred (exit_region, before_region);
redirect_edge_pred (false_edge, last_in_region);
redirect_edge_succ (false_edge, single_succ (dummy));
delete_basic_block (dummy);
exit_region->flags = EDGE_FALLTHRU;
recompute_all_dominators ();
SESE_EXIT (region) = false_edge;
if_region->false_region = region;
if (slot)
{
struct loop_exit *loop_exit = GGC_CNEW (struct loop_exit);
memcpy (loop_exit, *((struct loop_exit **) slot), sizeof (struct loop_exit));
htab_clear_slot (current_loops->exits, slot);
slot = htab_find_slot_with_hash (current_loops->exits, false_edge,
htab_hash_pointer (false_edge),
INSERT);
loop_exit->e = false_edge;
*slot = loop_exit;
false_edge->src->loop_father->exits->next = loop_exit;
}
}
/* Creates an IFSESE with CONDITION on edge ENTRY. */
ifsese
create_if_region_on_edge (edge entry, tree condition)
{
edge e;
edge_iterator ei;
sese sese_region = GGC_NEW (struct sese_s);
sese true_region = GGC_NEW (struct sese_s);
sese false_region = GGC_NEW (struct sese_s);
ifsese if_region = GGC_NEW (struct ifsese_s);
edge exit = create_empty_if_region_on_edge (entry, condition);
if_region->region = sese_region;
if_region->region->entry = entry;
if_region->region->exit = exit;
FOR_EACH_EDGE (e, ei, entry->dest->succs)
{
if (e->flags & EDGE_TRUE_VALUE)
{
true_region->entry = e;
true_region->exit = single_succ_edge (e->dest);
if_region->true_region = true_region;
}
else if (e->flags & EDGE_FALSE_VALUE)
{
false_region->entry = e;
false_region->exit = single_succ_edge (e->dest);
if_region->false_region = false_region;
}
}
return if_region;
}
/* Moves REGION in a condition expression:
| if (1)
| ;
| else
| REGION;
*/
ifsese
move_sese_in_condition (sese region)
{
basic_block pred_block = split_edge (SESE_ENTRY (region));
ifsese if_region = NULL;
SESE_ENTRY (region) = single_succ_edge (pred_block);
if_region = create_if_region_on_edge (single_pred_edge (pred_block), integer_one_node);
if_region_set_false_region (if_region, region);
return if_region;
}
/* Reset the loop->aux pointer for all loops in REGION. */
void
sese_reset_aux_in_loops (sese region)
{
int i;
loop_p loop;
for (i = 0; VEC_iterate (loop_p, SESE_LOOP_NEST (region), i, loop); i++)
loop->aux = NULL;
}
/* Returns the scalar evolution of T in REGION. Every variable that
is not defined in the REGION is considered a parameter. */
tree
scalar_evolution_in_region (sese region, loop_p loop, tree t)
{
gimple def;
struct loop *def_loop;
basic_block before = block_before_sese (region);
if (TREE_CODE (t) != SSA_NAME
|| loop_in_sese_p (loop, region))
return instantiate_scev (before, loop,
analyze_scalar_evolution (loop, t));
if (!defined_in_sese_p (t, region))
return t;
def = SSA_NAME_DEF_STMT (t);
def_loop = loop_containing_stmt (def);
if (loop_in_sese_p (def_loop, region))
{
t = analyze_scalar_evolution (def_loop, t);
def_loop = superloop_at_depth (def_loop, loop_depth (loop) + 1);
t = compute_overall_effect_of_inner_loop (def_loop, t);
return t;
}
else
return instantiate_scev (before, loop, t);
}
/* Single entry single exit control flow regions.
Copyright (C) 2008, 2009 Free Software Foundation, Inc.
Contributed by Jan Sjodin <jan.sjodin@amd.com> and
Sebastian Pop <sebastian.pop@amd.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_SESE_H
#define GCC_SESE_H
/* A Single Entry, Single Exit region is a part of the CFG delimited
by two edges. */
typedef struct sese_s
{
/* Single ENTRY and single EXIT from the SESE region. */
edge entry, exit;
/* Parameters used within the SCOP. */
VEC (tree, heap) *params;
/* Used to quickly retrieve the index of a parameter in PARAMS. */
htab_t params_index;
/* Store the names of the parameters that are passed to CLooG. */
char **params_names;
/* Loops completely contained in the SCOP. */
bitmap loops;
VEC (loop_p, heap) *loop_nest;
/* Are we allowed to add more params? This is for debugging purpose. We
can only add new params before generating the bb domains, otherwise they
become invalid. */
bool add_params;
} *sese;
#define SESE_ENTRY(S) (S->entry)
#define SESE_ENTRY_BB(S) (S->entry->dest)
#define SESE_EXIT(S) (S->exit)
#define SESE_EXIT_BB(S) (S->exit->dest)
#define SESE_PARAMS(S) (S->params)
#define SESE_PARAMS_INDEX(S) (S->params_index)
#define SESE_PARAMS_NAMES(S) (S->params_names)
#define SESE_LOOPS(S) (S->loops)
#define SESE_LOOP_NEST(S) (S->loop_nest)
#define SESE_ADD_PARAMS(S) (S->add_params)
extern sese new_sese (edge, edge);
extern void free_sese (sese);
extern void sese_insert_phis_for_liveouts (sese, basic_block, edge, edge);
extern void sese_adjust_liveout_phis (sese, htab_t, basic_block, edge, edge);
extern void build_sese_loop_nests (sese);
extern edge copy_bb_and_scalar_dependences (basic_block, sese, edge, htab_t);
extern struct loop *outermost_loop_in_sese (sese, basic_block);
extern void insert_loop_close_phis (htab_t, loop_p);
extern void insert_guard_phis (basic_block, edge, edge, htab_t, htab_t);
extern void sese_reset_aux_in_loops (sese);
extern tree scalar_evolution_in_region (sese, loop_p, tree);
/* Check that SESE contains LOOP. */
static inline bool
sese_contains_loop (sese sese, struct loop *loop)
{
return bitmap_bit_p (SESE_LOOPS (sese), loop->num);
}
/* The number of parameters in REGION. */
static inline unsigned
sese_nb_params (sese region)
{
return VEC_length (tree, SESE_PARAMS (region));
}
/* Checks whether BB is contained in the region delimited by ENTRY and
EXIT blocks. */
static inline bool
bb_in_region (basic_block bb, basic_block entry, basic_block exit)
{
#ifdef ENABLE_CHECKING
{
edge e;
edge_iterator ei;
/* Check that there are no edges coming in the region: all the
predecessors of EXIT are dominated by ENTRY. */
FOR_EACH_EDGE (e, ei, exit->preds)
dominated_by_p (CDI_DOMINATORS, e->src, entry);
/* Check that there are no edges going out of the region: the
entry is post-dominated by the exit. FIXME: This cannot be
checked right now as the CDI_POST_DOMINATORS are needed. */
}
#endif
return dominated_by_p (CDI_DOMINATORS, bb, entry)
&& !(dominated_by_p (CDI_DOMINATORS, bb, exit)
&& !dominated_by_p (CDI_DOMINATORS, entry, exit));
}
/* Checks whether BB is contained in the region delimited by ENTRY and
EXIT blocks. */
static inline bool
bb_in_sese_p (basic_block bb, sese region)
{
basic_block entry = SESE_ENTRY_BB (region);
basic_block exit = SESE_EXIT_BB (region);
return bb_in_region (bb, entry, exit);
}
/* Returns true when NAME is defined in REGION. */
static inline bool
defined_in_sese_p (tree name, sese region)
{
gimple stmt = SSA_NAME_DEF_STMT (name);
basic_block bb = gimple_bb (stmt);
return bb && bb_in_sese_p (bb, region);
}
/* Returns true when LOOP is in REGION. */
static inline bool
loop_in_sese_p (struct loop *loop, sese region)
{
return (bb_in_sese_p (loop->header, region)
&& bb_in_sese_p (loop->latch, region));
}
/* Returns the loop depth of LOOP in REGION. The loop depth
is the same as the normal loop depth, but limited by a region.
Example:
loop_0
loop_1
{
S0
<- region start
S1
loop_2
S2
S3
<- region end
}
loop_0 does not exist in the region -> invalid
loop_1 exists, but is not completely contained in the region -> depth 0
loop_2 is completely contained -> depth 1 */
static inline unsigned int
sese_loop_depth (sese region, loop_p loop)
{
unsigned int depth = 0;
gcc_assert ((!loop_in_sese_p (loop, region)
&& (SESE_ENTRY_BB (region)->loop_father == loop
|| SESE_EXIT (region)->src->loop_father == loop))
|| loop_in_sese_p (loop, region));
while (loop_in_sese_p (loop, region))
{
depth++;
loop = loop_outer (loop);
}
return depth;
}
/* Returns the block preceding the entry of a SESE. */
static inline basic_block
block_before_sese (sese sese)
{
return SESE_ENTRY (sese)->src;
}
/* Stores the INDEX in a vector for a given clast NAME. */
typedef struct clast_name_index {
int index;
const char *name;
} *clast_name_index_p;
/* Returns a pointer to a new element of type clast_name_index_p built
from NAME and INDEX. */
static inline clast_name_index_p
new_clast_name_index (const char *name, int index)
{
clast_name_index_p res = XNEW (struct clast_name_index);
res->name = name;
res->index = index;
return res;
}
/* For a given clast NAME, returns -1 if it does not correspond to any
parameter, or otherwise, returns the index in the PARAMS or
SCATTERING_DIMENSIONS vector. */
static inline int
clast_name_to_index (const char *name, htab_t index_table)
{
struct clast_name_index tmp;
PTR *slot;
tmp.name = name;
slot = htab_find_slot (index_table, &tmp, NO_INSERT);
if (slot && *slot)
return ((struct clast_name_index *) *slot)->index;
return -1;
}
/* Records in INDEX_TABLE the INDEX for NAME. */
static inline void
save_clast_name_index (htab_t index_table, const char *name, int index)
{
struct clast_name_index tmp;
PTR *slot;
tmp.name = name;
slot = htab_find_slot (index_table, &tmp, INSERT);
if (slot)
*slot = new_clast_name_index (name, index);
}
/* Print to stderr the element ELT. */
static inline void
debug_clast_name_index (clast_name_index_p elt)
{
fprintf (stderr, "(index = %d, name = %s)\n", elt->index, elt->name);
}
/* Helper function for debug_rename_map. */
static inline int
debug_clast_name_indexes_1 (void **slot, void *s ATTRIBUTE_UNUSED)
{
struct clast_name_index *entry = (struct clast_name_index *) *slot;
debug_clast_name_index (entry);
return 1;
}
/* Print to stderr all the elements of MAP. */
static inline void
debug_clast_name_indexes (htab_t map)
{
htab_traverse (map, debug_clast_name_indexes_1, NULL);
}
/* Computes a hash function for database element ELT. */
static inline hashval_t
clast_name_index_elt_info (const void *elt)
{
return htab_hash_pointer (((const struct clast_name_index *) elt)->name);
}
/* Compares database elements E1 and E2. */
static inline int
eq_clast_name_indexes (const void *e1, const void *e2)
{
const struct clast_name_index *elt1 = (const struct clast_name_index *) e1;
const struct clast_name_index *elt2 = (const struct clast_name_index *) e2;
return (elt1->name == elt2->name);
}
/* A single entry single exit specialized for conditions. */
typedef struct ifsese_s {
sese region;
sese true_region;
sese false_region;
} *ifsese;
extern void if_region_set_false_region (ifsese, sese);
extern ifsese create_if_region_on_edge (edge, tree);
extern ifsese move_sese_in_condition (sese);
extern edge get_true_edge_from_guard_bb (basic_block);
extern edge get_false_edge_from_guard_bb (basic_block);
static inline edge
if_region_entry (ifsese if_region)
{
return SESE_ENTRY (if_region->region);
}
static inline edge
if_region_exit (ifsese if_region)
{
return SESE_EXIT (if_region->region);
}
static inline basic_block
if_region_get_condition_block (ifsese if_region)
{
return if_region_entry (if_region)->dest;
}
/* Structure containing the mapping between the old names and the new
names used after block copy in the new loop context. */
typedef struct rename_map_elt_s
{
tree old_name, expr;
} *rename_map_elt;
DEF_VEC_P(rename_map_elt);
DEF_VEC_ALLOC_P (rename_map_elt, heap);
extern void debug_rename_map (htab_t);
extern hashval_t rename_map_elt_info (const void *);
extern int eq_rename_map_elts (const void *, const void *);
extern void set_rename (htab_t, tree, tree);
/* Constructs a new SCEV_INFO_STR structure for VAR and INSTANTIATED_BELOW. */
static inline rename_map_elt
new_rename_map_elt (tree old_name, tree expr)
{
rename_map_elt res;
res = XNEW (struct rename_map_elt_s);
res->old_name = old_name;
res->expr = expr;
return res;
}
/* Structure containing the mapping between the CLooG's induction
variable and the type of the old induction variable. */
typedef struct ivtype_map_elt_s
{
tree type;
const char *cloog_iv;
} *ivtype_map_elt;
extern void debug_ivtype_map (htab_t);
extern hashval_t ivtype_map_elt_info (const void *);
extern int eq_ivtype_map_elts (const void *, const void *);
/* Constructs a new SCEV_INFO_STR structure for VAR and INSTANTIATED_BELOW. */
static inline ivtype_map_elt
new_ivtype_map_elt (const char *cloog_iv, tree type)
{
ivtype_map_elt res;
res = XNEW (struct ivtype_map_elt_s);
res->cloog_iv = cloog_iv;
res->type = type;
return res;
}
/* Free and compute again all the dominators information. */
static inline void
recompute_all_dominators (void)
{
mark_irreducible_loops ();
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
calculate_dominance_info (CDI_DOMINATORS);
calculate_dominance_info (CDI_POST_DOMINATORS);
}
typedef struct gimple_bb
{
basic_block bb;
/* Lists containing the restrictions of the conditional statements
dominating this bb. This bb can only be executed, if all conditions
are true.
Example:
for (i = 0; i <= 20; i++)
{
A
if (2i <= 8)
B
}
So for B there is an additional condition (2i <= 8).
List of COND_EXPR and SWITCH_EXPR. A COND_EXPR is true only if the
corresponding element in CONDITION_CASES is not NULL_TREE. For a
SWITCH_EXPR the corresponding element in CONDITION_CASES is a
CASE_LABEL_EXPR. */
VEC (gimple, heap) *conditions;
VEC (gimple, heap) *condition_cases;
VEC (data_reference_p, heap) *data_refs;
htab_t cloog_iv_types;
} *gimple_bb_p;
#define GBB_BB(GBB) GBB->bb
#define GBB_DATA_REFS(GBB) GBB->data_refs
#define GBB_CONDITIONS(GBB) GBB->conditions
#define GBB_CONDITION_CASES(GBB) GBB->condition_cases
#define GBB_CLOOG_IV_TYPES(GBB) GBB->cloog_iv_types
/* Return the innermost loop that contains the basic block GBB. */
static inline struct loop *
gbb_loop (struct gimple_bb *gbb)
{
return GBB_BB (gbb)->loop_father;
}
/* Returns the gimple loop, that corresponds to the loop_iterator_INDEX.
If there is no corresponding gimple loop, we return NULL. */
static inline loop_p
gbb_loop_at_index (gimple_bb_p gbb, sese region, int index)
{
loop_p loop = gbb_loop (gbb);
int depth = sese_loop_depth (region, loop);
while (--depth > index)
loop = loop_outer (loop);
gcc_assert (sese_contains_loop (region, loop));
return loop;
}
/* The number of common loops in REGION for GBB1 and GBB2. */
static inline int
nb_common_loops (sese region, gimple_bb_p gbb1, gimple_bb_p gbb2)
{
loop_p l1 = gbb_loop (gbb1);
loop_p l2 = gbb_loop (gbb2);
loop_p common = find_common_loop (l1, l2);
return sese_loop_depth (region, common);
}
extern void print_gimple_bb (FILE *, gimple_bb_p, int, int);
extern void debug_gbb (gimple_bb_p, int);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment