llvm/polly/lib/External/isl/isl_scheduler_clustering.c

/*
 * Copyright 2015      Sven Verdoolaege
 *
 * Use of this software is governed by the MIT license
 *
 * Written by Sven Verdoolaege
 */

#include "isl_map_private.h"

#include <isl/id.h>
#include <isl/schedule_node.h>
#include <isl/union_set.h>

#include "isl_mat_private.h"
#include "isl_scheduler_clustering.h"
#include "isl_scheduler_scc.h"
#include "isl_seq.h"
#include "isl_tarjan.h"

/* Initialize the clustering data structure "c" from "graph".
 *
 * In particular, allocate memory, extract the SCCs from "graph"
 * into c->scc, initialize scc_cluster and construct
 * a band of schedule rows for each SCC.
 * Within each SCC, there is only one SCC by definition.
 * Each SCC initially belongs to a cluster containing only that SCC.
 */
static isl_stat clustering_init(isl_ctx *ctx, struct isl_clustering *c,
	struct isl_sched_graph *graph)
{}

/* Free all memory allocated for "c".
 */
static void clustering_free(isl_ctx *ctx, struct isl_clustering *c)
{}

/* Should we refrain from merging the cluster in "graph" with
 * any other cluster?
 * In particular, is its current schedule band empty and incomplete.
 */
static int bad_cluster(struct isl_sched_graph *graph)
{}

/* Is "edge" a proximity edge with a non-empty dependence relation?
 */
static isl_bool is_non_empty_proximity(struct isl_sched_edge *edge)
{}

/* Return the index of an edge in "graph" that can be used to merge
 * two clusters in "c".
 * Return graph->n_edge if no such edge can be found.
 * Return -1 on error.
 *
 * In particular, return a proximity edge between two clusters
 * that is not marked "no_merge" and such that neither of the
 * two clusters has an incomplete, empty band.
 *
 * If there are multiple such edges, then try and find the most
 * appropriate edge to use for merging.  In particular, pick the edge
 * with the greatest weight.  If there are multiple of those,
 * then pick one with the shortest distance between
 * the two cluster representatives.
 */
static int find_proximity(struct isl_sched_graph *graph,
	struct isl_clustering *c)
{}

/* Internal data structure used in mark_merge_sccs.
 *
 * "graph" is the dependence graph in which a strongly connected
 * component is constructed.
 * "scc_cluster" maps each SCC index to the cluster to which it belongs.
 * "src" and "dst" are the indices of the nodes that are being merged.
 */
struct isl_mark_merge_sccs_data {};

/* Check whether the cluster containing node "i" depends on the cluster
 * containing node "j".  If "i" and "j" belong to the same cluster,
 * then they are taken to depend on each other to ensure that
 * the resulting strongly connected component consists of complete
 * clusters.  Furthermore, if "i" and "j" are the two nodes that
 * are being merged, then they are taken to depend on each other as well.
 * Otherwise, check if there is a (conditional) validity dependence
 * from node[j] to node[i], forcing node[i] to follow node[j].
 */
static isl_bool cluster_follows(int i, int j, void *user)
{}

/* Mark all SCCs that belong to either of the two clusters in "c"
 * connected by the edge in "graph" with index "edge", or to any
 * of the intermediate clusters.
 * The marking is recorded in c->scc_in_merge.
 *
 * The given edge has been selected for merging two clusters,
 * meaning that there is at least a proximity edge between the two nodes.
 * However, there may also be (indirect) validity dependences
 * between the two nodes.  When merging the two clusters, all clusters
 * containing one or more of the intermediate nodes along the
 * indirect validity dependences need to be merged in as well.
 *
 * First collect all such nodes by computing the strongly connected
 * component (SCC) containing the two nodes connected by the edge, where
 * the two nodes are considered to depend on each other to make
 * sure they end up in the same SCC.  Similarly, each node is considered
 * to depend on every other node in the same cluster to ensure
 * that the SCC consists of complete clusters.
 *
 * Then the original SCCs that contain any of these nodes are marked
 * in c->scc_in_merge.
 */
static isl_stat mark_merge_sccs(isl_ctx *ctx, struct isl_sched_graph *graph,
	int edge, struct isl_clustering *c)
{}

