isl_schedule_constraints_compute_schedule: allow partial carrying schedule rows
[isl.git] / isl_schedule.c
blob1ad98d5df4d1276e962b5cec2c3d6a3f3e5f63ec
1 /*
2 * Copyright 2011 INRIA Saclay
3 * Copyright 2012-2014 Ecole Normale Superieure
5 * Use of this software is governed by the MIT license
7 * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
8 * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
9 * 91893 Orsay, France
10 * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
13 #include <isl/ctx.h>
14 #include <isl_aff_private.h>
15 #include <isl/map.h>
16 #include <isl/set.h>
17 #include <isl_sort.h>
18 #include <isl_schedule_private.h>
19 #include <isl_band_private.h>
21 __isl_null isl_schedule *isl_schedule_free(__isl_take isl_schedule *sched)
23 int i;
24 if (!sched)
25 return NULL;
27 if (--sched->ref > 0)
28 return NULL;
30 for (i = 0; i < sched->n; ++i) {
31 isl_multi_aff_free(sched->node[i].sched);
32 free(sched->node[i].band_end);
33 free(sched->node[i].band_id);
34 free(sched->node[i].coincident);
36 isl_space_free(sched->dim);
37 isl_band_list_free(sched->band_forest);
38 free(sched);
39 return NULL;
42 isl_ctx *isl_schedule_get_ctx(__isl_keep isl_schedule *schedule)
44 return schedule ? isl_space_get_ctx(schedule->dim) : NULL;
47 /* Set max_out to the maximal number of output dimensions over
48 * all maps.
50 static int update_max_out(__isl_take isl_map *map, void *user)
52 int *max_out = user;
53 int n_out = isl_map_dim(map, isl_dim_out);
55 if (n_out > *max_out)
56 *max_out = n_out;
58 isl_map_free(map);
59 return 0;
62 /* Internal data structure for map_pad_range.
64 * "max_out" is the maximal schedule dimension.
65 * "res" collects the results.
67 struct isl_pad_schedule_map_data {
68 int max_out;
69 isl_union_map *res;
72 /* Pad the range of the given map with zeros to data->max_out and
73 * then add the result to data->res.
75 static int map_pad_range(__isl_take isl_map *map, void *user)
77 struct isl_pad_schedule_map_data *data = user;
78 int i;
79 int n_out = isl_map_dim(map, isl_dim_out);
81 map = isl_map_add_dims(map, isl_dim_out, data->max_out - n_out);
82 for (i = n_out; i < data->max_out; ++i)
83 map = isl_map_fix_si(map, isl_dim_out, i, 0);
85 data->res = isl_union_map_add_map(data->res, map);
86 if (!data->res)
87 return -1;
89 return 0;
92 /* Pad the ranges of the maps in the union map with zeros such they all have
93 * the same dimension.
95 static __isl_give isl_union_map *pad_schedule_map(
96 __isl_take isl_union_map *umap)
98 struct isl_pad_schedule_map_data data;
100 if (!umap)
101 return NULL;
102 if (isl_union_map_n_map(umap) <= 1)
103 return umap;
105 data.max_out = 0;
106 if (isl_union_map_foreach_map(umap, &update_max_out, &data.max_out) < 0)
107 return isl_union_map_free(umap);
109 data.res = isl_union_map_empty(isl_union_map_get_space(umap));
110 if (isl_union_map_foreach_map(umap, &map_pad_range, &data) < 0)
111 data.res = isl_union_map_free(data.res);
113 isl_union_map_free(umap);
114 return data.res;
117 /* Return an isl_union_map of the schedule. If we have already constructed
118 * a band forest, then this band forest may have been modified so we need
119 * to extract the isl_union_map from the forest rather than from
120 * the originally computed schedule. This reconstructed schedule map
121 * then needs to be padded with zeros to unify the schedule space
122 * since the result of isl_band_list_get_suffix_schedule may not have
123 * a unified schedule space.
125 __isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched)
127 int i;
128 isl_union_map *umap;
130 if (!sched)
131 return NULL;
133 if (sched->band_forest) {
134 umap = isl_band_list_get_suffix_schedule(sched->band_forest);
135 return pad_schedule_map(umap);
138 umap = isl_union_map_empty(isl_space_copy(sched->dim));
139 for (i = 0; i < sched->n; ++i) {
140 isl_multi_aff *ma;
142 ma = isl_multi_aff_copy(sched->node[i].sched);
143 umap = isl_union_map_add_map(umap, isl_map_from_multi_aff(ma));
146 return umap;
149 static __isl_give isl_band_list *construct_band_list(
150 __isl_keep isl_schedule *schedule, __isl_keep isl_band *parent,
151 int band_nr, int *parent_active, int n_active);
153 /* Construct an isl_band structure for the band in the given schedule
154 * with sequence number band_nr for the n_active nodes marked by active.
155 * If the nodes don't have a band with the given sequence number,
156 * then a band without members is created.
158 * Because of the way the schedule is constructed, we know that
159 * the position of the band inside the schedule of a node is the same
160 * for all active nodes.
162 * The partial schedule for the band is created before the children
163 * are created to that construct_band_list can refer to the partial
164 * schedule of the parent.
166 static __isl_give isl_band *construct_band(__isl_keep isl_schedule *schedule,
167 __isl_keep isl_band *parent,
168 int band_nr, int *active, int n_active)
170 int i, j;
171 isl_ctx *ctx = isl_schedule_get_ctx(schedule);
172 isl_band *band;
173 unsigned start, end;
175 band = isl_band_alloc(ctx);
176 if (!band)
177 return NULL;
179 band->schedule = schedule;
180 band->parent = parent;
182 for (i = 0; i < schedule->n; ++i)
183 if (active[i])
184 break;
186 if (i >= schedule->n)
187 isl_die(ctx, isl_error_internal,
188 "band without active statements", goto error);
190 start = band_nr ? schedule->node[i].band_end[band_nr - 1] : 0;
191 end = band_nr < schedule->node[i].n_band ?
192 schedule->node[i].band_end[band_nr] : start;
193 band->n = end - start;
195 band->coincident = isl_alloc_array(ctx, int, band->n);
196 if (band->n && !band->coincident)
197 goto error;
199 for (j = 0; j < band->n; ++j)
200 band->coincident[j] = schedule->node[i].coincident[start + j];
202 band->pma = isl_union_pw_multi_aff_empty(isl_space_copy(schedule->dim));
203 for (i = 0; i < schedule->n; ++i) {
204 isl_multi_aff *ma;
205 isl_pw_multi_aff *pma;
206 unsigned n_out;
208 if (!active[i])
209 continue;
211 ma = isl_multi_aff_copy(schedule->node[i].sched);
212 n_out = isl_multi_aff_dim(ma, isl_dim_out);
213 ma = isl_multi_aff_drop_dims(ma, isl_dim_out, end, n_out - end);
214 ma = isl_multi_aff_drop_dims(ma, isl_dim_out, 0, start);
215 pma = isl_pw_multi_aff_from_multi_aff(ma);
216 band->pma = isl_union_pw_multi_aff_add_pw_multi_aff(band->pma,
217 pma);
219 if (!band->pma)
220 goto error;
222 for (i = 0; i < schedule->n; ++i)
223 if (active[i] && schedule->node[i].n_band > band_nr + 1)
224 break;
226 if (i < schedule->n) {
227 band->children = construct_band_list(schedule, band,
228 band_nr + 1, active, n_active);
229 if (!band->children)
230 goto error;
233 return band;
234 error:
235 isl_band_free(band);
236 return NULL;
239 /* Internal data structure used inside cmp_band and pw_multi_aff_extract_int.
241 * r is set to a negative value if anything goes wrong.
243 * c1 stores the result of extract_int.
244 * c2 is a temporary value used inside cmp_band_in_ancestor.
245 * t is a temporary value used inside extract_int.
247 * first and equal are used inside extract_int.
248 * first is set if we are looking at the first isl_multi_aff inside
249 * the isl_union_pw_multi_aff.
250 * equal is set if all the isl_multi_affs have been equal so far.
252 struct isl_cmp_band_data {
253 int r;
255 int first;
256 int equal;
258 isl_int t;
259 isl_int c1;
260 isl_int c2;
263 /* Check if "ma" assigns a constant value.
264 * Note that this function is only called on isl_multi_affs
265 * with a single output dimension.
267 * If "ma" assigns a constant value then we compare it to data->c1
268 * or assign it to data->c1 if this is the first isl_multi_aff we consider.
269 * If "ma" does not assign a constant value or if it assigns a value
270 * that is different from data->c1, then we set data->equal to zero
271 * and terminate the check.
273 static int multi_aff_extract_int(__isl_take isl_set *set,
274 __isl_take isl_multi_aff *ma, void *user)
276 isl_aff *aff;
277 struct isl_cmp_band_data *data = user;
279 aff = isl_multi_aff_get_aff(ma, 0);
280 data->r = isl_aff_is_cst(aff);
281 if (data->r >= 0 && data->r) {
282 isl_aff_get_constant(aff, &data->t);
283 if (data->first) {
284 isl_int_set(data->c1, data->t);
285 data->first = 0;
286 } else if (!isl_int_eq(data->c1, data->t))
287 data->equal = 0;
288 } else if (data->r >= 0 && !data->r)
289 data->equal = 0;
291 isl_aff_free(aff);
292 isl_set_free(set);
293 isl_multi_aff_free(ma);
295 if (data->r < 0)
296 return -1;
297 if (!data->equal)
298 return -1;
299 return 0;
302 /* This function is called for each isl_pw_multi_aff in
303 * the isl_union_pw_multi_aff checked by extract_int.
304 * Check all the isl_multi_affs inside "pma".
306 static int pw_multi_aff_extract_int(__isl_take isl_pw_multi_aff *pma,
307 void *user)
309 int r;
311 r = isl_pw_multi_aff_foreach_piece(pma, &multi_aff_extract_int, user);
312 isl_pw_multi_aff_free(pma);
314 return r;
317 /* Check if "upma" assigns a single constant value to its domain.
318 * If so, return 1 and store the result in data->c1.
319 * If not, return 0.
321 * A negative return value from isl_union_pw_multi_aff_foreach_pw_multi_aff
322 * means that either an error occurred or that we have broken off the check
323 * because we already know the result is going to be negative.
324 * In the latter case, data->equal is set to zero.
326 static int extract_int(__isl_keep isl_union_pw_multi_aff *upma,
327 struct isl_cmp_band_data *data)
329 data->first = 1;
330 data->equal = 1;
332 if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma,
333 &pw_multi_aff_extract_int, data) < 0) {
334 if (!data->equal)
335 return 0;
336 return -1;
339 return !data->first && data->equal;
342 /* Compare "b1" and "b2" based on the parent schedule of their ancestor
343 * "ancestor".
345 * If the parent of "ancestor" also has a single member, then we
346 * first try to compare the two band based on the partial schedule
347 * of this parent.
349 * Otherwise, or if the result is inconclusive, we look at the partial schedule
350 * of "ancestor" itself.
351 * In particular, we specialize the parent schedule based
352 * on the domains of the child schedules, check if both assign
353 * a single constant value and, if so, compare the two constant values.
354 * If the specialized parent schedules do not assign a constant value,
355 * then they cannot be used to order the two bands and so in this case
356 * we return 0.
358 static int cmp_band_in_ancestor(__isl_keep isl_band *b1,
359 __isl_keep isl_band *b2, struct isl_cmp_band_data *data,
360 __isl_keep isl_band *ancestor)
362 isl_union_pw_multi_aff *upma;
363 isl_union_set *domain;
364 int r;
366 if (data->r < 0)
367 return 0;
369 if (ancestor->parent && ancestor->parent->n == 1) {
370 r = cmp_band_in_ancestor(b1, b2, data, ancestor->parent);
371 if (data->r < 0)
372 return 0;
373 if (r)
374 return r;
377 upma = isl_union_pw_multi_aff_copy(b1->pma);
378 domain = isl_union_pw_multi_aff_domain(upma);
379 upma = isl_union_pw_multi_aff_copy(ancestor->pma);
380 upma = isl_union_pw_multi_aff_intersect_domain(upma, domain);
381 r = extract_int(upma, data);
382 isl_union_pw_multi_aff_free(upma);
384 if (r < 0)
385 data->r = -1;
386 if (r < 0 || !r)
387 return 0;
389 isl_int_set(data->c2, data->c1);
391 upma = isl_union_pw_multi_aff_copy(b2->pma);
392 domain = isl_union_pw_multi_aff_domain(upma);
393 upma = isl_union_pw_multi_aff_copy(ancestor->pma);
394 upma = isl_union_pw_multi_aff_intersect_domain(upma, domain);
395 r = extract_int(upma, data);
396 isl_union_pw_multi_aff_free(upma);
398 if (r < 0)
399 data->r = -1;
400 if (r < 0 || !r)
401 return 0;
403 return isl_int_cmp(data->c2, data->c1);
406 /* Compare "a" and "b" based on the parent schedule of their parent.
408 static int cmp_band(const void *a, const void *b, void *user)
410 isl_band *b1 = *(isl_band * const *) a;
411 isl_band *b2 = *(isl_band * const *) b;
412 struct isl_cmp_band_data *data = user;
414 return cmp_band_in_ancestor(b1, b2, data, b1->parent);
417 /* Sort the elements in "list" based on the partial schedules of its parent
418 * (and ancestors). In particular if the parent assigns constant values
419 * to the domains of the bands in "list", then the elements are sorted
420 * according to that order.
421 * This order should be a more "natural" order for the user, but otherwise
422 * shouldn't have any effect.
423 * If we would be constructing an isl_band forest directly in
424 * isl_schedule_constraints_compute_schedule then there wouldn't be any need
425 * for a reordering, since the children would be added to the list
426 * in their natural order automatically.
428 * If there is only one element in the list, then there is no need to sort
429 * anything.
430 * If the partial schedule of the parent has more than one member
431 * (or if there is no parent), then it's
432 * defnitely not assigning constant values to the different children in
433 * the list and so we wouldn't be able to use it to sort the list.
435 static __isl_give isl_band_list *sort_band_list(__isl_take isl_band_list *list,
436 __isl_keep isl_band *parent)
438 struct isl_cmp_band_data data;
440 if (!list)
441 return NULL;
442 if (list->n <= 1)
443 return list;
444 if (!parent || parent->n != 1)
445 return list;
447 data.r = 0;
448 isl_int_init(data.c1);
449 isl_int_init(data.c2);
450 isl_int_init(data.t);
451 isl_sort(list->p, list->n, sizeof(list->p[0]), &cmp_band, &data);
452 if (data.r < 0)
453 list = isl_band_list_free(list);
454 isl_int_clear(data.c1);
455 isl_int_clear(data.c2);
456 isl_int_clear(data.t);
458 return list;
461 /* Construct a list of bands that start at the same position (with
462 * sequence number band_nr) in the schedules of the nodes that
463 * were active in the parent band.
465 * A separate isl_band structure is created for each band_id
466 * and for each node that does not have a band with sequence
467 * number band_nr. In the latter case, a band without members
468 * is created.
469 * This ensures that if a band has any children, then each node
470 * that was active in the band is active in exactly one of the children.
472 static __isl_give isl_band_list *construct_band_list(
473 __isl_keep isl_schedule *schedule, __isl_keep isl_band *parent,
474 int band_nr, int *parent_active, int n_active)
476 int i, j;
477 isl_ctx *ctx = isl_schedule_get_ctx(schedule);
478 int *active;
479 int n_band;
480 isl_band_list *list;
482 n_band = 0;
483 for (i = 0; i < n_active; ++i) {
484 for (j = 0; j < schedule->n; ++j) {
485 if (!parent_active[j])
486 continue;
487 if (schedule->node[j].n_band <= band_nr)
488 continue;
489 if (schedule->node[j].band_id[band_nr] == i) {
490 n_band++;
491 break;
495 for (j = 0; j < schedule->n; ++j)
496 if (schedule->node[j].n_band <= band_nr)
497 n_band++;
499 if (n_band == 1) {
500 isl_band *band;
501 list = isl_band_list_alloc(ctx, n_band);
502 band = construct_band(schedule, parent, band_nr,
503 parent_active, n_active);
504 return isl_band_list_add(list, band);
507 active = isl_alloc_array(ctx, int, schedule->n);
508 if (schedule->n && !active)
509 return NULL;
511 list = isl_band_list_alloc(ctx, n_band);
513 for (i = 0; i < n_active; ++i) {
514 int n = 0;
515 isl_band *band;
517 for (j = 0; j < schedule->n; ++j) {
518 active[j] = parent_active[j] &&
519 schedule->node[j].n_band > band_nr &&
520 schedule->node[j].band_id[band_nr] == i;
521 if (active[j])
522 n++;
524 if (n == 0)
525 continue;
527 band = construct_band(schedule, parent, band_nr, active, n);
529 list = isl_band_list_add(list, band);
531 for (i = 0; i < schedule->n; ++i) {
532 isl_band *band;
533 if (!parent_active[i])
534 continue;
535 if (schedule->node[i].n_band > band_nr)
536 continue;
537 for (j = 0; j < schedule->n; ++j)
538 active[j] = j == i;
539 band = construct_band(schedule, parent, band_nr, active, 1);
540 list = isl_band_list_add(list, band);
543 free(active);
545 list = sort_band_list(list, parent);
547 return list;
550 /* Construct a band forest representation of the schedule and
551 * return the list of roots.
553 static __isl_give isl_band_list *construct_forest(
554 __isl_keep isl_schedule *schedule)
556 int i;
557 isl_ctx *ctx = isl_schedule_get_ctx(schedule);
558 isl_band_list *forest;
559 int *active;
561 active = isl_alloc_array(ctx, int, schedule->n);
562 if (schedule->n && !active)
563 return NULL;
565 for (i = 0; i < schedule->n; ++i)
566 active[i] = 1;
568 forest = construct_band_list(schedule, NULL, 0, active, schedule->n);
570 free(active);
572 return forest;
575 /* Return the roots of a band forest representation of the schedule.
577 __isl_give isl_band_list *isl_schedule_get_band_forest(
578 __isl_keep isl_schedule *schedule)
580 if (!schedule)
581 return NULL;
582 if (!schedule->band_forest)
583 schedule->band_forest = construct_forest(schedule);
584 return isl_band_list_dup(schedule->band_forest);
587 /* Call "fn" on each band in the schedule in depth-first post-order.
589 int isl_schedule_foreach_band(__isl_keep isl_schedule *sched,
590 int (*fn)(__isl_keep isl_band *band, void *user), void *user)
592 int r;
593 isl_band_list *forest;
595 if (!sched)
596 return -1;
598 forest = isl_schedule_get_band_forest(sched);
599 r = isl_band_list_foreach_band(forest, fn, user);
600 isl_band_list_free(forest);
602 return r;
605 static __isl_give isl_printer *print_band_list(__isl_take isl_printer *p,
606 __isl_keep isl_band_list *list);
608 static __isl_give isl_printer *print_band(__isl_take isl_printer *p,
609 __isl_keep isl_band *band)
611 isl_band_list *children;
613 p = isl_printer_start_line(p);
614 p = isl_printer_print_union_pw_multi_aff(p, band->pma);
615 p = isl_printer_end_line(p);
617 if (!isl_band_has_children(band))
618 return p;
620 children = isl_band_get_children(band);
622 p = isl_printer_indent(p, 4);
623 p = print_band_list(p, children);
624 p = isl_printer_indent(p, -4);
626 isl_band_list_free(children);
628 return p;
631 static __isl_give isl_printer *print_band_list(__isl_take isl_printer *p,
632 __isl_keep isl_band_list *list)
634 int i, n;
636 n = isl_band_list_n_band(list);
637 for (i = 0; i < n; ++i) {
638 isl_band *band;
639 band = isl_band_list_get_band(list, i);
640 p = print_band(p, band);
641 isl_band_free(band);
644 return p;
647 __isl_give isl_printer *isl_printer_print_schedule(__isl_take isl_printer *p,
648 __isl_keep isl_schedule *schedule)
650 isl_band_list *forest;
652 forest = isl_schedule_get_band_forest(schedule);
654 p = print_band_list(p, forest);
656 isl_band_list_free(forest);
658 return p;
661 void isl_schedule_dump(__isl_keep isl_schedule *schedule)
663 isl_printer *printer;
665 if (!schedule)
666 return;
668 printer = isl_printer_to_file(isl_schedule_get_ctx(schedule), stderr);
669 printer = isl_printer_print_schedule(printer, schedule);
671 isl_printer_free(printer);