/* * Copyright 2021 Sven Verdoolaege * * Use of this software is governed by the MIT license * * Written by Sven Verdoolaege */ #include <stdio.h> #include <isl/ctx.h> #include <isl/schedule_node.h> #include <isl/union_set.h> #include "isl_hash_private.h" #include "isl_scheduler_scc.h" #include "isl_sort.h" /* Internal data structure for ordering the SCCs of "graph", * where each SCC i consists of the single cluster determined * by c->scc_cluster[i]. The nodes in this cluster all have * their "scc" field set to i. * * "graph" is the original schedule graph. * "c" contains the clustering information. * * "n" is the number of SCCs in the isl_scc_graph, which may be * a subset of those in "graph". * "graph_scc" maps the local index of an SCC in this isl_scc_graph * to the corresponding index in "graph", i.e, the index of c->scc_cluster. * The entries of "graph_scc" are kept in topological order. * * "component" contains the component to which an SCC belongs, * where the component is represented by the index of the first SCC * in the component. * The index of this first SCC is always smaller than or equal * to the index of the SCC itself. * This field is initialized by isl_scc_graph_init_component and * used by detect_components. * During construction, "component" may also contain the index * of some other SCC in the component, but then it is necessarily * smaller than the index of the current SCC and the first SCC * can be reached by recursively looking up "component". * "size" contains the number of elements in the components * indexed by a component sequence number. * * "pos" is used locally inside isl_scc_graph_sort_components * to store the position of the next SCC within a component. * It is also used inside isl_scc_graph_sub to map * the position in the original graph to the position in the subgraph. * * "sorted" contains the (possibly) reordered local indices, * sorted per component. Within each component, the original * topological order is preserved. * * "edge_table" contains "n" edge tables, one for each SCC * in this isl_scc_graph. Each table contains the local indices * of the SCCs that depend on this SCC. These local indices * are encoded as pointers to the corresponding entry in "graph_scc". * The value stored at that location is the global SCC index. * "reverse_edge_table" contains the inverse edges. */ struct isl_scc_graph { … }; /* The source SCC of a collection of edges. * * "scc_graph" is the SCC graph containing the edges. * "src" is the local index of the source SCC. */ struct isl_edge_src { … }; /* isl_hash_table_foreach callback for printing an edge * between "src" and the node identified by "entry". * The edge is printed in terms of the global SCC indices. */ static isl_stat print_edge(void **entry, void *user) { … } /* Print some debugging information about "scc_graph". * * In particular, print the nodes and the edges (both forward and backward). */ void isl_scc_graph_dump(struct isl_scc_graph *scc_graph) { … } /* Free all memory allocated for "scc_graph" and return NULL. */ struct isl_scc_graph *isl_scc_graph_free(struct isl_scc_graph *scc_graph) { … } /* Return an encoding of the local SCC index "pos" in "scc_graph" * as a pointer. * In particular, return a pointer to the corresponding entry * in scc_graph->graph_scc. */ static void *isl_scc_graph_encode_local_index(struct isl_scc_graph *scc_graph, int pos) { … } /* Return the local SCC index in "scc_graph" corresponding * to the "data" encoding in the edge table. */ static int isl_scc_graph_local_index(struct isl_scc_graph *scc_graph, int *data) { … } /* isl_hash_table_find callback to check whether the given entry * refers to an SCC encoded as "val". */ static isl_bool is_scc_node(const void *entry, const void *val) { … } /* Return the edge from local SCC index "src" to local SCC index "dst" * in "edge_table" of "scc_graph", creating one if "reserve" is set. * If "reserve" is not set, then return isl_hash_table_entry_none * if there is no such edge. * * The destination of the edge is encoded as a pointer * to the corresponding entry in scc_graph->graph_scc. */ struct isl_hash_table_entry *isl_scc_graph_find_edge( struct isl_scc_graph *scc_graph, struct isl_hash_table **edge_table, int src, int dst, int reserve) { … } /* Remove the edge between the SCCs with local indices "src" and * "dst" in "scc_graph", if it exits. * Return isl_bool_true if this is the case. * * The edge is only removed from scc_graph->edge_table. * scc_graph->reverse_edge_table is assumed to be empty * when this function is called. */ static isl_bool isl_scc_graph_remove_edge(struct isl_scc_graph *scc_graph, int src, int dst) { … } /* Internal data structure used by next_nodes. * * "scc_graph" is the SCC graph. * "next" collects the next nodes. * "n" is the number of next nodes already collected. */ struct isl_extract_dst_data { … }; /* Given an entry in the edge table, add the corresponding * target local SCC index to data->next. */ static isl_stat extract_dst(void **entry, void *user) { … } /* isl_sort callback for sorting integers in increasing order. */ static int cmp_int(const void *a, const void *b, void *data) { … } /* Return the local indices of the SCCs in "scc_graph" * for which there is an edge from the SCC with local index "i". * The indices are returned in increasing order, * i.e., in the original topological order. */ static int *next_nodes(struct isl_scc_graph *scc_graph, int i) { … } /* Internal data structure for foreach_reachable. * * "scc_graph" is the SCC graph being visited. * "fn" is the function that needs to be called on each reachable node. * "user" is the user argument to "fn". */ struct isl_foreach_reachable_data { … }; static isl_stat foreach_reachable(struct isl_foreach_reachable_data *data, int pos); /* isl_hash_table_foreach callback for calling data->fn on each SCC * reachable from the SCC encoded in "entry", * continuing from an SCC as long as data->fn returns isl_bool_true. */ static isl_stat recurse_foreach_reachable(void **entry, void *user) { … } /* Call data->fn on each SCC reachable from the SCC with local index "pos", * continuing from an SCC as long as data->fn returns isl_bool_true. * * Handle chains directly and recurse when an SCC has more than one * outgoing edge. */ static isl_stat foreach_reachable(struct isl_foreach_reachable_data *data, int pos) { … } /* If there is an edge from data->src to "pos", then remove it. * Return isl_bool_true if descendants of "pos" still need to be considered. * * Descendants only need to be considered if no edge is removed. */ static isl_bool elim_or_next(int pos, void *user) { … } /* Remove transitive edges from "scc_graph". * * Consider the SCC nodes "i" in reverse topological order. * If there is more than one edge emanating from a node, * then eliminate the edges to those nodes that can also be reached * through an edge to a node with a smaller index. * In particular, consider all but the last next nodes "next[j]" * in reverse topological order. If any node "k" can be reached * from such a node for which there is also an edge from "i" * then this edge can be removed because this node can also * be reached from "i" through the edge to "next[j]". * If such an edge is removed, then any further descendant of "k" * does not need to be considered since these were already considered * for a previous "next[j]" equal to "k", or "k" is the last next node, * in which case there is no further node with an edge from "i". */ static struct isl_scc_graph *isl_scc_graph_reduce( struct isl_scc_graph *scc_graph) { … } /* Add an edge to "edge_table" between the SCCs with local indices "src" and * "dst" in "scc_graph". * * If the edge already appeared in the table, then it is simply overwritten * with the same information. */ static isl_stat isl_scc_graph_add_edge(struct isl_scc_graph *scc_graph, struct isl_hash_table **edge_table, int src, int dst) { … } /* Add an edge from "dst" to data->src * to data->scc_graph->reverse_edge_table. */ static isl_stat add_reverse(void **entry, void *user) { … } /* Add an (inverse) edge to scc_graph->reverse_edge_table * for each edge in scc_graph->edge_table. */ static struct isl_scc_graph *isl_scc_graph_add_reverse_edges( struct isl_scc_graph *scc_graph) { … } /* Given an edge in the schedule graph, add an edge between * the corresponding SCCs in "scc_graph", if they are distinct. * * This function is used to create edges in the original isl_scc_graph. * where the local SCC indices are equal to the corresponding global * indices. */ static isl_stat add_scc_edge(void **entry, void *user) { … } /* Allocate an isl_scc_graph for ordering "n" SCCs of "graph" * with clustering information in "c". * * The caller still needs to fill in the edges. */ static struct isl_scc_graph *isl_scc_graph_alloc(isl_ctx *ctx, int n, struct isl_sched_graph *graph, struct isl_clustering *c) { … } /* Construct an isl_scc_graph for ordering the SCCs of "graph", * where each SCC i consists of the single cluster determined * by c->scc_cluster[i]. The nodes in this cluster all have * their "scc" field set to i. * * The initial isl_scc_graph has as many SCCs as "graph" and * their local indices are the same as their indices in "graph". * * Add edges between different SCCs for each (conditional) validity edge * between nodes in those SCCs, remove transitive edges and * construct the inverse edges from the remaining forward edges. */ struct isl_scc_graph *isl_scc_graph_from_sched_graph(isl_ctx *ctx, struct isl_sched_graph *graph, struct isl_clustering *c) { … } /* Internal data structure for copy_edge. * * "scc_graph" is the original graph. * "sub" is the subgraph to which edges are being copied. * "src" is the local index in "scc_graph" of the source of the edges * currently being copied. */ struct isl_copy_edge_data { … }; /* isl_hash_table_foreach callback for copying the edge * from data->src to the node identified by "entry" * to data->sub, provided the two nodes belong to the same component. * Note that by construction, there are no edges between different components * in the region handled by detect_components, but there may * be edges to nodes outside this region. * The components therefore need to be initialized for all nodes * in isl_scc_graph_init_component. */ static isl_stat copy_edge(void **entry, void *user) { … } /* Construct a subgraph of "scc_graph" for the components * consisting of the "n" SCCs with local indices in "pos". * These SCCs have the same value in scc_graph->component and * this value is different from that of any other SCC. * * The forward edges with source and destination in the component * are copied from "scc_graph". * The local index in the subgraph corresponding to a local index * in "scc_graph" is stored in scc_graph->pos for use by copy_edge(). * The inverse edges are constructed directly from the forward edges. */ static struct isl_scc_graph *isl_scc_graph_sub(struct isl_scc_graph *scc_graph, int *pos, int n) { … } /* Return a union of universe domains corresponding to the nodes * in the SCC with local index "pos". */ static __isl_give isl_union_set *isl_scc_graph_extract_local_scc( struct isl_scc_graph *scc_graph, int pos) { … } /* Construct a filter corresponding to a sequence of "n" local SCC indices * determined by successive calls to "el", * add this filter to "list" and * return the result. */ static __isl_give isl_union_set_list *add_scc_seq( struct isl_scc_graph *scc_graph, int (*el)(int i, void *user), void *user, int n, __isl_take isl_union_set_list *list) { … } /* add_scc_seq callback that, on successive calls, returns a sequence * of local SCC indices starting at "first". */ static int offset(int i, void *user) { … } /* Construct a filter corresponding to a sequence of "n" local SCC indices * starting at "first", add this filter to "list" and return the result. */ static __isl_give isl_union_set_list *isl_scc_graph_add_scc_seq( struct isl_scc_graph *scc_graph, int first, int n, __isl_take isl_union_set_list *list) { … } /* add_scc_seq callback that, on successive calls, returns the sequence * of local SCC indices in "seq". */ static int at(int i, void *user) { … } /* Construct a filter corresponding to the sequence of "n" local SCC indices * stored in "seq", add this filter to "list" and return the result. */ static __isl_give isl_union_set_list *isl_scc_graph_add_scc_indirect_seq( struct isl_scc_graph *scc_graph, int *seq, int n, __isl_take isl_union_set_list *list) { … } /* Extract out a list of filters for a sequence node that splits * the graph along the SCC with local index "pos". * * The list contains (at most) three elements, * the SCCs before "pos" (in the topological order), * "pos" itself, and * the SCCs after "pos". */ static __isl_give isl_union_set_list *extract_split_scc( struct isl_scc_graph *scc_graph, int pos) { … } /* Call isl_schedule_node_compute_finish_band on the cluster * corresponding to the SCC with local index "pos". * * First obtain the corresponding SCC index in scc_graph->graph and * then obtain the corresponding cluster. */ static __isl_give isl_schedule_node *isl_scc_graph_finish_band( struct isl_scc_graph *scc_graph, __isl_take isl_schedule_node *node, int pos) { … } /* Given that the SCCs in "scc_graph" form a chain, * call isl_schedule_node_compute_finish_band on each of the clusters * in scc_graph->c and update "node" to arrange for them to be executed * in topological order. */ static __isl_give isl_schedule_node *isl_scc_graph_chain( struct isl_scc_graph *scc_graph, __isl_take isl_schedule_node *node) { … } /* Recursively call isl_scc_graph_decompose on a subgraph * consisting of the "n" SCCs with local indices in "pos". * * If this component contains only a single SCC, * then there is no need for a further recursion and * isl_schedule_node_compute_finish_band can be called directly. */ static __isl_give isl_schedule_node *recurse(struct isl_scc_graph *scc_graph, int *pos, int n, __isl_take isl_schedule_node *node) { … } /* Initialize the component field of "scc_graph". * Initially, each SCC belongs to its own single-element component. * * Note that the SCC on which isl_scc_graph_decompose performs a split * also needs to be assigned a component because the components * are also used in copy_edge to extract a subgraph. */ static void isl_scc_graph_init_component(struct isl_scc_graph *scc_graph) { … } /* Set the component of "a" to be the same as that of "b" and * return the original component of "a". */ static int assign(int *component, int a, int b) { … } /* Merge the components containing the SCCs with indices "a" and "b". * * If "a" and "b" already belong to the same component, then nothing * needs to be done. * Otherwise, make sure both point to the same component. * In particular, use the SCC in the component entries with the smallest index. * If the other SCC was the first of its component then the entire * component now (eventually) points to the other component. * Otherwise, the earlier parts of the component still need * to be merged with the other component. * * At each stage, either a or b is replaced by either a or b itself, * in which case the merging terminates because a and b already * point to the same component, or an SCC index with a smaller value. * This ensures the merging terminates at some point. */ static void isl_scc_graph_merge_src_dst(struct isl_scc_graph *scc_graph, int a, int b) { … } /* Internal data structure for isl_scc_graph_merge_components. * * "scc_graph" is the SCC graph containing the edges. * "src" is the local index of the source SCC. * "end" is the local index beyond the sequence being considered. */ struct isl_merge_src_dst_data { … }; /* isl_hash_table_foreach callback for merging the components * of data->src and the node represented by "entry", provided * it is within the sequence being considered. */ static isl_stat merge_src_dst(void **entry, void *user) { … } /* Merge components of the "n" SCCs starting at "first" that are connected * by an edge. */ static isl_stat isl_scc_graph_merge_components(struct isl_scc_graph *scc_graph, int first, int n) { … } /* Sort the "n" local SCC indices starting at "first" according * to component, store them in scc_graph->sorted and * return the number of components. * The sizes of the components are stored in scc_graph->size. * Only positions starting at "first" are used within * scc_graph->sorted and scc_graph->size. * * The representation of the components is first normalized. * The normalization ensures that each SCC in a component * points to the first SCC in the component, whereas * before this function is called, some SCCs may only point * to some other SCC in the component with a smaller index. * * Internally, the sizes of the components are first stored * at indices corresponding to the first SCC in the component. * They are subsequently moved into consecutive positions * while reordering the local indices. * This reordering is performed by first determining the position * of the first SCC in each component and * then putting the "n" local indices in the right position * according to the component, preserving the topological order * within each component. */ static int isl_scc_graph_sort_components(struct isl_scc_graph *scc_graph, int first, int n) { … } /* Extract out a list of filters for a set node that splits up * the graph into "n_component" components. * "first" is the initial position in "scc_graph" where information * about the components is stored. * In particular, the first "n_component" entries of scc_graph->size * at this position contain the number of SCCs in each component. * The entries of scc_graph->sorted starting at "first" * contain the local indices of the SCC in those components. */ static __isl_give isl_union_set_list *extract_components( struct isl_scc_graph *scc_graph, int first, int n_component) { … } /* Detect components in the subgraph consisting of the "n" SCCs * with local index starting at "first" and further decompose them, * calling isl_schedule_node_compute_finish_band on each * of the corresponding clusters. * * If there is only one SCC, then isl_schedule_node_compute_finish_band * can be called directly. * Otherwise, determine the components and rearrange the local indices * according to component, but preserving the topological order within * each component, in scc_graph->sorted. The sizes of the components * are stored in scc_graph->size. * If there is only one component, it can be further decomposed * directly by a call to recurse(). * Otherwise, introduce a set node separating the components and * call recurse() on each component separately. */ static __isl_give isl_schedule_node *detect_components( struct isl_scc_graph *scc_graph, int first, int n, __isl_take isl_schedule_node *node) { … } /* Given a sequence node "node", where the filter at position "child" * represents the "n" SCCs with local index starting at "first", * detect components in this subgraph and further decompose them, * calling isl_schedule_node_compute_finish_band on each * of the corresponding clusters. */ static __isl_give isl_schedule_node *detect_components_at( struct isl_scc_graph *scc_graph, int first, int n, __isl_take isl_schedule_node *node, int child) { … } /* Return the local index of an SCC on which to split "scc_graph". * Return scc_graph->n if no suitable split SCC can be found. * * In particular, look for an SCC that is involved in the largest number * of edges. Splitting the graph on such an SCC has the highest chance * of exposing independent SCCs in the remaining part(s). * There is no point in splitting a chain of nodes, * so return scc_graph->n if the entire graph forms a chain. */ static int best_split(struct isl_scc_graph *scc_graph) { … } /* Call isl_schedule_node_compute_finish_band on each of the clusters * in scc_graph->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 scc_graph->graph. * * First try and find a suitable SCC on which to split the graph. * If no such SCC can be found then the graph forms a chain and * it is handled as such. * Otherwise, break up the graph into (at most) three parts, * the SCCs before the selected SCC (in the topological order), * the selected SCC itself, and * the SCCs after the selected SCC. * The first and last part (if they exist) are decomposed recursively and * the three parts are combined in a sequence. * * Since the outermost node of the recursive pieces may also be a sequence, * these potential sequence nodes are spliced into the top-level sequence node. */ __isl_give isl_schedule_node *isl_scc_graph_decompose( struct isl_scc_graph *scc_graph, __isl_take isl_schedule_node *node) { … }