/* Construct the identifier "cluster_i".
 */
static __isl_give isl_id *cluster_id(isl_ctx *ctx, int i)
{}

/* Construct the space of the cluster with index "i" containing
 * the strongly connected component "scc".
 *
 * In particular, construct a space called cluster_i with dimension equal
 * to the number of schedule rows in the current band of "scc".
 */
static __isl_give isl_space *cluster_space(struct isl_sched_graph *scc, int i)
{}

/* Collect the domain of the graph for merging clusters.
 *
 * In particular, for each cluster with first SCC "i", construct
 * a set in the space called cluster_i with dimension equal
 * to the number of schedule rows in the current band of the cluster.
 */
static __isl_give isl_union_set *collect_domain(isl_ctx *ctx,
	struct isl_sched_graph *graph, struct isl_clustering *c)
{}

/* Construct a map from the original instances to the corresponding
 * cluster instance in the current bands of the clusters in "c".
 */
static __isl_give isl_union_map *collect_cluster_map(isl_ctx *ctx,
	struct isl_sched_graph *graph, struct isl_clustering *c)
{}

/* Add "umap" to the schedule constraints "sc" of all types of "edge"
 * that are not isl_edge_condition or isl_edge_conditional_validity.
 */
static __isl_give isl_schedule_constraints *add_non_conditional_constraints(
	struct isl_sched_edge *edge, __isl_keep isl_union_map *umap,
	__isl_take isl_schedule_constraints *sc)
{}

/* Add schedule constraints of types isl_edge_condition and
 * isl_edge_conditional_validity to "sc" by applying "umap" to
 * the domains of the wrapped relations in domain and range
 * of the corresponding tagged constraints of "edge".
 */
static __isl_give isl_schedule_constraints *add_conditional_constraints(
	struct isl_sched_edge *edge, __isl_keep isl_union_map *umap,
	__isl_take isl_schedule_constraints *sc)
{}

/* Given a mapping "cluster_map" from the original instances to
 * the cluster instances, add schedule constraints on the clusters
 * to "sc" corresponding to the original constraints represented by "edge".
 *
 * For non-tagged dependence constraints, the cluster constraints
 * are obtained by applying "cluster_map" to the edge->map.
 *
 * For tagged dependence constraints, "cluster_map" needs to be applied
 * to the domains of the wrapped relations in domain and range
 * of the tagged dependence constraints.  Pick out the mappings
 * from these domains from "cluster_map" and construct their product.
 * This mapping can then be applied to the pair of domains.
 */
static __isl_give isl_schedule_constraints *collect_edge_constraints(
	struct isl_sched_edge *edge, __isl_keep isl_union_map *cluster_map,
	__isl_take isl_schedule_constraints *sc)
{}

/* Given a mapping "cluster_map" from the original instances to
 * the cluster instances, add schedule constraints on the clusters
 * to "sc" corresponding to all edges in "graph" between nodes that
 * belong to SCCs that are marked for merging in "scc_in_merge".
 */
static __isl_give isl_schedule_constraints *collect_constraints(
	struct isl_sched_graph *graph, int *scc_in_merge,
	__isl_keep isl_union_map *cluster_map,
	__isl_take isl_schedule_constraints *sc)
{}

/* Construct a dependence graph for scheduling clusters with respect
 * to each other and store the result in "merge_graph".
 * In particular, the nodes of the graph correspond to the schedule
 * dimensions of the current bands of those clusters that have been
 * marked for merging in "c".
 *
 * First construct an isl_schedule_constraints object for this domain
 * by transforming the edges in "graph" to the domain.
 * Then initialize a dependence graph for scheduling from these
 * constraints.
 */
static isl_stat init_merge_graph(isl_ctx *ctx, struct isl_sched_graph *graph,
	struct isl_clustering *c, struct isl_sched_graph *merge_graph)
{}

/* Compute the maximal number of remaining schedule rows that still need
 * to be computed for the nodes that belong to clusters with the maximal
 * dimension for the current band (i.e., the band that is to be merged).
 * Only clusters that are about to be merged are considered.
 * "maxvar" is the maximal dimension for the current band.
 * "c" contains information about the clusters.
 *
 * Return the maximal number of remaining schedule rows or
 * isl_size_error on error.
 */
