forked from OSchip/llvm-project
685 lines
21 KiB
C
685 lines
21 KiB
C
/*
|
|
* Copyright 2016 Sven Verdoolaege
|
|
*
|
|
* Use of this software is governed by the MIT license
|
|
*
|
|
* Written by Sven Verdoolaege.
|
|
*/
|
|
|
|
#include <isl/ctx.h>
|
|
#include <isl/id.h>
|
|
#include <isl/val.h>
|
|
#include <isl/space.h>
|
|
#include <isl/aff.h>
|
|
#include <isl/set.h>
|
|
#include <isl/map.h>
|
|
#include <isl/union_set.h>
|
|
#include <isl/union_map.h>
|
|
#include <isl/schedule.h>
|
|
#include <isl/schedule_node.h>
|
|
|
|
#include "ppcg.h"
|
|
|
|
/* Internal data structure for use during the detection of statements
|
|
* that can be grouped.
|
|
*
|
|
* "sc" contains the original schedule constraints (not a copy).
|
|
* "dep" contains the intersection of the validity and the proximity
|
|
* constraints in "sc". It may be NULL if it has not been computed yet.
|
|
* "group_id" is the identifier for the next group that is extracted.
|
|
*
|
|
* "domain" is the set of statement instances that belong to any of the groups.
|
|
* "contraction" maps the elements of "domain" to the corresponding group
|
|
* instances.
|
|
* "schedule" schedules the statements in each group relatively to each other.
|
|
* These last three fields are NULL if no groups have been found so far.
|
|
*/
|
|
struct ppcg_grouping {
|
|
isl_schedule_constraints *sc;
|
|
|
|
isl_union_map *dep;
|
|
int group_id;
|
|
|
|
isl_union_set *domain;
|
|
isl_union_pw_multi_aff *contraction;
|
|
isl_schedule *schedule;
|
|
};
|
|
|
|
/* Clear all memory allocated by "grouping".
|
|
*/
|
|
static void ppcg_grouping_clear(struct ppcg_grouping *grouping)
|
|
{
|
|
isl_union_map_free(grouping->dep);
|
|
isl_union_set_free(grouping->domain);
|
|
isl_union_pw_multi_aff_free(grouping->contraction);
|
|
isl_schedule_free(grouping->schedule);
|
|
}
|
|
|
|
/* Compute the intersection of the proximity and validity dependences
|
|
* in grouping->sc and store the result in grouping->dep, unless
|
|
* this intersection has been computed before.
|
|
*/
|
|
static isl_stat ppcg_grouping_compute_dep(struct ppcg_grouping *grouping)
|
|
{
|
|
isl_union_map *validity, *proximity;
|
|
|
|
if (grouping->dep)
|
|
return isl_stat_ok;
|
|
|
|
validity = isl_schedule_constraints_get_validity(grouping->sc);
|
|
proximity = isl_schedule_constraints_get_proximity(grouping->sc);
|
|
grouping->dep = isl_union_map_intersect(validity, proximity);
|
|
|
|
if (!grouping->dep)
|
|
return isl_stat_error;
|
|
|
|
return isl_stat_ok;
|
|
}
|
|
|
|
/* Information extracted from one or more consecutive leaves
|
|
* in the input schedule.
|
|
*
|
|
* "list" contains the sets of statement instances in the leaves,
|
|
* one element in the list for each original leaf.
|
|
* "domain" contains the union of the sets in "list".
|
|
* "prefix" contains the prefix schedule of these elements.
|
|
*/
|
|
struct ppcg_grouping_leaf {
|
|
isl_union_set *domain;
|
|
isl_union_set_list *list;
|
|
isl_multi_union_pw_aff *prefix;
|
|
};
|
|
|
|
/* Free all memory allocated for "leaves".
|
|
*/
|
|
static void ppcg_grouping_leaf_free(int n, struct ppcg_grouping_leaf leaves[])
|
|
{
|
|
int i;
|
|
|
|
if (!leaves)
|
|
return;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
isl_union_set_free(leaves[i].domain);
|
|
isl_union_set_list_free(leaves[i].list);
|
|
isl_multi_union_pw_aff_free(leaves[i].prefix);
|
|
}
|
|
|
|
free(leaves);
|
|
}
|
|
|
|
/* Short-hand for retrieving the prefix schedule at "node"
|
|
* in the form of an isl_multi_union_pw_aff.
|
|
*/
|
|
static __isl_give isl_multi_union_pw_aff *get_prefix(
|
|
__isl_keep isl_schedule_node *node)
|
|
{
|
|
return isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
|
|
}
|
|
|
|
/* Return an array of "n" elements with information extracted from
|
|
* the "n" children of "node" starting at "first", all of which
|
|
* are known to be filtered leaves.
|
|
*/
|
|
struct ppcg_grouping_leaf *extract_leaves(__isl_keep isl_schedule_node *node,
|
|
int first, int n)
|
|
{
|
|
int i;
|
|
isl_ctx *ctx;
|
|
struct ppcg_grouping_leaf *leaves;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
ctx = isl_schedule_node_get_ctx(node);
|
|
leaves = isl_calloc_array(ctx, struct ppcg_grouping_leaf, n);
|
|
if (!leaves)
|
|
return NULL;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
isl_schedule_node *child;
|
|
isl_union_set *domain;
|
|
|
|
child = isl_schedule_node_get_child(node, first + i);
|
|
child = isl_schedule_node_child(child, 0);
|
|
domain = isl_schedule_node_get_domain(child);
|
|
leaves[i].domain = isl_union_set_copy(domain);
|
|
leaves[i].list = isl_union_set_list_from_union_set(domain);
|
|
leaves[i].prefix = get_prefix(child);
|
|
isl_schedule_node_free(child);
|
|
}
|
|
|
|
return leaves;
|
|
}
|
|
|
|
/* Internal data structure used by merge_leaves.
|
|
*
|
|
* "src" and "dst" point to the two consecutive leaves that are
|
|
* under investigation for being merged.
|
|
* "merge" is initially set to 0 and is set to 1 as soon as
|
|
* it turns out that it is useful to merge the two leaves.
|
|
*/
|
|
struct ppcg_merge_leaves_data {
|
|
int merge;
|
|
struct ppcg_grouping_leaf *src;
|
|
struct ppcg_grouping_leaf *dst;
|
|
};
|
|
|
|
/* Given a relation "map" between instances of two statements A and B,
|
|
* does it relate every instance of A (according to the domain of "src")
|
|
* to every instance of B (according to the domain of "dst")?
|
|
*/
|
|
static isl_bool covers_src_and_dst(__isl_keep isl_map *map,
|
|
struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
|
|
{
|
|
isl_space *space;
|
|
isl_set *set1, *set2;
|
|
isl_bool is_subset;
|
|
|
|
space = isl_space_domain(isl_map_get_space(map));
|
|
set1 = isl_union_set_extract_set(src->domain, space);
|
|
set2 = isl_map_domain(isl_map_copy(map));
|
|
is_subset = isl_set_is_subset(set1, set2);
|
|
isl_set_free(set1);
|
|
isl_set_free(set2);
|
|
if (is_subset < 0 || !is_subset)
|
|
return is_subset;
|
|
|
|
space = isl_space_range(isl_map_get_space(map));
|
|
set1 = isl_union_set_extract_set(dst->domain, space);
|
|
set2 = isl_map_range(isl_map_copy(map));
|
|
is_subset = isl_set_is_subset(set1, set2);
|
|
isl_set_free(set1);
|
|
isl_set_free(set2);
|
|
|
|
return is_subset;
|
|
}
|
|
|
|
/* Given a relation "map" between instances of two statements A and B,
|
|
* are pairs of related instances executed together in the input schedule?
|
|
* That is, is each pair of instances assigned the same value
|
|
* by the corresponding prefix schedules?
|
|
*
|
|
* In particular, select the subset of "map" that has pairs of elements
|
|
* with the same value for the prefix schedules and then check
|
|
* if "map" is still a subset of the result.
|
|
*/
|
|
static isl_bool matches_prefix(__isl_keep isl_map *map,
|
|
struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
|
|
{
|
|
isl_union_map *umap, *equal;
|
|
isl_multi_union_pw_aff *src_prefix, *dst_prefix, *prefix;
|
|
isl_bool is_subset;
|
|
|
|
src_prefix = isl_multi_union_pw_aff_copy(src->prefix);
|
|
dst_prefix = isl_multi_union_pw_aff_copy(dst->prefix);
|
|
prefix = isl_multi_union_pw_aff_union_add(src_prefix, dst_prefix);
|
|
|
|
umap = isl_union_map_from_map(isl_map_copy(map));
|
|
equal = isl_union_map_copy(umap);
|
|
equal = isl_union_map_eq_at_multi_union_pw_aff(equal, prefix);
|
|
|
|
is_subset = isl_union_map_is_subset(umap, equal);
|
|
|
|
isl_union_map_free(umap);
|
|
isl_union_map_free(equal);
|
|
|
|
return is_subset;
|
|
}
|
|
|
|
/* Given a set of validity and proximity schedule constraints "map"
|
|
* between statements in consecutive leaves in a valid schedule,
|
|
* should the two leaves be merged into one?
|
|
*
|
|
* In particular, the two are merged if the constraints form
|
|
* a bijection between every instance of the first statement and
|
|
* every instance of the second statement. Moreover, each
|
|
* pair of such dependent instances needs to be executed consecutively
|
|
* in the input schedule. That is, they need to be assigned
|
|
* the same value by their prefix schedules.
|
|
*
|
|
* What this means is that for each instance of the first statement
|
|
* there is exactly one instance of the second statement that
|
|
* is executed immediately after the instance of the first statement and
|
|
* that, moreover, both depends on this statement instance and
|
|
* should be brought as close as possible to this statement instance.
|
|
* In other words, it is both possible to execute the two instances
|
|
* together (according to the input schedule) and desirable to do so
|
|
* (according to the validity and proximity schedule constraints).
|
|
*/
|
|
static isl_stat check_merge(__isl_take isl_map *map, void *user)
|
|
{
|
|
struct ppcg_merge_leaves_data *data = user;
|
|
isl_bool ok;
|
|
|
|
ok = covers_src_and_dst(map, data->src, data->dst);
|
|
if (ok >= 0 && ok)
|
|
ok = isl_map_is_bijective(map);
|
|
if (ok >= 0 && ok)
|
|
ok = matches_prefix(map, data->src, data->dst);
|
|
|
|
isl_map_free(map);
|
|
|
|
if (ok < 0)
|
|
return isl_stat_error;
|
|
if (!ok)
|
|
return isl_stat_ok;
|
|
|
|
data->merge = 1;
|
|
return isl_stat_error;
|
|
}
|
|
|
|
/* Merge the leaves at position "pos" and "pos + 1" in "leaves".
|
|
*/
|
|
static isl_stat merge_pair(int n, struct ppcg_grouping_leaf leaves[], int pos)
|
|
{
|
|
int i;
|
|
|
|
leaves[pos].domain = isl_union_set_union(leaves[pos].domain,
|
|
leaves[pos + 1].domain);
|
|
leaves[pos].list = isl_union_set_list_concat(leaves[pos].list,
|
|
leaves[pos + 1].list);
|
|
leaves[pos].prefix = isl_multi_union_pw_aff_union_add(
|
|
leaves[pos].prefix, leaves[pos + 1].prefix);
|
|
for (i = pos + 1; i + 1 < n; ++i)
|
|
leaves[i] = leaves[i + 1];
|
|
leaves[n - 1].domain = NULL;
|
|
leaves[n - 1].list = NULL;
|
|
leaves[n - 1].prefix = NULL;
|
|
|
|
if (!leaves[pos].domain || !leaves[pos].list || !leaves[pos].prefix)
|
|
return isl_stat_error;
|
|
|
|
return isl_stat_ok;
|
|
}
|
|
|
|
/* Merge pairs of consecutive leaves in "leaves" taking into account
|
|
* the intersection of validity and proximity schedule constraints "dep".
|
|
*
|
|
* If a leaf has been merged with the next leaf, then the combination
|
|
* is checked again for merging with the next leaf.
|
|
* That is, if the leaves are A, B and C, then B may not have been
|
|
* merged with C, but after merging A and B, it could still be useful
|
|
* to merge the combination AB with C.
|
|
*
|
|
* Two leaves A and B are merged if there are instances of at least
|
|
* one pair of statements, one statement in A and one B, such that
|
|
* the validity and proximity schedule constraints between them
|
|
* make them suitable for merging according to check_merge.
|
|
*
|
|
* Return the final number of leaves in the sequence, or -1 on error.
|
|
*/
|
|
static int merge_leaves(int n, struct ppcg_grouping_leaf leaves[],
|
|
__isl_keep isl_union_map *dep)
|
|
{
|
|
int i;
|
|
struct ppcg_merge_leaves_data data;
|
|
|
|
for (i = n - 1; i >= 0; --i) {
|
|
isl_union_map *dep_i;
|
|
isl_stat ok;
|
|
|
|
if (i + 1 >= n)
|
|
continue;
|
|
|
|
dep_i = isl_union_map_copy(dep);
|
|
dep_i = isl_union_map_intersect_domain(dep_i,
|
|
isl_union_set_copy(leaves[i].domain));
|
|
dep_i = isl_union_map_intersect_range(dep_i,
|
|
isl_union_set_copy(leaves[i + 1].domain));
|
|
data.merge = 0;
|
|
data.src = &leaves[i];
|
|
data.dst = &leaves[i + 1];
|
|
ok = isl_union_map_foreach_map(dep_i, &check_merge, &data);
|
|
isl_union_map_free(dep_i);
|
|
if (ok < 0 && !data.merge)
|
|
return -1;
|
|
if (!data.merge)
|
|
continue;
|
|
if (merge_pair(n, leaves, i) < 0)
|
|
return -1;
|
|
--n;
|
|
++i;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/* Construct a schedule with "domain" as domain, that executes
|
|
* the elements of "list" in order (as a sequence).
|
|
*/
|
|
static __isl_give isl_schedule *schedule_from_domain_and_list(
|
|
__isl_keep isl_union_set *domain, __isl_keep isl_union_set_list *list)
|
|
{
|
|
isl_schedule *schedule;
|
|
isl_schedule_node *node;
|
|
|
|
schedule = isl_schedule_from_domain(isl_union_set_copy(domain));
|
|
node = isl_schedule_get_root(schedule);
|
|
isl_schedule_free(schedule);
|
|
node = isl_schedule_node_child(node, 0);
|
|
list = isl_union_set_list_copy(list);
|
|
node = isl_schedule_node_insert_sequence(node, list);
|
|
schedule = isl_schedule_node_get_schedule(node);
|
|
isl_schedule_node_free(node);
|
|
|
|
return schedule;
|
|
}
|
|
|
|
/* Construct a unique identifier for a group in "grouping".
|
|
*
|
|
* The name is of the form G_n, with n the first value starting at
|
|
* grouping->group_id that does not result in an identifier
|
|
* that is already in use in the domain of the original schedule
|
|
* constraints.
|
|
*/
|
|
static isl_id *construct_group_id(struct ppcg_grouping *grouping,
|
|
__isl_take isl_space *space)
|
|
{
|
|
isl_ctx *ctx;
|
|
isl_id *id;
|
|
isl_bool empty;
|
|
isl_union_set *domain;
|
|
|
|
if (!space)
|
|
return NULL;
|
|
|
|
ctx = isl_space_get_ctx(space);
|
|
domain = isl_schedule_constraints_get_domain(grouping->sc);
|
|
|
|
do {
|
|
char buffer[20];
|
|
isl_id *id;
|
|
isl_set *set;
|
|
|
|
snprintf(buffer, sizeof(buffer), "G_%d", grouping->group_id);
|
|
grouping->group_id++;
|
|
id = isl_id_alloc(ctx, buffer, NULL);
|
|
space = isl_space_set_tuple_id(space, isl_dim_set, id);
|
|
set = isl_union_set_extract_set(domain, isl_space_copy(space));
|
|
empty = isl_set_plain_is_empty(set);
|
|
isl_set_free(set);
|
|
} while (empty >= 0 && !empty);
|
|
|
|
if (empty < 0)
|
|
space = isl_space_free(space);
|
|
|
|
id = isl_space_get_tuple_id(space, isl_dim_set);
|
|
|
|
isl_space_free(space);
|
|
isl_union_set_free(domain);
|
|
|
|
return id;
|
|
}
|
|
|
|
/* Construct a contraction from "prefix" and "domain" for a new group
|
|
* in "grouping".
|
|
*
|
|
* The values of the prefix schedule "prefix" are used as instances
|
|
* of the new group. The identifier of the group is constructed
|
|
* in such a way that it does not conflict with those of earlier
|
|
* groups nor with statements in the domain of the original
|
|
* schedule constraints.
|
|
* The isl_multi_union_pw_aff "prefix" then simply needs to be
|
|
* converted to an isl_union_pw_multi_aff. However, this is not
|
|
* possible if "prefix" is zero-dimensional, so in this case,
|
|
* a contraction is constructed from "domain" instead.
|
|
*/
|
|
static isl_union_pw_multi_aff *group_contraction_from_prefix_and_domain(
|
|
struct ppcg_grouping *grouping,
|
|
__isl_keep isl_multi_union_pw_aff *prefix,
|
|
__isl_keep isl_union_set *domain)
|
|
{
|
|
isl_id *id;
|
|
isl_space *space;
|
|
int dim;
|
|
|
|
space = isl_multi_union_pw_aff_get_space(prefix);
|
|
if (!space)
|
|
return NULL;
|
|
dim = isl_space_dim(space, isl_dim_set);
|
|
id = construct_group_id(grouping, space);
|
|
if (dim == 0) {
|
|
isl_multi_val *mv;
|
|
|
|
space = isl_multi_union_pw_aff_get_space(prefix);
|
|
space = isl_space_set_tuple_id(space, isl_dim_set, id);
|
|
mv = isl_multi_val_zero(space);
|
|
domain = isl_union_set_copy(domain);
|
|
return isl_union_pw_multi_aff_multi_val_on_domain(domain, mv);
|
|
}
|
|
prefix = isl_multi_union_pw_aff_copy(prefix);
|
|
prefix = isl_multi_union_pw_aff_set_tuple_id(prefix, isl_dim_out, id);
|
|
return isl_union_pw_multi_aff_from_multi_union_pw_aff(prefix);
|
|
}
|
|
|
|
/* Extend "grouping" with groups corresponding to merged
|
|
* leaves in the list of potentially merged leaves "leaves".
|
|
*
|
|
* The "list" field of each element in "leaves" contains a list
|
|
* of the instances sets of the original leaves that have been
|
|
* merged into this element. If at least two of the original leaves
|
|
* have been merged into a given element, then add the corresponding
|
|
* group to "grouping".
|
|
* In particular, the domain is extended with the statement instances
|
|
* of the merged leaves, the contraction is extended with a mapping
|
|
* of these statement instances to instances of a new group and
|
|
* the schedule is extended with a schedule that executes
|
|
* the statement instances according to the order of the leaves
|
|
* in which they appear.
|
|
* Since the instances of the groups should already be scheduled apart
|
|
* in the schedule into which this schedule will be plugged in,
|
|
* the schedules of the individual groups are combined independently
|
|
* of each other (as a set).
|
|
*/
|
|
static isl_stat add_groups(struct ppcg_grouping *grouping,
|
|
int n, struct ppcg_grouping_leaf leaves[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
int n_leaf;
|
|
isl_schedule *schedule;
|
|
isl_union_set *domain;
|
|
isl_union_pw_multi_aff *upma;
|
|
|
|
n_leaf = isl_union_set_list_n_union_set(leaves[i].list);
|
|
if (n_leaf < 0)
|
|
return isl_stat_error;
|
|
if (n_leaf <= 1)
|
|
continue;
|
|
schedule = schedule_from_domain_and_list(leaves[i].domain,
|
|
leaves[i].list);
|
|
upma = group_contraction_from_prefix_and_domain(grouping,
|
|
leaves[i].prefix, leaves[i].domain);
|
|
|
|
domain = isl_union_set_copy(leaves[i].domain);
|
|
if (grouping->domain) {
|
|
domain = isl_union_set_union(domain, grouping->domain);
|
|
upma = isl_union_pw_multi_aff_union_add(upma,
|
|
grouping->contraction);
|
|
schedule = isl_schedule_set(schedule,
|
|
grouping->schedule);
|
|
}
|
|
grouping->domain = domain;
|
|
grouping->contraction = upma;
|
|
grouping->schedule = schedule;
|
|
|
|
if (!grouping->domain || !grouping->contraction ||
|
|
!grouping->schedule)
|
|
return isl_stat_error;
|
|
}
|
|
|
|
return isl_stat_ok;
|
|
}
|
|
|
|
/* Look for any pairs of consecutive leaves among the "n" children of "node"
|
|
* starting at "first" that should be merged together.
|
|
* Store the results in "grouping".
|
|
*
|
|
* First make sure the intersection of validity and proximity
|
|
* schedule constraints is available and extract the required
|
|
* information from the "n" leaves.
|
|
* Then try and merge consecutive leaves based on the validity
|
|
* and proximity constraints.
|
|
* If any pairs were successfully merged, then add groups
|
|
* corresponding to the merged leaves to "grouping".
|
|
*/
|
|
static isl_stat group_subsequence(__isl_keep isl_schedule_node *node,
|
|
int first, int n, struct ppcg_grouping *grouping)
|
|
{
|
|
int n_merge;
|
|
struct ppcg_grouping_leaf *leaves;
|
|
|
|
if (ppcg_grouping_compute_dep(grouping) < 0)
|
|
return isl_stat_error;
|
|
|
|
leaves = extract_leaves(node, first, n);
|
|
if (!leaves)
|
|
return isl_stat_error;
|
|
|
|
n_merge = merge_leaves(n, leaves, grouping->dep);
|
|
if (n_merge >= 0 && n_merge < n &&
|
|
add_groups(grouping, n_merge, leaves) < 0)
|
|
return isl_stat_error;
|
|
|
|
ppcg_grouping_leaf_free(n, leaves);
|
|
|
|
return isl_stat_ok;
|
|
}
|
|
|
|
/* If "node" is a sequence, then check if it has any consecutive
|
|
* leaves that should be merged together and store the results
|
|
* in "grouping".
|
|
*
|
|
* In particular, call group_subsequence on each consecutive
|
|
* sequence of (filtered) leaves among the children of "node".
|
|
*/
|
|
static isl_bool detect_groups(__isl_keep isl_schedule_node *node, void *user)
|
|
{
|
|
int i, n, first;
|
|
struct ppcg_grouping *grouping = user;
|
|
|
|
if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
|
|
return isl_bool_true;
|
|
|
|
n = isl_schedule_node_n_children(node);
|
|
if (n < 0)
|
|
return isl_bool_error;
|
|
|
|
first = -1;
|
|
for (i = 0; i < n; ++i) {
|
|
isl_schedule_node *child;
|
|
enum isl_schedule_node_type type;
|
|
|
|
child = isl_schedule_node_get_child(node, i);
|
|
child = isl_schedule_node_child(child, 0);
|
|
type = isl_schedule_node_get_type(child);
|
|
isl_schedule_node_free(child);
|
|
|
|
if (first >= 0 && type != isl_schedule_node_leaf) {
|
|
if (group_subsequence(node, first, i - first,
|
|
grouping) < 0)
|
|
return isl_bool_error;
|
|
first = -1;
|
|
}
|
|
if (first < 0 && type == isl_schedule_node_leaf)
|
|
first = i;
|
|
}
|
|
if (first >= 0) {
|
|
if (group_subsequence(node, first, n - first, grouping) < 0)
|
|
return isl_bool_error;
|
|
}
|
|
|
|
return isl_bool_true;
|
|
}
|
|
|
|
/* Complete "grouping" to cover all statement instances in the domain
|
|
* of grouping->sc.
|
|
*
|
|
* In particular, grouping->domain is set to the full set of statement
|
|
* instances; group->contraction is extended with an identity
|
|
* contraction on the additional instances and group->schedule
|
|
* is extended with an independent schedule on those additional instances.
|
|
* In the extension of group->contraction, the additional instances
|
|
* are split into those belong to different statements and those
|
|
* that belong to some of the same statements. The first group
|
|
* is replaced by its universe in order to simplify the contraction extension.
|
|
*/
|
|
static void complete_grouping(struct ppcg_grouping *grouping)
|
|
{
|
|
isl_union_set *domain, *left, *overlap;
|
|
isl_union_pw_multi_aff *upma;
|
|
isl_schedule *schedule;
|
|
|
|
domain = isl_schedule_constraints_get_domain(grouping->sc);
|
|
left = isl_union_set_subtract(isl_union_set_copy(domain),
|
|
isl_union_set_copy(grouping->domain));
|
|
schedule = isl_schedule_from_domain(isl_union_set_copy(left));
|
|
schedule = isl_schedule_set(schedule, grouping->schedule);
|
|
grouping->schedule = schedule;
|
|
|
|
overlap = isl_union_set_universe(grouping->domain);
|
|
grouping->domain = domain;
|
|
overlap = isl_union_set_intersect(isl_union_set_copy(left), overlap);
|
|
left = isl_union_set_subtract(left, isl_union_set_copy(overlap));
|
|
left = isl_union_set_universe(left);
|
|
left = isl_union_set_union(left, overlap);
|
|
upma = isl_union_set_identity_union_pw_multi_aff(left);
|
|
upma = isl_union_pw_multi_aff_union_add(upma, grouping->contraction);
|
|
grouping->contraction = upma;
|
|
}
|
|
|
|
/* Compute a schedule on the domain of "sc" that respects the schedule
|
|
* constraints in "sc".
|
|
*
|
|
* "schedule" is a known correct schedule that is used to combine
|
|
* groups of statements if options->group_chains is set.
|
|
* In particular, statements that are executed consecutively in a sequence
|
|
* in this schedule and where all instances of the second depend on
|
|
* the instance of the first that is executed in the same iteration
|
|
* of outer band nodes are grouped together into a single statement.
|
|
* The schedule constraints are then mapped to these groups of statements
|
|
* and the resulting schedule is expanded again to refer to the original
|
|
* statements.
|
|
*/
|
|
__isl_give isl_schedule *ppcg_compute_schedule(
|
|
__isl_take isl_schedule_constraints *sc,
|
|
__isl_keep isl_schedule *schedule, struct ppcg_options *options)
|
|
{
|
|
struct ppcg_grouping grouping = { sc };
|
|
isl_union_pw_multi_aff *contraction;
|
|
isl_union_map *umap;
|
|
isl_schedule *res, *expansion;
|
|
|
|
if (!options->group_chains)
|
|
return isl_schedule_constraints_compute_schedule(sc);
|
|
|
|
grouping.group_id = 0;
|
|
if (isl_schedule_foreach_schedule_node_top_down(schedule,
|
|
&detect_groups, &grouping) < 0)
|
|
goto error;
|
|
if (!grouping.contraction) {
|
|
ppcg_grouping_clear(&grouping);
|
|
return isl_schedule_constraints_compute_schedule(sc);
|
|
}
|
|
complete_grouping(&grouping);
|
|
contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
|
|
umap = isl_union_map_from_union_pw_multi_aff(contraction);
|
|
|
|
sc = isl_schedule_constraints_apply(sc, umap);
|
|
|
|
res = isl_schedule_constraints_compute_schedule(sc);
|
|
|
|
contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
|
|
expansion = isl_schedule_copy(grouping.schedule);
|
|
res = isl_schedule_expand(res, contraction, expansion);
|
|
|
|
ppcg_grouping_clear(&grouping);
|
|
return res;
|
|
error:
|
|
ppcg_grouping_clear(&grouping);
|
|
isl_schedule_constraints_free(sc);
|
|
return NULL;
|
|
}
|