scheduling: introduce band forest interface
authorSven Verdoolaege <skimo@kotnet.org>
Sun, 29 May 2011 15:21:23 +0000 (17:21 +0200)
committerSven Verdoolaege <skimo@kotnet.org>
Sun, 5 Jun 2011 08:16:24 +0000 (10:16 +0200)
The new interface exposes more properties of the constructed schedule.

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
Makefile.am
doc/user.pod
include/isl/band.h [new file with mode: 0644]
include/isl/list.h
include/isl/schedule.h
isl_band.c [new file with mode: 0644]
isl_band_private.h [new file with mode: 0644]
isl_list.c
isl_list_private.h
isl_schedule.c
isl_schedule_private.h

index 9bce9ed..eb1c817 100644 (file)
@@ -38,6 +38,8 @@ libisl_la_SOURCES = \
        isl_aff_private.h \
        isl_affine_hull.c \
        isl_arg.c \
+       isl_band.c \
+       isl_band_private.h \
        isl_basis_reduction.h \
        basis_reduction_tab.c \
        isl_bernstein.c \
@@ -187,6 +189,7 @@ nodist_pkginclude_HEADERS = \
 pkginclude_HEADERS = \
        include/isl/aff.h \
        include/isl/arg.h \
+       include/isl/band.h \
        include/isl/blk.h \
        include/isl/constraint.h \
        include/isl/ctx.h \
index 0d97f61..899fb58 100644 (file)
@@ -2919,7 +2919,64 @@ from an C<isl_schedule> using the following function.
        __isl_give isl_union_map *isl_schedule_get_map(
                __isl_keep isl_schedule *sched);
 
-This mapping can also be obtained in pieces using the following functions.
+A representation of the schedule as a forest of bands can be obtained
+using the following function.
+
+       __isl_give isl_band_list *isl_schedule_get_band_forest(
+               __isl_keep isl_schedule *schedule);
+
+The list can be manipulated as explained in L<"Lists">.
+The bands inside the list can be copied and freed using the following
+functions.
+
+       #include <isl/band.h>
+       __isl_give isl_band *isl_band_copy(
+               __isl_keep isl_band *band);
+       void *isl_band_free(__isl_take isl_band *band);
+
+Each band contains zero or more scheduling dimensions.
+These are referred to as the members of the band.
+The section of the schedule that corresponds to the band is
+referred to as the partial schedule of the band.
+For those nodes that participate in a band, the outer scheduling
+dimensions form the prefix schedule, while the inner scheduling
+dimensions form the suffix schedule.
+That is, if we take a cut of the band forest, then the union of
+the concatenations of the prefix, partial and suffix schedules of
+each band in the cut is equal to the entire schedule (modulo
+some possible padding at the end with zero scheduling dimensions).
+The properties of a band can be inspected using the following functions.
+
+       #include <isl/band.h>
+       isl_ctx *isl_band_get_ctx(__isl_keep isl_band *band);
+
+       int isl_band_has_children(__isl_keep isl_band *band);
+       __isl_give isl_band_list *isl_band_get_children(
+               __isl_keep isl_band *band);
+
+       __isl_give isl_union_map *isl_band_get_prefix_schedule(
+               __isl_keep isl_band *band);
+       __isl_give isl_union_map *isl_band_get_partial_schedule(
+               __isl_keep isl_band *band);
+       __isl_give isl_union_map *isl_band_get_suffix_schedule(
+               __isl_keep isl_band *band);
+
+       int isl_band_n_member(__isl_keep isl_band *band);
+       int isl_band_member_is_parallel(__isl_keep isl_band *band,
+               int pos);
+
+Note that a scheduling dimension is considered parallel if it
+does not carry any proximity dependences.
+
+A representation of the band can be printed using
+
+       #include <isl/band.h>
+       __isl_give isl_printer *isl_printer_print_band(
+               __isl_take isl_printer *p,
+               __isl_keep isl_band *band);
+
+Alternatively, the schedule mapping
+can also be obtained in pieces using the following functions.
 
        int isl_schedule_n_band(__isl_keep isl_schedule *sched);
        __isl_give isl_union_map *isl_schedule_get_band(
diff --git a/include/isl/band.h b/include/isl/band.h
new file mode 100644 (file)
index 0000000..b09ad4b
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef ISL_BAND_H
+#define ISL_BAND_H
+
+#include <isl/printer.h>
+#include <isl/list.h>
+#include <isl/union_map.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_band;
+typedef struct isl_band isl_band;
+
+__isl_give isl_band *isl_band_copy(__isl_keep isl_band *band);
+void *isl_band_free(__isl_take isl_band *band);
+
+isl_ctx *isl_band_get_ctx(__isl_keep isl_band *band);
+
+int isl_band_has_children(__isl_keep isl_band *band);
+__isl_give isl_band_list *isl_band_get_children(
+       __isl_keep isl_band *band);
+
+__isl_give isl_union_map *isl_band_get_prefix_schedule(
+       __isl_keep isl_band *band);
+__isl_give isl_union_map *isl_band_get_partial_schedule(
+       __isl_keep isl_band *band);
+__isl_give isl_union_map *isl_band_get_suffix_schedule(
+       __isl_keep isl_band *band);
+
+int isl_band_n_member(__isl_keep isl_band *band);
+int isl_band_member_is_parallel(__isl_keep isl_band *band, int pos);
+
+__isl_give isl_printer *isl_printer_print_band(__isl_take isl_printer *p,
+       __isl_keep isl_band *band);
+void isl_band_dump(__isl_keep isl_band *band);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
index 44fbd67..51387c3 100644 (file)
@@ -42,6 +42,7 @@ void isl_##EL##_list_dump(__isl_keep isl_##EL##_list *list);
 ISL_DECLARE_LIST(basic_set)
 ISL_DECLARE_LIST(set)
 ISL_DECLARE_LIST(aff)
+ISL_DECLARE_LIST(band)
 
 #if defined(__cplusplus)
 }