static isl_size compute_maxvar_max_slack(int maxvar, struct isl_clustering *c)
{}

/* If there are any clusters where the dimension of the current band
 * (i.e., the band that is to be merged) is smaller than "maxvar" and
 * if there are any nodes in such a cluster where the number
 * of remaining schedule rows that still need to be computed
 * is greater than "max_slack", then return the smallest current band
 * dimension of all these clusters.  Otherwise return the original value
 * of "maxvar".  Return isl_size_error in case of any error.
 * Only clusters that are about to be merged are considered.
 * "c" contains information about the clusters.
 */
static isl_size limit_maxvar_to_slack(int maxvar, int max_slack,
	struct isl_clustering *c)
{}

/* Adjust merge_graph->maxvar based on the number of remaining schedule rows
 * that still need to be computed.  In particular, if there is a node
 * in a cluster where the dimension of the current band is smaller
 * than merge_graph->maxvar, but the number of remaining schedule rows
 * is greater than that of any node in a cluster with the maximal
 * dimension for the current band (i.e., merge_graph->maxvar),
 * then adjust merge_graph->maxvar to the (smallest) current band dimension
 * of those clusters.  Without this adjustment, the total number of
 * schedule dimensions would be increased, resulting in a skewed view
 * of the number of coincident dimensions.
 * "c" contains information about the clusters.
 *
 * If the maximize_band_depth option is set and merge_graph->maxvar is reduced,
 * then there is no point in attempting any merge since it will be rejected
 * anyway.  Set merge_graph->maxvar to zero in such cases.
 */
static isl_stat adjust_maxvar_to_slack(isl_ctx *ctx,
	struct isl_sched_graph *merge_graph, struct isl_clustering *c)
{}

/* Return the number of coincident dimensions in the current band of "graph",
 * where the nodes of "graph" are assumed to be scheduled by a single band.
 */
static int get_n_coincident(struct isl_sched_graph *graph)
{}

/* Should the clusters be merged based on the cluster schedule
 * in the current (and only) band of "merge_graph", given that
 * coincidence should be maximized?
 *
 * If the number of coincident schedule dimensions in the merged band
 * would be less than the maximal number of coincident schedule dimensions
 * in any of the merged clusters, then the clusters should not be merged.
 */
static isl_bool ok_to_merge_coincident(struct isl_clustering *c,
	struct isl_sched_graph *merge_graph)
{}

/* Return the transformation on "node" expressed by the current (and only)
 * band of "merge_graph" applied to the clusters in "c".
 *
 * First find the representation of "node" in its SCC in "c" and
 * extract the transformation expressed by the current band.
 * Then extract the transformation applied by "merge_graph"
 * to the cluster to which this SCC belongs.
 * Combine the two to obtain the complete transformation on the node.
 *
 * Note that the range of the first transformation is an anonymous space,
 * while the domain of the second is named "cluster_X".  The range
 * of the former therefore needs to be adjusted before the two
 * can be combined.
 */
static __isl_give isl_map *extract_node_transformation(isl_ctx *ctx,
	struct isl_sched_node *node, struct isl_clustering *c,
	struct isl_sched_graph *merge_graph)
{}

/* Give a set of distances "set", are they bounded by a small constant
 * in direction "pos"?
 * In practice, check if they are bounded by 2 by checking that there
 * are no elements with a value greater than or equal to 3 or
 * smaller than or equal to -3.
 */
static isl_bool distance_is_bounded(__isl_keep isl_set *set, int pos)
{}

/* Does the set "set" have a fixed (but possible parametric) value
 * at dimension "pos"?
 */
static isl_bool has_single_value(__isl_keep isl_set *set, int pos)
{}

/* Does "map" have a fixed (but possible parametric) value
 * at dimension "pos" of either its domain or its range?
 */
static isl_bool has_singular_src_or_dst(__isl_keep isl_map *map, int pos)
{}

