1 /*
2 * Copyright 2016 Sven Verdoolaege
3 *
4 * Use of this software is governed by the MIT license
5 *
6 * Written by Sven Verdoolaege.
7 */
8
9 #include <isl/ctx.h>
10 #include <isl/id.h>
11 #include <isl/val.h>
12 #include <isl/space.h>
13 #include <isl/aff.h>
14 #include <isl/set.h>
15 #include <isl/map.h>
16 #include <isl/union_set.h>
17 #include <isl/union_map.h>
18 #include <isl/schedule.h>
19 #include <isl/schedule_node.h>
20
21 #include "ppcg.h"
22
23 /* Internal data structure for use during the detection of statements
24 * that can be grouped.
25 *
26 * "sc" contains the original schedule constraints (not a copy).
27 * "dep" contains the intersection of the validity and the proximity
28 * constraints in "sc". It may be NULL if it has not been computed yet.
29 * "group_id" is the identifier for the next group that is extracted.
30 *
31 * "domain" is the set of statement instances that belong to any of the groups.
32 * "contraction" maps the elements of "domain" to the corresponding group
33 * instances.
34 * "schedule" schedules the statements in each group relatively to each other.
35 * These last three fields are NULL if no groups have been found so far.
36 */
37 struct ppcg_grouping {
38 isl_schedule_constraints *sc;
39
40 isl_union_map *dep;
41 int group_id;
42
43 isl_union_set *domain;
44 isl_union_pw_multi_aff *contraction;
45 isl_schedule *schedule;
46 };
47
48 /* Clear all memory allocated by "grouping".
49 */
ppcg_grouping_clear(struct ppcg_grouping * grouping)50 static void ppcg_grouping_clear(struct ppcg_grouping *grouping)
51 {
52 isl_union_map_free(grouping->dep);
53 isl_union_set_free(grouping->domain);
54 isl_union_pw_multi_aff_free(grouping->contraction);
55 isl_schedule_free(grouping->schedule);
56 }
57
58 /* Compute the intersection of the proximity and validity dependences
59 * in grouping->sc and store the result in grouping->dep, unless
60 * this intersection has been computed before.
61 */
ppcg_grouping_compute_dep(struct ppcg_grouping * grouping)62 static isl_stat ppcg_grouping_compute_dep(struct ppcg_grouping *grouping)
63 {
64 isl_union_map *validity, *proximity;
65
66 if (grouping->dep)
67 return isl_stat_ok;
68
69 validity = isl_schedule_constraints_get_validity(grouping->sc);
70 proximity = isl_schedule_constraints_get_proximity(grouping->sc);
71 grouping->dep = isl_union_map_intersect(validity, proximity);
72
73 if (!grouping->dep)
74 return isl_stat_error;
75
76 return isl_stat_ok;
77 }
78
79 /* Information extracted from one or more consecutive leaves
80 * in the input schedule.
81 *
82 * "list" contains the sets of statement instances in the leaves,
83 * one element in the list for each original leaf.
84 * "domain" contains the union of the sets in "list".
85 * "prefix" contains the prefix schedule of these elements.
86 */
87 struct ppcg_grouping_leaf {
88 isl_union_set *domain;
89 isl_union_set_list *list;
90 isl_multi_union_pw_aff *prefix;
91 };
92
93 /* Free all memory allocated for "leaves".
94 */
ppcg_grouping_leaf_free(int n,struct ppcg_grouping_leaf leaves[])95 static void ppcg_grouping_leaf_free(int n, struct ppcg_grouping_leaf leaves[])
96 {
97 int i;
98
99 if (!leaves)
100 return;
101
102 for (i = 0; i < n; ++i) {
103 isl_union_set_free(leaves[i].domain);
104 isl_union_set_list_free(leaves[i].list);
105 isl_multi_union_pw_aff_free(leaves[i].prefix);
106 }
107
108 free(leaves);
109 }
110
111 /* Short-hand for retrieving the prefix schedule at "node"
112 * in the form of an isl_multi_union_pw_aff.
113 */
get_prefix(__isl_keep isl_schedule_node * node)114 static __isl_give isl_multi_union_pw_aff *get_prefix(
115 __isl_keep isl_schedule_node *node)
116 {
117 return isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
118 }
119
120 /* Return an array of "n" elements with information extracted from
121 * the "n" children of "node" starting at "first", all of which
122 * are known to be filtered leaves.
123 */
extract_leaves(__isl_keep isl_schedule_node * node,int first,int n)124 struct ppcg_grouping_leaf *extract_leaves(__isl_keep isl_schedule_node *node,
125 int first, int n)
126 {
127 int i;
128 isl_ctx *ctx;
129 struct ppcg_grouping_leaf *leaves;
130
131 if (!node)
132 return NULL;
133
134 ctx = isl_schedule_node_get_ctx(node);
135 leaves = isl_calloc_array(ctx, struct ppcg_grouping_leaf, n);
136 if (!leaves)
137 return NULL;
138
139 for (i = 0; i < n; ++i) {
140 isl_schedule_node *child;
141 isl_union_set *domain;
142
143 child = isl_schedule_node_get_child(node, first + i);
144 child = isl_schedule_node_child(child, 0);
145 domain = isl_schedule_node_get_domain(child);
146 leaves[i].domain = isl_union_set_copy(domain);
147 leaves[i].list = isl_union_set_list_from_union_set(domain);
148 leaves[i].prefix = get_prefix(child);
149 isl_schedule_node_free(child);
150 }
151
152 return leaves;
153 }
154
155 /* Internal data structure used by merge_leaves.
156 *
157 * "src" and "dst" point to the two consecutive leaves that are
158 * under investigation for being merged.
159 * "merge" is initially set to 0 and is set to 1 as soon as
160 * it turns out that it is useful to merge the two leaves.
161 */
162 struct ppcg_merge_leaves_data {
163 int merge;
164 struct ppcg_grouping_leaf *src;
165 struct ppcg_grouping_leaf *dst;
166 };
167
168 /* Given a relation "map" between instances of two statements A and B,
169 * does it relate every instance of A (according to the domain of "src")
170 * to every instance of B (according to the domain of "dst")?
171 */
covers_src_and_dst(__isl_keep isl_map * map,struct ppcg_grouping_leaf * src,struct ppcg_grouping_leaf * dst)172 static isl_bool covers_src_and_dst(__isl_keep isl_map *map,
173 struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
174 {
175 isl_space *space;
176 isl_set *set1, *set2;
177 isl_bool is_subset;
178
179 space = isl_space_domain(isl_map_get_space(map));
180 set1 = isl_union_set_extract_set(src->domain, space);
181 set2 = isl_map_domain(isl_map_copy(map));
182 is_subset = isl_set_is_subset(set1, set2);
183 isl_set_free(set1);
184 isl_set_free(set2);
185 if (is_subset < 0 || !is_subset)
186 return is_subset;
187
188 space = isl_space_range(isl_map_get_space(map));
189 set1 = isl_union_set_extract_set(dst->domain, space);
190 set2 = isl_map_range(isl_map_copy(map));
191 is_subset = isl_set_is_subset(set1, set2);
192 isl_set_free(set1);
193 isl_set_free(set2);
194
195 return is_subset;
196 }
197
198 /* Given a relation "map" between instances of two statements A and B,
199 * are pairs of related instances executed together in the input schedule?
200 * That is, is each pair of instances assigned the same value
201 * by the corresponding prefix schedules?
202 *
203 * In particular, select the subset of "map" that has pairs of elements
204 * with the same value for the prefix schedules and then check
205 * if "map" is still a subset of the result.
206 */
matches_prefix(__isl_keep isl_map * map,struct ppcg_grouping_leaf * src,struct ppcg_grouping_leaf * dst)207 static isl_bool matches_prefix(__isl_keep isl_map *map,
208 struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
209 {
210 isl_union_map *umap, *equal;
211 isl_multi_union_pw_aff *src_prefix, *dst_prefix, *prefix;
212 isl_bool is_subset;
213
214 src_prefix = isl_multi_union_pw_aff_copy(src->prefix);
215 dst_prefix = isl_multi_union_pw_aff_copy(dst->prefix);
216 prefix = isl_multi_union_pw_aff_union_add(src_prefix, dst_prefix);
217
218 umap = isl_union_map_from_map(isl_map_copy(map));
219 equal = isl_union_map_copy(umap);
220 equal = isl_union_map_eq_at_multi_union_pw_aff(equal, prefix);
221
222 is_subset = isl_union_map_is_subset(umap, equal);
223
224 isl_union_map_free(umap);
225 isl_union_map_free(equal);
226
227 return is_subset;
228 }
229
230 /* Given a set of validity and proximity schedule constraints "map"
231 * between statements in consecutive leaves in a valid schedule,
232 * should the two leaves be merged into one?
233 *
234 * In particular, the two are merged if the constraints form
235 * a bijection between every instance of the first statement and
236 * every instance of the second statement. Moreover, each
237 * pair of such dependent instances needs to be executed consecutively
238 * in the input schedule. That is, they need to be assigned
239 * the same value by their prefix schedules.
240 *
241 * What this means is that for each instance of the first statement
242 * there is exactly one instance of the second statement that
243 * is executed immediately after the instance of the first statement and
244 * that, moreover, both depends on this statement instance and
245 * should be brought as close as possible to this statement instance.
246 * In other words, it is both possible to execute the two instances
247 * together (according to the input schedule) and desirable to do so
248 * (according to the validity and proximity schedule constraints).
249 */
check_merge(__isl_take isl_map * map,void * user)250 static isl_stat check_merge(__isl_take isl_map *map, void *user)
251 {
252 struct ppcg_merge_leaves_data *data = user;
253 isl_bool ok;
254
255 ok = covers_src_and_dst(map, data->src, data->dst);
256 if (ok >= 0 && ok)
257 ok = isl_map_is_bijective(map);
258 if (ok >= 0 && ok)
259 ok = matches_prefix(map, data->src, data->dst);
260
261 isl_map_free(map);
262
263 if (ok < 0)
264 return isl_stat_error;
265 if (!ok)
266 return isl_stat_ok;
267
268 data->merge = 1;
269 return isl_stat_error;
270 }
271
272 /* Merge the leaves at position "pos" and "pos + 1" in "leaves".
273 */
merge_pair(int n,struct ppcg_grouping_leaf leaves[],int pos)274 static isl_stat merge_pair(int n, struct ppcg_grouping_leaf leaves[], int pos)
275 {
276 int i;
277
278 leaves[pos].domain = isl_union_set_union(leaves[pos].domain,
279 leaves[pos + 1].domain);
280 leaves[pos].list = isl_union_set_list_concat(leaves[pos].list,
281 leaves[pos + 1].list);
282 leaves[pos].prefix = isl_multi_union_pw_aff_union_add(
283 leaves[pos].prefix, leaves[pos + 1].prefix);
284 for (i = pos + 1; i + 1 < n; ++i)
285 leaves[i] = leaves[i + 1];
286 leaves[n - 1].domain = NULL;
287 leaves[n - 1].list = NULL;
288 leaves[n - 1].prefix = NULL;
289
290 if (!leaves[pos].domain || !leaves[pos].list || !leaves[pos].prefix)
291 return isl_stat_error;
292
293 return isl_stat_ok;
294 }
295
296 /* Merge pairs of consecutive leaves in "leaves" taking into account
297 * the intersection of validity and proximity schedule constraints "dep".
298 *
299 * If a leaf has been merged with the next leaf, then the combination
300 * is checked again for merging with the next leaf.
301 * That is, if the leaves are A, B and C, then B may not have been
302 * merged with C, but after merging A and B, it could still be useful
303 * to merge the combination AB with C.
304 *
305 * Two leaves A and B are merged if there are instances of at least
306 * one pair of statements, one statement in A and one B, such that
307 * the validity and proximity schedule constraints between them
308 * make them suitable for merging according to check_merge.
309 *
310 * Return the final number of leaves in the sequence, or -1 on error.
311 */
merge_leaves(int n,struct ppcg_grouping_leaf leaves[],__isl_keep isl_union_map * dep)312 static int merge_leaves(int n, struct ppcg_grouping_leaf leaves[],
313 __isl_keep isl_union_map *dep)
314 {
315 int i;
316 struct ppcg_merge_leaves_data data;
317
318 for (i = n - 1; i >= 0; --i) {
319 isl_union_map *dep_i;
320 isl_stat ok;
321
322 if (i + 1 >= n)
323 continue;
324
325 dep_i = isl_union_map_copy(dep);
326 dep_i = isl_union_map_intersect_domain(dep_i,
327 isl_union_set_copy(leaves[i].domain));
328 dep_i = isl_union_map_intersect_range(dep_i,
329 isl_union_set_copy(leaves[i + 1].domain));
330 data.merge = 0;
331 data.src = &leaves[i];
332 data.dst = &leaves[i + 1];
333 ok = isl_union_map_foreach_map(dep_i, &check_merge, &data);
334 isl_union_map_free(dep_i);
335 if (ok < 0 && !data.merge)
336 return -1;
337 if (!data.merge)
338 continue;
339 if (merge_pair(n, leaves, i) < 0)
340 return -1;
341 --n;
342 ++i;
343 }
344
345 return n;
346 }
347
348 /* Construct a schedule with "domain" as domain, that executes
349 * the elements of "list" in order (as a sequence).
350 */
schedule_from_domain_and_list(__isl_keep isl_union_set * domain,__isl_keep isl_union_set_list * list)351 static __isl_give isl_schedule *schedule_from_domain_and_list(
352 __isl_keep isl_union_set *domain, __isl_keep isl_union_set_list *list)
353 {
354 isl_schedule *schedule;
355 isl_schedule_node *node;
356
357 schedule = isl_schedule_from_domain(isl_union_set_copy(domain));
358 node = isl_schedule_get_root(schedule);
359 isl_schedule_free(schedule);
360 node = isl_schedule_node_child(node, 0);
361 list = isl_union_set_list_copy(list);
362 node = isl_schedule_node_insert_sequence(node, list);
363 schedule = isl_schedule_node_get_schedule(node);
364 isl_schedule_node_free(node);
365
366 return schedule;
367 }
368
369 /* Construct a unique identifier for a group in "grouping".
370 *
371 * The name is of the form G_n, with n the first value starting at
372 * grouping->group_id that does not result in an identifier
373 * that is already in use in the domain of the original schedule
374 * constraints.
375 */
construct_group_id(struct ppcg_grouping * grouping,__isl_take isl_space * space)376 static isl_id *construct_group_id(struct ppcg_grouping *grouping,
377 __isl_take isl_space *space)
378 {
379 isl_ctx *ctx;
380 isl_id *id;
381 isl_bool empty;
382 isl_union_set *domain;
383
384 if (!space)
385 return NULL;
386
387 ctx = isl_space_get_ctx(space);
388 domain = isl_schedule_constraints_get_domain(grouping->sc);
389
390 do {
391 char buffer[20];
392 isl_id *id;
393 isl_set *set;
394
395 snprintf(buffer, sizeof(buffer), "G_%d", grouping->group_id);
396 grouping->group_id++;
397 id = isl_id_alloc(ctx, buffer, NULL);
398 space = isl_space_set_tuple_id(space, isl_dim_set, id);
399 set = isl_union_set_extract_set(domain, isl_space_copy(space));
400 empty = isl_set_plain_is_empty(set);
401 isl_set_free(set);
402 } while (empty >= 0 && !empty);
403
404 if (empty < 0)
405 space = isl_space_free(space);
406
407 id = isl_space_get_tuple_id(space, isl_dim_set);
408
409 isl_space_free(space);
410 isl_union_set_free(domain);
411
412 return id;
413 }
414
415 /* Construct a contraction from "prefix" and "domain" for a new group
416 * in "grouping".
417 *
418 * The values of the prefix schedule "prefix" are used as instances
419 * of the new group. The identifier of the group is constructed
420 * in such a way that it does not conflict with those of earlier
421 * groups nor with statements in the domain of the original
422 * schedule constraints.
423 * The isl_multi_union_pw_aff "prefix" then simply needs to be
424 * converted to an isl_union_pw_multi_aff. However, this is not
425 * possible if "prefix" is zero-dimensional, so in this case,
426 * a contraction is constructed from "domain" instead.
427 */
group_contraction_from_prefix_and_domain(struct ppcg_grouping * grouping,__isl_keep isl_multi_union_pw_aff * prefix,__isl_keep isl_union_set * domain)428 static isl_union_pw_multi_aff *group_contraction_from_prefix_and_domain(
429 struct ppcg_grouping *grouping,
430 __isl_keep isl_multi_union_pw_aff *prefix,
431 __isl_keep isl_union_set *domain)
432 {
433 isl_id *id;
434 isl_space *space;
435 int dim;
436
437 space = isl_multi_union_pw_aff_get_space(prefix);
438 if (!space)
439 return NULL;
440 dim = isl_space_dim(space, isl_dim_set);
441 id = construct_group_id(grouping, space);
442 if (dim == 0) {
443 isl_multi_val *mv;
444
445 space = isl_multi_union_pw_aff_get_space(prefix);
446 space = isl_space_set_tuple_id(space, isl_dim_set, id);
447 mv = isl_multi_val_zero(space);
448 domain = isl_union_set_copy(domain);
449 return isl_union_pw_multi_aff_multi_val_on_domain(domain, mv);
450 }
451 prefix = isl_multi_union_pw_aff_copy(prefix);
452 prefix = isl_multi_union_pw_aff_set_tuple_id(prefix, isl_dim_out, id);
453 return isl_union_pw_multi_aff_from_multi_union_pw_aff(prefix);
454 }
455
456 /* Extend "grouping" with groups corresponding to merged
457 * leaves in the list of potentially merged leaves "leaves".
458 *
459 * The "list" field of each element in "leaves" contains a list
460 * of the instances sets of the original leaves that have been
461 * merged into this element. If at least two of the original leaves
462 * have been merged into a given element, then add the corresponding
463 * group to "grouping".
464 * In particular, the domain is extended with the statement instances
465 * of the merged leaves, the contraction is extended with a mapping
466 * of these statement instances to instances of a new group and
467 * the schedule is extended with a schedule that executes
468 * the statement instances according to the order of the leaves
469 * in which they appear.
470 * Since the instances of the groups should already be scheduled apart
471 * in the schedule into which this schedule will be plugged in,
472 * the schedules of the individual groups are combined independently
473 * of each other (as a set).
474 */
add_groups(struct ppcg_grouping * grouping,int n,struct ppcg_grouping_leaf leaves[])475 static isl_stat add_groups(struct ppcg_grouping *grouping,
476 int n, struct ppcg_grouping_leaf leaves[])
477 {
478 int i;
479
480 for (i = 0; i < n; ++i) {
481 int n_leaf;
482 isl_schedule *schedule;
483 isl_union_set *domain;
484 isl_union_pw_multi_aff *upma;
485
486 n_leaf = isl_union_set_list_n_union_set(leaves[i].list);
487 if (n_leaf < 0)
488 return isl_stat_error;
489 if (n_leaf <= 1)
490 continue;
491 schedule = schedule_from_domain_and_list(leaves[i].domain,
492 leaves[i].list);
493 upma = group_contraction_from_prefix_and_domain(grouping,
494 leaves[i].prefix, leaves[i].domain);
495
496 domain = isl_union_set_copy(leaves[i].domain);
497 if (grouping->domain) {
498 domain = isl_union_set_union(domain, grouping->domain);
499 upma = isl_union_pw_multi_aff_union_add(upma,
500 grouping->contraction);
501 schedule = isl_schedule_set(schedule,
502 grouping->schedule);
503 }
504 grouping->domain = domain;
505 grouping->contraction = upma;
506 grouping->schedule = schedule;
507
508 if (!grouping->domain || !grouping->contraction ||
509 !grouping->schedule)
510 return isl_stat_error;
511 }
512
513 return isl_stat_ok;
514 }
515
516 /* Look for any pairs of consecutive leaves among the "n" children of "node"
517 * starting at "first" that should be merged together.
518 * Store the results in "grouping".
519 *
520 * First make sure the intersection of validity and proximity
521 * schedule constraints is available and extract the required
522 * information from the "n" leaves.
523 * Then try and merge consecutive leaves based on the validity
524 * and proximity constraints.
525 * If any pairs were successfully merged, then add groups
526 * corresponding to the merged leaves to "grouping".
527 */
group_subsequence(__isl_keep isl_schedule_node * node,int first,int n,struct ppcg_grouping * grouping)528 static isl_stat group_subsequence(__isl_keep isl_schedule_node *node,
529 int first, int n, struct ppcg_grouping *grouping)
530 {
531 int n_merge;
532 struct ppcg_grouping_leaf *leaves;
533
534 if (ppcg_grouping_compute_dep(grouping) < 0)
535 return isl_stat_error;
536
537 leaves = extract_leaves(node, first, n);
538 if (!leaves)
539 return isl_stat_error;
540
541 n_merge = merge_leaves(n, leaves, grouping->dep);
542 if (n_merge >= 0 && n_merge < n &&
543 add_groups(grouping, n_merge, leaves) < 0)
544 return isl_stat_error;
545
546 ppcg_grouping_leaf_free(n, leaves);
547
548 return isl_stat_ok;
549 }
550
551 /* If "node" is a sequence, then check if it has any consecutive
552 * leaves that should be merged together and store the results
553 * in "grouping".
554 *
555 * In particular, call group_subsequence on each consecutive
556 * sequence of (filtered) leaves among the children of "node".
557 */
detect_groups(__isl_keep isl_schedule_node * node,void * user)558 static isl_bool detect_groups(__isl_keep isl_schedule_node *node, void *user)
559 {
560 int i, n, first;
561 struct ppcg_grouping *grouping = user;
562
563 if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
564 return isl_bool_true;
565
566 n = isl_schedule_node_n_children(node);
567 if (n < 0)
568 return isl_bool_error;
569
570 first = -1;
571 for (i = 0; i < n; ++i) {
572 isl_schedule_node *child;
573 enum isl_schedule_node_type type;
574
575 child = isl_schedule_node_get_child(node, i);
576 child = isl_schedule_node_child(child, 0);
577 type = isl_schedule_node_get_type(child);
578 isl_schedule_node_free(child);
579
580 if (first >= 0 && type != isl_schedule_node_leaf) {
581 if (group_subsequence(node, first, i - first,
582 grouping) < 0)
583 return isl_bool_error;
584 first = -1;
585 }
586 if (first < 0 && type == isl_schedule_node_leaf)
587 first = i;
588 }
589 if (first >= 0) {
590 if (group_subsequence(node, first, n - first, grouping) < 0)
591 return isl_bool_error;
592 }
593
594 return isl_bool_true;
595 }
596
597 /* Complete "grouping" to cover all statement instances in the domain
598 * of grouping->sc.
599 *
600 * In particular, grouping->domain is set to the full set of statement
601 * instances; group->contraction is extended with an identity
602 * contraction on the additional instances and group->schedule
603 * is extended with an independent schedule on those additional instances.
604 * In the extension of group->contraction, the additional instances
605 * are split into those belong to different statements and those
606 * that belong to some of the same statements. The first group
607 * is replaced by its universe in order to simplify the contraction extension.
608 */
complete_grouping(struct ppcg_grouping * grouping)609 static void complete_grouping(struct ppcg_grouping *grouping)
610 {
611 isl_union_set *domain, *left, *overlap;
612 isl_union_pw_multi_aff *upma;
613 isl_schedule *schedule;
614
615 domain = isl_schedule_constraints_get_domain(grouping->sc);
616 left = isl_union_set_subtract(isl_union_set_copy(domain),
617 isl_union_set_copy(grouping->domain));
618 schedule = isl_schedule_from_domain(isl_union_set_copy(left));
619 schedule = isl_schedule_set(schedule, grouping->schedule);
620 grouping->schedule = schedule;
621
622 overlap = isl_union_set_universe(grouping->domain);
623 grouping->domain = domain;
624 overlap = isl_union_set_intersect(isl_union_set_copy(left), overlap);
625 left = isl_union_set_subtract(left, isl_union_set_copy(overlap));
626 left = isl_union_set_universe(left);
627 left = isl_union_set_union(left, overlap);
628 upma = isl_union_set_identity_union_pw_multi_aff(left);
629 upma = isl_union_pw_multi_aff_union_add(upma, grouping->contraction);
630 grouping->contraction = upma;
631 }
632
633 /* Compute a schedule on the domain of "sc" that respects the schedule
634 * constraints in "sc".
635 *
636 * "schedule" is a known correct schedule that is used to combine
637 * groups of statements if options->group_chains is set.
638 * In particular, statements that are executed consecutively in a sequence
639 * in this schedule and where all instances of the second depend on
640 * the instance of the first that is executed in the same iteration
641 * of outer band nodes are grouped together into a single statement.
642 * The schedule constraints are then mapped to these groups of statements
643 * and the resulting schedule is expanded again to refer to the original
644 * statements.
645 */
ppcg_compute_schedule(__isl_take isl_schedule_constraints * sc,__isl_keep isl_schedule * schedule,struct ppcg_options * options)646 __isl_give isl_schedule *ppcg_compute_schedule(
647 __isl_take isl_schedule_constraints *sc,
648 __isl_keep isl_schedule *schedule, struct ppcg_options *options)
649 {
650 struct ppcg_grouping grouping = { sc };
651 isl_union_pw_multi_aff *contraction;
652 isl_union_map *umap;
653 isl_schedule *res, *expansion;
654
655 if (!options->group_chains)
656 return isl_schedule_constraints_compute_schedule(sc);
657
658 grouping.group_id = 0;
659 if (isl_schedule_foreach_schedule_node_top_down(schedule,
660 &detect_groups, &grouping) < 0)
661 goto error;
662 if (!grouping.contraction) {
663 ppcg_grouping_clear(&grouping);
664 return isl_schedule_constraints_compute_schedule(sc);
665 }
666 complete_grouping(&grouping);
667 contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
668 umap = isl_union_map_from_union_pw_multi_aff(contraction);
669
670 sc = isl_schedule_constraints_apply(sc, umap);
671
672 res = isl_schedule_constraints_compute_schedule(sc);
673
674 contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
675 expansion = isl_schedule_copy(grouping.schedule);
676 res = isl_schedule_expand(res, contraction, expansion);
677
678 ppcg_grouping_clear(&grouping);
679 return res;
680 error:
681 ppcg_grouping_clear(&grouping);
682 isl_schedule_constraints_free(sc);
683 return NULL;
684 }
685