index 07e0965..6899cfd 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <isl/union_set.h>
 #include <isl/union_map.h>
+#include <isl/list.h>
 
 #if defined(__cplusplus)
 extern "C" {
@@ -18,6 +19,9 @@ __isl_give isl_schedule *isl_union_set_compute_schedule(
 void *isl_schedule_free(__isl_take isl_schedule *sched);
 __isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched);
 
+__isl_give isl_band_list *isl_schedule_get_band_forest(
+       __isl_keep isl_schedule *schedule);
+
 int isl_schedule_n_band(__isl_keep isl_schedule *sched);
 __isl_give isl_union_map *isl_schedule_get_band(__isl_keep isl_schedule *sched,
        unsigned band);
diff --git a/isl_band.c b/isl_band.c
new file mode 100644 (file)
index 0000000..4a0bf4e
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2011      INRIA Saclay
+ *
+ * Use of this software is governed by the GNU LGPLv2.1 license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_band_private.h>
+#include <isl_schedule_private.h>
+
+isl_ctx *isl_band_get_ctx(__isl_keep isl_band *band)
+{
+       return band ? isl_union_map_get_ctx(band->map) : NULL;
+}
+
+/* We not only increment the reference count of the band,
+ * but also that of the schedule that contains this band.
+ * This ensures that the schedule won't disappear while there
+ * is still a reference to the band outside of the schedule.
+ * There is no need to increment the reference count of the parent
+ * band as the parent band is part of the same schedule.
+ */
+__isl_give isl_band *isl_band_copy(__isl_keep isl_band *band)
+{
+       if (!band)
+               return NULL;
+
+       band->ref++;
+       band->schedule->ref++;
+       return band;
+}
+
+/* If this is not the last reference to the band (the one from within the
+ * schedule), then we also need to decrement the reference count of the
+ * containing schedule as it was incremented in isl_band_copy.
+ */
+void *isl_band_free(__isl_take isl_band *band)
+{
+       if (!band)
+               return NULL;
+
+       if (--band->ref > 0)
+               return isl_schedule_free(band->schedule);
+
+       isl_union_map_free(band->map);
+       isl_band_list_free(band->children);
+       free(band->parallel);
+       free(band);
+
+       return NULL;
+}
+
+int isl_band_has_children(__isl_keep isl_band *band)
+{
+       if (!band)
+               return -1;
+
+       return band->children != NULL;
+}
+
+__isl_give isl_band_list *isl_band_get_children(
+       __isl_keep isl_band *band)
+{
+       if (!band)
+               return NULL;
+       if (!band->children)
+               isl_die(isl_band_get_ctx(band), isl_error_invalid,
+                       "band has no children", return NULL);
+       return isl_band_list_copy(band->children);
+}
+
+int isl_band_n_member(__isl_keep isl_band *band)
+{
+       return band ? band->n : 0;
+}
+
+/* Is the given scheduling dimension parallel within the band and
+ * with respect to the proximity dependences.
+ */
+int isl_band_member_is_parallel(__isl_keep isl_band *band, int pos)
+{
+       if (!band)
+               return -1;
+
+       if (pos < 0 || pos >= band->n)
+               isl_die(isl_band_get_ctx(band), isl_error_invalid,
+                       "invalid member position", return -1);
+
+       return band->parallel[pos];
+}
+
+/* Return the schedule that leads up to this band.
+ */
+__isl_give isl_union_map *isl_band_get_prefix_schedule(
+       __isl_keep isl_band *band)
+{
+       isl_union_map *prefix;
+       isl_band *a;
+
+       if (!band)
+               return NULL;
+
+       prefix = isl_union_map_copy(band->map);
+       prefix = isl_union_map_from_domain(isl_union_map_domain(prefix));
+
+       for (a = band->parent; a; a = a->parent) {
+               isl_union_map *partial = isl_union_map_copy(a->map);
+               prefix = isl_union_map_flat_range_product(partial, prefix);
+       }
+
+       return prefix;
+}
+
+/* Return the schedule of the band in isolation.
+ */
+__isl_give isl_union_map *isl_band_get_partial_schedule(
+       __isl_keep isl_band *band)
+{
+       return band ? isl_union_map_copy(band->map) : NULL;
+}
+
+/* Return the schedule for the forest underneath the given band.
+ */
+__isl_give isl_union_map *isl_band_get_suffix_schedule(
+       __isl_keep isl_band *band)
+{
+       isl_union_map *suffix;
+
+       if (!band)
+               return NULL;
+
+       if (!isl_band_has_children(band)) {
+               suffix = isl_union_map_copy(band->map);
+               suffix = isl_union_map_from_domain(isl_union_map_domain(suffix));
+       } else {
+               int i, n;
+               isl_band_list *children;
+
+               suffix = isl_union_map_empty(isl_union_map_get_dim(band->map));
+               children = isl_band_get_children(band);
+               n = isl_band_list_n_band(children);
+               for (i = 0; i < n; ++i) {
+                       isl_band *child;
+                       isl_union_map *partial_i;
+                       isl_union_map *suffix_i;
+
+                       child = isl_band_list_get_band(children, i);
+                       partial_i = isl_band_get_partial_schedule(child);
+                       suffix_i = isl_band_get_suffix_schedule(child);
+                       suffix_i = isl_union_map_flat_range_product(partial_i,
+                                                                   suffix_i);
+                       suffix = isl_union_map_union(suffix, suffix_i);
+
+                       isl_band_free(child);
+               }
+               isl_band_list_free(children);
+       }
+
+       return suffix;
+}
+
+__isl_give isl_printer *isl_printer_print_band(__isl_take isl_printer *p,
+       __isl_keep isl_band *band)
+{
+       isl_union_map *prefix, *partial, *suffix;
+
+       prefix = isl_band_get_prefix_schedule(band);
+       partial = isl_band_get_partial_schedule(band);
+       suffix = isl_band_get_suffix_schedule(band);
+
+       p = isl_printer_print_str(p, "(");
+       p = isl_printer_print_union_map(p, prefix);
+       p = isl_printer_print_str(p, ",");
+       p = isl_printer_print_union_map(p, partial);
+       p = isl_printer_print_str(p, ",");
+       p = isl_printer_print_union_map(p, suffix);
+       p = isl_printer_print_str(p, ")");
+
+       isl_union_map_free(prefix);
+       isl_union_map_free(partial);
+       isl_union_map_free(suffix);
+
+       return p;
+}
+
+void isl_band_dump(__isl_keep isl_band *band)
+{
+       isl_printer *printer;
+
+       if (!band)
+               return;
+
+       printer = isl_printer_to_file(isl_band_get_ctx(band), stderr);
+       printer = isl_printer_print_band(printer, band);
+       printer = isl_printer_end_line(printer);
+
+       isl_printer_free(printer);
+}
diff --git a/isl_band_private.h b/isl_band_private.h
new file mode 100644 (file)
index 0000000..a4e7273
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef ISL_BAND_PRIVATE_H
+#define ISL_BAND_PRIVATE_H
+
+#include <isl/band.h>
+#include <isl/list.h>
+#include <isl/schedule.h>
+#include <isl/union_map.h>
+
+/* Information about a band within a schedule.
+ *
+ * n is the number of scheduling dimensions within the band.
+ * parallel is an array of length n, indicating whether a scheduling dimension
+ *     is parallel.
+ * map is the partial map corresponding to this band.
+ * schedule is the schedule that contains this band.
+ * parent is the parent of this band (or NULL if the band is a root).
+ * children are the children of this band (or NULL if the band is a leaf).
+ *
+ * To avoid circular dependences in the reference counting,
+ * the schedule and parent pointers are not reference counted.
+ * isl_band_copy increments the reference count of schedule to ensure
+ * that outside references to the band keep the schedule alive.
+ */
+struct isl_band {
+       int ref;
+
+       int n;
+       int *parallel;
+
+       isl_union_map *map;
+       isl_schedule *schedule;
+       isl_band *parent;
+       isl_band_list *children;
+};
+
+#endif
index fefed4c..ec1dd29 100644 (file)
@@ -10,6 +10,7 @@
 #include <isl_list_private.h>
 #include <isl/set.h>
 #include <isl/aff.h>
+#include <isl/band.h>
 
 #undef BASE
 #define BASE basic_set
@@ -25,3 +26,8 @@
 #define BASE aff
 
 #include <isl_list_templ.c>
+
+#undef BASE
+#define BASE band
+
+#include <isl_list_templ.c>
index 517d262..15c0547 100644 (file)
@@ -18,4 +18,9 @@
 
 #include <isl_list_templ.h>
 
+#undef EL
+#define EL isl_band
+
+#include <isl_list_templ.h>
+
 #endif
index dd0cd82..0b1ff56 100644 (file)
@@ -22,6 +22,7 @@
 #include <isl_hmap_map_basic_set.h>
 #include <isl_qsort.h>
 #include <isl_schedule_private.h>
+#include <isl_band_private.h>
 
 /*
  * The scheduling algorithm implemented in this file was inspired by
  *
  * scc is the index of SCC (or WCC) this node belongs to
  *
- * band contains the band index for each of the rows of the schedule
+ * band contains the band index for each of the rows of the schedule.
+ * band_id is used to differentiate between separate bands at the same
+ * level within the same parent band, i.e., bands that are separated
+ * by the parent band or bands that are independent of each other.
  * parallel contains a boolean for each of the rows of the schedule,
  * indicating whether the corresponding scheduling dimension is parallel
  * within its band and with respect to the proximity edges.
@@ -73,6 +77,7 @@ struct isl_sched_node {
        int      scc;
 
        int     *band;
+       int     *band_id;
        int     *parallel;
 
        /* scc detection */
@@ -348,6 +353,7 @@ static void graph_free(isl_ctx *ctx, struct isl_sched_graph *graph)
                isl_mat_free(graph->node[i].cmap);
                if (graph->root) {
                        free(graph->node[i].band);
+                       free(graph->node[i].band_id);
                        free(graph->node[i].parallel);
                }
        }