/* Does the edge "edge" from "graph" have bounded dependence distances
 * in the merged graph "merge_graph" of a selection of clusters in "c"?
 *
 * Extract the complete transformations of the source and destination
 * nodes of the edge, apply them to the edge constraints and
 * compute the differences.  Finally, check if these differences are bounded
 * in each direction.
 *
 * If the dimension of the band is greater than the number of
 * dimensions that can be expected to be optimized by the edge
 * (based on its weight), then also allow the differences to be unbounded
 * in the remaining dimensions, but only if either the source or
 * the destination has a fixed value in that direction.
 * This allows a statement that produces values that are used by
 * several instances of another statement to be merged with that
 * other statement.
 * However, merging such clusters will introduce an inherently
 * large proximity distance inside the merged cluster, meaning
 * that proximity distances will no longer be optimized in
 * subsequent merges.  These merges are therefore only allowed
 * after all other possible merges have been tried.
 * The first time such a merge is encountered, the weight of the edge
 * is replaced by a negative weight.  The second time (i.e., after
 * all merges over edges with a non-negative weight have been tried),
 * the merge is allowed.
 */
static isl_bool has_bounded_distances(isl_ctx *ctx, struct isl_sched_edge *edge,
	struct isl_sched_graph *graph, struct isl_clustering *c,
	struct isl_sched_graph *merge_graph)
{}

/* Should the clusters be merged based on the cluster schedule
 * in the current (and only) band of "merge_graph"?
 * "graph" is the original dependence graph, while "c" records
 * which SCCs are involved in the latest merge.
 *
 * In particular, is there at least one proximity constraint
 * that is optimized by the merge?
 *
 * A proximity constraint is considered to be optimized
 * if the dependence distances are small.
 */
static isl_bool ok_to_merge_proximity(isl_ctx *ctx,
	struct isl_sched_graph *graph, struct isl_clustering *c,
	struct isl_sched_graph *merge_graph)
{}

/* Should the clusters be merged based on the cluster schedule
 * in the current (and only) band of "merge_graph"?
 * "graph" is the original dependence graph, while "c" records
 * which SCCs are involved in the latest merge.
 *
 * If the current band is empty, then the clusters should not be merged.
 *
 * If the band depth should be maximized and the merge schedule
 * is incomplete (meaning that the dimension of some of the schedule
 * bands in the original schedule will be reduced), then the clusters
 * should not be merged.
 *
 * If the schedule_maximize_coincidence option is set, then check that
 * the number of coincident schedule dimensions is not reduced.
 *
 * Finally, only allow the merge if at least one proximity
 * constraint is optimized.
 */
static isl_bool ok_to_merge(isl_ctx *ctx, struct isl_sched_graph *graph,
	struct isl_clustering *c, struct isl_sched_graph *merge_graph)
{}

/* Apply the schedule in "t_node" to the "n" rows starting at "first"
 * of the schedule in "node" and return the result.
 *
 * That is, essentially compute
 *
 *	T * N(first:first+n-1)
 *
 * taking into account the constant term and the parameter coefficients
 * in "t_node".
 */
static __isl_give isl_mat *node_transformation(isl_ctx *ctx,
	struct isl_sched_node *t_node, struct isl_sched_node *node,
	int first, int n)
{}

/* Apply the cluster schedule in "t_node" to the current band
 * schedule of the nodes in "graph".
 *
 * In particular, replace the rows starting at band_start
 * by the result of applying the cluster schedule in "t_node"
 * to the original rows.
 *
 * The coincidence of the schedule is determined by the coincidence
 * of the cluster schedule.
 */
static isl_stat transform(isl_ctx *ctx, struct isl_sched_graph *graph,
	struct isl_sched_node *t_node)
{}

/* Merge the clusters marked for merging in "c" into a single
 * cluster using the cluster schedule in the current band of "merge_graph".
 * The representative SCC for the new cluster is the SCC with
 * the smallest index.
 *
 * The current band schedule of each SCC in the new cluster is obtained
 * by applying the schedule of the corresponding original cluster
 * to the original band schedule.
 * All SCCs in the new cluster have the same number of schedule rows.
 */
static isl_stat merge(isl_ctx *ctx, struct isl_clustering *c,
	struct isl_sched_graph *merge_graph)
{}

