gpu_group.c: can_tile: return isl_bool
[ppcg.git] / grouping.c
blob51851c802ee1a15741f89172810d3a209d0ead3b
1 /*
2 * Copyright 2016 Sven Verdoolaege
4 * Use of this software is governed by the MIT license
6 * Written by Sven Verdoolaege.
7 */
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>
21 #include "grouping.h"
22 #include "schedule.h"
24 /* Internal data structure for use during the detection of statements
25 * that can be grouped.
27 * "sc" contains the original schedule constraints (not a copy).
28 * The validity constraints of "sc" are adjusted based on the groups
29 * found so far.
30 * "dep" contains the intersection of the validity and the proximity
31 * constraints in "sc". It may be NULL if it has not been computed yet.
32 * "group_id" is the identifier for the next group that is extracted.
34 * "domain" is the set of statement instances that belong to any of the groups.
35 * "contraction" maps the elements of "domain" to the corresponding group
36 * instances.
37 * "schedule" schedules the statements in each group relatively to each other.
38 * These last three fields are NULL if no groups have been found so far.
40 struct ppcg_grouping {
41 isl_schedule_constraints *sc;
43 isl_union_map *dep;
44 int group_id;
46 isl_union_set *domain;
47 isl_union_pw_multi_aff *contraction;
48 isl_schedule *schedule;
51 /* Clear all memory allocated by "grouping".
53 static void ppcg_grouping_clear(struct ppcg_grouping *grouping)
55 isl_union_map_free(grouping->dep);
56 isl_union_set_free(grouping->domain);
57 isl_union_pw_multi_aff_free(grouping->contraction);
58 isl_schedule_free(grouping->schedule);
61 /* Compute the intersection of the proximity and validity dependences
62 * in grouping->sc and store the result in grouping->dep, unless
63 * this intersection has been computed before.
65 static isl_stat ppcg_grouping_compute_dep(struct ppcg_grouping *grouping)
67 isl_union_map *validity, *proximity;
69 if (grouping->dep)
70 return isl_stat_ok;
72 validity = isl_schedule_constraints_get_validity(grouping->sc);
73 proximity = isl_schedule_constraints_get_proximity(grouping->sc);
74 grouping->dep = isl_union_map_intersect(validity, proximity);
76 if (!grouping->dep)
77 return isl_stat_error;
79 return isl_stat_ok;
82 /* Information extracted from one or more consecutive leaves
83 * in the input schedule.
85 * "list" contains the sets of statement instances in the leaves,
86 * one element in the list for each original leaf.
87 * "domain" contains the union of the sets in "list".
88 * "prefix" contains the prefix schedule of these elements.
90 struct ppcg_grouping_leaf {
91 isl_union_set *domain;
92 isl_union_set_list *list;
93 isl_multi_union_pw_aff *prefix;
96 /* Free all memory allocated for "leaves".
98 static void ppcg_grouping_leaf_free(int n, struct ppcg_grouping_leaf leaves[n])
100 int i;
102 if (!leaves)
103 return;
105 for (i = 0; i < n; ++i) {
106 isl_union_set_free(leaves[i].domain);
107 isl_union_set_list_free(leaves[i].list);
108 isl_multi_union_pw_aff_free(leaves[i].prefix);
111 free(leaves);
114 /* Short-hand for retrieving the prefix schedule at "node"
115 * in the form of an isl_multi_union_pw_aff.
117 static __isl_give isl_multi_union_pw_aff *get_prefix(
118 __isl_keep isl_schedule_node *node)
120 return isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
123 /* Return an array of "n" elements with information extracted from
124 * the "n" children of "node" starting at "first", all of which
125 * are known to be filtered leaves.
127 struct ppcg_grouping_leaf *extract_leaves(__isl_keep isl_schedule_node *node,
128 int first, int n)
130 int i;
131 isl_ctx *ctx;
132 struct ppcg_grouping_leaf *leaves;
134 if (!node)
135 return NULL;
137 ctx = isl_schedule_node_get_ctx(node);
138 leaves = isl_calloc_array(ctx, struct ppcg_grouping_leaf, n);
139 if (!leaves)
140 return NULL;
142 for (i = 0; i < n; ++i) {
143 isl_schedule_node *child;
144 isl_union_set *domain;
146 child = isl_schedule_node_get_child(node, first + i);
147 child = isl_schedule_node_child(child, 0);
148 domain = isl_schedule_node_get_domain(child);
149 leaves[i].domain = isl_union_set_copy(domain);
150 leaves[i].list = isl_union_set_list_from_union_set(domain);
151 leaves[i].prefix = get_prefix(child);
152 isl_schedule_node_free(child);
155 return leaves;
158 /* Internal data structure used by merge_leaves.
160 * "src" and "dst" point to the two consecutive leaves that are
161 * under investigation for being merged.
162 * "merge" is initially set to 0 and is set to 1 as soon as
163 * it turns out that it is useful to merge the two leaves.
165 struct ppcg_merge_leaves_data {
166 int merge;
167 struct ppcg_grouping_leaf *src;
168 struct ppcg_grouping_leaf *dst;
171 /* Given a relation "map" between instances of two statements A and B,
172 * does it relate every instance of A (according to the domain of "src")
173 * to every instance of B (according to the domain of "dst")?
175 static isl_bool covers_src_and_dst(__isl_keep isl_map *map,
176 struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
178 isl_space *space;
179 isl_set *set1, *set2;
180 isl_bool is_subset;
182 space = isl_space_domain(isl_map_get_space(map));
183 set1 = isl_union_set_extract_set(src->domain, space);
184 set2 = isl_map_domain(isl_map_copy(map));
185 is_subset = isl_set_is_subset(set1, set2);
186 isl_set_free(set1);
187 isl_set_free(set2);
188 if (is_subset < 0 || !is_subset)
189 return is_subset;
191 space = isl_space_range(isl_map_get_space(map));
192 set1 = isl_union_set_extract_set(dst->domain, space);
193 set2 = isl_map_range(isl_map_copy(map));
194 is_subset = isl_set_is_subset(set1, set2);
195 isl_set_free(set1);
196 isl_set_free(set2);
198 return is_subset;
201 /* Given a relation "map" between instances of two statements A and B,
202 * are pairs of related instances executed together in the input schedule?
203 * That is, is each pair of instances assigned the same value
204 * by the corresponding prefix schedules?
206 * In particular, select the subset of "map" that has pairs of elements
207 * with the same value for the prefix schedules and then check
208 * if "map" is still a subset of the result.
210 static isl_bool matches_prefix(__isl_keep isl_map *map,
211 struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
213 isl_union_map *umap, *equal;
214 isl_multi_union_pw_aff *src_prefix, *dst_prefix, *prefix;
215 isl_bool is_subset;
217 src_prefix = isl_multi_union_pw_aff_copy(src->prefix);
218 dst_prefix = isl_multi_union_pw_aff_copy(dst->prefix);
219 prefix = isl_multi_union_pw_aff_union_add(src_prefix, dst_prefix);
221 umap = isl_union_map_from_map(isl_map_copy(map));
222 equal = isl_union_map_copy(umap);
223 equal = isl_union_map_eq_at_multi_union_pw_aff(equal, prefix);
225 is_subset = isl_union_map_is_subset(umap, equal);
227 isl_union_map_free(umap);
228 isl_union_map_free(equal);
230 return is_subset;
233 /* Given a set of validity and proximity schedule constraints "map"
234 * between statements in consecutive leaves in a valid schedule,
235 * should the two leaves be merged into one?
237 * In particular, the two are merged if the constraints form
238 * a bijection between every instance of the first statement and
239 * every instance of the second statement. Moreover, each
240 * pair of such dependent instances needs to be executed consecutively
241 * in the input schedule. That is, they need to be assigned
242 * the same value by their prefix schedules.
244 * What this means is that for each instance of the first statement
245 * there is exactly one instance of the second statement that
246 * is executed immediately after the instance of the first statement and
247 * that, moreover, both depends on this statement instance and
248 * should be brought as close as possible to this statement instance.
249 * In other words, it is both possible to execute the two instances
250 * together (according to the input schedule) and desirable to do so
251 * (according to the validity and proximity schedule constraints).
253 static isl_stat check_merge(__isl_take isl_map *map, void *user)
255 struct ppcg_merge_leaves_data *data = user;
256 isl_bool ok;
258 ok = covers_src_and_dst(map, data->src, data->dst);
259 if (ok >= 0 && ok)
260 ok = isl_map_is_bijective(map);
261 if (ok >= 0 && ok)
262 ok = matches_prefix(map, data->src, data->dst);
264 isl_map_free(map);
266 if (ok < 0)
267 return isl_stat_error;
268 if (!ok)
269 return isl_stat_ok;
271 data->merge = 1;
272 return isl_stat_error;
275 /* Merge the leaves at position "pos" and "pos + 1" in "leaves".
277 static isl_stat merge_pair(int n, struct ppcg_grouping_leaf leaves[n], int pos)
279 int i;
281 leaves[pos].domain = isl_union_set_union(leaves[pos].domain,
282 leaves[pos + 1].domain);
283 leaves[pos].list = isl_union_set_list_concat(leaves[pos].list,
284 leaves[pos + 1].list);
285 leaves[pos].prefix = isl_multi_union_pw_aff_union_add(
286 leaves[pos].prefix, leaves[pos + 1].prefix);
287 for (i = pos + 1; i + 1 < n; ++i)
288 leaves[i] = leaves[i + 1];
289 leaves[n - 1].domain = NULL;
290 leaves[n - 1].list = NULL;
291 leaves[n - 1].prefix = NULL;
293 if (!leaves[pos].domain || !leaves[pos].list || !leaves[pos].prefix)
294 return isl_stat_error;
296 return isl_stat_ok;
299 /* Merge pairs of consecutive leaves in "leaves" taking into account
300 * the intersection of validity and proximity schedule constraints "dep".
302 * If a leaf has been merged with the next leaf, then the combination
303 * is checked again for merging with the next leaf.
304 * That is, if the leaves are A, B and C, then B may not have been
305 * merged with C, but after merging A and B, it could still be useful
306 * to merge the combination AB with C.
308 * Two leaves A and B are merged if there are instances of at least
309 * one pair of statements, one statement in A and one B, such that
310 * the validity and proximity schedule constraints between them
311 * make them suitable for merging according to check_merge.
313 * Return the final number of leaves in the sequence, or -1 on error.
315 static int merge_leaves(int n, struct ppcg_grouping_leaf leaves[n],
316 __isl_keep isl_union_map *dep)
318 int i;
319 struct ppcg_merge_leaves_data data;
321 for (i = n - 1; i >= 0; --i) {
322 isl_union_map *dep_i;
323 isl_stat ok;
325 if (i + 1 >= n)
326 continue;
328 dep_i = isl_union_map_copy(dep);
329 dep_i = isl_union_map_intersect_domain(dep_i,
330 isl_union_set_copy(leaves[i].domain));
331 dep_i = isl_union_map_intersect_range(dep_i,
332 isl_union_set_copy(leaves[i + 1].domain));
333 data.merge = 0;
334 data.src = &leaves[i];
335 data.dst = &leaves[i + 1];
336 ok = isl_union_map_foreach_map(dep_i, &check_merge, &data);
337 isl_union_map_free(dep_i);
338 if (ok < 0 && !data.merge)
339 return -1;
340 if (!data.merge)
341 continue;
342 if (merge_pair(n, leaves, i) < 0)
343 return -1;
344 --n;
345 ++i;
348 return n;
351 /* Construct a schedule with "domain" as domain, that executes
352 * the elements of "list" in order (as a sequence).
354 static __isl_give isl_schedule *schedule_from_domain_and_list(
355 __isl_keep isl_union_set *domain, __isl_keep isl_union_set_list *list)
357 isl_schedule *schedule;
358 isl_schedule_node *node;
360 schedule = isl_schedule_from_domain(isl_union_set_copy(domain));
361 node = isl_schedule_get_root(schedule);
362 isl_schedule_free(schedule);
363 node = isl_schedule_node_child(node, 0);
364 list = isl_union_set_list_copy(list);
365 node = isl_schedule_node_insert_sequence(node, list);
366 schedule = isl_schedule_node_get_schedule(node);
367 isl_schedule_node_free(node);
369 return schedule;
372 /* Construct a unique identifier for a group in "grouping".
374 * The name is of the form G_n, with n the first value starting at
375 * grouping->group_id that does not result in an identifier
376 * that is already in use in the domain of the original schedule
377 * constraints.
379 static isl_id *construct_group_id(struct ppcg_grouping *grouping,
380 __isl_take isl_space *space)
382 isl_ctx *ctx;
383 isl_id *id;
384 isl_bool empty;
385 isl_union_set *domain;
387 if (!space)
388 return NULL;
390 ctx = isl_space_get_ctx(space);
391 domain = isl_schedule_constraints_get_domain(grouping->sc);
393 do {
394 char buffer[20];
395 isl_id *id;
396 isl_set *set;
398 snprintf(buffer, sizeof(buffer), "G_%d", grouping->group_id);
399 grouping->group_id++;
400 id = isl_id_alloc(ctx, buffer, NULL);
401 space = isl_space_set_tuple_id(space, isl_dim_set, id);
402 set = isl_union_set_extract_set(domain, isl_space_copy(space));
403 empty = isl_set_plain_is_empty(set);
404 isl_set_free(set);
405 } while (empty >= 0 && !empty);
407 if (empty < 0)
408 space = isl_space_free(space);
410 id = isl_space_get_tuple_id(space, isl_dim_set);
412 isl_space_free(space);
413 isl_union_set_free(domain);
415 return id;
418 /* Construct a contraction from "prefix" and "domain" for a new group
419 * in "grouping".
421 * The values of the prefix schedule "prefix" are used as instances
422 * of the new group. The identifier of the group is constructed
423 * in such a way that it does not conflict with those of earlier
424 * groups nor with statements in the domain of the original
425 * schedule constraints.
426 * The isl_multi_union_pw_aff "prefix" then simply needs to be
427 * converted to an isl_union_pw_multi_aff. However, this is not
428 * possible if "prefix" is zero-dimensional, so in this case,
429 * a contraction is constructed from "domain" instead.
431 static isl_union_pw_multi_aff *group_contraction_from_prefix_and_domain(
432 struct ppcg_grouping *grouping,
433 __isl_keep isl_multi_union_pw_aff *prefix,
434 __isl_keep isl_union_set *domain)
436 isl_id *id;
437 isl_space *space;
438 int dim;
440 space = isl_multi_union_pw_aff_get_space(prefix);
441 if (!space)
442 return NULL;
443 dim = isl_space_dim(space, isl_dim_set);
444 id = construct_group_id(grouping, space);
445 if (dim == 0) {
446 isl_multi_val *mv;
448 space = isl_multi_union_pw_aff_get_space(prefix);
449 space = isl_space_set_tuple_id(space, isl_dim_set, id);
450 mv = isl_multi_val_zero(space);
451 domain = isl_union_set_copy(domain);
452 return isl_union_pw_multi_aff_multi_val_on_domain(domain, mv);
454 prefix = isl_multi_union_pw_aff_copy(prefix);
455 prefix = isl_multi_union_pw_aff_set_tuple_id(prefix, isl_dim_out, id);
456 return isl_union_pw_multi_aff_from_multi_union_pw_aff(prefix);
459 /* Remove the validity schedule constraints from "sc" between
460 * statement instances that get contracted to the same group instance
461 * by the contraction described by "prefix" and "domain".
463 * The values of the prefix schedule "prefix" are used as instances
464 * of the new group. This means that validity schedule constraints
465 * between instances with the same prefix schedule value need to be removed.
466 * If "prefix" is zero-dimensional, then it does not contain any
467 * information about the domain. Instead, those schedule constraints
468 * are removed that connect pairs of instances in "domain".
470 static __isl_give isl_schedule_constraints *remove_group_validity(
471 __isl_take isl_schedule_constraints *sc,
472 __isl_keep isl_multi_union_pw_aff *prefix,
473 __isl_keep isl_union_set *domain)
475 int n;
476 isl_union_map *validity, *joined;
478 validity = isl_schedule_constraints_get_validity(sc);
479 joined = isl_union_map_copy(validity);
480 n = isl_multi_union_pw_aff_dim(prefix, isl_dim_out);
481 if (n == 0) {
482 joined = isl_union_map_intersect_domain(joined,
483 isl_union_set_copy(domain));
484 joined = isl_union_map_intersect_range(joined,
485 isl_union_set_copy(domain));
486 } else {
487 joined = isl_union_map_eq_at_multi_union_pw_aff(joined,
488 isl_multi_union_pw_aff_copy(prefix));
490 validity = isl_union_map_subtract(validity, joined);
491 sc = isl_schedule_constraints_set_validity(sc, validity);
492 return sc;
495 /* Extend "grouping" with groups corresponding to merged
496 * leaves in the list of potentially merged leaves "leaves".
498 * The "list" field of each element in "leaves" contains a list
499 * of the instances sets of the original leaves that have been
500 * merged into this element. If at least two of the original leaves
501 * have been merged into a given element, then add the corresponding
502 * group to "grouping" and remove validity schedule constraints
503 * between statement instances that get mapped to the same group instance.
504 * In particular, the domain is extended with the statement instances
505 * of the merged leaves, the contraction is extended with a mapping
506 * of these statement instances to instances of a new group and
507 * the schedule is extended with a schedule that executes
508 * the statement instances according to the order of the leaves
509 * in which they appear.
510 * Since the instances of the groups should already be scheduled apart
511 * in the schedule into which this schedule will be plugged in,
512 * the schedules of the individual groups are combined independently
513 * of each other (as a set).
515 static isl_stat add_groups(struct ppcg_grouping *grouping,
516 int n, struct ppcg_grouping_leaf leaves[n])
518 int i;
520 for (i = 0; i < n; ++i) {
521 int n_leaf;
522 isl_schedule *schedule;
523 isl_union_set *domain;
524 isl_union_pw_multi_aff *upma;
526 n_leaf = isl_union_set_list_n_union_set(leaves[i].list);
527 if (n_leaf < 0)
528 return isl_stat_error;
529 if (n_leaf <= 1)
530 continue;
531 schedule = schedule_from_domain_and_list(leaves[i].domain,
532 leaves[i].list);
533 upma = group_contraction_from_prefix_and_domain(grouping,
534 leaves[i].prefix, leaves[i].domain);
535 grouping->sc = remove_group_validity(grouping->sc,
536 leaves[i].prefix, leaves[i].domain);
538 domain = isl_union_set_copy(leaves[i].domain);
539 if (grouping->domain) {
540 domain = isl_union_set_union(domain, grouping->domain);
541 upma = isl_union_pw_multi_aff_union_add(upma,
542 grouping->contraction);
543 schedule = isl_schedule_set(schedule,
544 grouping->schedule);
546 grouping->domain = domain;
547 grouping->contraction = upma;
548 grouping->schedule = schedule;
550 if (!grouping->domain || !grouping->contraction ||
551 !grouping->schedule)
552 return isl_stat_error;
555 return isl_stat_ok;
558 /* Look for any pairs of consecutive leaves among the "n" children of "node"
559 * starting at "first" that should be merged together.
560 * Store the results in "grouping".
562 * First make sure the intersection of validity and proximity
563 * schedule constraints is available and extract the required
564 * information from the "n" leaves.
565 * Then try and merge consecutive leaves based on the validity
566 * and proximity constraints.
567 * If any pairs were successfully merged, then add groups
568 * corresponding to the merged leaves to "grouping".
570 static isl_stat group_subsequence(__isl_keep isl_schedule_node *node,
571 int first, int n, struct ppcg_grouping *grouping)
573 int n_merge;
574 struct ppcg_grouping_leaf *leaves;
576 if (ppcg_grouping_compute_dep(grouping) < 0)
577 return isl_stat_error;
579 leaves = extract_leaves(node, first, n);
580 if (!leaves)
581 return isl_stat_error;
583 n_merge = merge_leaves(n, leaves, grouping->dep);
584 if (n_merge >= 0 && n_merge < n &&
585 add_groups(grouping, n_merge, leaves) < 0)
586 return isl_stat_error;
588 ppcg_grouping_leaf_free(n, leaves);
590 return isl_stat_ok;
593 /* If "node" is a sequence, then check if it has any consecutive
594 * leaves that should be merged together and store the results
595 * in "grouping".
597 * In particular, call group_subsequence on each consecutive
598 * sequence of (filtered) leaves among the children of "node".
600 static isl_bool detect_groups(__isl_keep isl_schedule_node *node, void *user)
602 int i, n, first;
603 struct ppcg_grouping *grouping = user;
605 if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
606 return isl_bool_true;
608 n = isl_schedule_node_n_children(node);
609 if (n < 0)
610 return isl_bool_error;
612 first = -1;
613 for (i = 0; i < n; ++i) {
614 isl_schedule_node *child;
615 enum isl_schedule_node_type type;
617 child = isl_schedule_node_get_child(node, i);
618 child = isl_schedule_node_child(child, 0);
619 type = isl_schedule_node_get_type(child);
620 isl_schedule_node_free(child);
622 if (first >= 0 && type != isl_schedule_node_leaf) {
623 if (group_subsequence(node, first, i - first,
624 grouping) < 0)
625 return isl_bool_error;
626 first = -1;
628 if (first < 0 && type == isl_schedule_node_leaf)
629 first = i;
631 if (first >= 0) {
632 if (group_subsequence(node, first, n - first, grouping) < 0)
633 return isl_bool_error;
636 return isl_bool_true;
639 /* Complete "grouping" to cover all statement instances in the domain
640 * of grouping->sc.
642 * In particular, grouping->domain is set to the full set of statement
643 * instances; group->contraction is extended with an identity
644 * contraction on the additional instances and group->schedule
645 * is extended with an independent schedule on those additional instances.
646 * In the extension of group->contraction, the additional instances
647 * are split into those belong to different statements and those
648 * that belong to some of the same statements. The first group
649 * is replaced by its universe in order to simplify the contraction extension.
651 static void complete_grouping(struct ppcg_grouping *grouping)
653 isl_union_set *domain, *left, *overlap;
654 isl_union_pw_multi_aff *upma;
655 isl_schedule *schedule;
657 domain = isl_schedule_constraints_get_domain(grouping->sc);
658 left = isl_union_set_subtract(isl_union_set_copy(domain),
659 isl_union_set_copy(grouping->domain));
660 schedule = isl_schedule_from_domain(isl_union_set_copy(left));
661 schedule = isl_schedule_set(schedule, grouping->schedule);
662 grouping->schedule = schedule;
664 overlap = isl_union_set_universe(grouping->domain);
665 grouping->domain = domain;
666 overlap = isl_union_set_intersect(isl_union_set_copy(left), overlap);
667 left = isl_union_set_subtract(left, isl_union_set_copy(overlap));
668 left = isl_union_set_universe(left);
669 left = isl_union_set_union(left, overlap);
670 upma = isl_union_set_identity_union_pw_multi_aff(left);
671 upma = isl_union_pw_multi_aff_union_add(upma, grouping->contraction);
672 grouping->contraction = upma;
675 /* Compute a schedule on the domain of "sc" that respects the schedule
676 * constraints in "sc", after trying to combine groups of statements.
678 * "schedule" is a known correct schedule that is used while combining
679 * groups of statements.
680 * In particular, statements that are executed consecutively in a sequence
681 * in this schedule and where all instances of the second depend on
682 * the instance of the first that is executed in the same iteration
683 * of outer band nodes are grouped together into a single statement.
684 * The schedule constraints are then mapped to these groups of statements
685 * and the resulting schedule is expanded again to refer to the original
686 * statements.
688 __isl_give isl_schedule *ppcg_compute_grouping_schedule(
689 __isl_take isl_schedule_constraints *sc,
690 __isl_keep isl_schedule *schedule, struct ppcg_options *options)
692 struct ppcg_grouping grouping = { sc };
693 isl_union_pw_multi_aff *contraction;
694 isl_union_map *umap;
695 isl_schedule *res, *expansion;
697 grouping.group_id = 0;
698 if (isl_schedule_foreach_schedule_node_top_down(schedule,
699 &detect_groups, &grouping) < 0)
700 goto error;
701 if (!grouping.contraction) {
702 ppcg_grouping_clear(&grouping);
703 return ppcg_compute_non_grouping_schedule(grouping.sc, options);
705 complete_grouping(&grouping);
706 contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
707 umap = isl_union_map_from_union_pw_multi_aff(contraction);
709 sc = isl_schedule_constraints_apply(grouping.sc, umap);
711 res = ppcg_compute_non_grouping_schedule(sc, options);
713 contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
714 expansion = isl_schedule_copy(grouping.schedule);
715 res = isl_schedule_expand(res, contraction, expansion);
717 ppcg_grouping_clear(&grouping);
718 return res;
719 error:
720 ppcg_grouping_clear(&grouping);
721 isl_schedule_constraints_free(sc);
722 return NULL;