forked from OSchip/llvm-project
2243 lines
65 KiB
C
2243 lines
65 KiB
C
/*
|
|
* Copyright 2013 Ecole Normale Superieure
|
|
* Copyright 2015 Sven Verdoolaege
|
|
*
|
|
* Use of this software is governed by the MIT license
|
|
*
|
|
* Written by Sven Verdoolaege,
|
|
* Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <isl/space.h>
|
|
#include <isl/constraint.h>
|
|
#include <isl/val.h>
|
|
#include <isl/aff.h>
|
|
#include <isl/set.h>
|
|
#include <isl/map.h>
|
|
#include <isl/union_set.h>
|
|
#include <isl/union_map.h>
|
|
|
|
#include "hybrid.h"
|
|
#include "schedule.h"
|
|
|
|
/* The hybrid tiling implemented in this file is based on
|
|
* Grosser et al., "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
|
|
/* Bounds on relative dependence distances in input to hybrid tiling.
|
|
* upper is an upper bound on the relative dependence distances
|
|
* in the first space dimension
|
|
* -lower is a lower bound on the relative dependence distances
|
|
* in all space dimensions.
|
|
*
|
|
* In particular,
|
|
*
|
|
* d_i >= -lower_i d_0
|
|
* and
|
|
* d_1 <= upper d_0
|
|
*
|
|
* for each dependence distance vector d, where d_1 is the component
|
|
* corresponding to the first space dimension.
|
|
*
|
|
* upper and lower are always non-negative.
|
|
* Some of the values may be NaN if no bound could be found.
|
|
*/
|
|
struct ppcg_ht_bounds {
|
|
isl_val *upper;
|
|
isl_multi_val *lower;
|
|
};
|
|
|
|
/* Free "bounds" along with all its fields.
|
|
*/
|
|
__isl_null ppcg_ht_bounds *ppcg_ht_bounds_free(
|
|
__isl_take ppcg_ht_bounds *bounds)
|
|
{
|
|
if (!bounds)
|
|
return NULL;
|
|
isl_val_free(bounds->upper);
|
|
isl_multi_val_free(bounds->lower);
|
|
free(bounds);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Create a ppcg_ht_bounds object for a band living in "space".
|
|
* The bounds are initialized to NaN.
|
|
*/
|
|
__isl_give ppcg_ht_bounds *ppcg_ht_bounds_alloc(__isl_take isl_space *space)
|
|
{
|
|
int i, n;
|
|
isl_ctx *ctx;
|
|
ppcg_ht_bounds *bounds;
|
|
|
|
if (!space)
|
|
return NULL;
|
|
|
|
ctx = isl_space_get_ctx(space);
|
|
bounds = isl_alloc_type(ctx, struct ppcg_ht_bounds);
|
|
if (!bounds)
|
|
goto error;
|
|
bounds->upper = isl_val_nan(ctx);
|
|
bounds->lower = isl_multi_val_zero(space);
|
|
n = isl_multi_val_dim(bounds->lower, isl_dim_set);
|
|
for (i = 0; i < n; ++i) {
|
|
isl_val *v = isl_val_copy(bounds->upper);
|
|
bounds->lower = isl_multi_val_set_val(bounds->lower, i, v);
|
|
}
|
|
|
|
if (!bounds->lower || !bounds->upper)
|
|
return ppcg_ht_bounds_free(bounds);
|
|
|
|
return bounds;
|
|
error:
|
|
isl_space_free(space);
|
|
return NULL;
|
|
}
|
|
|
|
void ppcg_ht_bounds_dump(__isl_keep ppcg_ht_bounds *bounds)
|
|
{
|
|
if (!bounds)
|
|
return;
|
|
|
|
fprintf(stderr, "lower: ");
|
|
isl_multi_val_dump(bounds->lower);
|
|
fprintf(stderr, "upper: ");
|
|
isl_val_dump(bounds->upper);
|
|
}
|
|
|
|
/* Return the upper bound on the relative dependence distances
|
|
* in the first space dimension.
|
|
*/
|
|
__isl_give isl_val *ppcg_ht_bounds_get_upper(__isl_keep ppcg_ht_bounds *bounds)
|
|
{
|
|
if (!bounds)
|
|
return NULL;
|
|
return isl_val_copy(bounds->upper);
|
|
}
|
|
|
|
/* Replace the upper bound on the relative dependence distances
|
|
* in the first space dimension by "upper".
|
|
*/
|
|
__isl_give ppcg_ht_bounds *ppcg_ht_bounds_set_upper(
|
|
__isl_take ppcg_ht_bounds *bounds, __isl_take isl_val *upper)
|
|
{
|
|
if (!bounds || !upper)
|
|
goto error;
|
|
isl_val_free(bounds->upper);
|
|
bounds->upper = upper;
|
|
return bounds;
|
|
error:
|
|
ppcg_ht_bounds_free(bounds);
|
|
isl_val_free(upper);
|
|
return NULL;
|
|
}
|
|
|
|
/* Return the lower bound on the relative dependence distances
|
|
* in space dimension "pos".
|
|
*/
|
|
__isl_give isl_val *ppcg_ht_bounds_get_lower(__isl_keep ppcg_ht_bounds *bounds,
|
|
int pos)
|
|
{
|
|
if (!bounds)
|
|
return NULL;
|
|
return isl_multi_val_get_val(bounds->lower, pos);
|
|
}
|
|
|
|
/* Replace the lower bound on the relative dependence distances
|
|
* in space dimension "pos" by "lower".
|
|
*/
|
|
__isl_give ppcg_ht_bounds *ppcg_ht_bounds_set_lower(
|
|
__isl_take ppcg_ht_bounds *bounds, int pos, __isl_take isl_val *lower)
|
|
{
|
|
if (!bounds || !lower)
|
|
goto error;
|
|
bounds->lower = isl_multi_val_set_val(bounds->lower, pos, lower);
|
|
if (!bounds->lower)
|
|
return ppcg_ht_bounds_free(bounds);
|
|
return bounds;
|
|
error:
|
|
ppcg_ht_bounds_free(bounds);
|
|
isl_val_free(lower);
|
|
return NULL;
|
|
}
|
|
|
|
/* Can the bounds on relative dependence distances recorded in "bounds"
|
|
* be used to perform hybrid tiling?
|
|
* In particular, have appropriate lower and upper bounds been found?
|
|
* Any NaN indicates that no corresponding bound was found.
|
|
*/
|
|
isl_bool ppcg_ht_bounds_is_valid(__isl_keep ppcg_ht_bounds *bounds)
|
|
{
|
|
isl_bool is_nan;
|
|
int i, n;
|
|
|
|
if (!bounds)
|
|
return isl_bool_error;
|
|
is_nan = isl_val_is_nan(bounds->upper);
|
|
if (is_nan < 0)
|
|
return isl_bool_error;
|
|
if (is_nan)
|
|
return isl_bool_false;
|
|
|
|
n = isl_multi_val_dim(bounds->lower, isl_dim_set);
|
|
for (i = 0; i < n; ++i) {
|
|
isl_val *v;
|
|
|
|
v = isl_multi_val_get_val(bounds->lower, i);
|
|
is_nan = isl_val_is_nan(v);
|
|
if (is_nan < 0)
|
|
return isl_bool_error;
|
|
if (is_nan)
|
|
return isl_bool_false;
|
|
isl_val_free(v);
|
|
}
|
|
|
|
return isl_bool_true;
|
|
}
|
|
|
|
/* Structure that represents the basic hexagonal tiling,
|
|
* along with information that is needed to perform the hybrid tiling.
|
|
*
|
|
* "bounds" are the bounds on the dependence distances that
|
|
* define the hexagonal shape and the required skewing in the remaining
|
|
* space dimensions.
|
|
*
|
|
* "input_node" points to the input pair of band nodes.
|
|
* "input_schedule" is the partial schedule of this input pair of band nodes.
|
|
* The space of this schedule is [P -> C], where P is the space
|
|
* of the parent node and C is the space of the child node.
|
|
*
|
|
* "space_sizes" represent the total size of a tile for the space
|
|
* dimensions, i.e., those corresponding to the child node.
|
|
* The space of "space_sizes" is C.
|
|
* If S_0 is the original tile size in the first space dimension,
|
|
* then the first entry of "space_sizes" is equal to
|
|
* W = 2*S_0 + floor(d_l h) + floor(d_u h).
|
|
* The remaining entries are the same as in the original tile sizes.
|
|
*
|
|
* The basic hexagonal tiling "hex" is defined
|
|
* in a "ts" (time-space) space and corresponds to the phase-1 tiles.
|
|
* "time_tile" maps the "ts" space to outer time tile.
|
|
* Is is equal to ts[t, s] -> floor(t/(2 * S_t)), with S_t the original tile
|
|
* size corresponding to the parent node.
|
|
* "local_time" maps the "ts" space to the time dimension inside each tile.
|
|
* It is equal to ts[t, s] -> t mod (2 S_t), with S_t the original tile
|
|
* size corresponding to the parent node.
|
|
* "shift_space" shifts the tiles at time tile T = floor(t/(2 S_t))
|
|
* in the space dimension such that they align to a multiple of W.
|
|
* It is equal to ts[t, s] -> s + (-(2 * shift_s)*T) % W,
|
|
* with shift_s = S_0 + floor(d_u h).
|
|
* "shift_phase" is the shift taken to go from phase 0 to phase 1
|
|
* It is equal to ts[t, s] -> ts[t + S_t, s + shift_s],
|
|
* with shift_s = S_0 + floor(d_u h).
|
|
*
|
|
* "project_ts" projects the space of the input schedule to the ts-space.
|
|
* It is equal to [P[t] -> C[s_0, ...]] -> ts[t, s_0].
|
|
*/
|
|
struct ppcg_ht_tiling {
|
|
int ref;
|
|
|
|
ppcg_ht_bounds *bounds;
|
|
isl_schedule_node *input_node;
|
|
isl_multi_union_pw_aff *input_schedule;
|
|
|
|
isl_multi_val *space_sizes;
|
|
|
|
isl_aff *time_tile;
|
|
isl_aff *local_time;
|
|
isl_aff *shift_space;
|
|
isl_multi_aff *shift_phase;
|
|
isl_set *hex;
|
|
|
|
isl_multi_aff *project_ts;
|
|
};
|
|
typedef struct ppcg_ht_tiling ppcg_ht_tiling;
|
|
|
|
/* Return the space of the pair of band nodes that form the input
|
|
* to the hybrid tiling.
|
|
* In particular, return the space [P -> C], where P is the space
|
|
* of the parent node and C is the space of the child node.
|
|
*/
|
|
__isl_give isl_space *ppcg_ht_tiling_get_input_space(
|
|
__isl_keep ppcg_ht_tiling *tile)
|
|
{
|
|
if (!tile)
|
|
return NULL;
|
|
|
|
return isl_multi_union_pw_aff_get_space(tile->input_schedule);
|
|
}
|
|
|
|
/* Remove a reference to "tile" and free "tile" along with all its fields
|
|
* as soon as the reference count drops to zero.
|
|
*/
|
|
static __isl_null ppcg_ht_tiling *ppcg_ht_tiling_free(
|
|
__isl_take ppcg_ht_tiling *tiling)
|
|
{
|
|
if (!tiling)
|
|
return NULL;
|
|
if (--tiling->ref > 0)
|
|
return NULL;
|
|
|
|
ppcg_ht_bounds_free(tiling->bounds);
|
|
isl_schedule_node_free(tiling->input_node);
|
|
isl_multi_union_pw_aff_free(tiling->input_schedule);
|
|
isl_multi_val_free(tiling->space_sizes);
|
|
isl_aff_free(tiling->time_tile);
|
|
isl_aff_free(tiling->local_time);
|
|
isl_aff_free(tiling->shift_space);
|
|
isl_multi_aff_free(tiling->shift_phase);
|
|
isl_set_free(tiling->hex);
|
|
isl_multi_aff_free(tiling->project_ts);
|
|
free(tiling);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Return a new reference to "tiling".
|
|
*/
|
|
__isl_give ppcg_ht_tiling *ppcg_ht_tiling_copy(
|
|
__isl_keep ppcg_ht_tiling *tiling)
|
|
{
|
|
if (!tiling)
|
|
return NULL;
|
|
|
|
tiling->ref++;
|
|
return tiling;
|
|
}
|
|
|
|
/* Return the isl_ctx to which "tiling" belongs.
|
|
*/
|
|
isl_ctx *ppcg_ht_tiling_get_ctx(__isl_keep ppcg_ht_tiling *tiling)
|
|
{
|
|
if (!tiling)
|
|
return NULL;
|
|
|
|
return isl_multi_union_pw_aff_get_ctx(tiling->input_schedule);
|
|
}
|
|
|
|
/* Representation of one of the two phases of hybrid tiling.
|
|
*
|
|
* "tiling" points to the shared tiling data.
|
|
*
|
|
* "time_tile", "local_time" and "shift_space" are equal to the corresponding
|
|
* fields of "tiling", pulled back to the input space.
|
|
* In case of phase 0, these expressions have also been moved
|
|
* from phase 1 to phase 0.
|
|
*
|
|
* "domain" contains the hexagonal tiling of this phase.
|
|
*
|
|
* "space_shift" is the shift that should be added to the space band
|
|
* in order to be able to apply rectangular tiling to the space.
|
|
* For phase 1, it is equal to
|
|
*
|
|
* [P[t] -> C[s_0, s_i]] -> C[(-(2 * shift_s)*T) % W, dl_i * u]
|
|
*
|
|
* with shift_s = S_0 + floor(d_u h),
|
|
* T equal to "time_tile" and u equal to "local_time".
|
|
* For phase 0, it is equal to
|
|
*
|
|
* [P[t] -> C[s_0, s_i]] -> C[shift_s + (-(2 * shift_s)*T) % W, dl_i * u]
|
|
*
|
|
* "space_tile" is the space tiling. It is equal to
|
|
*
|
|
* [P[t] -> C[s]] -> C[floor((s + space_shift)/space_size]
|
|
*/
|
|
struct ppcg_ht_phase {
|
|
ppcg_ht_tiling *tiling;
|
|
|
|
isl_aff *time_tile;
|
|
isl_aff *local_time;
|
|
isl_aff *shift_space;
|
|
isl_set *domain;
|
|
|
|
isl_multi_aff *space_shift;
|
|
isl_multi_aff *space_tile;
|
|
};
|
|
|
|
/* Free "phase" along with all its fields.
|
|
*/
|
|
static __isl_null ppcg_ht_phase *ppcg_ht_phase_free(
|
|
__isl_take ppcg_ht_phase *phase)
|
|
{
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
ppcg_ht_tiling_free(phase->tiling);
|
|
isl_aff_free(phase->time_tile);
|
|
isl_aff_free(phase->local_time);
|
|
isl_aff_free(phase->shift_space);
|
|
isl_set_free(phase->domain);
|
|
isl_multi_aff_free(phase->space_shift);
|
|
isl_multi_aff_free(phase->space_tile);
|
|
free(phase);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Wrapper around ppcg_ht_phase_free for use as an argument
|
|
* to isl_id_set_free_user.
|
|
*/
|
|
static void ppcg_ht_phase_free_wrap(void *user)
|
|
{
|
|
ppcg_ht_phase *phase = user;
|
|
|
|
ppcg_ht_phase_free(phase);
|
|
}
|
|
|
|
/* Return the domain of hybrid tiling phase "phase".
|
|
*/
|
|
static __isl_give isl_set *ppcg_ht_phase_get_domain(ppcg_ht_phase *phase)
|
|
{
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
return isl_set_copy(phase->domain);
|
|
}
|
|
|
|
/* Return the space of the pair of band nodes that form the input
|
|
* to the hybrid tiling of which "phase" is a phase.
|
|
* In particular, return the space [P -> C], where P is the space
|
|
* of the parent node and C is the space of the child node.
|
|
*/
|
|
static __isl_give isl_space *ppcg_ht_phase_get_input_space(
|
|
__isl_keep ppcg_ht_phase *phase)
|
|
{
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
return ppcg_ht_tiling_get_input_space(phase->tiling);
|
|
}
|
|
|
|
/* Construct the lower left constraint of the hexagonal tile, i.e.,
|
|
*
|
|
* du a - b <= (2h+1) du - duh
|
|
* -du a + b + (2h+1) du - duh >= 0
|
|
*
|
|
* where duh = floor(du * h).
|
|
*
|
|
* This constraint corresponds to (6) in
|
|
* "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
static __isl_give isl_constraint *hex_lower_left(__isl_take isl_local_space *ls,
|
|
__isl_keep isl_val *h, __isl_keep isl_val *du, __isl_keep isl_val *duh)
|
|
{
|
|
isl_val *v;
|
|
isl_aff *aff;
|
|
|
|
v = isl_val_add_ui(isl_val_mul_ui(isl_val_copy(h), 2), 1);
|
|
v = isl_val_mul(v, isl_val_copy(du));
|
|
v = isl_val_sub(v, isl_val_copy(duh));
|
|
aff = isl_aff_val_on_domain(ls, v);
|
|
v = isl_val_neg(isl_val_copy(du));
|
|
aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, v);
|
|
aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, 1);
|
|
|
|
return isl_inequality_from_aff(aff);
|
|
}
|
|
|
|
/* Construct the lower constraint of the hexagonal tile, i.e.,
|
|
*
|
|
* a <= 2h+1
|
|
* -a + 2h+1 >= 0
|
|
*
|
|
* This constraint corresponds to (7) in
|
|
* "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
static __isl_give isl_constraint *hex_lower(__isl_take isl_local_space *ls,
|
|
__isl_keep isl_val *h)
|
|
{
|
|
isl_val *v;
|
|
isl_aff *aff;
|
|
|
|
v = isl_val_add_ui(isl_val_mul_ui(isl_val_copy(h), 2), 1);
|
|
aff = isl_aff_val_on_domain(ls, v);
|
|
aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 0, -1);
|
|
|
|
return isl_inequality_from_aff(aff);
|
|
}
|
|
|
|
/* Construct the lower right constraint of the hexagonal tile, i.e.,
|
|
*
|
|
* dl a + b <= (2h+1) dl + duh + (s0-1)
|
|
* -dl a - b + (2h+1) dl + duh + (s0-1) >= 0
|
|
*
|
|
* where duh = floor(du * h).
|
|
*
|
|
* This constraint corresponds to (8) in
|
|
* "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
static __isl_give isl_constraint *hex_lower_right(
|
|
__isl_take isl_local_space *ls, __isl_keep isl_val *h,
|
|
__isl_keep isl_val *s0, __isl_keep isl_val *dl, __isl_keep isl_val *duh)
|
|
{
|
|
isl_val *v;
|
|
isl_aff *aff;
|
|
|
|
v = isl_val_add_ui(isl_val_mul_ui(isl_val_copy(h), 2), 1);
|
|
v = isl_val_mul(v, isl_val_copy(dl));
|
|
v = isl_val_add(v, isl_val_copy(duh));
|
|
v = isl_val_add(v, isl_val_copy(s0));
|
|
v = isl_val_sub_ui(v, 1);
|
|
aff = isl_aff_val_on_domain(ls, v);
|
|
v = isl_val_neg(isl_val_copy(dl));
|
|
aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, v);
|
|
aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, -1);
|
|
|
|
return isl_inequality_from_aff(aff);
|
|
}
|
|
|
|
/* Construct the upper left constraint of the hexagonal tile, i.e.,
|
|
*
|
|
* dl a + b >= h dl - (d - 1)/d with d = den(dl)
|
|
* dl a + b - h dl + (d - 1)/d >= 0
|
|
*
|
|
* This constraint corresponds to (10) in
|
|
* "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
static __isl_give isl_constraint *hex_upper_left(__isl_take isl_local_space *ls,
|
|
__isl_keep isl_val *h, __isl_keep isl_val *dl)
|
|
{
|
|
isl_val *v, *d;
|
|
isl_aff *aff;
|
|
|
|
d = isl_val_get_den_val(dl);
|
|
v = isl_val_sub_ui(isl_val_copy(d), 1);
|
|
v = isl_val_div(v, d);
|
|
v = isl_val_sub(v, isl_val_mul(isl_val_copy(h), isl_val_copy(dl)));
|
|
aff = isl_aff_val_on_domain(ls, v);
|
|
aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, isl_val_copy(dl));
|
|
aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, 1);
|
|
|
|
return isl_inequality_from_aff(aff);
|
|
}
|
|
|
|
/* Construct the upper right constraint of the hexagonal tile, i.e.,
|
|
*
|
|
* du a - b >= du h - duh - (s0-1) - dlh - (d - 1)/d with d = den(du)
|
|
* du a - b - du h + duh + (s0-1) + dlh + (d - 1)/d >= 0
|
|
*
|
|
* where dlh = floor(dl * h) and duh = floor(du * h).
|
|
*
|
|
* This constraint corresponds to (12) in
|
|
* "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
static __isl_give isl_constraint *hex_upper_right(
|
|
__isl_take isl_local_space *ls, __isl_keep isl_val *h,
|
|
__isl_keep isl_val *s0, __isl_keep isl_val *du,
|
|
__isl_keep isl_val *dlh, __isl_keep isl_val *duh)
|
|
{
|
|
isl_val *v, *d;
|
|
isl_aff *aff;
|
|
|
|
d = isl_val_get_den_val(du);
|
|
v = isl_val_sub_ui(isl_val_copy(d), 1);
|
|
v = isl_val_div(v, d);
|
|
v = isl_val_sub(v, isl_val_mul(isl_val_copy(h), isl_val_copy(du)));
|
|
v = isl_val_add(v, isl_val_copy(duh));
|
|
v = isl_val_add(v, isl_val_copy(dlh));
|
|
v = isl_val_add(v, isl_val_copy(s0));
|
|
v = isl_val_sub_ui(v, 1);
|
|
aff = isl_aff_val_on_domain(ls, v);
|
|
aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, isl_val_copy(du));
|
|
aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, -1);
|
|
|
|
return isl_inequality_from_aff(aff);
|
|
}
|
|
|
|
/* Construct the uppper constraint of the hexagonal tile, i.e.,
|
|
*
|
|
* a >= 0
|
|
*
|
|
* This constraint corresponds to (13) in
|
|
* "Hybrid Hexagonal/Classical Tiling for GPUs".
|
|
*/
|
|
static __isl_give isl_constraint *hex_upper(__isl_take isl_local_space *ls)
|
|
{
|
|
isl_aff *aff;
|
|
|
|
aff = isl_aff_var_on_domain(ls, isl_dim_set, 0);
|
|
|
|
return isl_inequality_from_aff(aff);
|
|
}
|
|
|
|
/* Construct the basic hexagonal tile shape.
|
|
* "space" is the 2D space in which the hexagon should be constructed.
|
|
* h is st-1, with st the tile size in the time dimension
|
|
* s0 is the tile size in the space dimension
|
|
* dl is a bound on the negative relative dependence distances, i.e.,
|
|
*
|
|
* d_s >= -dl d_t
|
|
*
|
|
* du is a bound on the positive relative dependence distances, i.e.,
|
|
*
|
|
* d_s <= du d_t
|
|
*
|
|
* with (d_t,d_s) any dependence distance vector.
|
|
* dlh = floor(dl * h)
|
|
* duh = floor(du * h)
|
|
*
|
|
* The shape of the hexagon is as follows:
|
|
*
|
|
* 0 dlh dlh+s0-1
|
|
* ______ __
|
|
* 0 / \_ /
|
|
* / \_ /
|
|
* h / \ ______ /
|
|
* h+1 \_ // \\_
|
|
* \_ // \\_
|
|
* 2h+1 \______// \\
|
|
* 0 duh duh+s0-1
|
|
* duh+s0-1+dlh
|
|
* duh+s0-1+dlh+1+s0+1
|
|
*
|
|
* The next hexagon is shifted by duh + dlh + 2 * s0.
|
|
*
|
|
* The slope of the "/" constraints is dl.
|
|
* The slope of the "\_" constraints is du.
|
|
*/
|
|
static __isl_give isl_set *compute_hexagon(__isl_take isl_space *space,
|
|
__isl_keep isl_val *h, __isl_keep isl_val *s0,
|
|
__isl_keep isl_val *dl, __isl_keep isl_val *du,
|
|
__isl_keep isl_val *dlh, __isl_keep isl_val *duh)
|
|
{
|
|
isl_local_space *ls;
|
|
isl_constraint *c;
|
|
isl_basic_set *bset;
|
|
|
|
ls = isl_local_space_from_space(space);
|
|
|
|
c = hex_lower_left(isl_local_space_copy(ls), h, du, duh);
|
|
bset = isl_basic_set_from_constraint(c);
|
|
|
|
c = hex_lower(isl_local_space_copy(ls), h);
|
|
bset = isl_basic_set_add_constraint(bset, c);
|
|
|
|
c = hex_lower_right(isl_local_space_copy(ls), h, s0, dl, duh);
|
|
bset = isl_basic_set_add_constraint(bset, c);
|
|
|
|
c = hex_upper_left(isl_local_space_copy(ls), h, dl);
|
|
bset = isl_basic_set_add_constraint(bset, c);
|
|
|
|
c = hex_upper_right(isl_local_space_copy(ls), h, s0, du, dlh, duh);
|
|
bset = isl_basic_set_add_constraint(bset, c);
|
|
|
|
c = hex_upper(ls);
|
|
bset = isl_basic_set_add_constraint(bset, c);
|
|
|
|
return isl_set_from_basic_set(bset);
|
|
}
|
|
|
|
/* Name of the ts-space.
|
|
*/
|
|
static const char *ts_space_name = "ts";
|
|
|
|
/* Construct and return the space ts[t, s].
|
|
*/
|
|
static __isl_give isl_space *construct_ts_space(isl_ctx *ctx)
|
|
{
|
|
isl_space *s;
|
|
|
|
s = isl_space_set_alloc(ctx, 0, 2);
|
|
s = isl_space_set_tuple_name(s, isl_dim_set, ts_space_name);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Name of the local ts-space.
|
|
*/
|
|
static const char *local_ts_space_name = "local_ts";
|
|
|
|
/* Construct and return the space local_ts[t, s].
|
|
*/
|
|
static __isl_give isl_space *construct_local_ts_space(isl_ctx *ctx)
|
|
{
|
|
isl_space *s;
|
|
|
|
s = isl_space_set_alloc(ctx, 0, 2);
|
|
s = isl_space_set_tuple_name(s, isl_dim_set, local_ts_space_name);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Compute the total size of a tile for the space dimensions,
|
|
* i.e., those corresponding to the child node
|
|
* of the input pattern.
|
|
* If S_0 is the original tile size in the first space dimension,
|
|
* then the first entry of "space_sizes" is equal to
|
|
* W = 2*S_0 + floor(d_l h) + floor(d_u h).
|
|
* The remaining entries are the same as in the original tile sizes.
|
|
* "tile_sizes" contains the original tile sizes, including
|
|
* the tile size corresponding to the parent node.
|
|
* "dlh" is equal to floor(d_l h).
|
|
* "duh" is equal to floor(d_u h).
|
|
*/
|
|
static __isl_give isl_multi_val *compute_space_sizes(
|
|
__isl_keep isl_multi_val *tile_sizes,
|
|
__isl_keep isl_val *dlh, __isl_keep isl_val *duh)
|
|
{
|
|
isl_val *size;
|
|
isl_multi_val *space_sizes;
|
|
|
|
space_sizes = isl_multi_val_copy(tile_sizes);
|
|
space_sizes = isl_multi_val_factor_range(space_sizes);
|
|
size = isl_multi_val_get_val(space_sizes, 0);
|
|
size = isl_val_mul_ui(size, 2);
|
|
size = isl_val_add(size, isl_val_copy(duh));
|
|
size = isl_val_add(size, isl_val_copy(dlh));
|
|
space_sizes = isl_multi_val_set_val(space_sizes, 0, size);
|
|
|
|
return space_sizes;
|
|
}
|
|
|
|
/* Compute the offset of phase 1 with respect to phase 0
|
|
* in the ts-space ("space").
|
|
* In particular, return
|
|
*
|
|
* ts[st, s0 + duh]
|
|
*/
|
|
static __isl_give isl_multi_val *compute_phase_shift(
|
|
__isl_keep isl_space *space, __isl_keep isl_val *st,
|
|
__isl_keep isl_val *s0, __isl_keep isl_val *duh)
|
|
{
|
|
isl_val *v;
|
|
isl_multi_val *phase_shift;
|
|
|
|
phase_shift = isl_multi_val_zero(isl_space_copy(space));
|
|
phase_shift = isl_multi_val_set_val(phase_shift, 0, isl_val_copy(st));
|
|
v = isl_val_add(isl_val_copy(duh), isl_val_copy(s0));
|
|
phase_shift = isl_multi_val_set_val(phase_shift, 1, v);
|
|
|
|
return phase_shift;
|
|
}
|
|
|
|
/* Return the function
|
|
*
|
|
* ts[t, s] -> floor(t/(2 * st))
|
|
*
|
|
* representing the time tile.
|
|
* "space" is the space ts[t, s].
|
|
*/
|
|
static __isl_give isl_aff *compute_time_tile(__isl_keep isl_space *space,
|
|
__isl_keep isl_val *st)
|
|
{
|
|
isl_val *v;
|
|
isl_aff *t;
|
|
isl_local_space *ls;
|
|
|
|
ls = isl_local_space_from_space(isl_space_copy(space));
|
|
t = isl_aff_var_on_domain(ls, isl_dim_set, 0);
|
|
v = isl_val_mul_ui(isl_val_copy(st), 2);
|
|
t = isl_aff_floor(isl_aff_scale_down_val(t, v));
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Compute a shift in the space dimension for tiles
|
|
* at time tile T = floor(t/(2 * S_t))
|
|
* such that they align to a multiple of the total space tile dimension W.
|
|
* In particular, compute
|
|
*
|
|
* ts[t, s] -> s + (-(2 * shift_s)*T) % W
|
|
*
|
|
* where shift_s is the shift of phase 1 with respect to phase 0
|
|
* in the space dimension (the first element of "phase_shift").
|
|
* W is stored in the first element of "space_sizes".
|
|
* "time_tile" is the function
|
|
*
|
|
* ts[t, s] -> floor(t/(2 * S_T))
|
|
*
|
|
* Since phase 1 is shifted by shift_s with respect to phase 0,
|
|
* the next line of phase 0 (at T+1) is shifted by 2*shift_s
|
|
* with respect to the previous line (at T).
|
|
* A shift of -(2 * shift_s)*T therefore allows the basic pattern
|
|
* (which starts at 0) to be applied.
|
|
* However, this shift will be used to obtain the tile coordinate
|
|
* in the first space dimension and if the original values
|
|
* in the space dimension are non-negative, then the shift should
|
|
* not make them negative. Moreover, the shift should be as minimal
|
|
* as possible.
|
|
* Since the pattern repeats itself with a period of W in the space
|
|
* dimension, the shift can be replaced by (-(2 * shift_s)*T) % W.
|
|
*/
|
|
static __isl_give isl_aff *compute_shift_space(__isl_keep isl_aff *time_tile,
|
|
__isl_keep isl_multi_val *space_sizes,
|
|
__isl_keep isl_multi_val *phase_shift)
|
|
{
|
|
isl_val *v;
|
|
isl_aff *s, *t;
|
|
isl_local_space *ls;
|
|
|
|
ls = isl_local_space_from_space(isl_aff_get_domain_space(time_tile));
|
|
t = isl_aff_copy(time_tile);
|
|
v = isl_val_mul_ui(isl_multi_val_get_val(phase_shift, 1), 2);
|
|
v = isl_val_neg(v);
|
|
t = isl_aff_scale_val(t, v);
|
|
v = isl_multi_val_get_val(space_sizes, 0);
|
|
t = isl_aff_mod_val(t, v);
|
|
s = isl_aff_var_on_domain(ls, isl_dim_set, 1);
|
|
s = isl_aff_add(s, t);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Give the phase_shift ts[S_t, S_0 + floor(d_u h)],
|
|
* compute a function that applies the shift, i.e.,
|
|
*
|
|
* ts[t, s] -> ts[t + S_t, s + S_0 + floor(d_u h)],
|
|
*/
|
|
static __isl_give isl_multi_aff *compute_shift_phase(
|
|
__isl_keep isl_multi_val *phase_shift)
|
|
{
|
|
isl_space *space;
|
|
isl_multi_aff *shift;
|
|
|
|
space = isl_multi_val_get_space(phase_shift);
|
|
shift = isl_multi_aff_multi_val_on_space(space,
|
|
isl_multi_val_copy(phase_shift));
|
|
space = isl_multi_aff_get_space(shift);
|
|
shift = isl_multi_aff_add(shift, isl_multi_aff_identity(space));
|
|
|
|
return shift;
|
|
}
|
|
|
|
/* Compute a mapping from the ts-space to the local coordinates
|
|
* within each tile. In particular, compute
|
|
*
|
|
* ts[t, s] -> local_ts[t % (2 S_t), (s + (-(2 * shift_s)*T) % W) % W]
|
|
*
|
|
* "ts" is the space ts[t, s]
|
|
* "local_ts" is the space local_ts[t, s]
|
|
* "shift_space" is equal to ts[t, s] -> s + (-(2 * shift_s)*T) % W
|
|
* "st" is the tile size in the time dimension S_t.
|
|
* The first element of "space_sizes" is equal to W.
|
|
*/
|
|
static __isl_give isl_multi_aff *compute_localize(
|
|
__isl_keep isl_space *local_ts, __isl_keep isl_aff *shift_space,
|
|
__isl_keep isl_val *st, __isl_keep isl_multi_val *space_sizes)
|
|
{
|
|
isl_val *v;
|
|
isl_space *space;
|
|
isl_aff *s, *t;
|
|
isl_multi_aff *localize;
|
|
|
|
space = isl_aff_get_domain_space(shift_space);
|
|
local_ts = isl_space_copy(local_ts);
|
|
space = isl_space_map_from_domain_and_range(space, local_ts);
|
|
localize = isl_multi_aff_identity(space);
|
|
t = isl_multi_aff_get_aff(localize, 0);
|
|
v = isl_val_mul_ui(isl_val_copy(st), 2);
|
|
t = isl_aff_mod_val(t, v);
|
|
localize = isl_multi_aff_set_aff(localize, 0, t);
|
|
s = isl_aff_copy(shift_space);
|
|
v = isl_multi_val_get_val(space_sizes, 0);
|
|
s = isl_aff_mod_val(s, v);
|
|
localize = isl_multi_aff_set_aff(localize, 1, s);
|
|
|
|
return localize;
|
|
}
|
|
|
|
/* Set the project_ts field of "tiling".
|
|
*
|
|
* This field projects the space of the input schedule to the ts-space.
|
|
* It is equal to [P[t] -> C[s_0, ...]] -> ts[t, s_0].
|
|
*/
|
|
static __isl_give ppcg_ht_tiling *ppcg_ht_tiling_set_project_ts(
|
|
__isl_take ppcg_ht_tiling *tiling)
|
|
{
|
|
int n;
|
|
isl_space *space;
|
|
isl_multi_aff *project;
|
|
|
|
if (!tiling)
|
|
return NULL;
|
|
|
|
space = ppcg_ht_tiling_get_input_space(tiling);
|
|
n = isl_space_dim(space, isl_dim_set);
|
|
project = isl_multi_aff_project_out_map(space, isl_dim_set, 2, n - 2);
|
|
project = isl_multi_aff_set_tuple_name(project,
|
|
isl_dim_out, ts_space_name);
|
|
if (!project)
|
|
return ppcg_ht_tiling_free(tiling);
|
|
|
|
tiling->project_ts = project;
|
|
|
|
return tiling;
|
|
}
|
|
|
|
/* Construct a hybrid tiling description from bounds on the dependence
|
|
* distances "bounds".
|
|
* "input_node" points to the original parent node.
|
|
* "input_schedule" is the combined schedule of the parent and child
|
|
* node in the input.
|
|
* "tile_sizes" are the original, user specified tile sizes.
|
|
*/
|
|
static __isl_give ppcg_ht_tiling *ppcg_ht_bounds_construct_tiling(
|
|
__isl_take ppcg_ht_bounds *bounds,
|
|
__isl_keep isl_schedule_node *input_node,
|
|
__isl_keep isl_multi_union_pw_aff *input_schedule,
|
|
__isl_keep isl_multi_val *tile_sizes)
|
|
{
|
|
isl_ctx *ctx;
|
|
ppcg_ht_tiling *tiling;
|
|
isl_multi_val *space_sizes, *phase_shift;
|
|
isl_aff *time_tile, *shift_space;
|
|
isl_multi_aff *localize;
|
|
isl_val *h, *duh, *dlh;
|
|
isl_val *st, *s0, *du, *dl;
|
|
isl_space *ts, *local_ts;
|
|
|
|
if (!bounds || !input_node || !input_schedule || !tile_sizes)
|
|
goto error;
|
|
|
|
ctx = isl_multi_union_pw_aff_get_ctx(input_schedule);
|
|
tiling = isl_calloc_type(ctx, struct ppcg_ht_tiling);
|
|
if (!tiling)
|
|
goto error;
|
|
tiling->ref = 1;
|
|
|
|
st = isl_multi_val_get_val(tile_sizes, 0);
|
|
h = isl_val_sub_ui(isl_val_copy(st), 1);
|
|
s0 = isl_multi_val_get_val(tile_sizes, 1);
|
|
du = ppcg_ht_bounds_get_upper(bounds);
|
|
dl = ppcg_ht_bounds_get_lower(bounds, 0);
|
|
|
|
duh = isl_val_floor(isl_val_mul(isl_val_copy(du), isl_val_copy(h)));
|
|
dlh = isl_val_floor(isl_val_mul(isl_val_copy(dl), isl_val_copy(h)));
|
|
|
|
ts = construct_ts_space(ctx);
|
|
local_ts = construct_local_ts_space(ctx);
|
|
|
|
space_sizes = compute_space_sizes(tile_sizes, dlh, duh);
|
|
phase_shift = compute_phase_shift(ts, st, s0, duh);
|
|
time_tile = compute_time_tile(ts, st);
|
|
shift_space = compute_shift_space(time_tile, space_sizes, phase_shift);
|
|
localize = compute_localize(local_ts, shift_space, st, space_sizes);
|
|
isl_space_free(ts);
|
|
|
|
tiling->input_node = isl_schedule_node_copy(input_node);
|
|
tiling->input_schedule = isl_multi_union_pw_aff_copy(input_schedule);
|
|
tiling->space_sizes = space_sizes;
|
|
tiling->bounds = bounds;
|
|
tiling->local_time = isl_multi_aff_get_aff(localize, 0);
|
|
tiling->hex = compute_hexagon(local_ts, h, s0, dl, du, dlh, duh);
|
|
tiling->hex = isl_set_preimage_multi_aff(tiling->hex, localize);
|
|
tiling->time_tile = time_tile;
|
|
tiling->shift_space = shift_space;
|
|
tiling->shift_phase = compute_shift_phase(phase_shift);
|
|
isl_multi_val_free(phase_shift);
|
|
|
|
isl_val_free(duh);
|
|
isl_val_free(dlh);
|
|
isl_val_free(du);
|
|
isl_val_free(dl);
|
|
isl_val_free(s0);
|
|
isl_val_free(st);
|
|
isl_val_free(h);
|
|
|
|
if (!tiling->input_schedule || !tiling->local_time || !tiling->hex ||
|
|
!tiling->shift_space || !tiling->shift_phase)
|
|
return ppcg_ht_tiling_free(tiling);
|
|
|
|
tiling = ppcg_ht_tiling_set_project_ts(tiling);
|
|
|
|
return tiling;
|
|
error:
|
|
ppcg_ht_bounds_free(bounds);
|
|
return NULL;
|
|
}
|
|
|
|
/* Are all members of the band node "node" coincident?
|
|
*/
|
|
static isl_bool all_coincident(__isl_keep isl_schedule_node *node)
|
|
{
|
|
int i, n;
|
|
|
|
n = isl_schedule_node_band_n_member(node);
|
|
for (i = 0; i < n; ++i) {
|
|
isl_bool c;
|
|
|
|
c = isl_schedule_node_band_member_get_coincident(node, i);
|
|
if (c < 0 || !c)
|
|
return c;
|
|
}
|
|
|
|
return isl_bool_true;
|
|
}
|
|
|
|
/* Does "node" satisfy the properties of the inner node in the input
|
|
* pattern for hybrid tiling?
|
|
* That is, is it a band node with only coincident members, of which
|
|
* there is at least one?
|
|
*/
|
|
static isl_bool has_child_properties(__isl_keep isl_schedule_node *node)
|
|
{
|
|
if (!node)
|
|
return isl_bool_error;
|
|
if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
|
|
return isl_bool_false;
|
|
if (isl_schedule_node_band_n_member(node) < 1)
|
|
return isl_bool_false;
|
|
return all_coincident(node);
|
|
}
|
|
|
|
/* Does "node" satisfy the properties of the outer node in the input
|
|
* pattern for hybrid tiling?
|
|
* That is, is it a band node with a single member?
|
|
*/
|
|
static isl_bool has_parent_properties(__isl_keep isl_schedule_node *node)
|
|
{
|
|
if (!node)
|
|
return isl_bool_error;
|
|
if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
|
|
return isl_bool_false;
|
|
if (isl_schedule_node_band_n_member(node) != 1)
|
|
return isl_bool_false;
|
|
return isl_bool_true;
|
|
}
|
|
|
|
/* Does the parent of "node" satisfy the input patttern for hybrid tiling?
|
|
* That is, does "node" satisfy the properties of the inner node and
|
|
* does the parent of "node" satisfy the properties of the outer node?
|
|
*/
|
|
isl_bool ppcg_ht_parent_has_input_pattern(__isl_keep isl_schedule_node *node)
|
|
{
|
|
isl_bool has_pattern;
|
|
|
|
has_pattern = has_child_properties(node);
|
|
if (has_pattern < 0 || !has_pattern)
|
|
return has_pattern;
|
|
|
|
node = isl_schedule_node_copy(node);
|
|
node = isl_schedule_node_parent(node);
|
|
has_pattern = has_parent_properties(node);
|
|
isl_schedule_node_free(node);
|
|
|
|
return has_pattern;
|
|
}
|
|
|
|
/* Does "node" satisfy the input patttern for hybrid tiling?
|
|
* That is, does "node" satisfy the properties of the outer node and
|
|
* does the child of "node" satisfy the properties of the inner node?
|
|
*/
|
|
isl_bool ppcg_ht_has_input_pattern(__isl_keep isl_schedule_node *node)
|
|
{
|
|
isl_bool has_pattern;
|
|
|
|
has_pattern = has_parent_properties(node);
|
|
if (has_pattern < 0 || !has_pattern)
|
|
return has_pattern;
|
|
|
|
node = isl_schedule_node_get_child(node, 0);
|
|
has_pattern = has_child_properties(node);
|
|
isl_schedule_node_free(node);
|
|
|
|
return has_pattern;
|
|
}
|
|
|
|
/* Check that "node" satisfies the input pattern for hybrid tiling.
|
|
* Error out if it does not.
|
|
*/
|
|
static isl_stat check_input_pattern(__isl_keep isl_schedule_node *node)
|
|
{
|
|
isl_bool has_pattern;
|
|
|
|
has_pattern = ppcg_ht_has_input_pattern(node);
|
|
if (has_pattern < 0)
|
|
return isl_stat_error;
|
|
if (!has_pattern)
|
|
isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
|
|
"invalid input pattern for hybrid tiling",
|
|
return isl_stat_error);
|
|
|
|
return isl_stat_ok;
|
|
}
|
|
|
|
/* Extract the input schedule from "node", i.e., the product
|
|
* of the partial schedules of the parent and child nodes
|
|
* in the input pattern.
|
|
*/
|
|
static __isl_give isl_multi_union_pw_aff *extract_input_schedule(
|
|
__isl_keep isl_schedule_node *node)
|
|
{
|
|
isl_multi_union_pw_aff *partial, *partial2;
|
|
|
|
partial = isl_schedule_node_band_get_partial_schedule(node);
|
|
node = isl_schedule_node_get_child(node, 0);
|
|
partial2 = isl_schedule_node_band_get_partial_schedule(node);
|
|
isl_schedule_node_free(node);
|
|
|
|
return isl_multi_union_pw_aff_range_product(partial, partial2);
|
|
}
|
|
|
|
/* Collect all dependences from "scop" that are relevant for performing
|
|
* hybrid tiling on "node" and its child and map them to the schedule
|
|
* space of this pair of nodes.
|
|
*
|
|
* In case live range reordering is not used,
|
|
* the flow and the false dependences are collected.
|
|
* In case live range reordering is used,
|
|
* the flow and the forced dependences are collected, as well
|
|
* as the order dependences that are adjacent to non-local
|
|
* flow dependences.
|
|
*
|
|
* In all cases, only dependences that map to the same instance
|
|
* of the outer part of the schedule are considered.
|
|
*/
|
|
static __isl_give isl_map *collect_deps(struct ppcg_scop *scop,
|
|
__isl_keep isl_schedule_node *node)
|
|
{
|
|
isl_space *space;
|
|
isl_multi_union_pw_aff *prefix, *partial;
|
|
isl_union_map *flow, *other, *dep, *umap;
|
|
isl_map *map;
|
|
|
|
prefix = isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
|
|
partial = extract_input_schedule(node);
|
|
space = isl_multi_union_pw_aff_get_space(partial);
|
|
|
|
flow = isl_union_map_copy(scop->dep_flow);
|
|
flow = isl_union_map_eq_at_multi_union_pw_aff(flow,
|
|
isl_multi_union_pw_aff_copy(prefix));
|
|
if (!scop->options->live_range_reordering) {
|
|
other = isl_union_map_copy(scop->dep_false);
|
|
other = isl_union_map_eq_at_multi_union_pw_aff(other, prefix);
|
|
} else {
|
|
isl_union_map *local, *non_local, *order, *adj;
|
|
isl_union_set *domain, *range;
|
|
|
|
other = isl_union_map_copy(scop->dep_forced);
|
|
other = isl_union_map_eq_at_multi_union_pw_aff(other,
|
|
isl_multi_union_pw_aff_copy(prefix));
|
|
local = isl_union_map_copy(flow);
|
|
local = isl_union_map_eq_at_multi_union_pw_aff(local,
|
|
isl_multi_union_pw_aff_copy(partial));
|
|
non_local = isl_union_map_copy(flow);
|
|
non_local = isl_union_map_subtract(non_local, local);
|
|
|
|
order = isl_union_map_copy(scop->dep_order);
|
|
order = isl_union_map_eq_at_multi_union_pw_aff(order, prefix);
|
|
adj = isl_union_map_copy(order);
|
|
domain = isl_union_map_domain(isl_union_map_copy(non_local));
|
|
domain = isl_union_set_coalesce(domain);
|
|
adj = isl_union_map_intersect_range(adj, domain);
|
|
other = isl_union_map_union(other, adj);
|
|
|
|
adj = order;
|
|
range = isl_union_map_range(non_local);
|
|
range = isl_union_set_coalesce(range);
|
|
adj = isl_union_map_intersect_domain(adj, range);
|
|
other = isl_union_map_union(other, adj);
|
|
}
|
|
dep = isl_union_map_union(flow, other);
|
|
|
|
umap = isl_union_map_from_multi_union_pw_aff(partial);
|
|
dep = isl_union_map_apply_domain(dep, isl_union_map_copy(umap));
|
|
dep = isl_union_map_apply_range(dep, umap);
|
|
|
|
space = isl_space_map_from_set(space);
|
|
map = isl_union_map_extract_map(dep, space);
|
|
isl_union_map_free(dep);
|
|
|
|
map = isl_map_coalesce(map);
|
|
|
|
return map;
|
|
}
|
|
|
|
/* Given a constraint of the form
|
|
*
|
|
* a i_0 + b i_1 >= 0
|
|
* or
|
|
* a i_0 + b i_1 = 0
|
|
*
|
|
* use it to update one or both of the non-negative bounds
|
|
* in "list" = (min, max) such that
|
|
*
|
|
* i_1 >= -min i_0
|
|
* and
|
|
* i_1 <= max i_0
|
|
*
|
|
* If b = 0, then the constraint cannot be used.
|
|
* Otherwise, the constraint is equivalent to
|
|
*
|
|
* sgn(b) i_1 >= - a/abs(b) i_0
|
|
* i.e.,
|
|
* i_1 >= - a/abs(b) i_0
|
|
* or
|
|
* i_1 <= a/abs(b) i_0
|
|
*
|
|
* Set the first or second element of "list" to max(0, a/abs(b)),
|
|
* according to the sign of "b". Or set both in case the constraint
|
|
* is an equality, taking into account the sign change.
|
|
*/
|
|
static __isl_give isl_val_list *list_set_min_max(__isl_take isl_val_list *list,
|
|
__isl_keep isl_constraint *c)
|
|
{
|
|
isl_val *a, *b;
|
|
int sign;
|
|
int pos;
|
|
isl_bool eq, is_zero, is_neg;
|
|
|
|
eq = isl_constraint_is_equality(c);
|
|
if (eq < 0)
|
|
return isl_val_list_free(list);
|
|
|
|
b = isl_constraint_get_coefficient_val(c, isl_dim_set, 1);
|
|
is_zero = isl_val_is_zero(b);
|
|
if (is_zero == isl_bool_true) {
|
|
isl_val_free(b);
|
|
return list;
|
|
}
|
|
a = isl_constraint_get_coefficient_val(c, isl_dim_set, 0);
|
|
sign = isl_val_sgn(b);
|
|
b = isl_val_abs(b);
|
|
a = isl_val_div(a, b);
|
|
|
|
if (eq)
|
|
b = isl_val_copy(a);
|
|
|
|
pos = sign > 0 ? 0 : 1;
|
|
is_neg = isl_val_is_neg(a);
|
|
if (is_neg == isl_bool_true)
|
|
a = isl_val_set_si(a, 0);
|
|
list = isl_val_list_set_val(list, pos, a);
|
|
|
|
if (!eq)
|
|
return is_neg < 0 ? isl_val_list_free(list) : list;
|
|
|
|
pos = 1 - pos;
|
|
a = isl_val_neg(b);
|
|
is_neg = isl_val_is_neg(a);
|
|
if (is_neg == isl_bool_true)
|
|
a = isl_val_set_si(a, 0);
|
|
list = isl_val_list_set_val(list, pos, a);
|
|
|
|
return is_neg < 0 ? isl_val_list_free(list) : list;
|
|
}
|
|
|
|
/* If constraint "c" passes through the origin, then try and use it
|
|
* to update the non-negative bounds in "list" = (min, max) such that
|
|
*
|
|
* i_1 >= -min i_0
|
|
* and
|
|
* i_1 <= max i_0
|
|
*/
|
|
static isl_stat set_min_max(__isl_take isl_constraint *c, void *user)
|
|
{
|
|
isl_val *v;
|
|
isl_val_list **list = user;
|
|
isl_bool is_zero;
|
|
|
|
v = isl_constraint_get_constant_val(c);
|
|
is_zero = isl_val_is_zero(v);
|
|
isl_val_free(v);
|
|
|
|
if (is_zero == isl_bool_true)
|
|
*list = list_set_min_max(*list, c);
|
|
|
|
isl_constraint_free(c);
|
|
return is_zero < 0 ? isl_stat_error : isl_stat_ok;
|
|
}
|
|
|
|
/* Given a set of dependence distance vectors "dist", compute
|
|
* pair of non-negative bounds min and max such that
|
|
*
|
|
* d_pos >= -min d_0
|
|
* and
|
|
* d_pos <= max d_0
|
|
*
|
|
* and return the pair (min, max).
|
|
* If no bound can be found in either direction, then the bound
|
|
* is replaced by NaN.
|
|
*
|
|
* The dependence distances are first projected onto the (d_0, d_pos).
|
|
* Then the zero dependence distance is added and the convex hull is computed.
|
|
* Finally, the bounds are extracted from the constraints of the convex hull
|
|
* that pass through the origin.
|
|
*/
|
|
static __isl_give isl_val_list *min_max_dist(__isl_keep isl_set *dist, int pos)
|
|
{
|
|
isl_space *space;
|
|
isl_basic_set *hull;
|
|
int dim;
|
|
isl_ctx *ctx;
|
|
isl_val *nan;
|
|
isl_val_list *list;
|
|
|
|
ctx = isl_set_get_ctx(dist);
|
|
nan = isl_val_nan(ctx);
|
|
list = isl_val_list_alloc(ctx, 2);
|
|
list = isl_val_list_add(list, isl_val_copy(nan));
|
|
list = isl_val_list_add(list, nan);
|
|
|
|
dist = isl_set_copy(dist);
|
|
dim = isl_set_dim(dist, isl_dim_set);
|
|
if (dist && pos >= dim)
|
|
isl_die(ctx, isl_error_internal, "position out of bounds",
|
|
dist = isl_set_free(dist));
|
|
dist = isl_set_project_out(dist, isl_dim_set, pos + 1, dim - (pos + 1));
|
|
dist = isl_set_project_out(dist, isl_dim_set, 1, pos - 1);
|
|
|
|
space = isl_set_get_space(dist);
|
|
dist = isl_set_union(dist, isl_set_from_point(isl_point_zero(space)));
|
|
dist = isl_set_remove_divs(dist);
|
|
hull = isl_set_convex_hull(dist);
|
|
|
|
if (isl_basic_set_foreach_constraint(hull, &set_min_max, &list) < 0)
|
|
list = isl_val_list_free(list);
|
|
isl_basic_set_free(hull);
|
|
|
|
return list;
|
|
}
|
|
|
|
/* Given a schedule node "node" that, together with its child,
|
|
* satisfies the input pattern for hybrid tiling, compute bounds
|
|
* on the relative dependence distances of the child node with
|
|
* respect to the parent node. These bounds are needed to
|
|
* construct a hybrid tiling.
|
|
*
|
|
* First all relevant dependences are collected and mapped
|
|
* to the schedule space of the pair of nodes. Then, the
|
|
* dependence distances are computed in this space.
|
|
*
|
|
* These dependence distances are then projected onto a two-dimensional
|
|
* space consisting of the single schedule dimension of the outer node
|
|
* and one of the schedule dimensions of the inner node.
|
|
* The maximal and minimal relative dependence distances are extracted
|
|
* from these projections.
|
|
* This process is repeated for each of the schedule dimensions
|
|
* of the inner node. For the first dimension, both minimal and
|
|
* maximal relative dependence distances are stored in the result.
|
|
* For the other dimensions, only the minimal relative dependence
|
|
* distance is stored.
|
|
*/
|
|
__isl_give ppcg_ht_bounds *ppcg_ht_compute_bounds(struct ppcg_scop *scop,
|
|
__isl_keep isl_schedule_node *node)
|
|
{
|
|
ppcg_ht_bounds *bnd;
|
|
isl_space *space;
|
|
isl_map *map;
|
|
isl_set *dist;
|
|
isl_val_list *pair;
|
|
isl_schedule_node *child;
|
|
int n;
|
|
int i, dim;
|
|
|
|
if (!scop || !node || check_input_pattern(node) < 0)
|
|
return NULL;
|
|
|
|
child = isl_schedule_node_get_child(node, 0);
|
|
space = isl_schedule_node_band_get_space(child);
|
|
dim = isl_schedule_node_band_n_member(child);
|
|
isl_schedule_node_free(child);
|
|
bnd = ppcg_ht_bounds_alloc(space);
|
|
if (!bnd)
|
|
return NULL;
|
|
|
|
map = collect_deps(scop, node);
|
|
|
|
dist = isl_map_deltas(map);
|
|
n = isl_set_dim(dist, isl_dim_param);
|
|
dist = isl_set_project_out(dist, isl_dim_param, 0, n);
|
|
|
|
pair = min_max_dist(dist, 1);
|
|
bnd = ppcg_ht_bounds_set_lower(bnd, 0, isl_val_list_get_val(pair, 0));
|
|
bnd = ppcg_ht_bounds_set_upper(bnd, isl_val_list_get_val(pair, 1));
|
|
isl_val_list_free(pair);
|
|
|
|
for (i = 1; i < dim; ++i) {
|
|
pair = min_max_dist(dist, 1 + i);
|
|
bnd = ppcg_ht_bounds_set_lower(bnd, i,
|
|
isl_val_list_get_val(pair, 0));
|
|
isl_val_list_free(pair);
|
|
}
|
|
|
|
isl_set_free(dist);
|
|
|
|
return bnd;
|
|
}
|
|
|
|
/* Check if all the fields of "phase" are valid, freeing "phase"
|
|
* if they are not.
|
|
*/
|
|
static __isl_give ppcg_ht_phase *check_phase(__isl_take ppcg_ht_phase *phase)
|
|
{
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
if (!phase->tiling || !phase->local_time ||
|
|
!phase->shift_space || !phase->domain)
|
|
return ppcg_ht_phase_free(phase);
|
|
|
|
return phase;
|
|
}
|
|
|
|
/* Construct a ppcg_ht_phase object, that simply copies
|
|
* information from "tiling".
|
|
* That is, the result is defined over the "ts" space and
|
|
* corresponds to phase 1.
|
|
*/
|
|
static __isl_give ppcg_ht_phase *construct_phase(
|
|
__isl_keep ppcg_ht_tiling *tiling)
|
|
{
|
|
isl_ctx *ctx;
|
|
ppcg_ht_phase *phase;
|
|
|
|
if (!tiling)
|
|
return NULL;
|
|
|
|
ctx = ppcg_ht_tiling_get_ctx(tiling);
|
|
phase = isl_calloc_type(ctx, struct ppcg_ht_phase);
|
|
if (!phase)
|
|
return NULL;
|
|
phase->tiling = ppcg_ht_tiling_copy(tiling);
|
|
phase->time_tile = isl_aff_copy(tiling->time_tile);
|
|
phase->local_time = isl_aff_copy(tiling->local_time);
|
|
phase->shift_space = isl_aff_copy(tiling->shift_space);
|
|
phase->domain = isl_set_copy(tiling->hex);
|
|
|
|
return check_phase(phase);
|
|
}
|
|
|
|
/* Align the parameters of the elements of "phase" to those of "space".
|
|
*/
|
|
static __isl_give ppcg_ht_phase *phase_align_params(
|
|
__isl_take ppcg_ht_phase *phase, __isl_take isl_space *space)
|
|
{
|
|
if (!phase)
|
|
goto error;
|
|
|
|
phase->time_tile = isl_aff_align_params(phase->time_tile,
|
|
isl_space_copy(space));
|
|
phase->local_time = isl_aff_align_params(phase->local_time,
|
|
isl_space_copy(space));
|
|
phase->shift_space = isl_aff_align_params(phase->shift_space,
|
|
isl_space_copy(space));
|
|
phase->domain = isl_set_align_params(phase->domain, space);
|
|
|
|
return check_phase(phase);
|
|
error:
|
|
isl_space_free(space);
|
|
return NULL;
|
|
}
|
|
|
|
/* Pull back "phase" over "ma".
|
|
* That is, take a phase defined over the range of "ma" and
|
|
* turn it into a phase defined over the domain of "ma".
|
|
*/
|
|
static __isl_give ppcg_ht_phase *pullback_phase(__isl_take ppcg_ht_phase *phase,
|
|
__isl_take isl_multi_aff *ma)
|
|
{
|
|
phase = phase_align_params(phase, isl_multi_aff_get_space(ma));
|
|
if (!phase)
|
|
goto error;
|
|
|
|
phase->time_tile = isl_aff_pullback_multi_aff(phase->time_tile,
|
|
isl_multi_aff_copy(ma));
|
|
phase->local_time = isl_aff_pullback_multi_aff(phase->local_time,
|
|
isl_multi_aff_copy(ma));
|
|
phase->shift_space = isl_aff_pullback_multi_aff(phase->shift_space,
|
|
isl_multi_aff_copy(ma));
|
|
phase->domain = isl_set_preimage_multi_aff(phase->domain, ma);
|
|
|
|
return check_phase(phase);
|
|
error:
|
|
isl_multi_aff_free(ma);
|
|
return NULL;
|
|
}
|
|
|
|
/* Pullback "phase" over phase->tiling->shift_phase, which shifts
|
|
* phase 0 to phase 1. The pullback therefore takes a phase 1
|
|
* description and turns it into a phase 0 description.
|
|
*/
|
|
static __isl_give ppcg_ht_phase *shift_phase(__isl_take ppcg_ht_phase *phase)
|
|
{
|
|
ppcg_ht_tiling *tiling;
|
|
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
tiling = phase->tiling;
|
|
return pullback_phase(phase, isl_multi_aff_copy(tiling->shift_phase));
|
|
}
|
|
|
|
/* Take a "phase" defined over the ts-space and plug in the projection
|
|
* from the input schedule space to the ts-space.
|
|
* The result is then defined over this input schedule space.
|
|
*/
|
|
static __isl_give ppcg_ht_phase *lift_phase(__isl_take ppcg_ht_phase *phase)
|
|
{
|
|
ppcg_ht_tiling *tiling;
|
|
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
tiling = phase->tiling;
|
|
return pullback_phase(phase, isl_multi_aff_copy(tiling->project_ts));
|
|
}
|
|
|
|
/* Compute the shift that should be added to the space band
|
|
* in order to be able to apply rectangular tiling to the space.
|
|
* Store the shift in phase->space_shift.
|
|
*
|
|
* In the first dimension, it is equal to shift_space - s.
|
|
* For phase 1, this results in
|
|
*
|
|
* (-(2 * shift_s)*T) % W
|
|
*
|
|
* In phase 0, the "s" in shift_space has been replaced by "s + shift_s",
|
|
* so the result is
|
|
*
|
|
* shift_s + (-(2 * shift_s)*T) % W
|
|
*
|
|
* In the other dimensions, the shift is equal to
|
|
*
|
|
* dl_i * local_time.
|
|
*/
|
|
static __isl_give ppcg_ht_phase *compute_space_shift(
|
|
__isl_take ppcg_ht_phase *phase)
|
|
{
|
|
int i, n;
|
|
isl_space *space;
|
|
isl_local_space *ls;
|
|
isl_aff *aff, *s;
|
|
isl_multi_aff *space_shift;
|
|
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
space = ppcg_ht_phase_get_input_space(phase);
|
|
space = isl_space_unwrap(space);
|
|
space = isl_space_range_map(space);
|
|
|
|
space_shift = isl_multi_aff_zero(space);
|
|
aff = isl_aff_copy(phase->shift_space);
|
|
ls = isl_local_space_from_space(isl_aff_get_domain_space(aff));
|
|
s = isl_aff_var_on_domain(ls, isl_dim_set, 1);
|
|
aff = isl_aff_sub(aff, s);
|
|
space_shift = isl_multi_aff_set_aff(space_shift, 0, aff);
|
|
|
|
n = isl_multi_aff_dim(space_shift, isl_dim_out);
|
|
for (i = 1; i < n; ++i) {
|
|
isl_val *v;
|
|
isl_aff *time;
|
|
|
|
v = ppcg_ht_bounds_get_lower(phase->tiling->bounds, i);
|
|
time = isl_aff_copy(phase->local_time);
|
|
time = isl_aff_scale_val(time, v);
|
|
space_shift = isl_multi_aff_set_aff(space_shift, i, time);
|
|
}
|
|
|
|
if (!space_shift)
|
|
return ppcg_ht_phase_free(phase);
|
|
phase->space_shift = space_shift;
|
|
return phase;
|
|
}
|
|
|
|
/* Compute the space tiling and store the result in phase->space_tile.
|
|
* The space tiling is of the form
|
|
*
|
|
* [P[t] -> C[s]] -> C[floor((s + space_shift)/space_size]
|
|
*/
|
|
static __isl_give ppcg_ht_phase *compute_space_tile(
|
|
__isl_take ppcg_ht_phase *phase)
|
|
{
|
|
isl_space *space;
|
|
isl_multi_val *space_sizes;
|
|
isl_multi_aff *space_shift;
|
|
isl_multi_aff *tile;
|
|
|
|
if (!phase)
|
|
return NULL;
|
|
|
|
space = ppcg_ht_phase_get_input_space(phase);
|
|
space = isl_space_unwrap(space);
|
|
tile = isl_multi_aff_range_map(space);
|
|
space_shift = isl_multi_aff_copy(phase->space_shift);
|
|
tile = isl_multi_aff_add(space_shift, tile);
|
|
space_sizes = isl_multi_val_copy(phase->tiling->space_sizes);
|
|
tile = isl_multi_aff_scale_down_multi_val(tile, space_sizes);
|
|
tile = isl_multi_aff_floor(tile);
|
|
|
|
if (!tile)
|
|
return ppcg_ht_phase_free(phase);
|
|
phase->space_tile = tile;
|
|
return phase;
|
|
}
|
|
|
|
/* Construct a representation for one of the two phase for hybrid tiling
|
|
* "tiling". If "shift" is not set, then the phase is constructed
|
|
* directly from the hexagonal tile shape in "tiling", which represents
|
|
* the phase-1 tiles. If "shift" is set, then this tile shape is shifted
|
|
* back over tiling->shift_phase to obtain the phase-0 tiles.
|
|
*
|
|
* First copy data from "tiling", then optionally shift the phase and
|
|
* finally move the tiling from the "ts" space of "tiling" to
|
|
* the space of the input pattern.
|
|
*
|
|
* After the basic phase has been computed, also compute
|
|
* the corresponding space shift.
|
|
*/
|
|
static __isl_give ppcg_ht_phase *ppcg_ht_tiling_compute_phase(
|
|
__isl_keep ppcg_ht_tiling *tiling, int shift)
|
|
{
|
|
ppcg_ht_phase *phase;
|
|
|
|
phase = construct_phase(tiling);
|
|
if (shift)
|
|
phase = shift_phase(phase);
|
|
phase = lift_phase(phase);
|
|
|
|
phase = compute_space_shift(phase);
|
|
phase = compute_space_tile(phase);
|
|
|
|
return phase;
|
|
}
|
|
|
|
/* Consruct a function that is equal to the time tile of "phase0"
|
|
* on the domain of "phase0" and equal to the time tile of "phase1"
|
|
* on the domain of "phase1".
|
|
* The two domains are assumed to form a partition of the input
|
|
* schedule space.
|
|
*/
|
|
static __isl_give isl_pw_multi_aff *combine_time_tile(
|
|
__isl_keep ppcg_ht_phase *phase0, __isl_keep ppcg_ht_phase *phase1)
|
|
{
|
|
isl_aff *T;
|
|
isl_pw_aff *time, *time1;
|
|
|
|
if (!phase0 || !phase1)
|
|
return NULL;
|
|
|
|
T = isl_aff_copy(phase0->time_tile);
|
|
time = isl_pw_aff_alloc(ppcg_ht_phase_get_domain(phase0), T);
|
|
|
|
T = isl_aff_copy(phase1->time_tile);
|
|
time1 = isl_pw_aff_alloc(ppcg_ht_phase_get_domain(phase1), T);
|
|
|
|
time = isl_pw_aff_union_add(time, time1);
|
|
|
|
return isl_pw_multi_aff_from_pw_aff(time);
|
|
}
|
|
|
|
/* Name used in mark nodes that contain a pointer to a ppcg_ht_phase.
|
|
*/
|
|
static char *ppcg_phase_name = "phase";
|
|
|
|
/* Does "id" contain a pointer to a ppcg_ht_phase?
|
|
* That is, is it called "phase"?
|
|
*/
|
|
static isl_bool is_phase_id(__isl_keep isl_id *id)
|
|
{
|
|
const char *name;
|
|
|
|
name = isl_id_get_name(id);
|
|
if (!name)
|
|
return isl_bool_error;
|
|
|
|
return !strcmp(name, ppcg_phase_name);
|
|
}
|
|
|
|
/* Given a mark node with an identifier that points to a ppcg_ht_phase,
|
|
* extract this ppcg_ht_phase pointer.
|
|
*/
|
|
__isl_keep ppcg_ht_phase *ppcg_ht_phase_extract_from_mark(
|
|
__isl_keep isl_schedule_node *node)
|
|
{
|
|
isl_bool is_phase;
|
|
isl_id *id;
|
|
void *p;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
if (isl_schedule_node_get_type(node) != isl_schedule_node_mark)
|
|
isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
|
|
"not a phase mark", return NULL);
|
|
|
|
id = isl_schedule_node_mark_get_id(node);
|
|
is_phase = is_phase_id(id);
|
|
p = isl_id_get_user(id);
|
|
isl_id_free(id);
|
|
|
|
if (is_phase < 0)
|
|
return NULL;
|
|
if (!is_phase)
|
|
isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
|
|
"not a phase mark", return NULL);
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Insert a mark node at "node" holding a pointer to "phase".
|
|
*/
|
|
static __isl_give isl_schedule_node *insert_phase(
|
|
__isl_take isl_schedule_node *node, __isl_take ppcg_ht_phase *phase)
|
|
{
|
|
isl_ctx *ctx;
|
|
isl_id *id;
|
|
|
|
if (!node)
|
|
goto error;
|
|
ctx = isl_schedule_node_get_ctx(node);
|
|
id = isl_id_alloc(ctx, ppcg_phase_name, phase);
|
|
if (!id)
|
|
goto error;
|
|
id = isl_id_set_free_user(id, &ppcg_ht_phase_free_wrap);
|
|
node = isl_schedule_node_insert_mark(node, id);
|
|
|
|
return node;
|
|
error:
|
|
ppcg_ht_phase_free(phase);
|
|
isl_schedule_node_free(node);
|
|
return NULL;
|
|
}
|
|
|
|
/* Construct a mapping from the elements of the original pair of bands
|
|
* to which tiling was applied that belong to a tile of "phase"
|
|
* to that tile, preserving the values for the outer bands.
|
|
*
|
|
* The mapping is of the form
|
|
*
|
|
* [[outer] -> [P -> C]] -> [[outer] -> [tile]]
|
|
*
|
|
* where tile is defined by a concatenation of the time_tile and
|
|
* the space_tile.
|
|
*/
|
|
static __isl_give isl_map *construct_tile_map(__isl_keep ppcg_ht_phase *phase)
|
|
{
|
|
int depth;
|
|
isl_space *space;
|
|
isl_multi_aff *ma;
|
|
isl_multi_aff *tiling;
|
|
isl_map *el2tile;
|
|
|
|
depth = isl_schedule_node_get_schedule_depth(
|
|
phase->tiling->input_node);
|
|
space = isl_aff_get_space(phase->time_tile);
|
|
space = isl_space_params(space);
|
|
space = isl_space_set_from_params(space);
|
|
space = isl_space_add_dims(space, isl_dim_set, depth);
|
|
space = isl_space_map_from_set(space);
|
|
ma = isl_multi_aff_identity(space);
|
|
|
|
tiling = isl_multi_aff_flat_range_product(
|
|
isl_multi_aff_from_aff(isl_aff_copy(phase->time_tile)),
|
|
isl_multi_aff_copy(phase->space_tile));
|
|
el2tile = isl_map_from_multi_aff(tiling);
|
|
el2tile = isl_map_intersect_domain(el2tile,
|
|
isl_set_copy(phase->domain));
|
|
el2tile = isl_map_product(isl_map_from_multi_aff(ma), el2tile);
|
|
|
|
return el2tile;
|
|
}
|
|
|
|
/* Return a description of the full tiles of "phase" at the point
|
|
* in the original schedule tree where the tiling was applied.
|
|
*
|
|
* First construct a mapping from the input schedule dimensions
|
|
* up to an including the original pair of bands to which hybrid tiling
|
|
* was applied to schedule dimensions in which this original pair
|
|
* has been replaced by the tiles.
|
|
* This mapping is of the form
|
|
*
|
|
* [[outer] -> [P -> C]] -> [[outer] -> [tile]]
|
|
*
|
|
* Apply this mapping to the set of all values for the input
|
|
* schedule dimensions and then apply its inverse.
|
|
* The result is the set of values for the input schedule dimensions
|
|
* that would map to any of the tiles. Subtracting from this set
|
|
* the set of values that are actually executed produces the set
|
|
* of values that belong to a tile but that are not executed.
|
|
* Mapping these back to the tiles produces a description of
|
|
* the partial tiles. Subtracting these from the set of all tiles
|
|
* produces a description of the full tiles in the form
|
|
*
|
|
* [[outer] -> [tile]]
|
|
*/
|
|
static __isl_give isl_set *compute_full_tile(__isl_keep ppcg_ht_phase *phase)
|
|
{
|
|
isl_schedule_node *node;
|
|
isl_union_set *domain;
|
|
isl_union_map *prefix, *schedule;
|
|
isl_set *all, *partial, *all_el;
|
|
isl_map *tile2el, *el2tile;
|
|
isl_multi_union_pw_aff *mupa;
|
|
|
|
el2tile = construct_tile_map(phase);
|
|
tile2el = isl_map_reverse(isl_map_copy(el2tile));
|
|
|
|
node = phase->tiling->input_node;
|
|
prefix = isl_schedule_node_get_prefix_schedule_union_map(node);
|
|
domain = isl_schedule_node_get_domain(node);
|
|
mupa = isl_multi_union_pw_aff_copy(phase->tiling->input_schedule);
|
|
schedule = isl_union_map_from_multi_union_pw_aff(mupa);
|
|
schedule = isl_union_map_range_product(prefix, schedule);
|
|
all_el = isl_set_from_union_set(isl_union_set_apply(domain, schedule));
|
|
all_el = isl_set_coalesce(all_el);
|
|
|
|
all = isl_set_apply(isl_set_copy(all_el), isl_map_copy(el2tile));
|
|
|
|
partial = isl_set_copy(all);
|
|
partial = isl_set_apply(partial, tile2el);
|
|
partial = isl_set_subtract(partial, all_el);
|
|
partial = isl_set_apply(partial, el2tile);
|
|
|
|
return isl_set_subtract(all, partial);
|
|
}
|
|
|
|
/* Copy the AST loop types of the non-isolated part to those
|
|
* of the isolated part.
|
|
*/
|
|
static __isl_give isl_schedule_node *set_isolate_loop_type(
|
|
__isl_take isl_schedule_node *node)
|
|
{
|
|
int i, n;
|
|
|
|
n = isl_schedule_node_band_n_member(node);
|
|
for (i = 0; i < n; ++i) {
|
|
enum isl_ast_loop_type type;
|
|
|
|
type = isl_schedule_node_band_member_get_ast_loop_type(node, i);
|
|
node = isl_schedule_node_band_member_set_isolate_ast_loop_type(
|
|
node, i, type);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/* If options->isolate_full_tiles is set, then mark the full tiles
|
|
* in "node" for isolation. The full tiles are derived from "phase".
|
|
* "node" may point to a part of the tiling, e.g., the space tiling.
|
|
*
|
|
* The full tiles are originally computed in the form
|
|
*
|
|
* [[outer] -> [tile]]
|
|
*
|
|
* However, the band that "node" points to may only contain
|
|
* subset of the tile dimensions.
|
|
* The description above is therefore treated as
|
|
*
|
|
* [[outer] -> [before; this; after]]
|
|
*
|
|
* before is of size "pos"; this is of size "dim"; and
|
|
* after is of size "out - pos - dim".
|
|
* The after part is first project out. Then the range is split
|
|
* into a before and this part and finally the before part is moved
|
|
* to the domain, resulting in
|
|
*
|
|
* [[outer; before] -> [this]]
|
|
*
|
|
* This description is then used as the isolate option.
|
|
*
|
|
* The AST loop type for the isolated part is set to be the same
|
|
* as that of the non-isolated part.
|
|
*/
|
|
static __isl_give isl_schedule_node *ppcg_ht_phase_isolate_full_tile_node(
|
|
__isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node,
|
|
struct ppcg_options *options)
|
|
{
|
|
int in, out, pos, depth, dim;
|
|
isl_space *space;
|
|
isl_multi_aff *ma1, *ma2;
|
|
isl_set *tile;
|
|
isl_map *map;
|
|
isl_set *set;
|
|
isl_union_set *opt;
|
|
|
|
if (!options->isolate_full_tiles)
|
|
return node;
|
|
|
|
depth = isl_schedule_node_get_schedule_depth(node);
|
|
dim = isl_schedule_node_band_n_member(node);
|
|
|
|
tile = compute_full_tile(phase);
|
|
map = isl_set_unwrap(tile);
|
|
in = isl_map_dim(map, isl_dim_in);
|
|
out = isl_map_dim(map, isl_dim_out);
|
|
pos = depth - in;
|
|
map = isl_map_project_out(map, isl_dim_out, pos + dim,
|
|
out - (pos + dim));
|
|
space = isl_space_range(isl_map_get_space(map));
|
|
ma1 = isl_multi_aff_project_out_map(isl_space_copy(space),
|
|
isl_dim_set, pos, dim);
|
|
ma2 = isl_multi_aff_project_out_map(space, isl_dim_set, 0, pos);
|
|
ma1 = isl_multi_aff_range_product(ma1, ma2);
|
|
map = isl_map_apply_range(map, isl_map_from_multi_aff(ma1));
|
|
map = isl_map_uncurry(map);
|
|
map = isl_map_flatten_domain(map);
|
|
set = isl_map_wrap(map);
|
|
set = isl_set_set_tuple_name(set, "isolate");
|
|
|
|
opt = isl_schedule_node_band_get_ast_build_options(node);
|
|
opt = isl_union_set_add_set(opt, set);
|
|
node = isl_schedule_node_band_set_ast_build_options(node, opt);
|
|
node = set_isolate_loop_type(node);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* Insert a band node for performing the space tiling for "phase" at "node".
|
|
* In particular, insert a band node with partial schedule
|
|
*
|
|
* [P[t] -> C[s]] -> C[floor((s + space_shift)/space_size)]
|
|
*
|
|
* pulled back over the input schedule.
|
|
* "options" determines whether full tiles should be separated
|
|
* from partial tiles.
|
|
*
|
|
* The first tile dimension iterates over the hexagons in the same
|
|
* phase, which are independent by construction. The first dimension
|
|
* is therefore marked coincident.
|
|
* All dimensions are also marked for being generated as atomic loops
|
|
* because separation is usually not desirable on tile loops.
|
|
*/
|
|
static __isl_give isl_schedule_node *insert_space_tiling(
|
|
__isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node,
|
|
struct ppcg_options *options)
|
|
{
|
|
isl_multi_aff *space_tile;
|
|
isl_multi_union_pw_aff *mupa;
|
|
|
|
if (!phase)
|
|
return isl_schedule_node_free(node);
|
|
|
|
space_tile = isl_multi_aff_copy(phase->space_tile);
|
|
mupa = isl_multi_union_pw_aff_copy(phase->tiling->input_schedule);
|
|
mupa = isl_multi_union_pw_aff_apply_multi_aff(mupa, space_tile);
|
|
node = isl_schedule_node_insert_partial_schedule(node, mupa);
|
|
node = ppcg_set_schedule_node_type(node, isl_ast_loop_atomic);
|
|
node = ppcg_ht_phase_isolate_full_tile_node(phase, node, options);
|
|
node = isl_schedule_node_band_member_set_coincident(node, 0, 1);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* Given a pointer "node" to (a copy of) the original child node
|
|
* in the input pattern, adjust its partial schedule such that
|
|
* it starts at zero within each tile.
|
|
*
|
|
* That is, replace "s" by (s + space_shift) % space_sizes.
|
|
*/
|
|
__isl_give isl_schedule_node *ppcg_ht_phase_shift_space_point(
|
|
__isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node)
|
|
{
|
|
isl_multi_val *space_sizes;
|
|
isl_multi_aff *space_shift;
|
|
isl_multi_union_pw_aff *mupa;
|
|
|
|
space_shift = isl_multi_aff_copy(phase->space_shift);
|
|
mupa = isl_multi_union_pw_aff_copy(phase->tiling->input_schedule);
|
|
mupa = isl_multi_union_pw_aff_apply_multi_aff(mupa, space_shift);
|
|
node = isl_schedule_node_band_shift(node, mupa);
|
|
space_sizes = isl_multi_val_copy(phase->tiling->space_sizes);
|
|
node = isl_schedule_node_band_mod(node, space_sizes);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* Does
|
|
*
|
|
* s0 > delta + 2 * {delta * h} - 1
|
|
*
|
|
* hold?
|
|
*/
|
|
static isl_bool wide_enough(__isl_keep isl_val *s0, __isl_keep isl_val *delta,
|
|
__isl_keep isl_val *h)
|
|
{
|
|
isl_val *v, *v2;
|
|
isl_bool ok;
|
|
|
|
v = isl_val_mul(isl_val_copy(delta), isl_val_copy(h));
|
|
v2 = isl_val_floor(isl_val_copy(v));
|
|
v = isl_val_sub(v, v2);
|
|
v = isl_val_mul_ui(v, 2);
|
|
v = isl_val_add(v, isl_val_copy(delta));
|
|
v = isl_val_sub_ui(v, 1);
|
|
ok = isl_val_gt(s0, v);
|
|
isl_val_free(v);
|
|
|
|
return ok;
|
|
}
|
|
|
|
/* Is the tile size specified by "sizes" wide enough in the first space
|
|
* dimension, i.e., the base of the hexagon? This ensures that,
|
|
* after hybrid tiling using "bounds" and these sizes,
|
|
* neighboring hexagons in the same phase are far enough apart
|
|
* that they do not depend on each other.
|
|
* The test is only meaningful if the bounds are valid.
|
|
*
|
|
* Let st be (half) the size in the time dimension and s0 the base
|
|
* size in the first space dimension. Let delta be the dependence
|
|
* distance in either positive or negative direction. In principle,
|
|
* it should be enough to have s0 + 1 > delta, i.e., s0 >= delta.
|
|
* However, in case of fractional delta, the tile is not extended
|
|
* with delta * (st - 1), but instead with floor(delta * (st - 1)).
|
|
* The condition therefore needs to be adjusted to
|
|
*
|
|
* s0 + 1 > delta + 2 {delta * (st - 1)}
|
|
*
|
|
* (with {} the fractional part) to account for the two slanted sides.
|
|
* The condition in the paper "Hybrid Hexagonal/Classical Tiling for GPUs"
|
|
* translates to
|
|
*
|
|
* s0 >= delta + {delta * (st - 1)}
|
|
*
|
|
* Since 1 > frac(delta * (st - 1)), this condition implies
|
|
* the condition above.
|
|
*
|
|
* The condition is checked for both directions.
|
|
*/
|
|
isl_bool ppcg_ht_bounds_supports_sizes(__isl_keep ppcg_ht_bounds *bounds,
|
|
__isl_keep isl_multi_val *sizes)
|
|
{
|
|
isl_val *s0, *h;
|
|
isl_val *delta;
|
|
isl_bool ok;
|
|
|
|
ok = ppcg_ht_bounds_is_valid(bounds);
|
|
if (ok < 0 || !ok)
|
|
return ok;
|
|
|
|
h = isl_val_sub_ui(isl_multi_val_get_val(sizes, 0), 1);
|
|
s0 = isl_multi_val_get_val(sizes, 1);
|
|
|
|
delta = ppcg_ht_bounds_get_lower(bounds, 0);
|
|
ok = wide_enough(s0, delta, h);
|
|
isl_val_free(delta);
|
|
|
|
delta = ppcg_ht_bounds_get_upper(bounds);
|
|
if (ok == isl_bool_true)
|
|
ok = wide_enough(s0, delta, h);
|
|
isl_val_free(delta);
|
|
|
|
isl_val_free(s0);
|
|
isl_val_free(h);
|
|
|
|
return ok;
|
|
}
|
|
|
|
/* Check that the tile will be wide enough in the first space
|
|
* dimension, i.e., the base of the hexagon. This ensures that
|
|
* neighboring hexagons in the same phase are far enough apart
|
|
* that they do not depend on each other.
|
|
*
|
|
* Error out if the condition fails to hold.
|
|
*/
|
|
static isl_stat check_width(__isl_keep ppcg_ht_bounds *bounds,
|
|
__isl_keep isl_multi_val *sizes)
|
|
{
|
|
isl_bool ok;
|
|
|
|
ok = ppcg_ht_bounds_supports_sizes(bounds, sizes);
|
|
|
|
if (ok < 0)
|
|
return isl_stat_error;
|
|
if (!ok)
|
|
isl_die(isl_multi_val_get_ctx(sizes), isl_error_invalid,
|
|
"base of hybrid tiling hexagon not sufficiently wide",
|
|
return isl_stat_error);
|
|
|
|
return isl_stat_ok;
|
|
}
|
|
|
|
/* Given valid bounds on the relative dependence distances for
|
|
* the pair of nested nodes that "node" point to, as well as sufficiently
|
|
* wide tile sizes "sizes", insert the corresponding time and space tiling
|
|
* at "node", along with a pair of phase nodes that can be used
|
|
* to make further changes.
|
|
* The space of "sizes" should be the product of the spaces
|
|
* of the schedules of the pair of parent and child nodes.
|
|
* "options" determines whether full tiles should be separated
|
|
* from partial tiles.
|
|
*
|
|
* In particular, given an input of the form
|
|
*
|
|
* P - C - ...
|
|
*
|
|
* the output has the form
|
|
*
|
|
* /- F0 - M0 - CT0 - P - C - ...
|
|
* PT - seq
|
|
* \- F1 - M1 - CT1 - P - C - ...
|
|
*
|
|
* PT is the global time tiling. Within each of these tiles,
|
|
* two phases are executed in order. Within each phase, the schedule
|
|
* space is further subdivided into tiles through CT0 and CT1.
|
|
* The first dimension of each of these iterates over the hexagons
|
|
* within a phase and these are independent by construction.
|
|
* The F0 and F1 filters filter the statement instances that belong
|
|
* to the corresponding phase. The M0 and M1 marks contain a pointer
|
|
* to a ppcg_ht_phase object that can be used to perform further changes.
|
|
*
|
|
* After checking that input satisfies the requirements,
|
|
* a data structure is constructed that represents the tiling and
|
|
* two additional data structures are constructed for the two phases
|
|
* of the tiling. These are then used to define the filters F0 and F1 and
|
|
* combined to construct the time tiling PT.
|
|
* Then the time tiling node PT is inserted, followed by
|
|
* the sequence with the two filters, the CT space tiling nodes and
|
|
* the phase markers M.
|
|
*/
|
|
__isl_give isl_schedule_node *ppcg_ht_bounds_insert_tiling(
|
|
__isl_take ppcg_ht_bounds *bounds, __isl_take isl_multi_val *sizes,
|
|
__isl_take isl_schedule_node *node, struct ppcg_options *options)
|
|
{
|
|
isl_ctx *ctx;
|
|
isl_union_set *phase0;
|
|
isl_union_set *phase1;
|
|
isl_multi_union_pw_aff *input, *dom_time;
|
|
isl_union_pw_multi_aff *upma;
|
|
isl_pw_multi_aff *time;
|
|
isl_union_set_list *phases;
|
|
ppcg_ht_tiling *tiling;
|
|
ppcg_ht_phase *phase_0;
|
|
ppcg_ht_phase *phase_1;
|
|
|
|
if (!node || !sizes || !bounds)
|
|
goto error;
|
|
if (check_input_pattern(node) < 0 || check_width(bounds, sizes) < 0)
|
|
goto error;
|
|
|
|
ctx = isl_schedule_node_get_ctx(node);
|
|
|
|
input = extract_input_schedule(node);
|
|
|
|
tiling = ppcg_ht_bounds_construct_tiling(bounds, node, input, sizes);
|
|
phase_0 = ppcg_ht_tiling_compute_phase(tiling, 1);
|
|
phase_1 = ppcg_ht_tiling_compute_phase(tiling, 0);
|
|
time = combine_time_tile(phase_0, phase_1);
|
|
ppcg_ht_tiling_free(tiling);
|
|
|
|
upma = isl_union_pw_multi_aff_from_multi_union_pw_aff(
|
|
isl_multi_union_pw_aff_copy(input));
|
|
phase0 = isl_union_set_from_set(ppcg_ht_phase_get_domain(phase_0));
|
|
phase0 = isl_union_set_preimage_union_pw_multi_aff(phase0,
|
|
isl_union_pw_multi_aff_copy(upma));
|
|
phase1 = isl_union_set_from_set(ppcg_ht_phase_get_domain(phase_1));
|
|
phase1 = isl_union_set_preimage_union_pw_multi_aff(phase1, upma);
|
|
|
|
phases = isl_union_set_list_alloc(ctx, 2);
|
|
phases = isl_union_set_list_add(phases, phase0);
|
|
phases = isl_union_set_list_add(phases, phase1);
|
|
|
|
dom_time = isl_multi_union_pw_aff_apply_pw_multi_aff(input, time);
|
|
node = isl_schedule_node_insert_partial_schedule(node, dom_time);
|
|
|
|
node = isl_schedule_node_child(node, 0);
|
|
|
|
node = isl_schedule_node_insert_sequence(node, phases);
|
|
node = isl_schedule_node_child(node, 0);
|
|
node = isl_schedule_node_child(node, 0);
|
|
node = insert_space_tiling(phase_0, node, options);
|
|
node = insert_phase(node, phase_0);
|
|
node = isl_schedule_node_parent(node);
|
|
node = isl_schedule_node_next_sibling(node);
|
|
node = isl_schedule_node_child(node, 0);
|
|
node = insert_space_tiling(phase_1, node, options);
|
|
node = insert_phase(node, phase_1);
|
|
node = isl_schedule_node_parent(node);
|
|
node = isl_schedule_node_parent(node);
|
|
|
|
node = isl_schedule_node_parent(node);
|
|
|
|
isl_multi_val_free(sizes);
|
|
return node;
|
|
error:
|
|
isl_multi_val_free(sizes);
|
|
isl_schedule_node_free(node);
|
|
ppcg_ht_bounds_free(bounds);
|
|
return NULL;
|
|
}
|
|
|
|
/* Given a branch "node" that contains a sequence node with two phases
|
|
* of hybrid tiling as input, call "fn" on each of the two phase marker
|
|
* nodes.
|
|
*
|
|
* That is, the input is as follows
|
|
*
|
|
* /- F0 - M0 - ...
|
|
* ... - seq
|
|
* \- F1 - M1 - ...
|
|
*
|
|
* and "fn" is called on M0 and on M1.
|
|
*/
|
|
__isl_give isl_schedule_node *hybrid_tile_foreach_phase(
|
|
__isl_take isl_schedule_node *node,
|
|
__isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
|
|
void *user), void *user)
|
|
{
|
|
int depth0, depth;
|
|
|
|
depth0 = isl_schedule_node_get_tree_depth(node);
|
|
|
|
while (node &&
|
|
isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
|
|
node = isl_schedule_node_child(node, 0);
|
|
|
|
node = isl_schedule_node_child(node, 0);
|
|
node = isl_schedule_node_child(node, 0);
|
|
if (!node)
|
|
return NULL;
|
|
node = fn(node, user);
|
|
node = isl_schedule_node_parent(node);
|
|
node = isl_schedule_node_next_sibling(node);
|
|
node = isl_schedule_node_child(node, 0);
|
|
if (!node)
|
|
return NULL;
|
|
node = fn(node, user);
|
|
node = isl_schedule_node_parent(node);
|
|
node = isl_schedule_node_parent(node);
|
|
|
|
depth = isl_schedule_node_get_tree_depth(node);
|
|
node = isl_schedule_node_ancestor(node, depth - depth0);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* This function is called on each of the two phase marks
|
|
* in a hybrid tiling tree.
|
|
* Drop the phase mark at "node".
|
|
*/
|
|
static __isl_give isl_schedule_node *drop_phase_mark(
|
|
__isl_take isl_schedule_node *node, void *user)
|
|
{
|
|
isl_id *id;
|
|
isl_bool is_phase;
|
|
|
|
if (isl_schedule_node_get_type(node) != isl_schedule_node_mark)
|
|
return node;
|
|
|
|
id = isl_schedule_node_mark_get_id(node);
|
|
is_phase = is_phase_id(id);
|
|
isl_id_free(id);
|
|
|
|
if (is_phase < 0)
|
|
return isl_schedule_node_free(node);
|
|
if (is_phase)
|
|
node = isl_schedule_node_delete(node);
|
|
|
|
return node;
|
|
}
|
|
|
|
/* Given a branch "node" that contains a sequence node with two phases
|
|
* of hybrid tiling as input, remove the two phase marker nodes.
|
|
*
|
|
* That is, the input is as follows
|
|
*
|
|
* /- F0 - M0 - ...
|
|
* ... - seq
|
|
* \- F1 - M1 - ...
|
|
*
|
|
* and the output is
|
|
*
|
|
* /- F0 - ...
|
|
* ... - seq
|
|
* \- F1 - ...
|
|
*/
|
|
__isl_give isl_schedule_node *hybrid_tile_drop_phase_marks(
|
|
__isl_take isl_schedule_node *node)
|
|
{
|
|
return hybrid_tile_foreach_phase(node, &drop_phase_mark, NULL);
|
|
}
|