/* Try and merge the clusters of SCCs marked in c->scc_in_merge
 * by scheduling the current cluster bands with respect to each other.
 *
 * Construct a dependence graph with a space for each cluster and
 * with the coordinates of each space corresponding to the schedule
 * dimensions of the current band of that cluster.
 * Construct a cluster schedule in this cluster dependence graph and
 * apply it to the current cluster bands if it is applicable
 * according to ok_to_merge.
 *
 * If the number of remaining schedule dimensions in a cluster
 * with a non-maximal current schedule dimension is greater than
 * the number of remaining schedule dimensions in clusters
 * with a maximal current schedule dimension, then restrict
 * the number of rows to be computed in the cluster schedule
 * to the minimal such non-maximal current schedule dimension.
 * Do this by adjusting merge_graph.maxvar.
 *
 * Return isl_bool_true if the clusters have effectively been merged
 * into a single cluster.
 *
 * Note that since the standard scheduling algorithm minimizes the maximal
 * distance over proximity constraints, the proximity constraints between
 * the merged clusters may not be optimized any further than what is
 * sufficient to bring the distances within the limits of the internal
 * proximity constraints inside the individual clusters.
 * It may therefore make sense to perform an additional translation step
 * to bring the clusters closer to each other, while maintaining
 * the linear part of the merging schedule found using the standard
 * scheduling algorithm.
 */
static isl_bool try_merge(isl_ctx *ctx, struct isl_sched_graph *graph,
	struct isl_clustering *c)
{}

/* Is there any edge marked "no_merge" between two SCCs that are
 * about to be merged (i.e., that are set in "scc_in_merge")?
 * "merge_edge" is the proximity edge along which the clusters of SCCs
 * are going to be merged.
 *
 * If there is any edge between two SCCs with a negative weight,
 * while the weight of "merge_edge" is non-negative, then this
 * means that the edge was postponed.  "merge_edge" should then
 * also be postponed since merging along the edge with negative weight should
 * be postponed until all edges with non-negative weight have been tried.
 * Replace the weight of "merge_edge" by a negative weight as well and
 * tell the caller not to attempt a merge.
 */
static int any_no_merge(struct isl_sched_graph *graph, int *scc_in_merge,
	struct isl_sched_edge *merge_edge)
{}

/* Merge the two clusters in "c" connected by the edge in "graph"
 * with index "edge" into a single cluster.
 * If it turns out to be impossible to merge these two clusters,
 * then mark the edge as "no_merge" such that it will not be
 * considered again.
 *
 * First mark all SCCs that need to be merged.  This includes the SCCs
 * in the two clusters, but it may also include the SCCs
 * of intermediate clusters.
 * If there is already a no_merge edge between any pair of such SCCs,
 * then simply mark the current edge as no_merge as well.
 * Likewise, if any of those edges was postponed by has_bounded_distances,
 * then postpone the current edge as well.
 * Otherwise, try and merge the clusters and mark "edge" as "no_merge"
 * if the clusters did not end up getting merged, unless the non-merge
 * is due to the fact that the edge was postponed.  This postponement
 * can be recognized by a change in weight (from non-negative to negative).
 */
static isl_stat merge_clusters_along_edge(isl_ctx *ctx,
	struct isl_sched_graph *graph, int edge, struct isl_clustering *c)
{}

/* Does "node" belong to the cluster identified by "cluster"?
 */
static int node_cluster_exactly(struct isl_sched_node *node, int cluster)
{}

/* Does "edge" connect two nodes belonging to the cluster
 * identified by "cluster"?
 */
static int edge_cluster_exactly(struct isl_sched_edge *edge, int cluster)
{}

/* Swap the schedule of "node1" and "node2".
 * Both nodes have been derived from the same node in a common parent graph.
 * Since the "coincident" field is shared with that node
 * in the parent graph, there is no need to also swap this field.
 */
static void swap_sched(struct isl_sched_node *node1,
	struct isl_sched_node *node2)
{}

/* Copy the current band schedule from the SCCs that form the cluster
 * with index "pos" to the actual cluster at position "pos".
 * By construction, the index of the first SCC that belongs to the cluster
 * is also "pos".
 *
 * The order of the nodes inside both the SCCs and the cluster
 * is assumed to be same as the order in the original "graph".
 *
 * Since the SCC graphs will no longer be used after this function,
 * the schedules are actually swapped rather than copied.
 */
static isl_stat copy_partial(struct isl_sched_graph *graph,
	struct isl_clustering *c, int pos)
{}