@@ -372,7 +378,7 @@ static int extract_node(__isl_take isl_set *set, void *user)
        isl_dim *dim;
        isl_mat *sched;
        struct isl_sched_graph *graph = user;
-       int *band, *parallel;
+       int *band, *band_id, *parallel;
 
        ctx = isl_set_get_ctx(set);
        dim = isl_set_get_dim(set);
@@ -389,11 +395,13 @@ static int extract_node(__isl_take isl_set *set, void *user)
        graph->node[graph->n].sched_map = NULL;
        band = isl_alloc_array(ctx, int, graph->n_edge + nvar);
        graph->node[graph->n].band = band;
+       band_id = isl_calloc_array(ctx, int, graph->n_edge + nvar);
+       graph->node[graph->n].band_id = band_id;
        parallel = isl_calloc_array(ctx, int, graph->n_edge + nvar);
        graph->node[graph->n].parallel = parallel;
        graph->n++;
 
-       if (!sched || !band || !parallel)
+       if (!sched || !band || !band_id || !parallel)
                return -1;
 
        return 0;
@@ -1505,20 +1513,23 @@ static __isl_give isl_schedule *extract_schedule(struct isl_sched_graph *graph,
        if (!sched)
                goto error;
 
+       sched->ref = 1;
        sched->n = graph->n;
        sched->n_band = graph->n_band;
        sched->n_total_row = graph->n_total_row;
 
        for (i = 0; i < sched->n; ++i) {
                int r, b;
-               int *band_end, *parallel;
+               int *band_end, *band_id, *parallel;
 
                band_end = isl_alloc_array(ctx, int, graph->n_band);
+               band_id = isl_alloc_array(ctx, int, graph->n_band);
                parallel = isl_alloc_array(ctx, int, graph->n_total_row);
                sched->node[i].sched = node_extract_schedule(&graph->node[i]);
                sched->node[i].band_end = band_end;
+               sched->node[i].band_id = band_id;
                sched->node[i].parallel = parallel;
-               if (!band_end || !parallel)
+               if (!band_end || !band_id || !parallel)
                        goto error;
 
                for (r = 0; r < graph->n_total_row; ++r)
@@ -1533,6 +1544,8 @@ static __isl_give isl_schedule *extract_schedule(struct isl_sched_graph *graph,
                if (r == graph->n_total_row)
                        band_end[b++] = r;
                sched->node[i].n_band = b;
+               for (--b; b >= 0; --b)
+                       band_id[b] = graph->node[i].band_id[b];
        }
 
        sched->dim = dim;
@@ -1563,6 +1576,7 @@ static int copy_nodes(struct isl_sched_graph *dst, struct isl_sched_graph *src,
                dst->node[dst->n].sched_map =
                        isl_map_copy(src->node[i].sched_map);
                dst->node[dst->n].band = src->node[i].band;
+               dst->node[dst->n].band_id = src->node[i].band_id;
                dst->node[dst->n].parallel = src->node[i].parallel;
                dst->n++;
        }
@@ -1776,6 +1790,11 @@ static int pad_schedule(struct isl_sched_graph *graph)
  * It would be possible to reuse them as the first rows in the next
  * band, but recomputing them may result in better rows as we are looking
  * at a smaller part of the dependence graph.
+ *
+ * The band_id of the second group is set to n, where n is the number
+ * of nodes in the first group.  This ensures that the band_ids over
+ * the two groups remain disjoint, even if either or both of the two
+ * groups contain independent components.
  */
 static int compute_split_schedule(isl_ctx *ctx, struct isl_sched_graph *graph)
 {
@@ -1824,6 +1843,12 @@ static int compute_split_schedule(isl_ctx *ctx, struct isl_sched_graph *graph)
        graph->n_total_row++;
        next_band(graph);
 
+       for (i = 0; i < graph->n; ++i) {
+               struct isl_sched_node *node = &graph->node[i];
+               if (node->scc > graph->src_scc)
+                       node->band_id[graph->n_band] = n;
+       }
+
        orig_total_row = graph->n_total_row;
        orig_band = graph->n_band;
        if (compute_sub_schedule(ctx, graph, n, e1,
@@ -2178,6 +2203,10 @@ static int compute_schedule_wcc(isl_ctx *ctx, struct isl_sched_graph *graph)
 
 /* Compute a schedule for each component (identified by node->scc)
  * of the dependence graph separately and then combine the results.
+ *
+ * The band_id is adjusted such that each component has a separate id.
+ * Note that the band_id may have already been set to a value different
+ * from zero by compute_split_schedule.
  */
 static int compute_component_schedule(isl_ctx *ctx,
        struct isl_sched_graph *graph)
@@ -2191,6 +2220,8 @@ static int compute_component_schedule(isl_ctx *ctx,
        orig_total_row = graph->n_total_row;
        n_band = 0;
        orig_band = graph->n_band;
+       for (i = 0; i < graph->n; ++i)
+               graph->node[i].band_id[graph->n_band] += graph->node[i].scc;
        for (wcc = 0; wcc < graph->scc; ++wcc) {
                n = 0;
                for (i = 0; i < graph->n; ++i)
@@ -2304,16 +2335,27 @@ void *isl_schedule_free(__isl_take isl_schedule *sched)
        int i;
        if (!sched)
                return NULL;
+
+       if (--sched->ref > 0)
+               return NULL;
+
        for (i = 0; i < sched->n; ++i) {
                isl_map_free(sched->node[i].sched);
                free(sched->node[i].band_end);
+               free(sched->node[i].band_id);
                free(sched->node[i].parallel);
        }
        isl_dim_free(sched->dim);
+       isl_band_list_free(sched->band_forest);
        free(sched);
        return NULL;
 }
 
+isl_ctx *isl_schedule_get_ctx(__isl_keep isl_schedule *schedule)
+{
+       return schedule ? isl_dim_get_ctx(schedule->dim) : NULL;
+}
+
 __isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched)
 {
        int i;
@@ -2370,3 +2412,212 @@ __isl_give isl_union_map *isl_schedule_get_band(__isl_keep isl_schedule *sched,
 
        return umap;
 }
+
+static __isl_give isl_band_list *construct_band_list(
+       __isl_keep isl_schedule *schedule, __isl_keep isl_band *parent,
+       int band_nr, int *parent_active, int n_active);
+
+/* Construct an isl_band structure for the band in the given schedule
+ * with sequence number band_nr for the n_active nodes marked by active.
+ * If the nodes don't have a band with the given sequence number,
+ * then a band without members is created.
+ *
+ * Because of the way the schedule is constructed, we know that
+ * the position of the band inside the schedule of a node is the same
+ * for all active nodes.
+ */
+static __isl_give isl_band *construct_band(__isl_keep isl_schedule *schedule,
+       __isl_keep isl_band *parent,
+       int band_nr, int *active, int n_active)
+{
+       int i, j;
+       isl_ctx *ctx = isl_schedule_get_ctx(schedule);
+       isl_band *band;
+       unsigned start, end;
+
+       band = isl_calloc_type(ctx, isl_band);
+       if (!band)
+               return NULL;
+
+       band->ref = 1;
+       band->schedule = schedule;
+       band->parent = parent;
+
+       for (i = 0; i < schedule->n; ++i)
+               if (active[i] && schedule->node[i].n_band > band_nr + 1)
+                       break;
+
+       if (i < schedule->n) {
+               band->children = construct_band_list(schedule, band,
+                                               band_nr + 1, active, n_active);
+               if (!band->children)
+                       goto error;
+       }
+
+       for (i = 0; i < schedule->n; ++i)
+               if (active[i])
+                       break;
+
+       if (i >= schedule->n)
+               isl_die(ctx, isl_error_internal,
+                       "band without active statements", goto error);
+
+       start = band_nr ? schedule->node[i].band_end[band_nr - 1] : 0;
+       end = band_nr < schedule->node[i].n_band ?
+               schedule->node[i].band_end[band_nr] : start;
+       band->n = end - start;
+
+       band->parallel = isl_alloc_array(ctx, int, band->n);
+       if (!band->parallel)
+               goto error;
+
+       for (j = 0; j < band->n; ++j)
+               band->parallel[j] = schedule->node[i].parallel[start + j];
+
+       band->map = isl_union_map_empty(isl_dim_copy(schedule->dim));
+       for (i = 0; i < schedule->n; ++i) {
+               isl_map *map;
+               unsigned n_out;
+
+               if (!active[i])
+                       continue;
+
+               map = isl_map_copy(schedule->node[i].sched);
+               n_out = isl_map_dim(map, isl_dim_out);
+               map = isl_map_project_out(map, isl_dim_out, end, n_out - end);
+               map = isl_map_project_out(map, isl_dim_out, 0, start);
+               band->map = isl_union_map_union(band->map,
+                                               isl_union_map_from_map(map));
+       }
+       if (!band->map)
+               goto error;
+
+       return band;
+error:
+       isl_band_free(band);
+       return NULL;
+}
+
+/* Construct a list of bands that start at the same position (with
+ * sequence number band_nr) in the schedules of the nodes that
+ * were active in the parent band.
+ *
+ * A separate isl_band structure is created for each band_id
+ * and for each node that does not have a band with sequence
+ * number band_nr.  In the latter case, a band without members
+ * is created.
+ * This ensures that if a band has any children, then each node
+ * that was active in the band is active in exactly one of the children.
+ */
+static __isl_give isl_band_list *construct_band_list(
+       __isl_keep isl_schedule *schedule, __isl_keep isl_band *parent,
+       int band_nr, int *parent_active, int n_active)
+{
+       int i, j;
+       isl_ctx *ctx = isl_schedule_get_ctx(schedule);
+       int *active;
+       int n_band;
+       isl_band_list *list;
+
+       n_band = 0;
+       for (i = 0; i < n_active; ++i) {
+               for (j = 0; j < schedule->n; ++j) {
+                       if (!parent_active[j])
+                               continue;
+                       if (schedule->node[j].n_band <= band_nr)
+                               continue;
+                       if (schedule->node[j].band_id[band_nr] == i) {
+                               n_band++;
+                               break;
+                       }
+               }
+       }
+       for (j = 0; j < schedule->n; ++j)
+               if (schedule->node[j].n_band <= band_nr)
+                       n_band++;
+
+       if (n_band == 1) {
+               isl_band *band;
+               list = isl_band_list_alloc(ctx, n_band);
+               band = construct_band(schedule, parent, band_nr,
+                                       parent_active, n_active);
+               return isl_band_list_add(list, band);
+       }
+
+       active = isl_alloc_array(ctx, int, schedule->n);
+       if (!active)
+               return NULL;
+
+       list = isl_band_list_alloc(ctx, n_band);
+
+       for (i = 0; i < n_active; ++i) {
+               int n = 0;
+               isl_band *band;
+
+               for (j = 0; j < schedule->n; ++j) {
+                       active[j] = parent_active[j] &&
+                                       schedule->node[j].n_band > band_nr &&
+                                       schedule->node[j].band_id[band_nr] == i;
+                       if (active[j])
+                               n++;
+               }
+               if (n == 0)
+                       continue;
+
+               band = construct_band(schedule, parent, band_nr, active, n);
+
+               list = isl_band_list_add(list, band);
+       }
+       for (i = 0; i < schedule->n; ++i) {
+               isl_band *band;
+               if (!parent_active[i])
+                       continue;
+               if (schedule->node[i].n_band > band_nr)
+                       continue;
+               for (j = 0; j < schedule->n; ++j)
+                       active[j] = j == i;
+               band = construct_band(schedule, parent, band_nr, active, 1);
+               list = isl_band_list_add(list, band);
+       }
+
+       free(active);
+
+       return list;
+}
+
+/* Construct a band forest representation of the schedule and
+ * return the list of roots.
+ */
+static __isl_give isl_band_list *construct_forest(
+       __isl_keep isl_schedule *schedule)
+{
+       int i;
+       isl_ctx *ctx = isl_schedule_get_ctx(schedule);
+       isl_band_list *forest;
+       int *active;
+
+       active = isl_alloc_array(ctx, int, schedule->n);
+       if (!active)
+               return NULL;
+
+       for (i = 0; i < schedule->n; ++i)
+               active[i] = 1;
+
+       forest = construct_band_list(schedule, NULL, 0, active, schedule->n);
+
+       free(active);
+
+       return forest;
+}
+
+/* Return the roots of a band forest representation of the schedule.
+ */
+__isl_give isl_band_list *isl_schedule_get_band_forest(
+       __isl_keep isl_schedule *schedule)
+{
+       if (!schedule)
+               return NULL;
+       if (!schedule->band_forest)
+               schedule->band_forest = construct_forest(schedule);
+       return isl_band_list_copy(schedule->band_forest);
+}
index 19801e6..e45dece 100644 (file)
@@ -24,6 +24,8 @@ struct isl_schedule_node {
  * n_band is the maximal number of bands.
  * n_total_row is the number of coordinates of the schedule.
  * dim contains a description of the parameters.
+ * band_forest points to a band forest representation of the schedule
+ * and may be NULL if the forest hasn't been created yet.
  */
 struct isl_schedule {
        int ref;
@@ -33,6 +35,8 @@ struct isl_schedule {
        int n_total_row;
        isl_dim *dim;
 
+       isl_band_list *band_forest;
+
        struct isl_schedule_node node[1];
 };