/* Is there a (conditional) validity dependence from node[j] to node[i],
 * forcing node[i] to follow node[j] or do the nodes belong to the same
 * cluster?
 */
static isl_bool node_follows_strong_or_same_cluster(int i, int j, void *user)
{}

/* Extract the merged clusters of SCCs in "graph", sort them, and
 * store them in c->clusters.  Update c->scc_cluster accordingly.
 *
 * First keep track of the cluster containing the SCC to which a node
 * belongs in the node itself.
 * Then extract the clusters into c->clusters, copying the current
 * band schedule from the SCCs that belong to the cluster.
 * Do this only once per cluster.
 *
 * Finally, topologically sort the clusters and update c->scc_cluster
 * to match the new scc numbering.  While the SCCs were originally
 * sorted already, some SCCs that depend on some other SCCs may
 * have been merged with SCCs that appear before these other SCCs.
 * A reordering may therefore be required.
 */
static isl_stat extract_clusters(isl_ctx *ctx, struct isl_sched_graph *graph,
	struct isl_clustering *c)
{}

/* Compute weights on the proximity edges of "graph" that can
 * be used by find_proximity to find the most appropriate
 * proximity edge to use to merge two clusters in "c".
 * The weights are also used by has_bounded_distances to determine
 * whether the merge should be allowed.
 * Store the maximum of the computed weights in graph->max_weight.
 *
 * The computed weight is a measure for the number of remaining schedule
 * dimensions that can still be completely aligned.
 * In particular, compute the number of equalities between
 * input dimensions and output dimensions in the proximity constraints.
 * The directions that are already handled by outer schedule bands
 * are projected out prior to determining this number.
 *
 * Edges that will never be considered by find_proximity are ignored.
 */
static isl_stat compute_weights(struct isl_sched_graph *graph,
	struct isl_clustering *c)
{}

/* Call isl_schedule_node_compute_finish_band on each of the clusters in "c" and
 * update "node" to arrange for them to be executed in an order
 * possibly involving set nodes that generalizes the topological order
 * determined by the scc fields of the nodes in "graph".
 *
 * Note that at this stage, there are graph->scc clusters and
 * their positions in c->cluster are determined by the values
 * of c->scc_cluster.
 *
 * Construct an isl_scc_graph and perform the decomposition
 * using this graph.
 */
static __isl_give isl_schedule_node *finish_bands_decompose(
	__isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
	struct isl_clustering *c)
{}

/* Call isl_schedule_node_compute_finish_band on each of the clusters in "c"
 * in their topological order.  This order is determined by the scc
 * fields of the nodes in "graph".
 * Combine the results in a sequence expressing the topological order.
 *
 * If there is only one cluster left, then there is no need to introduce
 * a sequence node.  Also, in this case, the cluster necessarily contains
 * the SCC at position 0 in the original graph and is therefore also
 * stored in the first cluster of "c".
 *
 * If there are more than two clusters left, then some subsets of the clusters
 * may still be independent of each other.  These could then still
 * be reordered with respect to each other.  Call finish_bands_decompose
 * to try and construct an ordering involving set and sequence nodes
 * that generalizes the topological order.
 * Note that at the outermost level there can be no independent components
 * because isl_schedule_node_compute_wcc_clustering is called
 * on a (weakly) connected component.
 */
static __isl_give isl_schedule_node *finish_bands_clustering(
	__isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
	struct isl_clustering *c)
{}

/* Compute a schedule for a connected dependence graph by first considering
 * each strongly connected component (SCC) in the graph separately and then
 * incrementally combining them into clusters.
 * Return the updated schedule node.
 *
 * Initially, each cluster consists of a single SCC, each with its
 * own band schedule.  The algorithm then tries to merge pairs
 * of clusters along a proximity edge until no more suitable
 * proximity edges can be found.  During this merging, the schedule
 * is maintained in the individual SCCs.
 * After the merging is completed, the full resulting clusters
 * are extracted and in finish_bands_clustering,
 * isl_schedule_node_compute_finish_band is called on each of them to integrate
 * the band into "node" and to continue the computation.
 *
 * compute_weights initializes the weights that are used by find_proximity.
 */
__isl_give isl_schedule_node *isl_schedule_node_compute_wcc_clustering(
	__isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
{}