1 /*
2 * Copyright 2013-2014 Ecole Normale Superieure
3 * Copyright 2014 INRIA Rocquencourt
4 * Copyright 2016 Sven Verdoolaege
5 *
6 * Use of this software is governed by the MIT license
7 *
8 * Written by Sven Verdoolaege,
9 * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
10 * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
11 * B.P. 105 - 78153 Le Chesnay, France
12 */
13
14 #include <isl/id.h>
15 #include <isl/val.h>
16 #include <isl/space.h>
17 #include <isl/set.h>
18 #include <isl_schedule_band.h>
19 #include <isl_schedule_private.h>
20 #include <isl_schedule_node_private.h>
21
22 /* Create a new schedule node in the given schedule, point at the given
23 * tree with given ancestors and child positions.
24 * "child_pos" may be NULL if there are no ancestors.
25 */
isl_schedule_node_alloc(__isl_take isl_schedule * schedule,__isl_take isl_schedule_tree * tree,__isl_take isl_schedule_tree_list * ancestors,int * child_pos)26 __isl_give isl_schedule_node *isl_schedule_node_alloc(
27 __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree,
28 __isl_take isl_schedule_tree_list *ancestors, int *child_pos)
29 {
30 isl_ctx *ctx;
31 isl_schedule_node *node;
32 int i;
33 isl_size n;
34
35 n = isl_schedule_tree_list_n_schedule_tree(ancestors);
36 if (!schedule || !tree || n < 0)
37 goto error;
38 if (n > 0 && !child_pos)
39 goto error;
40 ctx = isl_schedule_get_ctx(schedule);
41 node = isl_calloc_type(ctx, isl_schedule_node);
42 if (!node)
43 goto error;
44 node->ref = 1;
45 node->schedule = schedule;
46 node->tree = tree;
47 node->ancestors = ancestors;
48 node->child_pos = isl_alloc_array(ctx, int, n);
49 if (n && !node->child_pos)
50 return isl_schedule_node_free(node);
51 for (i = 0; i < n; ++i)
52 node->child_pos[i] = child_pos[i];
53
54 return node;
55 error:
56 isl_schedule_free(schedule);
57 isl_schedule_tree_free(tree);
58 isl_schedule_tree_list_free(ancestors);
59 return NULL;
60 }
61
62 /* Return a pointer to the root of a schedule tree with as single
63 * node a domain node with the given domain.
64 */
isl_schedule_node_from_domain(__isl_take isl_union_set * domain)65 __isl_give isl_schedule_node *isl_schedule_node_from_domain(
66 __isl_take isl_union_set *domain)
67 {
68 isl_schedule *schedule;
69 isl_schedule_node *node;
70
71 schedule = isl_schedule_from_domain(domain);
72 node = isl_schedule_get_root(schedule);
73 isl_schedule_free(schedule);
74
75 return node;
76 }
77
78 /* Return a pointer to the root of a schedule tree with as single
79 * node a extension node with the given extension.
80 */
isl_schedule_node_from_extension(__isl_take isl_union_map * extension)81 __isl_give isl_schedule_node *isl_schedule_node_from_extension(
82 __isl_take isl_union_map *extension)
83 {
84 isl_ctx *ctx;
85 isl_schedule *schedule;
86 isl_schedule_tree *tree;
87 isl_schedule_node *node;
88
89 if (!extension)
90 return NULL;
91
92 ctx = isl_union_map_get_ctx(extension);
93 tree = isl_schedule_tree_from_extension(extension);
94 schedule = isl_schedule_from_schedule_tree(ctx, tree);
95 node = isl_schedule_get_root(schedule);
96 isl_schedule_free(schedule);
97
98 return node;
99 }
100
101 /* Return the isl_ctx to which "node" belongs.
102 */
isl_schedule_node_get_ctx(__isl_keep isl_schedule_node * node)103 isl_ctx *isl_schedule_node_get_ctx(__isl_keep isl_schedule_node *node)
104 {
105 return node ? isl_schedule_get_ctx(node->schedule) : NULL;
106 }
107
108 /* Return a pointer to the leaf of the schedule into which "node" points.
109 */
isl_schedule_node_peek_leaf(__isl_keep isl_schedule_node * node)110 __isl_keep isl_schedule_tree *isl_schedule_node_peek_leaf(
111 __isl_keep isl_schedule_node *node)
112 {
113 return node ? isl_schedule_peek_leaf(node->schedule) : NULL;
114 }
115
116 /* Return a copy of the leaf of the schedule into which "node" points.
117 */
isl_schedule_node_get_leaf(__isl_keep isl_schedule_node * node)118 __isl_give isl_schedule_tree *isl_schedule_node_get_leaf(
119 __isl_keep isl_schedule_node *node)
120 {
121 return isl_schedule_tree_copy(isl_schedule_node_peek_leaf(node));
122 }
123
124 /* Return the type of the node or isl_schedule_node_error on error.
125 */
isl_schedule_node_get_type(__isl_keep isl_schedule_node * node)126 enum isl_schedule_node_type isl_schedule_node_get_type(
127 __isl_keep isl_schedule_node *node)
128 {
129 return node ? isl_schedule_tree_get_type(node->tree)
130 : isl_schedule_node_error;
131 }
132
133 /* Return the type of the parent of "node" or isl_schedule_node_error on error.
134 */
isl_schedule_node_get_parent_type(__isl_keep isl_schedule_node * node)135 enum isl_schedule_node_type isl_schedule_node_get_parent_type(
136 __isl_keep isl_schedule_node *node)
137 {
138 isl_size n;
139 int pos;
140 int has_parent;
141 isl_schedule_tree *parent;
142 enum isl_schedule_node_type type;
143
144 if (!node)
145 return isl_schedule_node_error;
146 has_parent = isl_schedule_node_has_parent(node);
147 if (has_parent < 0)
148 return isl_schedule_node_error;
149 if (!has_parent)
150 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
151 "node has no parent", return isl_schedule_node_error);
152 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
153 if (n < 0)
154 return isl_schedule_node_error;
155
156 pos = n - 1;
157 parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors, pos);
158 type = isl_schedule_tree_get_type(parent);
159 isl_schedule_tree_free(parent);
160
161 return type;
162 }
163
164 /* Return a copy of the subtree that this node points to.
165 */
isl_schedule_node_get_tree(__isl_keep isl_schedule_node * node)166 __isl_give isl_schedule_tree *isl_schedule_node_get_tree(
167 __isl_keep isl_schedule_node *node)
168 {
169 if (!node)
170 return NULL;
171
172 return isl_schedule_tree_copy(node->tree);
173 }
174
175 /* Return a copy of the schedule into which "node" points.
176 */
isl_schedule_node_get_schedule(__isl_keep isl_schedule_node * node)177 __isl_give isl_schedule *isl_schedule_node_get_schedule(
178 __isl_keep isl_schedule_node *node)
179 {
180 if (!node)
181 return NULL;
182 return isl_schedule_copy(node->schedule);
183 }
184
185 /* Return a fresh copy of "node".
186 */
isl_schedule_node_dup(__isl_keep isl_schedule_node * node)187 __isl_take isl_schedule_node *isl_schedule_node_dup(
188 __isl_keep isl_schedule_node *node)
189 {
190 if (!node)
191 return NULL;
192
193 return isl_schedule_node_alloc(isl_schedule_copy(node->schedule),
194 isl_schedule_tree_copy(node->tree),
195 isl_schedule_tree_list_copy(node->ancestors),
196 node->child_pos);
197 }
198
199 /* Return an isl_schedule_node that is equal to "node" and that has only
200 * a single reference.
201 */
isl_schedule_node_cow(__isl_take isl_schedule_node * node)202 __isl_give isl_schedule_node *isl_schedule_node_cow(
203 __isl_take isl_schedule_node *node)
204 {
205 if (!node)
206 return NULL;
207
208 if (node->ref == 1)
209 return node;
210 node->ref--;
211 return isl_schedule_node_dup(node);
212 }
213
214 /* Return a new reference to "node".
215 */
isl_schedule_node_copy(__isl_keep isl_schedule_node * node)216 __isl_give isl_schedule_node *isl_schedule_node_copy(
217 __isl_keep isl_schedule_node *node)
218 {
219 if (!node)
220 return NULL;
221
222 node->ref++;
223 return node;
224 }
225
226 /* Free "node" and return NULL.
227 */
isl_schedule_node_free(__isl_take isl_schedule_node * node)228 __isl_null isl_schedule_node *isl_schedule_node_free(
229 __isl_take isl_schedule_node *node)
230 {
231 if (!node)
232 return NULL;
233 if (--node->ref > 0)
234 return NULL;
235
236 isl_schedule_tree_list_free(node->ancestors);
237 free(node->child_pos);
238 isl_schedule_tree_free(node->tree);
239 isl_schedule_free(node->schedule);
240 free(node);
241
242 return NULL;
243 }
244
245 /* Do "node1" and "node2" point to the same position in the same
246 * schedule?
247 */
isl_schedule_node_is_equal(__isl_keep isl_schedule_node * node1,__isl_keep isl_schedule_node * node2)248 isl_bool isl_schedule_node_is_equal(__isl_keep isl_schedule_node *node1,
249 __isl_keep isl_schedule_node *node2)
250 {
251 int i;
252 isl_size n1, n2;
253
254 if (!node1 || !node2)
255 return isl_bool_error;
256 if (node1 == node2)
257 return isl_bool_true;
258 if (node1->schedule != node2->schedule)
259 return isl_bool_false;
260
261 n1 = isl_schedule_node_get_tree_depth(node1);
262 n2 = isl_schedule_node_get_tree_depth(node2);
263 if (n1 < 0 || n2 < 0)
264 return isl_bool_error;
265 if (n1 != n2)
266 return isl_bool_false;
267 for (i = 0; i < n1; ++i)
268 if (node1->child_pos[i] != node2->child_pos[i])
269 return isl_bool_false;
270
271 return isl_bool_true;
272 }
273
274 /* Return the number of outer schedule dimensions of "node"
275 * in its schedule tree.
276 *
277 * Return isl_size_error on error.
278 */
isl_schedule_node_get_schedule_depth(__isl_keep isl_schedule_node * node)279 isl_size isl_schedule_node_get_schedule_depth(
280 __isl_keep isl_schedule_node *node)
281 {
282 int i;
283 isl_size n;
284 int depth = 0;
285
286 if (!node)
287 return isl_size_error;
288
289 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
290 if (n < 0)
291 return isl_size_error;
292 for (i = n - 1; i >= 0; --i) {
293 isl_schedule_tree *tree;
294 isl_size n;
295
296 tree = isl_schedule_tree_list_get_schedule_tree(
297 node->ancestors, i);
298 if (!tree)
299 return isl_size_error;
300 n = 0;
301 if (tree->type == isl_schedule_node_band)
302 n = isl_schedule_tree_band_n_member(tree);
303 depth += n;
304 isl_schedule_tree_free(tree);
305 if (n < 0)
306 return isl_size_error;
307 }
308
309 return depth;
310 }
311
312 /* Internal data structure for
313 * isl_schedule_node_get_prefix_schedule_union_pw_multi_aff
314 *
315 * "initialized" is set if the filter field has been initialized.
316 * If "universe_domain" is not set, then the collected filter is intersected
317 * with the domain of the root domain node.
318 * "universe_filter" is set if we are only collecting the universes of filters
319 * "collect_prefix" is set if we are collecting prefixes.
320 * "filter" collects all outer filters and is NULL until "initialized" is set.
321 * "prefix" collects all outer band partial schedules (if "collect_prefix"
322 * is set). If it is used, then it is initialized by the caller
323 * of collect_filter_prefix to a zero-dimensional function.
324 */
325 struct isl_schedule_node_get_filter_prefix_data {
326 int initialized;
327 int universe_domain;
328 int universe_filter;
329 int collect_prefix;
330 isl_union_set *filter;
331 isl_multi_union_pw_aff *prefix;
332 };
333
334 static isl_stat collect_filter_prefix(__isl_keep isl_schedule_tree_list *list,
335 int n, struct isl_schedule_node_get_filter_prefix_data *data);
336
337 /* Update the filter and prefix information in "data" based on the first "n"
338 * elements in "list" and the expansion tree root "tree".
339 *
340 * We first collect the information from the elements in "list",
341 * initializing the filter based on the domain of the expansion.
342 * Then we map the results to the expanded space and combined them
343 * with the results already in "data".
344 */
collect_filter_prefix_expansion(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_tree_list * list,int n,struct isl_schedule_node_get_filter_prefix_data * data)345 static isl_stat collect_filter_prefix_expansion(
346 __isl_take isl_schedule_tree *tree,
347 __isl_keep isl_schedule_tree_list *list, int n,
348 struct isl_schedule_node_get_filter_prefix_data *data)
349 {
350 struct isl_schedule_node_get_filter_prefix_data contracted;
351 isl_union_pw_multi_aff *c;
352 isl_union_map *exp, *universe;
353 isl_union_set *filter;
354
355 c = isl_schedule_tree_expansion_get_contraction(tree);
356 exp = isl_schedule_tree_expansion_get_expansion(tree);
357
358 contracted.initialized = 1;
359 contracted.universe_domain = data->universe_domain;
360 contracted.universe_filter = data->universe_filter;
361 contracted.collect_prefix = data->collect_prefix;
362 universe = isl_union_map_universe(isl_union_map_copy(exp));
363 filter = isl_union_map_domain(universe);
364 if (data->collect_prefix) {
365 isl_space *space = isl_union_set_get_space(filter);
366 space = isl_space_set_from_params(space);
367 contracted.prefix = isl_multi_union_pw_aff_zero(space);
368 }
369 contracted.filter = filter;
370
371 if (collect_filter_prefix(list, n, &contracted) < 0)
372 contracted.filter = isl_union_set_free(contracted.filter);
373 if (data->collect_prefix) {
374 isl_multi_union_pw_aff *prefix;
375
376 prefix = contracted.prefix;
377 prefix =
378 isl_multi_union_pw_aff_pullback_union_pw_multi_aff(prefix,
379 isl_union_pw_multi_aff_copy(c));
380 data->prefix = isl_multi_union_pw_aff_flat_range_product(
381 prefix, data->prefix);
382 }
383 filter = contracted.filter;
384 if (data->universe_domain)
385 filter = isl_union_set_preimage_union_pw_multi_aff(filter,
386 isl_union_pw_multi_aff_copy(c));
387 else
388 filter = isl_union_set_apply(filter, isl_union_map_copy(exp));
389 if (!data->initialized)
390 data->filter = filter;
391 else
392 data->filter = isl_union_set_intersect(filter, data->filter);
393 data->initialized = 1;
394
395 isl_union_pw_multi_aff_free(c);
396 isl_union_map_free(exp);
397 isl_schedule_tree_free(tree);
398
399 return isl_stat_ok;
400 }
401
402 /* Update the filter information in "data" based on the first "n"
403 * elements in "list" and the extension tree root "tree", in case
404 * data->universe_domain is set and data->collect_prefix is not.
405 *
406 * We collect the universe domain of the elements in "list" and
407 * add it to the universe range of the extension (intersected
408 * with the already collected filter, if any).
409 */
collect_universe_domain_extension(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_tree_list * list,int n,struct isl_schedule_node_get_filter_prefix_data * data)410 static isl_stat collect_universe_domain_extension(
411 __isl_take isl_schedule_tree *tree,
412 __isl_keep isl_schedule_tree_list *list, int n,
413 struct isl_schedule_node_get_filter_prefix_data *data)
414 {
415 struct isl_schedule_node_get_filter_prefix_data data_outer;
416 isl_union_map *extension;
417 isl_union_set *filter;
418
419 data_outer.initialized = 0;
420 data_outer.universe_domain = 1;
421 data_outer.universe_filter = data->universe_filter;
422 data_outer.collect_prefix = 0;
423 data_outer.filter = NULL;
424 data_outer.prefix = NULL;
425
426 if (collect_filter_prefix(list, n, &data_outer) < 0)
427 data_outer.filter = isl_union_set_free(data_outer.filter);
428
429 extension = isl_schedule_tree_extension_get_extension(tree);
430 extension = isl_union_map_universe(extension);
431 filter = isl_union_map_range(extension);
432 if (data_outer.initialized)
433 filter = isl_union_set_union(filter, data_outer.filter);
434 if (data->initialized)
435 filter = isl_union_set_intersect(filter, data->filter);
436
437 data->filter = filter;
438
439 isl_schedule_tree_free(tree);
440
441 return isl_stat_ok;
442 }
443
444 /* Update "data" based on the tree node "tree" in case "data" has
445 * not been initialized yet.
446 *
447 * Return 0 on success and -1 on error.
448 *
449 * If "tree" is a filter, then we set data->filter to this filter
450 * (or its universe).
451 * If "tree" is a domain, then this means we have reached the root
452 * of the schedule tree without being able to extract any information.
453 * We therefore initialize data->filter to the universe of the domain,
454 * or the domain itself if data->universe_domain is not set.
455 * If "tree" is a band with at least one member, then we set data->filter
456 * to the universe of the schedule domain and replace the zero-dimensional
457 * data->prefix by the band schedule (if data->collect_prefix is set).
458 */
collect_filter_prefix_init(__isl_keep isl_schedule_tree * tree,struct isl_schedule_node_get_filter_prefix_data * data)459 static isl_stat collect_filter_prefix_init(__isl_keep isl_schedule_tree *tree,
460 struct isl_schedule_node_get_filter_prefix_data *data)
461 {
462 enum isl_schedule_node_type type;
463 isl_multi_union_pw_aff *mupa;
464 isl_union_set *filter;
465 isl_size n;
466
467 type = isl_schedule_tree_get_type(tree);
468 switch (type) {
469 case isl_schedule_node_error:
470 return isl_stat_error;
471 case isl_schedule_node_expansion:
472 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
473 "should be handled by caller", return isl_stat_error);
474 case isl_schedule_node_extension:
475 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
476 "cannot handle extension nodes", return isl_stat_error);
477 case isl_schedule_node_context:
478 case isl_schedule_node_leaf:
479 case isl_schedule_node_guard:
480 case isl_schedule_node_mark:
481 case isl_schedule_node_sequence:
482 case isl_schedule_node_set:
483 return isl_stat_ok;
484 case isl_schedule_node_domain:
485 filter = isl_schedule_tree_domain_get_domain(tree);
486 if (data->universe_domain)
487 filter = isl_union_set_universe(filter);
488 data->filter = filter;
489 break;
490 case isl_schedule_node_band:
491 n = isl_schedule_tree_band_n_member(tree);
492 if (n < 0)
493 return isl_stat_error;
494 if (n == 0)
495 return isl_stat_ok;
496 mupa = isl_schedule_tree_band_get_partial_schedule(tree);
497 if (data->collect_prefix) {
498 isl_multi_union_pw_aff_free(data->prefix);
499 mupa = isl_multi_union_pw_aff_reset_tuple_id(mupa,
500 isl_dim_set);
501 data->prefix = isl_multi_union_pw_aff_copy(mupa);
502 }
503 filter = isl_multi_union_pw_aff_domain(mupa);
504 filter = isl_union_set_universe(filter);
505 data->filter = filter;
506 break;
507 case isl_schedule_node_filter:
508 filter = isl_schedule_tree_filter_get_filter(tree);
509 if (data->universe_filter)
510 filter = isl_union_set_universe(filter);
511 data->filter = filter;
512 break;
513 }
514
515 if ((data->collect_prefix && !data->prefix) || !data->filter)
516 return isl_stat_error;
517
518 data->initialized = 1;
519
520 return isl_stat_ok;
521 }
522
523 /* Update "data" based on the tree node "tree" in case "data" has
524 * already been initialized.
525 *
526 * Return 0 on success and -1 on error.
527 *
528 * If "tree" is a domain and data->universe_domain is not set, then
529 * intersect data->filter with the domain.
530 * If "tree" is a filter, then we intersect data->filter with this filter
531 * (or its universe).
532 * If "tree" is a band with at least one member and data->collect_prefix
533 * is set, then we extend data->prefix with the band schedule.
534 * If "tree" is an extension, then we make sure that we are not collecting
535 * information on any extended domain elements.
536 */
collect_filter_prefix_update(__isl_keep isl_schedule_tree * tree,struct isl_schedule_node_get_filter_prefix_data * data)537 static isl_stat collect_filter_prefix_update(__isl_keep isl_schedule_tree *tree,
538 struct isl_schedule_node_get_filter_prefix_data *data)
539 {
540 enum isl_schedule_node_type type;
541 isl_multi_union_pw_aff *mupa;
542 isl_union_set *filter;
543 isl_union_map *extension;
544 isl_bool empty;
545 isl_size n;
546
547 type = isl_schedule_tree_get_type(tree);
548 switch (type) {
549 case isl_schedule_node_error:
550 return isl_stat_error;
551 case isl_schedule_node_expansion:
552 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
553 "should be handled by caller", return isl_stat_error);
554 case isl_schedule_node_extension:
555 extension = isl_schedule_tree_extension_get_extension(tree);
556 extension = isl_union_map_intersect_range(extension,
557 isl_union_set_copy(data->filter));
558 empty = isl_union_map_is_empty(extension);
559 isl_union_map_free(extension);
560 if (empty < 0)
561 return isl_stat_error;
562 if (empty)
563 break;
564 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
565 "cannot handle extension nodes", return isl_stat_error);
566 case isl_schedule_node_context:
567 case isl_schedule_node_leaf:
568 case isl_schedule_node_guard:
569 case isl_schedule_node_mark:
570 case isl_schedule_node_sequence:
571 case isl_schedule_node_set:
572 break;
573 case isl_schedule_node_domain:
574 if (data->universe_domain)
575 break;
576 filter = isl_schedule_tree_domain_get_domain(tree);
577 data->filter = isl_union_set_intersect(data->filter, filter);
578 break;
579 case isl_schedule_node_band:
580 n = isl_schedule_tree_band_n_member(tree);
581 if (n < 0)
582 return isl_stat_error;
583 if (n == 0)
584 break;
585 if (!data->collect_prefix)
586 break;
587 mupa = isl_schedule_tree_band_get_partial_schedule(tree);
588 data->prefix = isl_multi_union_pw_aff_flat_range_product(mupa,
589 data->prefix);
590 if (!data->prefix)
591 return isl_stat_error;
592 break;
593 case isl_schedule_node_filter:
594 filter = isl_schedule_tree_filter_get_filter(tree);
595 if (data->universe_filter)
596 filter = isl_union_set_universe(filter);
597 data->filter = isl_union_set_intersect(data->filter, filter);
598 if (!data->filter)
599 return isl_stat_error;
600 break;
601 }
602
603 return isl_stat_ok;
604 }
605
606 /* Collect filter and/or prefix information from the first "n"
607 * elements in "list" (which represent the ancestors of a node).
608 * Store the results in "data".
609 *
610 * Extension nodes are only supported if they do not affect the outcome,
611 * i.e., if we are collecting information on non-extended domain elements,
612 * or if we are collecting the universe domain (without prefix).
613 *
614 * Return 0 on success and -1 on error.
615 *
616 * We traverse the list from innermost ancestor (last element)
617 * to outermost ancestor (first element), calling collect_filter_prefix_init
618 * on each node as long as we have not been able to extract any information
619 * yet and collect_filter_prefix_update afterwards.
620 * If we come across an expansion node, then we interrupt the traversal
621 * and call collect_filter_prefix_expansion to restart the traversal
622 * over the remaining ancestors and to combine the results with those
623 * that have already been collected.
624 * If we come across an extension node and we are only computing
625 * the universe domain, then we interrupt the traversal and call
626 * collect_universe_domain_extension to restart the traversal
627 * over the remaining ancestors and to combine the results with those
628 * that have already been collected.
629 * On successful return, data->initialized will be set since the outermost
630 * ancestor is a domain node, which always results in an initialization.
631 */
collect_filter_prefix(__isl_keep isl_schedule_tree_list * list,int n,struct isl_schedule_node_get_filter_prefix_data * data)632 static isl_stat collect_filter_prefix(__isl_keep isl_schedule_tree_list *list,
633 int n, struct isl_schedule_node_get_filter_prefix_data *data)
634 {
635 int i;
636
637 if (!list)
638 return isl_stat_error;
639
640 for (i = n - 1; i >= 0; --i) {
641 isl_schedule_tree *tree;
642 enum isl_schedule_node_type type;
643 isl_stat r;
644
645 tree = isl_schedule_tree_list_get_schedule_tree(list, i);
646 if (!tree)
647 return isl_stat_error;
648 type = isl_schedule_tree_get_type(tree);
649 if (type == isl_schedule_node_expansion)
650 return collect_filter_prefix_expansion(tree, list, i,
651 data);
652 if (type == isl_schedule_node_extension &&
653 data->universe_domain && !data->collect_prefix)
654 return collect_universe_domain_extension(tree, list, i,
655 data);
656 if (!data->initialized)
657 r = collect_filter_prefix_init(tree, data);
658 else
659 r = collect_filter_prefix_update(tree, data);
660 isl_schedule_tree_free(tree);
661 if (r < 0)
662 return isl_stat_error;
663 }
664
665 return isl_stat_ok;
666 }
667
668 /* Return the concatenation of the partial schedules of all outer band
669 * nodes of "node" interesected with all outer filters
670 * as an isl_multi_union_pw_aff.
671 * None of the ancestors of "node" may be an extension node, unless
672 * there is also a filter ancestor that filters out all the extended
673 * domain elements.
674 *
675 * If "node" is pointing at the root of the schedule tree, then
676 * there are no domain elements reaching the current node, so
677 * we return an empty result.
678 *
679 * We collect all the filters and partial schedules in collect_filter_prefix
680 * and intersect the domain of the combined schedule with the combined filter.
681 */
682 __isl_give isl_multi_union_pw_aff *
isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(__isl_keep isl_schedule_node * node)683 isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(
684 __isl_keep isl_schedule_node *node)
685 {
686 isl_size n;
687 isl_space *space;
688 struct isl_schedule_node_get_filter_prefix_data data;
689
690 if (!node)
691 return NULL;
692
693 space = isl_schedule_get_space(node->schedule);
694 space = isl_space_set_from_params(space);
695 if (node->tree == node->schedule->root)
696 return isl_multi_union_pw_aff_zero(space);
697
698 data.initialized = 0;
699 data.universe_domain = 1;
700 data.universe_filter = 0;
701 data.collect_prefix = 1;
702 data.filter = NULL;
703 data.prefix = isl_multi_union_pw_aff_zero(space);
704
705 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
706 if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
707 data.prefix = isl_multi_union_pw_aff_free(data.prefix);
708
709 data.prefix = isl_multi_union_pw_aff_intersect_domain(data.prefix,
710 data.filter);
711
712 return data.prefix;
713 }
714
715 /* Return the concatenation of the partial schedules of all outer band
716 * nodes of "node" interesected with all outer filters
717 * as an isl_union_pw_multi_aff.
718 * None of the ancestors of "node" may be an extension node, unless
719 * there is also a filter ancestor that filters out all the extended
720 * domain elements.
721 *
722 * If "node" is pointing at the root of the schedule tree, then
723 * there are no domain elements reaching the current node, so
724 * we return an empty result.
725 *
726 * We collect all the filters and partial schedules in collect_filter_prefix.
727 * The partial schedules are collected as an isl_multi_union_pw_aff.
728 * If this isl_multi_union_pw_aff is zero-dimensional, then it does not
729 * contain any domain information, so we construct the isl_union_pw_multi_aff
730 * result as a zero-dimensional function on the collected filter.
731 * Otherwise, we convert the isl_multi_union_pw_aff to
732 * an isl_multi_union_pw_aff and intersect the domain with the filter.
733 */
734 __isl_give isl_union_pw_multi_aff *
isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(__isl_keep isl_schedule_node * node)735 isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(
736 __isl_keep isl_schedule_node *node)
737 {
738 isl_size n, dim;
739 isl_space *space;
740 isl_union_pw_multi_aff *prefix;
741 struct isl_schedule_node_get_filter_prefix_data data;
742
743 if (!node)
744 return NULL;
745
746 space = isl_schedule_get_space(node->schedule);
747 if (node->tree == node->schedule->root)
748 return isl_union_pw_multi_aff_empty(space);
749
750 space = isl_space_set_from_params(space);
751 data.initialized = 0;
752 data.universe_domain = 1;
753 data.universe_filter = 0;
754 data.collect_prefix = 1;
755 data.filter = NULL;
756 data.prefix = isl_multi_union_pw_aff_zero(space);
757
758 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
759 if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
760 data.prefix = isl_multi_union_pw_aff_free(data.prefix);
761
762 dim = isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set);
763 if (dim < 0)
764 data.prefix = isl_multi_union_pw_aff_free(data.prefix);
765 if (data.prefix && dim == 0) {
766 isl_multi_union_pw_aff_free(data.prefix);
767 prefix = isl_union_pw_multi_aff_from_domain(data.filter);
768 } else {
769 prefix =
770 isl_union_pw_multi_aff_from_multi_union_pw_aff(data.prefix);
771 prefix = isl_union_pw_multi_aff_intersect_domain(prefix,
772 data.filter);
773 }
774
775 return prefix;
776 }
777
778 /* Return the concatenation of the partial schedules of all outer band
779 * nodes of "node" interesected with all outer filters
780 * as an isl_union_map.
781 */
isl_schedule_node_get_prefix_schedule_union_map(__isl_keep isl_schedule_node * node)782 __isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_union_map(
783 __isl_keep isl_schedule_node *node)
784 {
785 isl_union_pw_multi_aff *upma;
786
787 upma = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node);
788 return isl_union_map_from_union_pw_multi_aff(upma);
789 }
790
791 /* Return the concatenation of the partial schedules of all outer band
792 * nodes of "node" intersected with all outer domain constraints.
793 * None of the ancestors of "node" may be an extension node, unless
794 * there is also a filter ancestor that filters out all the extended
795 * domain elements.
796 *
797 * Essentially, this function intersects the domain of the output
798 * of isl_schedule_node_get_prefix_schedule_union_map with the output
799 * of isl_schedule_node_get_domain, except that it only traverses
800 * the ancestors of "node" once.
801 */
isl_schedule_node_get_prefix_schedule_relation(__isl_keep isl_schedule_node * node)802 __isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation(
803 __isl_keep isl_schedule_node *node)
804 {
805 isl_size n, dim;
806 isl_space *space;
807 isl_union_map *prefix;
808 struct isl_schedule_node_get_filter_prefix_data data;
809
810 if (!node)
811 return NULL;
812
813 space = isl_schedule_get_space(node->schedule);
814 if (node->tree == node->schedule->root)
815 return isl_union_map_empty(space);
816
817 space = isl_space_set_from_params(space);
818 data.initialized = 0;
819 data.universe_domain = 0;
820 data.universe_filter = 0;
821 data.collect_prefix = 1;
822 data.filter = NULL;
823 data.prefix = isl_multi_union_pw_aff_zero(space);
824
825 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
826 if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
827 data.prefix = isl_multi_union_pw_aff_free(data.prefix);
828
829 dim = isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set);
830 if (dim < 0)
831 data.prefix = isl_multi_union_pw_aff_free(data.prefix);
832 if (data.prefix && dim == 0) {
833 isl_multi_union_pw_aff_free(data.prefix);
834 prefix = isl_union_map_from_domain(data.filter);
835 } else {
836 prefix = isl_union_map_from_multi_union_pw_aff(data.prefix);
837 prefix = isl_union_map_intersect_domain(prefix, data.filter);
838 }
839
840 return prefix;
841 }
842
843 /* Return the domain elements that reach "node".
844 *
845 * If "node" is pointing at the root of the schedule tree, then
846 * there are no domain elements reaching the current node, so
847 * we return an empty result.
848 * None of the ancestors of "node" may be an extension node, unless
849 * there is also a filter ancestor that filters out all the extended
850 * domain elements.
851 *
852 * Otherwise, we collect all filters reaching the node,
853 * intersected with the root domain in collect_filter_prefix.
854 */
isl_schedule_node_get_domain(__isl_keep isl_schedule_node * node)855 __isl_give isl_union_set *isl_schedule_node_get_domain(
856 __isl_keep isl_schedule_node *node)
857 {
858 isl_size n;
859 struct isl_schedule_node_get_filter_prefix_data data;
860
861 if (!node)
862 return NULL;
863
864 if (node->tree == node->schedule->root) {
865 isl_space *space;
866
867 space = isl_schedule_get_space(node->schedule);
868 return isl_union_set_empty(space);
869 }
870
871 data.initialized = 0;
872 data.universe_domain = 0;
873 data.universe_filter = 0;
874 data.collect_prefix = 0;
875 data.filter = NULL;
876 data.prefix = NULL;
877
878 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
879 if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
880 data.filter = isl_union_set_free(data.filter);
881
882 return data.filter;
883 }
884
885 /* Return the union of universe sets of the domain elements that reach "node".
886 *
887 * If "node" is pointing at the root of the schedule tree, then
888 * there are no domain elements reaching the current node, so
889 * we return an empty result.
890 *
891 * Otherwise, we collect the universes of all filters reaching the node
892 * in collect_filter_prefix.
893 */
isl_schedule_node_get_universe_domain(__isl_keep isl_schedule_node * node)894 __isl_give isl_union_set *isl_schedule_node_get_universe_domain(
895 __isl_keep isl_schedule_node *node)
896 {
897 isl_size n;
898 struct isl_schedule_node_get_filter_prefix_data data;
899
900 if (!node)
901 return NULL;
902
903 if (node->tree == node->schedule->root) {
904 isl_space *space;
905
906 space = isl_schedule_get_space(node->schedule);
907 return isl_union_set_empty(space);
908 }
909
910 data.initialized = 0;
911 data.universe_domain = 1;
912 data.universe_filter = 1;
913 data.collect_prefix = 0;
914 data.filter = NULL;
915 data.prefix = NULL;
916
917 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
918 if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
919 data.filter = isl_union_set_free(data.filter);
920
921 return data.filter;
922 }
923
924 /* Return the subtree schedule of "node".
925 *
926 * Since isl_schedule_tree_get_subtree_schedule_union_map does not handle
927 * trees that do not contain any schedule information, we first
928 * move down to the first relevant descendant and handle leaves ourselves.
929 *
930 * If the subtree rooted at "node" contains any expansion nodes, then
931 * the returned subtree schedule is formulated in terms of the expanded
932 * domains.
933 * The subtree is not allowed to contain any extension nodes.
934 */
isl_schedule_node_get_subtree_schedule_union_map(__isl_keep isl_schedule_node * node)935 __isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map(
936 __isl_keep isl_schedule_node *node)
937 {
938 isl_schedule_tree *tree, *leaf;
939 isl_union_map *umap;
940
941 tree = isl_schedule_node_get_tree(node);
942 leaf = isl_schedule_node_peek_leaf(node);
943 tree = isl_schedule_tree_first_schedule_descendant(tree, leaf);
944 if (!tree)
945 return NULL;
946 if (tree == leaf) {
947 isl_union_set *domain;
948 domain = isl_schedule_node_get_universe_domain(node);
949 isl_schedule_tree_free(tree);
950 return isl_union_map_from_domain(domain);
951 }
952
953 umap = isl_schedule_tree_get_subtree_schedule_union_map(tree);
954 isl_schedule_tree_free(tree);
955 return umap;
956 }
957
958 /* Return the number of ancestors of "node" in its schedule tree.
959 */
isl_schedule_node_get_tree_depth(__isl_keep isl_schedule_node * node)960 isl_size isl_schedule_node_get_tree_depth(__isl_keep isl_schedule_node *node)
961 {
962 if (!node)
963 return isl_size_error;
964 return isl_schedule_tree_list_n_schedule_tree(node->ancestors);
965 }
966
967 /* Does "node" have a parent?
968 *
969 * That is, does it point to any node of the schedule other than the root?
970 */
isl_schedule_node_has_parent(__isl_keep isl_schedule_node * node)971 isl_bool isl_schedule_node_has_parent(__isl_keep isl_schedule_node *node)
972 {
973 isl_size depth;
974
975 depth = isl_schedule_node_get_tree_depth(node);
976 if (depth < 0)
977 return isl_bool_error;
978 return isl_bool_ok(depth != 0);
979 }
980
981 /* Return the position of "node" among the children of its parent.
982 */
isl_schedule_node_get_child_position(__isl_keep isl_schedule_node * node)983 isl_size isl_schedule_node_get_child_position(
984 __isl_keep isl_schedule_node *node)
985 {
986 isl_size n;
987 isl_bool has_parent;
988
989 if (!node)
990 return isl_size_error;
991 has_parent = isl_schedule_node_has_parent(node);
992 if (has_parent < 0)
993 return isl_size_error;
994 if (!has_parent)
995 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
996 "node has no parent", return isl_size_error);
997
998 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
999 return n < 0 ? isl_size_error : node->child_pos[n - 1];
1000 }
1001
1002 /* Does the parent (if any) of "node" have any children with a smaller child
1003 * position than this one?
1004 */
isl_schedule_node_has_previous_sibling(__isl_keep isl_schedule_node * node)1005 isl_bool isl_schedule_node_has_previous_sibling(
1006 __isl_keep isl_schedule_node *node)
1007 {
1008 isl_size n;
1009 isl_bool has_parent;
1010
1011 if (!node)
1012 return isl_bool_error;
1013 has_parent = isl_schedule_node_has_parent(node);
1014 if (has_parent < 0 || !has_parent)
1015 return has_parent;
1016
1017 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
1018 if (n < 0)
1019 return isl_bool_error;
1020
1021 return isl_bool_ok(node->child_pos[n - 1] > 0);
1022 }
1023
1024 /* Does the parent (if any) of "node" have any children with a greater child
1025 * position than this one?
1026 */
isl_schedule_node_has_next_sibling(__isl_keep isl_schedule_node * node)1027 isl_bool isl_schedule_node_has_next_sibling(__isl_keep isl_schedule_node *node)
1028 {
1029 isl_size n, n_child;
1030 isl_bool has_parent;
1031 isl_schedule_tree *tree;
1032
1033 if (!node)
1034 return isl_bool_error;
1035 has_parent = isl_schedule_node_has_parent(node);
1036 if (has_parent < 0 || !has_parent)
1037 return has_parent;
1038
1039 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
1040 if (n < 0)
1041 return isl_bool_error;
1042 tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, n - 1);
1043 n_child = isl_schedule_tree_n_children(tree);
1044 isl_schedule_tree_free(tree);
1045 if (n_child < 0)
1046 return isl_bool_error;
1047
1048 return isl_bool_ok(node->child_pos[n - 1] + 1 < n_child);
1049 }
1050
1051 /* Does "node" have any children?
1052 *
1053 * Any node other than the leaf nodes is considered to have at least
1054 * one child, even if the corresponding isl_schedule_tree does not
1055 * have any children.
1056 */
isl_schedule_node_has_children(__isl_keep isl_schedule_node * node)1057 isl_bool isl_schedule_node_has_children(__isl_keep isl_schedule_node *node)
1058 {
1059 if (!node)
1060 return isl_bool_error;
1061 return isl_bool_ok(!isl_schedule_tree_is_leaf(node->tree));
1062 }
1063
1064 /* Return the number of children of "node"?
1065 *
1066 * Any node other than the leaf nodes is considered to have at least
1067 * one child, even if the corresponding isl_schedule_tree does not
1068 * have any children. That is, the number of children of "node" is
1069 * only zero if its tree is the explicit empty tree. Otherwise,
1070 * if the isl_schedule_tree has any children, then it is equal
1071 * to the number of children of "node". If it has zero children,
1072 * then "node" still has a leaf node as child.
1073 */
isl_schedule_node_n_children(__isl_keep isl_schedule_node * node)1074 isl_size isl_schedule_node_n_children(__isl_keep isl_schedule_node *node)
1075 {
1076 isl_size n;
1077
1078 if (!node)
1079 return isl_size_error;
1080
1081 if (isl_schedule_tree_is_leaf(node->tree))
1082 return 0;
1083
1084 n = isl_schedule_tree_n_children(node->tree);
1085 if (n < 0)
1086 return isl_size_error;
1087 if (n == 0)
1088 return 1;
1089
1090 return n;
1091 }
1092
1093 /* Move the "node" pointer to the ancestor of the given generation
1094 * of the node it currently points to, where generation 0 is the node
1095 * itself and generation 1 is its parent.
1096 */
isl_schedule_node_ancestor(__isl_take isl_schedule_node * node,int generation)1097 __isl_give isl_schedule_node *isl_schedule_node_ancestor(
1098 __isl_take isl_schedule_node *node, int generation)
1099 {
1100 isl_size n;
1101 isl_schedule_tree *tree;
1102
1103 if (!node)
1104 return NULL;
1105 if (generation == 0)
1106 return node;
1107 n = isl_schedule_node_get_tree_depth(node);
1108 if (n < 0)
1109 return isl_schedule_node_free(node);
1110 if (generation < 0 || generation > n)
1111 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1112 "generation out of bounds",
1113 return isl_schedule_node_free(node));
1114 node = isl_schedule_node_cow(node);
1115 if (!node)
1116 return NULL;
1117
1118 tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
1119 n - generation);
1120 isl_schedule_tree_free(node->tree);
1121 node->tree = tree;
1122 node->ancestors = isl_schedule_tree_list_drop(node->ancestors,
1123 n - generation, generation);
1124 if (!node->ancestors || !node->tree)
1125 return isl_schedule_node_free(node);
1126
1127 return node;
1128 }
1129
1130 /* Move the "node" pointer to the parent of the node it currently points to.
1131 */
isl_schedule_node_parent(__isl_take isl_schedule_node * node)1132 __isl_give isl_schedule_node *isl_schedule_node_parent(
1133 __isl_take isl_schedule_node *node)
1134 {
1135 if (!node)
1136 return NULL;
1137 if (!isl_schedule_node_has_parent(node))
1138 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1139 "node has no parent",
1140 return isl_schedule_node_free(node));
1141 return isl_schedule_node_ancestor(node, 1);
1142 }
1143
1144 /* Move the "node" pointer to the root of its schedule tree.
1145 */
isl_schedule_node_root(__isl_take isl_schedule_node * node)1146 __isl_give isl_schedule_node *isl_schedule_node_root(
1147 __isl_take isl_schedule_node *node)
1148 {
1149 isl_size n;
1150
1151 if (!node)
1152 return NULL;
1153 n = isl_schedule_node_get_tree_depth(node);
1154 if (n < 0)
1155 return isl_schedule_node_free(node);
1156 return isl_schedule_node_ancestor(node, n);
1157 }
1158
1159 /* Move the "node" pointer to the child at position "pos" of the node
1160 * it currently points to.
1161 */
isl_schedule_node_child(__isl_take isl_schedule_node * node,int pos)1162 __isl_give isl_schedule_node *isl_schedule_node_child(
1163 __isl_take isl_schedule_node *node, int pos)
1164 {
1165 isl_size n;
1166 isl_ctx *ctx;
1167 isl_schedule_tree *tree;
1168 int *child_pos;
1169
1170 node = isl_schedule_node_cow(node);
1171 if (!node)
1172 return NULL;
1173 if (!isl_schedule_node_has_children(node))
1174 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1175 "node has no children",
1176 return isl_schedule_node_free(node));
1177
1178 ctx = isl_schedule_node_get_ctx(node);
1179 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
1180 if (n < 0)
1181 return isl_schedule_node_free(node);
1182 child_pos = isl_realloc_array(ctx, node->child_pos, int, n + 1);
1183 if (!child_pos)
1184 return isl_schedule_node_free(node);
1185 node->child_pos = child_pos;
1186 node->child_pos[n] = pos;
1187
1188 node->ancestors = isl_schedule_tree_list_add(node->ancestors,
1189 isl_schedule_tree_copy(node->tree));
1190 tree = node->tree;
1191 if (isl_schedule_tree_has_children(tree))
1192 tree = isl_schedule_tree_get_child(tree, pos);
1193 else
1194 tree = isl_schedule_node_get_leaf(node);
1195 isl_schedule_tree_free(node->tree);
1196 node->tree = tree;
1197
1198 if (!node->tree || !node->ancestors)
1199 return isl_schedule_node_free(node);
1200
1201 return node;
1202 }
1203
1204 /* Move the "node" pointer to the first child of the node
1205 * it currently points to.
1206 */
isl_schedule_node_first_child(__isl_take isl_schedule_node * node)1207 __isl_give isl_schedule_node *isl_schedule_node_first_child(
1208 __isl_take isl_schedule_node *node)
1209 {
1210 return isl_schedule_node_child(node, 0);
1211 }
1212
1213 /* Move the "node" pointer to the child of this node's parent in
1214 * the previous child position.
1215 */
isl_schedule_node_previous_sibling(__isl_take isl_schedule_node * node)1216 __isl_give isl_schedule_node *isl_schedule_node_previous_sibling(
1217 __isl_take isl_schedule_node *node)
1218 {
1219 isl_size n;
1220 isl_schedule_tree *parent, *tree;
1221
1222 node = isl_schedule_node_cow(node);
1223 if (!node)
1224 return NULL;
1225 if (!isl_schedule_node_has_previous_sibling(node))
1226 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1227 "node has no previous sibling",
1228 return isl_schedule_node_free(node));
1229
1230 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
1231 if (n < 0)
1232 return isl_schedule_node_free(node);
1233 parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
1234 n - 1);
1235 if (!parent)
1236 return isl_schedule_node_free(node);
1237 node->child_pos[n - 1]--;
1238 tree = isl_schedule_tree_list_get_schedule_tree(parent->children,
1239 node->child_pos[n - 1]);
1240 isl_schedule_tree_free(parent);
1241 if (!tree)
1242 return isl_schedule_node_free(node);
1243 isl_schedule_tree_free(node->tree);
1244 node->tree = tree;
1245
1246 return node;
1247 }
1248
1249 /* Move the "node" pointer to the child of this node's parent in
1250 * the next child position.
1251 */
isl_schedule_node_next_sibling(__isl_take isl_schedule_node * node)1252 __isl_give isl_schedule_node *isl_schedule_node_next_sibling(
1253 __isl_take isl_schedule_node *node)
1254 {
1255 isl_size n;
1256 isl_schedule_tree *parent, *tree;
1257
1258 node = isl_schedule_node_cow(node);
1259 if (!node)
1260 return NULL;
1261 if (!isl_schedule_node_has_next_sibling(node))
1262 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1263 "node has no next sibling",
1264 return isl_schedule_node_free(node));
1265
1266 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
1267 if (n < 0)
1268 return isl_schedule_node_free(node);
1269 parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
1270 n - 1);
1271 if (!parent)
1272 return isl_schedule_node_free(node);
1273 node->child_pos[n - 1]++;
1274 tree = isl_schedule_tree_list_get_schedule_tree(parent->children,
1275 node->child_pos[n - 1]);
1276 isl_schedule_tree_free(parent);
1277 if (!tree)
1278 return isl_schedule_node_free(node);
1279 isl_schedule_tree_free(node->tree);
1280 node->tree = tree;
1281
1282 return node;
1283 }
1284
1285 /* Return a copy to the child at position "pos" of "node".
1286 */
isl_schedule_node_get_child(__isl_keep isl_schedule_node * node,int pos)1287 __isl_give isl_schedule_node *isl_schedule_node_get_child(
1288 __isl_keep isl_schedule_node *node, int pos)
1289 {
1290 return isl_schedule_node_child(isl_schedule_node_copy(node), pos);
1291 }
1292
1293 /* Traverse the descendant of "node" in depth-first order, including
1294 * "node" itself. Call "enter" whenever a node is entered and "leave"
1295 * whenever a node is left. The callback "enter" is responsible
1296 * for moving to the deepest initial subtree of its argument that
1297 * should be traversed.
1298 */
traverse(__isl_take isl_schedule_node * node,__isl_give isl_schedule_node * (* enter)(__isl_take isl_schedule_node * node,void * user),__isl_give isl_schedule_node * (* leave)(__isl_take isl_schedule_node * node,void * user),void * user)1299 static __isl_give isl_schedule_node *traverse(
1300 __isl_take isl_schedule_node *node,
1301 __isl_give isl_schedule_node *(*enter)(
1302 __isl_take isl_schedule_node *node, void *user),
1303 __isl_give isl_schedule_node *(*leave)(
1304 __isl_take isl_schedule_node *node, void *user),
1305 void *user)
1306 {
1307 isl_size depth;
1308 isl_size node_depth;
1309
1310 depth = isl_schedule_node_get_tree_depth(node);
1311 if (depth < 0)
1312 return isl_schedule_node_free(node);
1313
1314 do {
1315 node = enter(node, user);
1316 node = leave(node, user);
1317 while ((node_depth = isl_schedule_node_get_tree_depth(node)) >
1318 depth &&
1319 !isl_schedule_node_has_next_sibling(node)) {
1320 node = isl_schedule_node_parent(node);
1321 node = leave(node, user);
1322 }
1323 if (node_depth < 0)
1324 return isl_schedule_node_free(node);
1325 if (node_depth > depth)
1326 node = isl_schedule_node_next_sibling(node);
1327 } while (node_depth > depth);
1328
1329 return node;
1330 }
1331
1332 /* Internal data structure for isl_schedule_node_foreach_descendant_top_down.
1333 *
1334 * "fn" is the user-specified callback function.
1335 * "user" is the user-specified argument for the callback.
1336 */
1337 struct isl_schedule_node_preorder_data {
1338 isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user);
1339 void *user;
1340 };
1341
1342 /* Callback for "traverse" to enter a node and to move
1343 * to the deepest initial subtree that should be traversed
1344 * for use in a preorder visit.
1345 *
1346 * If the user callback returns a negative value, then we abort
1347 * the traversal. If this callback returns zero, then we skip
1348 * the subtree rooted at the current node. Otherwise, we move
1349 * down to the first child and repeat the process until a leaf
1350 * is reached.
1351 */
preorder_enter(__isl_take isl_schedule_node * node,void * user)1352 static __isl_give isl_schedule_node *preorder_enter(
1353 __isl_take isl_schedule_node *node, void *user)
1354 {
1355 struct isl_schedule_node_preorder_data *data = user;
1356
1357 if (!node)
1358 return NULL;
1359
1360 do {
1361 isl_bool r;
1362
1363 r = data->fn(node, data->user);
1364 if (r < 0)
1365 return isl_schedule_node_free(node);
1366 if (r == isl_bool_false)
1367 return node;
1368 } while (isl_schedule_node_has_children(node) &&
1369 (node = isl_schedule_node_first_child(node)) != NULL);
1370
1371 return node;
1372 }
1373
1374 /* Callback for "traverse" to leave a node
1375 * for use in a preorder visit.
1376 * Since we already visited the node when we entered it,
1377 * we do not need to do anything here.
1378 */
preorder_leave(__isl_take isl_schedule_node * node,void * user)1379 static __isl_give isl_schedule_node *preorder_leave(
1380 __isl_take isl_schedule_node *node, void *user)
1381 {
1382 return node;
1383 }
1384
1385 /* Traverse the descendants of "node" (including the node itself)
1386 * in depth first preorder.
1387 *
1388 * If "fn" returns isl_bool_error on any of the nodes,
1389 * then the traversal is aborted.
1390 * If "fn" returns isl_bool_false on any of the nodes, then the subtree rooted
1391 * at that node is skipped.
1392 *
1393 * Return isl_stat_ok on success and isl_stat_error on failure.
1394 */
isl_schedule_node_foreach_descendant_top_down(__isl_keep isl_schedule_node * node,isl_bool (* fn)(__isl_keep isl_schedule_node * node,void * user),void * user)1395 isl_stat isl_schedule_node_foreach_descendant_top_down(
1396 __isl_keep isl_schedule_node *node,
1397 isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user),
1398 void *user)
1399 {
1400 struct isl_schedule_node_preorder_data data = { fn, user };
1401
1402 node = isl_schedule_node_copy(node);
1403 node = traverse(node, &preorder_enter, &preorder_leave, &data);
1404 isl_schedule_node_free(node);
1405
1406 return node ? isl_stat_ok : isl_stat_error;
1407 }
1408
1409 /* Internal data structure for isl_schedule_node_every_descendant.
1410 *
1411 * "test" is the user-specified callback function.
1412 * "user" is the user-specified callback function argument.
1413 *
1414 * "failed" is initialized to 0 and set to 1 if "test" fails
1415 * on any node.
1416 */
1417 struct isl_union_map_every_data {
1418 isl_bool (*test)(__isl_keep isl_schedule_node *node, void *user);
1419 void *user;
1420 int failed;
1421 };
1422
1423 /* isl_schedule_node_foreach_descendant_top_down callback
1424 * that sets data->failed if data->test returns false and
1425 * subsequently aborts the traversal.
1426 */
call_every(__isl_keep isl_schedule_node * node,void * user)1427 static isl_bool call_every(__isl_keep isl_schedule_node *node, void *user)
1428 {
1429 struct isl_union_map_every_data *data = user;
1430 isl_bool r;
1431
1432 r = data->test(node, data->user);
1433 if (r < 0)
1434 return isl_bool_error;
1435 if (r)
1436 return isl_bool_true;
1437 data->failed = 1;
1438 return isl_bool_error;
1439 }
1440
1441 /* Does "test" succeed on every descendant of "node" (including "node" itself)?
1442 */
isl_schedule_node_every_descendant(__isl_keep isl_schedule_node * node,isl_bool (* test)(__isl_keep isl_schedule_node * node,void * user),void * user)1443 isl_bool isl_schedule_node_every_descendant(__isl_keep isl_schedule_node *node,
1444 isl_bool (*test)(__isl_keep isl_schedule_node *node, void *user),
1445 void *user)
1446 {
1447 struct isl_union_map_every_data data = { test, user, 0 };
1448 isl_stat r;
1449
1450 r = isl_schedule_node_foreach_descendant_top_down(node, &call_every,
1451 &data);
1452 if (r >= 0)
1453 return isl_bool_true;
1454 if (data.failed)
1455 return isl_bool_false;
1456 return isl_bool_error;
1457 }
1458
1459 /* Internal data structure for isl_schedule_node_map_descendant_bottom_up.
1460 *
1461 * "fn" is the user-specified callback function.
1462 * "user" is the user-specified argument for the callback.
1463 */
1464 struct isl_schedule_node_postorder_data {
1465 __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
1466 void *user);
1467 void *user;
1468 };
1469
1470 /* Callback for "traverse" to enter a node and to move
1471 * to the deepest initial subtree that should be traversed
1472 * for use in a postorder visit.
1473 *
1474 * Since we are performing a postorder visit, we only need
1475 * to move to the deepest initial leaf here.
1476 */
postorder_enter(__isl_take isl_schedule_node * node,void * user)1477 static __isl_give isl_schedule_node *postorder_enter(
1478 __isl_take isl_schedule_node *node, void *user)
1479 {
1480 while (node && isl_schedule_node_has_children(node))
1481 node = isl_schedule_node_first_child(node);
1482
1483 return node;
1484 }
1485
1486 /* Callback for "traverse" to leave a node
1487 * for use in a postorder visit.
1488 *
1489 * Since we are performing a postorder visit, we need
1490 * to call the user callback here.
1491 */
postorder_leave(__isl_take isl_schedule_node * node,void * user)1492 static __isl_give isl_schedule_node *postorder_leave(
1493 __isl_take isl_schedule_node *node, void *user)
1494 {
1495 struct isl_schedule_node_postorder_data *data = user;
1496
1497 return data->fn(node, data->user);
1498 }
1499
1500 /* Traverse the descendants of "node" (including the node itself)
1501 * in depth first postorder, allowing the user to modify the visited node.
1502 * The traversal continues from the node returned by the callback function.
1503 * It is the responsibility of the user to ensure that this does not
1504 * lead to an infinite loop. It is safest to always return a pointer
1505 * to the same position (same ancestors and child positions) as the input node.
1506 */
isl_schedule_node_map_descendant_bottom_up(__isl_take isl_schedule_node * node,__isl_give isl_schedule_node * (* fn)(__isl_take isl_schedule_node * node,void * user),void * user)1507 __isl_give isl_schedule_node *isl_schedule_node_map_descendant_bottom_up(
1508 __isl_take isl_schedule_node *node,
1509 __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
1510 void *user), void *user)
1511 {
1512 struct isl_schedule_node_postorder_data data = { fn, user };
1513
1514 return traverse(node, &postorder_enter, &postorder_leave, &data);
1515 }
1516
1517 /* Traverse the ancestors of "node" from the root down to and including
1518 * the parent of "node", calling "fn" on each of them.
1519 *
1520 * If "fn" returns -1 on any of the nodes, then the traversal is aborted.
1521 *
1522 * Return 0 on success and -1 on failure.
1523 */
isl_schedule_node_foreach_ancestor_top_down(__isl_keep isl_schedule_node * node,isl_stat (* fn)(__isl_keep isl_schedule_node * node,void * user),void * user)1524 isl_stat isl_schedule_node_foreach_ancestor_top_down(
1525 __isl_keep isl_schedule_node *node,
1526 isl_stat (*fn)(__isl_keep isl_schedule_node *node, void *user),
1527 void *user)
1528 {
1529 int i;
1530 isl_size n;
1531
1532 n = isl_schedule_node_get_tree_depth(node);
1533 if (n < 0)
1534 return isl_stat_error;
1535
1536 for (i = 0; i < n; ++i) {
1537 isl_schedule_node *ancestor;
1538 isl_stat r;
1539
1540 ancestor = isl_schedule_node_copy(node);
1541 ancestor = isl_schedule_node_ancestor(ancestor, n - i);
1542 r = fn(ancestor, user);
1543 isl_schedule_node_free(ancestor);
1544 if (r < 0)
1545 return isl_stat_error;
1546 }
1547
1548 return isl_stat_ok;
1549 }
1550
1551 /* Is any node in the subtree rooted at "node" anchored?
1552 * That is, do any of these nodes reference the outer band nodes?
1553 */
isl_schedule_node_is_subtree_anchored(__isl_keep isl_schedule_node * node)1554 isl_bool isl_schedule_node_is_subtree_anchored(
1555 __isl_keep isl_schedule_node *node)
1556 {
1557 if (!node)
1558 return isl_bool_error;
1559 return isl_schedule_tree_is_subtree_anchored(node->tree);
1560 }
1561
1562 /* Return the number of members in the given band node.
1563 */
isl_schedule_node_band_n_member(__isl_keep isl_schedule_node * node)1564 isl_size isl_schedule_node_band_n_member(__isl_keep isl_schedule_node *node)
1565 {
1566 if (!node)
1567 return isl_size_error;
1568 return isl_schedule_tree_band_n_member(node->tree);
1569 }
1570
1571 /* Is the band member at position "pos" of the band node "node"
1572 * marked coincident?
1573 */
isl_schedule_node_band_member_get_coincident(__isl_keep isl_schedule_node * node,int pos)1574 isl_bool isl_schedule_node_band_member_get_coincident(
1575 __isl_keep isl_schedule_node *node, int pos)
1576 {
1577 if (!node)
1578 return isl_bool_error;
1579 return isl_schedule_tree_band_member_get_coincident(node->tree, pos);
1580 }
1581
1582 /* Mark the band member at position "pos" the band node "node"
1583 * as being coincident or not according to "coincident".
1584 */
isl_schedule_node_band_member_set_coincident(__isl_take isl_schedule_node * node,int pos,int coincident)1585 __isl_give isl_schedule_node *isl_schedule_node_band_member_set_coincident(
1586 __isl_take isl_schedule_node *node, int pos, int coincident)
1587 {
1588 int c;
1589 isl_schedule_tree *tree;
1590
1591 if (!node)
1592 return NULL;
1593 c = isl_schedule_node_band_member_get_coincident(node, pos);
1594 if (c == coincident)
1595 return node;
1596
1597 tree = isl_schedule_tree_copy(node->tree);
1598 tree = isl_schedule_tree_band_member_set_coincident(tree, pos,
1599 coincident);
1600 node = isl_schedule_node_graft_tree(node, tree);
1601
1602 return node;
1603 }
1604
1605 /* Is the band node "node" marked permutable?
1606 */
isl_schedule_node_band_get_permutable(__isl_keep isl_schedule_node * node)1607 isl_bool isl_schedule_node_band_get_permutable(
1608 __isl_keep isl_schedule_node *node)
1609 {
1610 if (!node)
1611 return isl_bool_error;
1612
1613 return isl_schedule_tree_band_get_permutable(node->tree);
1614 }
1615
1616 /* Mark the band node "node" permutable or not according to "permutable"?
1617 */
isl_schedule_node_band_set_permutable(__isl_take isl_schedule_node * node,int permutable)1618 __isl_give isl_schedule_node *isl_schedule_node_band_set_permutable(
1619 __isl_take isl_schedule_node *node, int permutable)
1620 {
1621 isl_schedule_tree *tree;
1622
1623 if (!node)
1624 return NULL;
1625 if (isl_schedule_node_band_get_permutable(node) == permutable)
1626 return node;
1627
1628 tree = isl_schedule_tree_copy(node->tree);
1629 tree = isl_schedule_tree_band_set_permutable(tree, permutable);
1630 node = isl_schedule_node_graft_tree(node, tree);
1631
1632 return node;
1633 }
1634
1635 /* Return the schedule space of the band node.
1636 */
isl_schedule_node_band_get_space(__isl_keep isl_schedule_node * node)1637 __isl_give isl_space *isl_schedule_node_band_get_space(
1638 __isl_keep isl_schedule_node *node)
1639 {
1640 if (!node)
1641 return NULL;
1642
1643 return isl_schedule_tree_band_get_space(node->tree);
1644 }
1645
1646 /* Return the schedule of the band node in isolation.
1647 */
isl_schedule_node_band_get_partial_schedule(__isl_keep isl_schedule_node * node)1648 __isl_give isl_multi_union_pw_aff *isl_schedule_node_band_get_partial_schedule(
1649 __isl_keep isl_schedule_node *node)
1650 {
1651 if (!node)
1652 return NULL;
1653
1654 return isl_schedule_tree_band_get_partial_schedule(node->tree);
1655 }
1656
1657 /* Return the schedule of the band node in isolation in the form of
1658 * an isl_union_map.
1659 *
1660 * If the band does not have any members, then we construct a universe map
1661 * with the universe of the domain elements reaching the node as domain.
1662 * Otherwise, we extract an isl_multi_union_pw_aff representation and
1663 * convert that to an isl_union_map.
1664 */
isl_schedule_node_band_get_partial_schedule_union_map(__isl_keep isl_schedule_node * node)1665 __isl_give isl_union_map *isl_schedule_node_band_get_partial_schedule_union_map(
1666 __isl_keep isl_schedule_node *node)
1667 {
1668 isl_size n;
1669 isl_multi_union_pw_aff *mupa;
1670
1671 if (!node)
1672 return NULL;
1673
1674 if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
1675 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1676 "not a band node", return NULL);
1677 n = isl_schedule_node_band_n_member(node);
1678 if (n < 0)
1679 return NULL;
1680 if (n == 0) {
1681 isl_union_set *domain;
1682
1683 domain = isl_schedule_node_get_universe_domain(node);
1684 return isl_union_map_from_domain(domain);
1685 }
1686
1687 mupa = isl_schedule_node_band_get_partial_schedule(node);
1688 return isl_union_map_from_multi_union_pw_aff(mupa);
1689 }
1690
1691 /* Return the loop AST generation type for the band member of band node "node"
1692 * at position "pos".
1693 */
isl_schedule_node_band_member_get_ast_loop_type(__isl_keep isl_schedule_node * node,int pos)1694 enum isl_ast_loop_type isl_schedule_node_band_member_get_ast_loop_type(
1695 __isl_keep isl_schedule_node *node, int pos)
1696 {
1697 if (!node)
1698 return isl_ast_loop_error;
1699
1700 return isl_schedule_tree_band_member_get_ast_loop_type(node->tree, pos);
1701 }
1702
1703 /* Set the loop AST generation type for the band member of band node "node"
1704 * at position "pos" to "type".
1705 */
isl_schedule_node_band_member_set_ast_loop_type(__isl_take isl_schedule_node * node,int pos,enum isl_ast_loop_type type)1706 __isl_give isl_schedule_node *isl_schedule_node_band_member_set_ast_loop_type(
1707 __isl_take isl_schedule_node *node, int pos,
1708 enum isl_ast_loop_type type)
1709 {
1710 isl_schedule_tree *tree;
1711
1712 if (!node)
1713 return NULL;
1714
1715 tree = isl_schedule_tree_copy(node->tree);
1716 tree = isl_schedule_tree_band_member_set_ast_loop_type(tree, pos, type);
1717 return isl_schedule_node_graft_tree(node, tree);
1718 }
1719
1720 /* Return the loop AST generation type for the band member of band node "node"
1721 * at position "pos" for the isolated part.
1722 */
isl_schedule_node_band_member_get_isolate_ast_loop_type(__isl_keep isl_schedule_node * node,int pos)1723 enum isl_ast_loop_type isl_schedule_node_band_member_get_isolate_ast_loop_type(
1724 __isl_keep isl_schedule_node *node, int pos)
1725 {
1726 if (!node)
1727 return isl_ast_loop_error;
1728
1729 return isl_schedule_tree_band_member_get_isolate_ast_loop_type(
1730 node->tree, pos);
1731 }
1732
1733 /* Set the loop AST generation type for the band member of band node "node"
1734 * at position "pos" for the isolated part to "type".
1735 */
1736 __isl_give isl_schedule_node *
isl_schedule_node_band_member_set_isolate_ast_loop_type(__isl_take isl_schedule_node * node,int pos,enum isl_ast_loop_type type)1737 isl_schedule_node_band_member_set_isolate_ast_loop_type(
1738 __isl_take isl_schedule_node *node, int pos,
1739 enum isl_ast_loop_type type)
1740 {
1741 isl_schedule_tree *tree;
1742
1743 if (!node)
1744 return NULL;
1745
1746 tree = isl_schedule_tree_copy(node->tree);
1747 tree = isl_schedule_tree_band_member_set_isolate_ast_loop_type(tree,
1748 pos, type);
1749 return isl_schedule_node_graft_tree(node, tree);
1750 }
1751
1752 /* Return the AST build options associated to band node "node".
1753 */
isl_schedule_node_band_get_ast_build_options(__isl_keep isl_schedule_node * node)1754 __isl_give isl_union_set *isl_schedule_node_band_get_ast_build_options(
1755 __isl_keep isl_schedule_node *node)
1756 {
1757 if (!node)
1758 return NULL;
1759
1760 return isl_schedule_tree_band_get_ast_build_options(node->tree);
1761 }
1762
1763 /* Replace the AST build options associated to band node "node" by "options".
1764 */
isl_schedule_node_band_set_ast_build_options(__isl_take isl_schedule_node * node,__isl_take isl_union_set * options)1765 __isl_give isl_schedule_node *isl_schedule_node_band_set_ast_build_options(
1766 __isl_take isl_schedule_node *node, __isl_take isl_union_set *options)
1767 {
1768 isl_schedule_tree *tree;
1769
1770 if (!node || !options)
1771 goto error;
1772
1773 tree = isl_schedule_tree_copy(node->tree);
1774 tree = isl_schedule_tree_band_set_ast_build_options(tree, options);
1775 return isl_schedule_node_graft_tree(node, tree);
1776 error:
1777 isl_schedule_node_free(node);
1778 isl_union_set_free(options);
1779 return NULL;
1780 }
1781
1782 /* Return the "isolate" option associated to band node "node".
1783 */
isl_schedule_node_band_get_ast_isolate_option(__isl_keep isl_schedule_node * node)1784 __isl_give isl_set *isl_schedule_node_band_get_ast_isolate_option(
1785 __isl_keep isl_schedule_node *node)
1786 {
1787 isl_size depth;
1788
1789 depth = isl_schedule_node_get_schedule_depth(node);
1790 if (depth < 0)
1791 return NULL;
1792
1793 return isl_schedule_tree_band_get_ast_isolate_option(node->tree, depth);
1794 }
1795
1796 /* Make sure that that spaces of "node" and "mv" are the same.
1797 * Return -1 on error, reporting the error to the user.
1798 */
check_space_multi_val(__isl_keep isl_schedule_node * node,__isl_keep isl_multi_val * mv)1799 static int check_space_multi_val(__isl_keep isl_schedule_node *node,
1800 __isl_keep isl_multi_val *mv)
1801 {
1802 isl_space *node_space, *mv_space;
1803 int equal;
1804
1805 node_space = isl_schedule_node_band_get_space(node);
1806 mv_space = isl_multi_val_get_space(mv);
1807 equal = isl_space_tuple_is_equal(node_space, isl_dim_set,
1808 mv_space, isl_dim_set);
1809 isl_space_free(mv_space);
1810 isl_space_free(node_space);
1811 if (equal < 0)
1812 return -1;
1813 if (!equal)
1814 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1815 "spaces don't match", return -1);
1816
1817 return 0;
1818 }
1819
1820 /* Multiply the partial schedule of the band node "node"
1821 * with the factors in "mv".
1822 */
isl_schedule_node_band_scale(__isl_take isl_schedule_node * node,__isl_take isl_multi_val * mv)1823 __isl_give isl_schedule_node *isl_schedule_node_band_scale(
1824 __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv)
1825 {
1826 isl_schedule_tree *tree;
1827 int anchored;
1828
1829 if (!node || !mv)
1830 goto error;
1831 if (check_space_multi_val(node, mv) < 0)
1832 goto error;
1833 anchored = isl_schedule_node_is_subtree_anchored(node);
1834 if (anchored < 0)
1835 goto error;
1836 if (anchored)
1837 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1838 "cannot scale band node with anchored subtree",
1839 goto error);
1840
1841 tree = isl_schedule_node_get_tree(node);
1842 tree = isl_schedule_tree_band_scale(tree, mv);
1843 return isl_schedule_node_graft_tree(node, tree);
1844 error:
1845 isl_multi_val_free(mv);
1846 isl_schedule_node_free(node);
1847 return NULL;
1848 }
1849
1850 /* Divide the partial schedule of the band node "node"
1851 * by the factors in "mv".
1852 */
isl_schedule_node_band_scale_down(__isl_take isl_schedule_node * node,__isl_take isl_multi_val * mv)1853 __isl_give isl_schedule_node *isl_schedule_node_band_scale_down(
1854 __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv)
1855 {
1856 isl_schedule_tree *tree;
1857 int anchored;
1858
1859 if (!node || !mv)
1860 goto error;
1861 if (check_space_multi_val(node, mv) < 0)
1862 goto error;
1863 anchored = isl_schedule_node_is_subtree_anchored(node);
1864 if (anchored < 0)
1865 goto error;
1866 if (anchored)
1867 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1868 "cannot scale down band node with anchored subtree",
1869 goto error);
1870
1871 tree = isl_schedule_node_get_tree(node);
1872 tree = isl_schedule_tree_band_scale_down(tree, mv);
1873 return isl_schedule_node_graft_tree(node, tree);
1874 error:
1875 isl_multi_val_free(mv);
1876 isl_schedule_node_free(node);
1877 return NULL;
1878 }
1879
1880 /* Reduce the partial schedule of the band node "node"
1881 * modulo the factors in "mv".
1882 */
isl_schedule_node_band_mod(__isl_take isl_schedule_node * node,__isl_take isl_multi_val * mv)1883 __isl_give isl_schedule_node *isl_schedule_node_band_mod(
1884 __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv)
1885 {
1886 isl_schedule_tree *tree;
1887 isl_bool anchored;
1888
1889 if (!node || !mv)
1890 goto error;
1891 if (check_space_multi_val(node, mv) < 0)
1892 goto error;
1893 anchored = isl_schedule_node_is_subtree_anchored(node);
1894 if (anchored < 0)
1895 goto error;
1896 if (anchored)
1897 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1898 "cannot perform mod on band node with anchored subtree",
1899 goto error);
1900
1901 tree = isl_schedule_node_get_tree(node);
1902 tree = isl_schedule_tree_band_mod(tree, mv);
1903 return isl_schedule_node_graft_tree(node, tree);
1904 error:
1905 isl_multi_val_free(mv);
1906 isl_schedule_node_free(node);
1907 return NULL;
1908 }
1909
1910 /* Make sure that that spaces of "node" and "mupa" are the same.
1911 * Return isl_stat_error on error, reporting the error to the user.
1912 */
check_space_multi_union_pw_aff(__isl_keep isl_schedule_node * node,__isl_keep isl_multi_union_pw_aff * mupa)1913 static isl_stat check_space_multi_union_pw_aff(
1914 __isl_keep isl_schedule_node *node,
1915 __isl_keep isl_multi_union_pw_aff *mupa)
1916 {
1917 isl_space *node_space, *mupa_space;
1918 isl_bool equal;
1919
1920 node_space = isl_schedule_node_band_get_space(node);
1921 mupa_space = isl_multi_union_pw_aff_get_space(mupa);
1922 equal = isl_space_tuple_is_equal(node_space, isl_dim_set,
1923 mupa_space, isl_dim_set);
1924 isl_space_free(mupa_space);
1925 isl_space_free(node_space);
1926 if (equal < 0)
1927 return isl_stat_error;
1928 if (!equal)
1929 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1930 "spaces don't match", return isl_stat_error);
1931
1932 return isl_stat_ok;
1933 }
1934
1935 /* Shift the partial schedule of the band node "node" by "shift".
1936 */
isl_schedule_node_band_shift(__isl_take isl_schedule_node * node,__isl_take isl_multi_union_pw_aff * shift)1937 __isl_give isl_schedule_node *isl_schedule_node_band_shift(
1938 __isl_take isl_schedule_node *node,
1939 __isl_take isl_multi_union_pw_aff *shift)
1940 {
1941 isl_schedule_tree *tree;
1942 int anchored;
1943
1944 if (!node || !shift)
1945 goto error;
1946 if (check_space_multi_union_pw_aff(node, shift) < 0)
1947 goto error;
1948 anchored = isl_schedule_node_is_subtree_anchored(node);
1949 if (anchored < 0)
1950 goto error;
1951 if (anchored)
1952 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
1953 "cannot shift band node with anchored subtree",
1954 goto error);
1955
1956 tree = isl_schedule_node_get_tree(node);
1957 tree = isl_schedule_tree_band_shift(tree, shift);
1958 return isl_schedule_node_graft_tree(node, tree);
1959 error:
1960 isl_multi_union_pw_aff_free(shift);
1961 isl_schedule_node_free(node);
1962 return NULL;
1963 }
1964
1965 /* Tile "node" with tile sizes "sizes".
1966 *
1967 * The current node is replaced by two nested nodes corresponding
1968 * to the tile dimensions and the point dimensions.
1969 *
1970 * Return a pointer to the outer (tile) node.
1971 *
1972 * If any of the descendants of "node" depend on the set of outer band nodes,
1973 * then we refuse to tile the node.
1974 *
1975 * If the scale tile loops option is set, then the tile loops
1976 * are scaled by the tile sizes. If the shift point loops option is set,
1977 * then the point loops are shifted to start at zero.
1978 * In particular, these options affect the tile and point loop schedules
1979 * as follows
1980 *
1981 * scale shift original tile point
1982 *
1983 * 0 0 i floor(i/s) i
1984 * 1 0 i s * floor(i/s) i
1985 * 0 1 i floor(i/s) i - s * floor(i/s)
1986 * 1 1 i s * floor(i/s) i - s * floor(i/s)
1987 */
isl_schedule_node_band_tile(__isl_take isl_schedule_node * node,__isl_take isl_multi_val * sizes)1988 __isl_give isl_schedule_node *isl_schedule_node_band_tile(
1989 __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes)
1990 {
1991 isl_schedule_tree *tree;
1992 int anchored;
1993
1994 if (!node || !sizes)
1995 goto error;
1996 anchored = isl_schedule_node_is_subtree_anchored(node);
1997 if (anchored < 0)
1998 goto error;
1999 if (anchored)
2000 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2001 "cannot tile band node with anchored subtree",
2002 goto error);
2003
2004 if (check_space_multi_val(node, sizes) < 0)
2005 goto error;
2006
2007 tree = isl_schedule_node_get_tree(node);
2008 tree = isl_schedule_tree_band_tile(tree, sizes);
2009 return isl_schedule_node_graft_tree(node, tree);
2010 error:
2011 isl_multi_val_free(sizes);
2012 isl_schedule_node_free(node);
2013 return NULL;
2014 }
2015
2016 /* Move the band node "node" down to all the leaves in the subtree
2017 * rooted at "node".
2018 * Return a pointer to the node in the resulting tree that is in the same
2019 * position as the node pointed to by "node" in the original tree.
2020 *
2021 * If the node only has a leaf child, then nothing needs to be done.
2022 * Otherwise, the child of the node is removed and the result is
2023 * appended to all the leaves in the subtree rooted at the original child.
2024 * Since the node is moved to the leaves, it needs to be expanded
2025 * according to the expansion, if any, defined by that subtree.
2026 * In the end, the original node is replaced by the result of
2027 * attaching copies of the expanded node to the leaves.
2028 *
2029 * If any of the nodes in the subtree rooted at "node" depend on
2030 * the set of outer band nodes then we refuse to sink the band node.
2031 */
isl_schedule_node_band_sink(__isl_take isl_schedule_node * node)2032 __isl_give isl_schedule_node *isl_schedule_node_band_sink(
2033 __isl_take isl_schedule_node *node)
2034 {
2035 enum isl_schedule_node_type type;
2036 isl_schedule_tree *tree, *child;
2037 isl_union_pw_multi_aff *contraction;
2038 isl_bool anchored;
2039 isl_size n;
2040
2041 if (!node)
2042 return NULL;
2043
2044 type = isl_schedule_node_get_type(node);
2045 if (type != isl_schedule_node_band)
2046 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2047 "not a band node", return isl_schedule_node_free(node));
2048 anchored = isl_schedule_node_is_subtree_anchored(node);
2049 if (anchored < 0)
2050 return isl_schedule_node_free(node);
2051 if (anchored)
2052 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2053 "cannot sink band node in anchored subtree",
2054 return isl_schedule_node_free(node));
2055 n = isl_schedule_tree_n_children(node->tree);
2056 if (n < 0)
2057 return isl_schedule_node_free(node);
2058 if (n == 0)
2059 return node;
2060
2061 contraction = isl_schedule_node_get_subtree_contraction(node);
2062
2063 tree = isl_schedule_node_get_tree(node);
2064 child = isl_schedule_tree_get_child(tree, 0);
2065 tree = isl_schedule_tree_reset_children(tree);
2066 tree = isl_schedule_tree_pullback_union_pw_multi_aff(tree, contraction);
2067 tree = isl_schedule_tree_append_to_leaves(child, tree);
2068
2069 return isl_schedule_node_graft_tree(node, tree);
2070 }
2071
2072 /* Split "node" into two nested band nodes, one with the first "pos"
2073 * dimensions and one with the remaining dimensions.
2074 * The schedules of the two band nodes live in anonymous spaces.
2075 * The loop AST generation type options and the isolate option
2076 * are split over the two band nodes.
2077 */
isl_schedule_node_band_split(__isl_take isl_schedule_node * node,int pos)2078 __isl_give isl_schedule_node *isl_schedule_node_band_split(
2079 __isl_take isl_schedule_node *node, int pos)
2080 {
2081 isl_size depth;
2082 isl_schedule_tree *tree;
2083
2084 depth = isl_schedule_node_get_schedule_depth(node);
2085 if (depth < 0)
2086 return isl_schedule_node_free(node);
2087 tree = isl_schedule_node_get_tree(node);
2088 tree = isl_schedule_tree_band_split(tree, pos, depth);
2089 return isl_schedule_node_graft_tree(node, tree);
2090 }
2091
2092 /* Return the context of the context node "node".
2093 */
isl_schedule_node_context_get_context(__isl_keep isl_schedule_node * node)2094 __isl_give isl_set *isl_schedule_node_context_get_context(
2095 __isl_keep isl_schedule_node *node)
2096 {
2097 if (!node)
2098 return NULL;
2099
2100 return isl_schedule_tree_context_get_context(node->tree);
2101 }
2102
2103 /* Return the domain of the domain node "node".
2104 */
isl_schedule_node_domain_get_domain(__isl_keep isl_schedule_node * node)2105 __isl_give isl_union_set *isl_schedule_node_domain_get_domain(
2106 __isl_keep isl_schedule_node *node)
2107 {
2108 if (!node)
2109 return NULL;
2110
2111 return isl_schedule_tree_domain_get_domain(node->tree);
2112 }
2113
2114 /* Return the expansion map of expansion node "node".
2115 */
isl_schedule_node_expansion_get_expansion(__isl_keep isl_schedule_node * node)2116 __isl_give isl_union_map *isl_schedule_node_expansion_get_expansion(
2117 __isl_keep isl_schedule_node *node)
2118 {
2119 if (!node)
2120 return NULL;
2121
2122 return isl_schedule_tree_expansion_get_expansion(node->tree);
2123 }
2124
2125 /* Return the contraction of expansion node "node".
2126 */
isl_schedule_node_expansion_get_contraction(__isl_keep isl_schedule_node * node)2127 __isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction(
2128 __isl_keep isl_schedule_node *node)
2129 {
2130 if (!node)
2131 return NULL;
2132
2133 return isl_schedule_tree_expansion_get_contraction(node->tree);
2134 }
2135
2136 /* Replace the contraction and the expansion of the expansion node "node"
2137 * by "contraction" and "expansion".
2138 */
2139 __isl_give isl_schedule_node *
isl_schedule_node_expansion_set_contraction_and_expansion(__isl_take isl_schedule_node * node,__isl_take isl_union_pw_multi_aff * contraction,__isl_take isl_union_map * expansion)2140 isl_schedule_node_expansion_set_contraction_and_expansion(
2141 __isl_take isl_schedule_node *node,
2142 __isl_take isl_union_pw_multi_aff *contraction,
2143 __isl_take isl_union_map *expansion)
2144 {
2145 isl_schedule_tree *tree;
2146
2147 if (!node || !contraction || !expansion)
2148 goto error;
2149
2150 tree = isl_schedule_tree_copy(node->tree);
2151 tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree,
2152 contraction, expansion);
2153 return isl_schedule_node_graft_tree(node, tree);
2154 error:
2155 isl_schedule_node_free(node);
2156 isl_union_pw_multi_aff_free(contraction);
2157 isl_union_map_free(expansion);
2158 return NULL;
2159 }
2160
2161 /* Return the extension of the extension node "node".
2162 */
isl_schedule_node_extension_get_extension(__isl_keep isl_schedule_node * node)2163 __isl_give isl_union_map *isl_schedule_node_extension_get_extension(
2164 __isl_keep isl_schedule_node *node)
2165 {
2166 if (!node)
2167 return NULL;
2168
2169 return isl_schedule_tree_extension_get_extension(node->tree);
2170 }
2171
2172 /* Replace the extension of extension node "node" by "extension".
2173 */
isl_schedule_node_extension_set_extension(__isl_take isl_schedule_node * node,__isl_take isl_union_map * extension)2174 __isl_give isl_schedule_node *isl_schedule_node_extension_set_extension(
2175 __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
2176 {
2177 isl_schedule_tree *tree;
2178
2179 if (!node || !extension)
2180 goto error;
2181
2182 tree = isl_schedule_tree_copy(node->tree);
2183 tree = isl_schedule_tree_extension_set_extension(tree, extension);
2184 return isl_schedule_node_graft_tree(node, tree);
2185 error:
2186 isl_schedule_node_free(node);
2187 isl_union_map_free(extension);
2188 return NULL;
2189 }
2190
2191 /* Return the filter of the filter node "node".
2192 */
isl_schedule_node_filter_get_filter(__isl_keep isl_schedule_node * node)2193 __isl_give isl_union_set *isl_schedule_node_filter_get_filter(
2194 __isl_keep isl_schedule_node *node)
2195 {
2196 if (!node)
2197 return NULL;
2198
2199 return isl_schedule_tree_filter_get_filter(node->tree);
2200 }
2201
2202 /* Replace the filter of filter node "node" by "filter".
2203 */
isl_schedule_node_filter_set_filter(__isl_take isl_schedule_node * node,__isl_take isl_union_set * filter)2204 __isl_give isl_schedule_node *isl_schedule_node_filter_set_filter(
2205 __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
2206 {
2207 isl_schedule_tree *tree;
2208
2209 if (!node || !filter)
2210 goto error;
2211
2212 tree = isl_schedule_tree_copy(node->tree);
2213 tree = isl_schedule_tree_filter_set_filter(tree, filter);
2214 return isl_schedule_node_graft_tree(node, tree);
2215 error:
2216 isl_schedule_node_free(node);
2217 isl_union_set_free(filter);
2218 return NULL;
2219 }
2220
2221 /* Intersect the filter of filter node "node" with "filter".
2222 *
2223 * If the filter of the node is already a subset of "filter",
2224 * then leave the node unchanged.
2225 */
isl_schedule_node_filter_intersect_filter(__isl_take isl_schedule_node * node,__isl_take isl_union_set * filter)2226 __isl_give isl_schedule_node *isl_schedule_node_filter_intersect_filter(
2227 __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
2228 {
2229 isl_union_set *node_filter = NULL;
2230 isl_bool subset;
2231
2232 if (!node || !filter)
2233 goto error;
2234
2235 node_filter = isl_schedule_node_filter_get_filter(node);
2236 subset = isl_union_set_is_subset(node_filter, filter);
2237 if (subset < 0)
2238 goto error;
2239 if (subset) {
2240 isl_union_set_free(node_filter);
2241 isl_union_set_free(filter);
2242 return node;
2243 }
2244 node_filter = isl_union_set_intersect(node_filter, filter);
2245 node = isl_schedule_node_filter_set_filter(node, node_filter);
2246 return node;
2247 error:
2248 isl_schedule_node_free(node);
2249 isl_union_set_free(node_filter);
2250 isl_union_set_free(filter);
2251 return NULL;
2252 }
2253
2254 /* Return the guard of the guard node "node".
2255 */
isl_schedule_node_guard_get_guard(__isl_keep isl_schedule_node * node)2256 __isl_give isl_set *isl_schedule_node_guard_get_guard(
2257 __isl_keep isl_schedule_node *node)
2258 {
2259 if (!node)
2260 return NULL;
2261
2262 return isl_schedule_tree_guard_get_guard(node->tree);
2263 }
2264
2265 /* Return the mark identifier of the mark node "node".
2266 */
isl_schedule_node_mark_get_id(__isl_keep isl_schedule_node * node)2267 __isl_give isl_id *isl_schedule_node_mark_get_id(
2268 __isl_keep isl_schedule_node *node)
2269 {
2270 if (!node)
2271 return NULL;
2272
2273 return isl_schedule_tree_mark_get_id(node->tree);
2274 }
2275
2276 /* Replace the child at position "pos" of the sequence node "node"
2277 * by the children of sequence root node of "tree".
2278 */
isl_schedule_node_sequence_splice(__isl_take isl_schedule_node * node,int pos,__isl_take isl_schedule_tree * tree)2279 __isl_give isl_schedule_node *isl_schedule_node_sequence_splice(
2280 __isl_take isl_schedule_node *node, int pos,
2281 __isl_take isl_schedule_tree *tree)
2282 {
2283 isl_schedule_tree *node_tree;
2284
2285 if (!node || !tree)
2286 goto error;
2287 if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
2288 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2289 "not a sequence node", goto error);
2290 if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence)
2291 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2292 "not a sequence node", goto error);
2293 node_tree = isl_schedule_node_get_tree(node);
2294 node_tree = isl_schedule_tree_sequence_splice(node_tree, pos, tree);
2295 node = isl_schedule_node_graft_tree(node, node_tree);
2296
2297 return node;
2298 error:
2299 isl_schedule_node_free(node);
2300 isl_schedule_tree_free(tree);
2301 return NULL;
2302 }
2303
2304 /* Given a sequence node "node", with a child at position "pos" that
2305 * is also a sequence node, attach the children of that node directly
2306 * as children of "node" at that position, replacing the original child.
2307 *
2308 * The filters of these children are intersected with the filter
2309 * of the child at position "pos".
2310 */
isl_schedule_node_sequence_splice_child(__isl_take isl_schedule_node * node,int pos)2311 __isl_give isl_schedule_node *isl_schedule_node_sequence_splice_child(
2312 __isl_take isl_schedule_node *node, int pos)
2313 {
2314 int i;
2315 isl_size n;
2316 isl_union_set *filter;
2317 isl_schedule_node *child;
2318 isl_schedule_tree *tree;
2319
2320 if (!node)
2321 return NULL;
2322 if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
2323 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2324 "not a sequence node",
2325 return isl_schedule_node_free(node));
2326 node = isl_schedule_node_child(node, pos);
2327 node = isl_schedule_node_child(node, 0);
2328 if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
2329 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2330 "not a sequence node",
2331 return isl_schedule_node_free(node));
2332 n = isl_schedule_node_n_children(node);
2333 if (n < 0)
2334 return isl_schedule_node_free(node);
2335 child = isl_schedule_node_copy(node);
2336 node = isl_schedule_node_parent(node);
2337 filter = isl_schedule_node_filter_get_filter(node);
2338 for (i = 0; i < n; ++i) {
2339 child = isl_schedule_node_child(child, i);
2340 child = isl_schedule_node_filter_intersect_filter(child,
2341 isl_union_set_copy(filter));
2342 child = isl_schedule_node_parent(child);
2343 }
2344 isl_union_set_free(filter);
2345 tree = isl_schedule_node_get_tree(child);
2346 isl_schedule_node_free(child);
2347 node = isl_schedule_node_parent(node);
2348 node = isl_schedule_node_sequence_splice(node, pos, tree);
2349
2350 return node;
2351 }
2352
2353 /* Update the ancestors of "node" to point to the tree that "node"
2354 * now points to.
2355 * That is, replace the child in the original parent that corresponds
2356 * to the current tree position by node->tree and continue updating
2357 * the ancestors in the same way until the root is reached.
2358 *
2359 * If "fn" is not NULL, then it is called on each ancestor as we move up
2360 * the tree so that it can modify the ancestor before it is added
2361 * to the list of ancestors of the modified node.
2362 * The additional "pos" argument records the position
2363 * of the "tree" argument in the original schedule tree.
2364 *
2365 * If "node" originally points to a leaf of the schedule tree, then make sure
2366 * that in the end it points to a leaf in the updated schedule tree.
2367 */
update_ancestors(__isl_take isl_schedule_node * node,__isl_give isl_schedule_tree * (* fn)(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_node * pos,void * user),void * user)2368 static __isl_give isl_schedule_node *update_ancestors(
2369 __isl_take isl_schedule_node *node,
2370 __isl_give isl_schedule_tree *(*fn)(__isl_take isl_schedule_tree *tree,
2371 __isl_keep isl_schedule_node *pos, void *user), void *user)
2372 {
2373 int i;
2374 isl_size n;
2375 int is_leaf;
2376 isl_schedule_tree *tree;
2377 isl_schedule_node *pos = NULL;
2378
2379 if (fn)
2380 pos = isl_schedule_node_copy(node);
2381
2382 node = isl_schedule_node_cow(node);
2383 if (!node)
2384 return isl_schedule_node_free(pos);
2385
2386 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
2387 if (n < 0)
2388 return isl_schedule_node_free(pos);
2389 tree = isl_schedule_tree_copy(node->tree);
2390
2391 for (i = n - 1; i >= 0; --i) {
2392 isl_schedule_tree *parent;
2393
2394 parent = isl_schedule_tree_list_get_schedule_tree(
2395 node->ancestors, i);
2396 parent = isl_schedule_tree_replace_child(parent,
2397 node->child_pos[i], tree);
2398 if (fn) {
2399 pos = isl_schedule_node_parent(pos);
2400 parent = fn(parent, pos, user);
2401 }
2402 node->ancestors = isl_schedule_tree_list_set_schedule_tree(
2403 node->ancestors, i, isl_schedule_tree_copy(parent));
2404
2405 tree = parent;
2406 }
2407
2408 if (fn)
2409 isl_schedule_node_free(pos);
2410
2411 is_leaf = isl_schedule_tree_is_leaf(node->tree);
2412 node->schedule = isl_schedule_set_root(node->schedule, tree);
2413 if (is_leaf) {
2414 isl_schedule_tree_free(node->tree);
2415 node->tree = isl_schedule_node_get_leaf(node);
2416 }
2417
2418 if (!node->schedule || !node->ancestors)
2419 return isl_schedule_node_free(node);
2420
2421 return node;
2422 }
2423
2424 /* Replace the subtree that "pos" points to by "tree", updating
2425 * the ancestors to maintain a consistent state.
2426 */
isl_schedule_node_graft_tree(__isl_take isl_schedule_node * pos,__isl_take isl_schedule_tree * tree)2427 __isl_give isl_schedule_node *isl_schedule_node_graft_tree(
2428 __isl_take isl_schedule_node *pos, __isl_take isl_schedule_tree *tree)
2429 {
2430 if (!tree || !pos)
2431 goto error;
2432 if (pos->tree == tree) {
2433 isl_schedule_tree_free(tree);
2434 return pos;
2435 }
2436
2437 pos = isl_schedule_node_cow(pos);
2438 if (!pos)
2439 goto error;
2440
2441 isl_schedule_tree_free(pos->tree);
2442 pos->tree = tree;
2443
2444 return update_ancestors(pos, NULL, NULL);
2445 error:
2446 isl_schedule_node_free(pos);
2447 isl_schedule_tree_free(tree);
2448 return NULL;
2449 }
2450
2451 /* Make sure we can insert a node between "node" and its parent.
2452 * Return -1 on error, reporting the reason why we cannot insert a node.
2453 */
check_insert(__isl_keep isl_schedule_node * node)2454 static int check_insert(__isl_keep isl_schedule_node *node)
2455 {
2456 int has_parent;
2457 enum isl_schedule_node_type type;
2458
2459 has_parent = isl_schedule_node_has_parent(node);
2460 if (has_parent < 0)
2461 return -1;
2462 if (!has_parent)
2463 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2464 "cannot insert node outside of root", return -1);
2465
2466 type = isl_schedule_node_get_parent_type(node);
2467 if (type == isl_schedule_node_error)
2468 return -1;
2469 if (type == isl_schedule_node_set || type == isl_schedule_node_sequence)
2470 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2471 "cannot insert node between set or sequence node "
2472 "and its filter children", return -1);
2473
2474 return 0;
2475 }
2476
2477 /* Insert a band node with partial schedule "mupa" between "node" and
2478 * its parent.
2479 * Return a pointer to the new band node.
2480 *
2481 * If any of the nodes in the subtree rooted at "node" depend on
2482 * the set of outer band nodes then we refuse to insert the band node.
2483 */
isl_schedule_node_insert_partial_schedule(__isl_take isl_schedule_node * node,__isl_take isl_multi_union_pw_aff * mupa)2484 __isl_give isl_schedule_node *isl_schedule_node_insert_partial_schedule(
2485 __isl_take isl_schedule_node *node,
2486 __isl_take isl_multi_union_pw_aff *mupa)
2487 {
2488 int anchored;
2489 isl_schedule_band *band;
2490 isl_schedule_tree *tree;
2491
2492 if (check_insert(node) < 0)
2493 node = isl_schedule_node_free(node);
2494 anchored = isl_schedule_node_is_subtree_anchored(node);
2495 if (anchored < 0)
2496 goto error;
2497 if (anchored)
2498 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2499 "cannot insert band node in anchored subtree",
2500 goto error);
2501
2502 tree = isl_schedule_node_get_tree(node);
2503 band = isl_schedule_band_from_multi_union_pw_aff(mupa);
2504 tree = isl_schedule_tree_insert_band(tree, band);
2505 node = isl_schedule_node_graft_tree(node, tree);
2506
2507 return node;
2508 error:
2509 isl_schedule_node_free(node);
2510 isl_multi_union_pw_aff_free(mupa);
2511 return NULL;
2512 }
2513
2514 /* Insert a context node with context "context" between "node" and its parent.
2515 * Return a pointer to the new context node.
2516 */
isl_schedule_node_insert_context(__isl_take isl_schedule_node * node,__isl_take isl_set * context)2517 __isl_give isl_schedule_node *isl_schedule_node_insert_context(
2518 __isl_take isl_schedule_node *node, __isl_take isl_set *context)
2519 {
2520 isl_schedule_tree *tree;
2521
2522 if (check_insert(node) < 0)
2523 node = isl_schedule_node_free(node);
2524
2525 tree = isl_schedule_node_get_tree(node);
2526 tree = isl_schedule_tree_insert_context(tree, context);
2527 node = isl_schedule_node_graft_tree(node, tree);
2528
2529 return node;
2530 }
2531
2532 /* Insert an expansion node with the given "contraction" and "expansion"
2533 * between "node" and its parent.
2534 * Return a pointer to the new expansion node.
2535 *
2536 * Typically the domain and range spaces of the expansion are different.
2537 * This means that only one of them can refer to the current domain space
2538 * in a consistent tree. It is up to the caller to ensure that the tree
2539 * returns to a consistent state.
2540 */
isl_schedule_node_insert_expansion(__isl_take isl_schedule_node * node,__isl_take isl_union_pw_multi_aff * contraction,__isl_take isl_union_map * expansion)2541 __isl_give isl_schedule_node *isl_schedule_node_insert_expansion(
2542 __isl_take isl_schedule_node *node,
2543 __isl_take isl_union_pw_multi_aff *contraction,
2544 __isl_take isl_union_map *expansion)
2545 {
2546 isl_schedule_tree *tree;
2547
2548 if (check_insert(node) < 0)
2549 node = isl_schedule_node_free(node);
2550
2551 tree = isl_schedule_node_get_tree(node);
2552 tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion);
2553 node = isl_schedule_node_graft_tree(node, tree);
2554
2555 return node;
2556 }
2557
2558 /* Insert an extension node with extension "extension" between "node" and
2559 * its parent.
2560 * Return a pointer to the new extension node.
2561 */
isl_schedule_node_insert_extension(__isl_take isl_schedule_node * node,__isl_take isl_union_map * extension)2562 __isl_give isl_schedule_node *isl_schedule_node_insert_extension(
2563 __isl_take isl_schedule_node *node,
2564 __isl_take isl_union_map *extension)
2565 {
2566 isl_schedule_tree *tree;
2567
2568 tree = isl_schedule_node_get_tree(node);
2569 tree = isl_schedule_tree_insert_extension(tree, extension);
2570 node = isl_schedule_node_graft_tree(node, tree);
2571
2572 return node;
2573 }
2574
2575 /* Insert a filter node with filter "filter" between "node" and its parent.
2576 * Return a pointer to the new filter node.
2577 */
isl_schedule_node_insert_filter(__isl_take isl_schedule_node * node,__isl_take isl_union_set * filter)2578 __isl_give isl_schedule_node *isl_schedule_node_insert_filter(
2579 __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
2580 {
2581 isl_schedule_tree *tree;
2582
2583 if (check_insert(node) < 0)
2584 node = isl_schedule_node_free(node);
2585
2586 tree = isl_schedule_node_get_tree(node);
2587 tree = isl_schedule_tree_insert_filter(tree, filter);
2588 node = isl_schedule_node_graft_tree(node, tree);
2589
2590 return node;
2591 }
2592
2593 /* Insert a guard node with guard "guard" between "node" and its parent.
2594 * Return a pointer to the new guard node.
2595 */
isl_schedule_node_insert_guard(__isl_take isl_schedule_node * node,__isl_take isl_set * guard)2596 __isl_give isl_schedule_node *isl_schedule_node_insert_guard(
2597 __isl_take isl_schedule_node *node, __isl_take isl_set *guard)
2598 {
2599 isl_schedule_tree *tree;
2600
2601 if (check_insert(node) < 0)
2602 node = isl_schedule_node_free(node);
2603
2604 tree = isl_schedule_node_get_tree(node);
2605 tree = isl_schedule_tree_insert_guard(tree, guard);
2606 node = isl_schedule_node_graft_tree(node, tree);
2607
2608 return node;
2609 }
2610
2611 /* Insert a mark node with mark identifier "mark" between "node" and
2612 * its parent.
2613 * Return a pointer to the new mark node.
2614 */
isl_schedule_node_insert_mark(__isl_take isl_schedule_node * node,__isl_take isl_id * mark)2615 __isl_give isl_schedule_node *isl_schedule_node_insert_mark(
2616 __isl_take isl_schedule_node *node, __isl_take isl_id *mark)
2617 {
2618 isl_schedule_tree *tree;
2619
2620 if (check_insert(node) < 0)
2621 node = isl_schedule_node_free(node);
2622
2623 tree = isl_schedule_node_get_tree(node);
2624 tree = isl_schedule_tree_insert_mark(tree, mark);
2625 node = isl_schedule_node_graft_tree(node, tree);
2626
2627 return node;
2628 }
2629
2630 /* Attach the current subtree of "node" to a sequence of filter tree nodes
2631 * with filters described by "filters", attach this sequence
2632 * of filter tree nodes as children to a new tree of type "type" and
2633 * replace the original subtree of "node" by this new tree.
2634 * Each copy of the original subtree is simplified with respect
2635 * to the corresponding filter.
2636 */
isl_schedule_node_insert_children(__isl_take isl_schedule_node * node,enum isl_schedule_node_type type,__isl_take isl_union_set_list * filters)2637 static __isl_give isl_schedule_node *isl_schedule_node_insert_children(
2638 __isl_take isl_schedule_node *node,
2639 enum isl_schedule_node_type type,
2640 __isl_take isl_union_set_list *filters)
2641 {
2642 int i;
2643 isl_size n;
2644 isl_ctx *ctx;
2645 isl_schedule_tree *tree;
2646 isl_schedule_tree_list *list;
2647
2648 if (check_insert(node) < 0)
2649 node = isl_schedule_node_free(node);
2650
2651 n = isl_union_set_list_n_union_set(filters);
2652 if (!node || n < 0)
2653 goto error;
2654
2655 ctx = isl_schedule_node_get_ctx(node);
2656 list = isl_schedule_tree_list_alloc(ctx, n);
2657 for (i = 0; i < n; ++i) {
2658 isl_schedule_node *node_i;
2659 isl_schedule_tree *tree;
2660 isl_union_set *filter;
2661
2662 filter = isl_union_set_list_get_union_set(filters, i);
2663 node_i = isl_schedule_node_copy(node);
2664 node_i = isl_schedule_node_gist(node_i,
2665 isl_union_set_copy(filter));
2666 tree = isl_schedule_node_get_tree(node_i);
2667 isl_schedule_node_free(node_i);
2668 tree = isl_schedule_tree_insert_filter(tree, filter);
2669 list = isl_schedule_tree_list_add(list, tree);
2670 }
2671 tree = isl_schedule_tree_from_children(type, list);
2672 node = isl_schedule_node_graft_tree(node, tree);
2673
2674 isl_union_set_list_free(filters);
2675 return node;
2676 error:
2677 isl_union_set_list_free(filters);
2678 isl_schedule_node_free(node);
2679 return NULL;
2680 }
2681
2682 /* Insert a sequence node with child filters "filters" between "node" and
2683 * its parent. That is, the tree that "node" points to is attached
2684 * to each of the child nodes of the filter nodes.
2685 * Return a pointer to the new sequence node.
2686 */
isl_schedule_node_insert_sequence(__isl_take isl_schedule_node * node,__isl_take isl_union_set_list * filters)2687 __isl_give isl_schedule_node *isl_schedule_node_insert_sequence(
2688 __isl_take isl_schedule_node *node,
2689 __isl_take isl_union_set_list *filters)
2690 {
2691 return isl_schedule_node_insert_children(node,
2692 isl_schedule_node_sequence, filters);
2693 }
2694
2695 /* Insert a set node with child filters "filters" between "node" and
2696 * its parent. That is, the tree that "node" points to is attached
2697 * to each of the child nodes of the filter nodes.
2698 * Return a pointer to the new set node.
2699 */
isl_schedule_node_insert_set(__isl_take isl_schedule_node * node,__isl_take isl_union_set_list * filters)2700 __isl_give isl_schedule_node *isl_schedule_node_insert_set(
2701 __isl_take isl_schedule_node *node,
2702 __isl_take isl_union_set_list *filters)
2703 {
2704 return isl_schedule_node_insert_children(node,
2705 isl_schedule_node_set, filters);
2706 }
2707
2708 /* Remove "node" from its schedule tree and return a pointer
2709 * to the leaf at the same position in the updated schedule tree.
2710 *
2711 * It is not allowed to remove the root of a schedule tree or
2712 * a child of a set or sequence node.
2713 */
isl_schedule_node_cut(__isl_take isl_schedule_node * node)2714 __isl_give isl_schedule_node *isl_schedule_node_cut(
2715 __isl_take isl_schedule_node *node)
2716 {
2717 isl_schedule_tree *leaf;
2718 enum isl_schedule_node_type parent_type;
2719
2720 if (!node)
2721 return NULL;
2722 if (!isl_schedule_node_has_parent(node))
2723 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2724 "cannot cut root", return isl_schedule_node_free(node));
2725
2726 parent_type = isl_schedule_node_get_parent_type(node);
2727 if (parent_type == isl_schedule_node_set ||
2728 parent_type == isl_schedule_node_sequence)
2729 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2730 "cannot cut child of set or sequence",
2731 return isl_schedule_node_free(node));
2732
2733 leaf = isl_schedule_node_get_leaf(node);
2734 return isl_schedule_node_graft_tree(node, leaf);
2735 }
2736
2737 /* Remove a single node from the schedule tree, attaching the child
2738 * of "node" directly to its parent.
2739 * Return a pointer to this former child or to the leaf the position
2740 * of the original node if there was no child.
2741 * It is not allowed to remove the root of a schedule tree,
2742 * a set or sequence node, a child of a set or sequence node or
2743 * a band node with an anchored subtree.
2744 */
isl_schedule_node_delete(__isl_take isl_schedule_node * node)2745 __isl_give isl_schedule_node *isl_schedule_node_delete(
2746 __isl_take isl_schedule_node *node)
2747 {
2748 isl_size n, depth;
2749 isl_schedule_tree *tree;
2750 enum isl_schedule_node_type type;
2751
2752 depth = isl_schedule_node_get_tree_depth(node);
2753 n = isl_schedule_node_n_children(node);
2754 if (depth < 0 || n < 0)
2755 return isl_schedule_node_free(node);
2756
2757 if (depth == 0)
2758 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2759 "cannot delete root node",
2760 return isl_schedule_node_free(node));
2761 if (n != 1)
2762 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2763 "can only delete node with a single child",
2764 return isl_schedule_node_free(node));
2765 type = isl_schedule_node_get_parent_type(node);
2766 if (type == isl_schedule_node_sequence || type == isl_schedule_node_set)
2767 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
2768 "cannot delete child of set or sequence",
2769 return isl_schedule_node_free(node));
2770 if (isl_schedule_node_get_type(node) == isl_schedule_node_band) {
2771 int anchored;
2772
2773 anchored = isl_schedule_node_is_subtree_anchored(node);
2774 if (anchored < 0)
2775 return isl_schedule_node_free(node);
2776 if (anchored)
2777 isl_die(isl_schedule_node_get_ctx(node),
2778 isl_error_invalid,
2779 "cannot delete band node with anchored subtree",
2780 return isl_schedule_node_free(node));
2781 }
2782
2783 tree = isl_schedule_node_get_tree(node);
2784 if (!tree || isl_schedule_tree_has_children(tree)) {
2785 tree = isl_schedule_tree_child(tree, 0);
2786 } else {
2787 isl_schedule_tree_free(tree);
2788 tree = isl_schedule_node_get_leaf(node);
2789 }
2790 node = isl_schedule_node_graft_tree(node, tree);
2791
2792 return node;
2793 }
2794
2795 /* Internal data structure for the group_ancestor callback.
2796 *
2797 * If "finished" is set, then we no longer need to modify
2798 * any further ancestors.
2799 *
2800 * "contraction" and "expansion" represent the expansion
2801 * that reflects the grouping.
2802 *
2803 * "domain" contains the domain elements that reach the position
2804 * where the grouping is performed. That is, it is the range
2805 * of the resulting expansion.
2806 * "domain_universe" is the universe of "domain".
2807 * "group" is the set of group elements, i.e., the domain
2808 * of the resulting expansion.
2809 * "group_universe" is the universe of "group".
2810 *
2811 * "sched" is the schedule for the group elements, in pratice
2812 * an identity mapping on "group_universe".
2813 * "dim" is the dimension of "sched".
2814 */
2815 struct isl_schedule_group_data {
2816 int finished;
2817
2818 isl_union_map *expansion;
2819 isl_union_pw_multi_aff *contraction;
2820
2821 isl_union_set *domain;
2822 isl_union_set *domain_universe;
2823 isl_union_set *group;
2824 isl_union_set *group_universe;
2825
2826 int dim;
2827 isl_multi_aff *sched;
2828 };
2829
2830 /* Is domain covered by data->domain within data->domain_universe?
2831 */
locally_covered_by_domain(__isl_keep isl_union_set * domain,struct isl_schedule_group_data * data)2832 static isl_bool locally_covered_by_domain(__isl_keep isl_union_set *domain,
2833 struct isl_schedule_group_data *data)
2834 {
2835 isl_bool is_subset;
2836 isl_union_set *test;
2837
2838 test = isl_union_set_copy(domain);
2839 test = isl_union_set_intersect(test,
2840 isl_union_set_copy(data->domain_universe));
2841 is_subset = isl_union_set_is_subset(test, data->domain);
2842 isl_union_set_free(test);
2843
2844 return is_subset;
2845 }
2846
2847 /* Update the band tree root "tree" to refer to the group instances
2848 * in data->group rather than the original domain elements in data->domain.
2849 * "pos" is the position in the original schedule tree where the modified
2850 * "tree" will be attached.
2851 *
2852 * Add the part of the identity schedule on the group instances data->sched
2853 * that corresponds to this band node to the band schedule.
2854 * If the domain elements that reach the node and that are part
2855 * of data->domain_universe are all elements of data->domain (and therefore
2856 * replaced by the group instances) then this data->domain_universe
2857 * is removed from the domain of the band schedule.
2858 */
group_band(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_node * pos,struct isl_schedule_group_data * data)2859 static __isl_give isl_schedule_tree *group_band(
2860 __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
2861 struct isl_schedule_group_data *data)
2862 {
2863 isl_union_set *domain;
2864 isl_multi_aff *ma;
2865 isl_multi_union_pw_aff *mupa, *partial;
2866 isl_bool is_covered;
2867 isl_size depth, n;
2868 isl_bool has_id;
2869
2870 domain = isl_schedule_node_get_domain(pos);
2871 is_covered = locally_covered_by_domain(domain, data);
2872 if (is_covered >= 0 && is_covered) {
2873 domain = isl_union_set_universe(domain);
2874 domain = isl_union_set_subtract(domain,
2875 isl_union_set_copy(data->domain_universe));
2876 tree = isl_schedule_tree_band_intersect_domain(tree, domain);
2877 } else
2878 isl_union_set_free(domain);
2879 if (is_covered < 0)
2880 return isl_schedule_tree_free(tree);
2881 depth = isl_schedule_node_get_schedule_depth(pos);
2882 n = isl_schedule_tree_band_n_member(tree);
2883 if (depth < 0 || n < 0)
2884 return isl_schedule_tree_free(tree);
2885 ma = isl_multi_aff_copy(data->sched);
2886 ma = isl_multi_aff_drop_dims(ma, isl_dim_out, 0, depth);
2887 ma = isl_multi_aff_drop_dims(ma, isl_dim_out, n, data->dim - depth - n);
2888 mupa = isl_multi_union_pw_aff_from_multi_aff(ma);
2889 partial = isl_schedule_tree_band_get_partial_schedule(tree);
2890 has_id = isl_multi_union_pw_aff_has_tuple_id(partial, isl_dim_set);
2891 if (has_id < 0) {
2892 partial = isl_multi_union_pw_aff_free(partial);
2893 } else if (has_id) {
2894 isl_id *id;
2895 id = isl_multi_union_pw_aff_get_tuple_id(partial, isl_dim_set);
2896 mupa = isl_multi_union_pw_aff_set_tuple_id(mupa,
2897 isl_dim_set, id);
2898 }
2899 partial = isl_multi_union_pw_aff_union_add(partial, mupa);
2900 tree = isl_schedule_tree_band_set_partial_schedule(tree, partial);
2901
2902 return tree;
2903 }
2904
2905 /* Drop the parameters in "uset" that are not also in "space".
2906 * "n" is the number of parameters in "space".
2907 */
union_set_drop_extra_params(__isl_take isl_union_set * uset,__isl_keep isl_space * space,int n)2908 static __isl_give isl_union_set *union_set_drop_extra_params(
2909 __isl_take isl_union_set *uset, __isl_keep isl_space *space, int n)
2910 {
2911 isl_size n2;
2912
2913 uset = isl_union_set_align_params(uset, isl_space_copy(space));
2914 n2 = isl_union_set_dim(uset, isl_dim_param);
2915 if (n2 < 0)
2916 return isl_union_set_free(uset);
2917 uset = isl_union_set_project_out(uset, isl_dim_param, n, n2 - n);
2918
2919 return uset;
2920 }
2921
2922 /* Update the context tree root "tree" to refer to the group instances
2923 * in data->group rather than the original domain elements in data->domain.
2924 * "pos" is the position in the original schedule tree where the modified
2925 * "tree" will be attached.
2926 *
2927 * We do not actually need to update "tree" since a context node only
2928 * refers to the schedule space. However, we may need to update "data"
2929 * to not refer to any parameters introduced by the context node.
2930 */
group_context(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_node * pos,struct isl_schedule_group_data * data)2931 static __isl_give isl_schedule_tree *group_context(
2932 __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
2933 struct isl_schedule_group_data *data)
2934 {
2935 isl_space *space;
2936 isl_union_set *domain;
2937 isl_size n1, n2;
2938 isl_bool involves;
2939 isl_size depth;
2940
2941 depth = isl_schedule_node_get_tree_depth(pos);
2942 if (depth < 0)
2943 return isl_schedule_tree_free(tree);
2944 if (depth == 1)
2945 return tree;
2946
2947 domain = isl_schedule_node_get_universe_domain(pos);
2948 space = isl_union_set_get_space(domain);
2949 isl_union_set_free(domain);
2950
2951 n1 = isl_space_dim(space, isl_dim_param);
2952 data->expansion = isl_union_map_align_params(data->expansion, space);
2953 n2 = isl_union_map_dim(data->expansion, isl_dim_param);
2954
2955 if (n1 < 0 || n2 < 0)
2956 return isl_schedule_tree_free(tree);
2957 if (n1 == n2)
2958 return tree;
2959
2960 involves = isl_union_map_involves_dims(data->expansion,
2961 isl_dim_param, n1, n2 - n1);
2962 if (involves < 0)
2963 return isl_schedule_tree_free(tree);
2964 if (involves)
2965 isl_die(isl_schedule_node_get_ctx(pos), isl_error_invalid,
2966 "grouping cannot only refer to global parameters",
2967 return isl_schedule_tree_free(tree));
2968
2969 data->expansion = isl_union_map_project_out(data->expansion,
2970 isl_dim_param, n1, n2 - n1);
2971 space = isl_union_map_get_space(data->expansion);
2972
2973 data->contraction = isl_union_pw_multi_aff_align_params(
2974 data->contraction, isl_space_copy(space));
2975 n2 = isl_union_pw_multi_aff_dim(data->contraction, isl_dim_param);
2976 if (n2 < 0)
2977 data->contraction =
2978 isl_union_pw_multi_aff_free(data->contraction);
2979 data->contraction = isl_union_pw_multi_aff_drop_dims(data->contraction,
2980 isl_dim_param, n1, n2 - n1);
2981
2982 data->domain = union_set_drop_extra_params(data->domain, space, n1);
2983 data->domain_universe =
2984 union_set_drop_extra_params(data->domain_universe, space, n1);
2985 data->group = union_set_drop_extra_params(data->group, space, n1);
2986 data->group_universe =
2987 union_set_drop_extra_params(data->group_universe, space, n1);
2988
2989 data->sched = isl_multi_aff_align_params(data->sched,
2990 isl_space_copy(space));
2991 n2 = isl_multi_aff_dim(data->sched, isl_dim_param);
2992 if (n2 < 0)
2993 data->sched = isl_multi_aff_free(data->sched);
2994 data->sched = isl_multi_aff_drop_dims(data->sched,
2995 isl_dim_param, n1, n2 - n1);
2996
2997 isl_space_free(space);
2998
2999 return tree;
3000 }
3001
3002 /* Update the domain tree root "tree" to refer to the group instances
3003 * in data->group rather than the original domain elements in data->domain.
3004 * "pos" is the position in the original schedule tree where the modified
3005 * "tree" will be attached.
3006 *
3007 * We first double-check that all grouped domain elements are actually
3008 * part of the root domain and then replace those elements by the group
3009 * instances.
3010 */
group_domain(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_node * pos,struct isl_schedule_group_data * data)3011 static __isl_give isl_schedule_tree *group_domain(
3012 __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
3013 struct isl_schedule_group_data *data)
3014 {
3015 isl_union_set *domain;
3016 isl_bool is_subset;
3017
3018 domain = isl_schedule_tree_domain_get_domain(tree);
3019 is_subset = isl_union_set_is_subset(data->domain, domain);
3020 isl_union_set_free(domain);
3021 if (is_subset < 0)
3022 return isl_schedule_tree_free(tree);
3023 if (!is_subset)
3024 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
3025 "grouped domain should be part of outer domain",
3026 return isl_schedule_tree_free(tree));
3027 domain = isl_schedule_tree_domain_get_domain(tree);
3028 domain = isl_union_set_subtract(domain,
3029 isl_union_set_copy(data->domain));
3030 domain = isl_union_set_union(domain, isl_union_set_copy(data->group));
3031 tree = isl_schedule_tree_domain_set_domain(tree, domain);
3032
3033 return tree;
3034 }
3035
3036 /* Update the expansion tree root "tree" to refer to the group instances
3037 * in data->group rather than the original domain elements in data->domain.
3038 * "pos" is the position in the original schedule tree where the modified
3039 * "tree" will be attached.
3040 *
3041 * Let G_1 -> D_1 be the expansion of "tree" and G_2 -> D_2 the newly
3042 * introduced expansion in a descendant of "tree".
3043 * We first double-check that D_2 is a subset of D_1.
3044 * Then we remove D_2 from the range of G_1 -> D_1 and add the mapping
3045 * G_1 -> D_1 . D_2 -> G_2.
3046 * Simmilarly, we restrict the domain of the contraction to the universe
3047 * of the range of the updated expansion and add G_2 -> D_2 . D_1 -> G_1,
3048 * attempting to remove the domain constraints of this additional part.
3049 */
group_expansion(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_node * pos,struct isl_schedule_group_data * data)3050 static __isl_give isl_schedule_tree *group_expansion(
3051 __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
3052 struct isl_schedule_group_data *data)
3053 {
3054 isl_union_set *domain;
3055 isl_union_map *expansion, *umap;
3056 isl_union_pw_multi_aff *contraction, *upma;
3057 int is_subset;
3058
3059 expansion = isl_schedule_tree_expansion_get_expansion(tree);
3060 domain = isl_union_map_range(expansion);
3061 is_subset = isl_union_set_is_subset(data->domain, domain);
3062 isl_union_set_free(domain);
3063 if (is_subset < 0)
3064 return isl_schedule_tree_free(tree);
3065 if (!is_subset)
3066 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
3067 "grouped domain should be part "
3068 "of outer expansion domain",
3069 return isl_schedule_tree_free(tree));
3070 expansion = isl_schedule_tree_expansion_get_expansion(tree);
3071 umap = isl_union_map_from_union_pw_multi_aff(
3072 isl_union_pw_multi_aff_copy(data->contraction));
3073 umap = isl_union_map_apply_range(expansion, umap);
3074 expansion = isl_schedule_tree_expansion_get_expansion(tree);
3075 expansion = isl_union_map_subtract_range(expansion,
3076 isl_union_set_copy(data->domain));
3077 expansion = isl_union_map_union(expansion, umap);
3078 umap = isl_union_map_universe(isl_union_map_copy(expansion));
3079 domain = isl_union_map_range(umap);
3080 contraction = isl_schedule_tree_expansion_get_contraction(tree);
3081 umap = isl_union_map_from_union_pw_multi_aff(contraction);
3082 umap = isl_union_map_apply_range(isl_union_map_copy(data->expansion),
3083 umap);
3084 upma = isl_union_pw_multi_aff_from_union_map(umap);
3085 contraction = isl_schedule_tree_expansion_get_contraction(tree);
3086 contraction = isl_union_pw_multi_aff_intersect_domain(contraction,
3087 domain);
3088 domain = isl_union_pw_multi_aff_domain(
3089 isl_union_pw_multi_aff_copy(upma));
3090 upma = isl_union_pw_multi_aff_gist(upma, domain);
3091 contraction = isl_union_pw_multi_aff_union_add(contraction, upma);
3092 tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree,
3093 contraction, expansion);
3094
3095 return tree;
3096 }
3097
3098 /* Update the tree root "tree" to refer to the group instances
3099 * in data->group rather than the original domain elements in data->domain.
3100 * "pos" is the position in the original schedule tree where the modified
3101 * "tree" will be attached.
3102 *
3103 * If we have come across a domain or expansion node before (data->finished
3104 * is set), then we no longer need perform any modifications.
3105 *
3106 * If "tree" is a filter, then we add data->group_universe to the filter.
3107 * We also remove data->domain_universe from the filter if all the domain
3108 * elements in this universe that reach the filter node are part of
3109 * the elements that are being grouped by data->expansion.
3110 * If "tree" is a band, domain or expansion, then it is handled
3111 * in a separate function.
3112 */
group_ancestor(__isl_take isl_schedule_tree * tree,__isl_keep isl_schedule_node * pos,void * user)3113 static __isl_give isl_schedule_tree *group_ancestor(
3114 __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
3115 void *user)
3116 {
3117 struct isl_schedule_group_data *data = user;
3118 isl_union_set *domain;
3119 isl_bool is_covered;
3120
3121 if (!tree || !pos)
3122 return isl_schedule_tree_free(tree);
3123
3124 if (data->finished)
3125 return tree;
3126
3127 switch (isl_schedule_tree_get_type(tree)) {
3128 case isl_schedule_node_error:
3129 return isl_schedule_tree_free(tree);
3130 case isl_schedule_node_extension:
3131 isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
3132 "grouping not allowed in extended tree",
3133 return isl_schedule_tree_free(tree));
3134 case isl_schedule_node_band:
3135 tree = group_band(tree, pos, data);
3136 break;
3137 case isl_schedule_node_context:
3138 tree = group_context(tree, pos, data);
3139 break;
3140 case isl_schedule_node_domain:
3141 tree = group_domain(tree, pos, data);
3142 data->finished = 1;
3143 break;
3144 case isl_schedule_node_filter:
3145 domain = isl_schedule_node_get_domain(pos);
3146 is_covered = locally_covered_by_domain(domain, data);
3147 isl_union_set_free(domain);
3148 if (is_covered < 0)
3149 return isl_schedule_tree_free(tree);
3150 domain = isl_schedule_tree_filter_get_filter(tree);
3151 if (is_covered)
3152 domain = isl_union_set_subtract(domain,
3153 isl_union_set_copy(data->domain_universe));
3154 domain = isl_union_set_union(domain,
3155 isl_union_set_copy(data->group_universe));
3156 tree = isl_schedule_tree_filter_set_filter(tree, domain);
3157 break;
3158 case isl_schedule_node_expansion:
3159 tree = group_expansion(tree, pos, data);
3160 data->finished = 1;
3161 break;
3162 case isl_schedule_node_leaf:
3163 case isl_schedule_node_guard:
3164 case isl_schedule_node_mark:
3165 case isl_schedule_node_sequence:
3166 case isl_schedule_node_set:
3167 break;
3168 }
3169
3170 return tree;
3171 }
3172
3173 /* Group the domain elements that reach "node" into instances
3174 * of a single statement with identifier "group_id".
3175 * In particular, group the domain elements according to their
3176 * prefix schedule.
3177 *
3178 * That is, introduce an expansion node with as contraction
3179 * the prefix schedule (with the target space replaced by "group_id")
3180 * and as expansion the inverse of this contraction (with its range
3181 * intersected with the domain elements that reach "node").
3182 * The outer nodes are then modified to refer to the group instances
3183 * instead of the original domain elements.
3184 *
3185 * No instance of "group_id" is allowed to reach "node" prior
3186 * to the grouping.
3187 * No ancestor of "node" is allowed to be an extension node.
3188 *
3189 * Return a pointer to original node in tree, i.e., the child
3190 * of the newly introduced expansion node.
3191 */
isl_schedule_node_group(__isl_take isl_schedule_node * node,__isl_take isl_id * group_id)3192 __isl_give isl_schedule_node *isl_schedule_node_group(
3193 __isl_take isl_schedule_node *node, __isl_take isl_id *group_id)
3194 {
3195 struct isl_schedule_group_data data = { 0 };
3196 isl_space *space;
3197 isl_union_set *domain;
3198 isl_union_pw_multi_aff *contraction;
3199 isl_union_map *expansion;
3200 isl_bool disjoint;
3201 isl_size depth;
3202
3203 depth = isl_schedule_node_get_schedule_depth(node);
3204 if (depth < 0 || !group_id)
3205 goto error;
3206 if (check_insert(node) < 0)
3207 goto error;
3208
3209 domain = isl_schedule_node_get_domain(node);
3210 data.domain = isl_union_set_copy(domain);
3211 data.domain_universe = isl_union_set_copy(domain);
3212 data.domain_universe = isl_union_set_universe(data.domain_universe);
3213
3214 data.dim = depth;
3215 if (data.dim == 0) {
3216 isl_ctx *ctx;
3217 isl_set *set;
3218 isl_union_set *group;
3219 isl_union_map *univ;
3220
3221 ctx = isl_schedule_node_get_ctx(node);
3222 space = isl_space_set_alloc(ctx, 0, 0);
3223 space = isl_space_set_tuple_id(space, isl_dim_set, group_id);
3224 set = isl_set_universe(isl_space_copy(space));
3225 group = isl_union_set_from_set(set);
3226 expansion = isl_union_map_from_domain_and_range(domain, group);
3227 univ = isl_union_map_universe(isl_union_map_copy(expansion));
3228 contraction = isl_union_pw_multi_aff_from_union_map(univ);
3229 expansion = isl_union_map_reverse(expansion);
3230 } else {
3231 isl_multi_union_pw_aff *prefix;
3232 isl_union_set *univ;
3233
3234 prefix =
3235 isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
3236 prefix = isl_multi_union_pw_aff_set_tuple_id(prefix,
3237 isl_dim_set, group_id);
3238 space = isl_multi_union_pw_aff_get_space(prefix);
3239 contraction = isl_union_pw_multi_aff_from_multi_union_pw_aff(
3240 prefix);
3241 univ = isl_union_set_universe(isl_union_set_copy(domain));
3242 contraction =
3243 isl_union_pw_multi_aff_intersect_domain(contraction, univ);
3244 expansion = isl_union_map_from_union_pw_multi_aff(
3245 isl_union_pw_multi_aff_copy(contraction));
3246 expansion = isl_union_map_reverse(expansion);
3247 expansion = isl_union_map_intersect_range(expansion, domain);
3248 }
3249 space = isl_space_map_from_set(space);
3250 data.sched = isl_multi_aff_identity(space);
3251 data.group = isl_union_map_domain(isl_union_map_copy(expansion));
3252 data.group = isl_union_set_coalesce(data.group);
3253 data.group_universe = isl_union_set_copy(data.group);
3254 data.group_universe = isl_union_set_universe(data.group_universe);
3255 data.expansion = isl_union_map_copy(expansion);
3256 data.contraction = isl_union_pw_multi_aff_copy(contraction);
3257 node = isl_schedule_node_insert_expansion(node, contraction, expansion);
3258
3259 disjoint = isl_union_set_is_disjoint(data.domain_universe,
3260 data.group_universe);
3261
3262 node = update_ancestors(node, &group_ancestor, &data);
3263
3264 isl_union_set_free(data.domain);
3265 isl_union_set_free(data.domain_universe);
3266 isl_union_set_free(data.group);
3267 isl_union_set_free(data.group_universe);
3268 isl_multi_aff_free(data.sched);
3269 isl_union_map_free(data.expansion);
3270 isl_union_pw_multi_aff_free(data.contraction);
3271
3272 node = isl_schedule_node_child(node, 0);
3273
3274 if (!node || disjoint < 0)
3275 return isl_schedule_node_free(node);
3276 if (!disjoint)
3277 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
3278 "group instances already reach node",
3279 return isl_schedule_node_free(node));
3280
3281 return node;
3282 error:
3283 isl_schedule_node_free(node);
3284 isl_id_free(group_id);
3285 return NULL;
3286 }
3287
3288 /* Compute the gist of the given band node with respect to "context".
3289 */
isl_schedule_node_band_gist(__isl_take isl_schedule_node * node,__isl_take isl_union_set * context)3290 __isl_give isl_schedule_node *isl_schedule_node_band_gist(
3291 __isl_take isl_schedule_node *node, __isl_take isl_union_set *context)
3292 {
3293 isl_schedule_tree *tree;
3294
3295 tree = isl_schedule_node_get_tree(node);
3296 tree = isl_schedule_tree_band_gist(tree, context);
3297 return isl_schedule_node_graft_tree(node, tree);
3298 }
3299
3300 /* Internal data structure for isl_schedule_node_gist.
3301 * "n_expansion" is the number of outer expansion nodes
3302 * with respect to the current position
3303 * "filters" contains an element for each outer filter, expansion or
3304 * extension node with respect to the current position, each representing
3305 * the intersection of the previous element and the filter on the filter node
3306 * or the expansion/extension of the previous element.
3307 * The first element in the original context passed to isl_schedule_node_gist.
3308 */
3309 struct isl_node_gist_data {
3310 int n_expansion;
3311 isl_union_set_list *filters;
3312 };
3313
3314 /* Enter the expansion node "node" during a isl_schedule_node_gist traversal.
3315 *
3316 * In particular, add an extra element to data->filters containing
3317 * the expansion of the previous element and replace the expansion
3318 * and contraction on "node" by the gist with respect to these filters.
3319 * Also keep track of the fact that we have entered another expansion.
3320 */
gist_enter_expansion(__isl_take isl_schedule_node * node,struct isl_node_gist_data * data)3321 static __isl_give isl_schedule_node *gist_enter_expansion(
3322 __isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
3323 {
3324 isl_size n;
3325 isl_union_set *inner;
3326 isl_union_map *expansion;
3327 isl_union_pw_multi_aff *contraction;
3328
3329 data->n_expansion++;
3330
3331 n = isl_union_set_list_n_union_set(data->filters);
3332 if (n < 0)
3333 return isl_schedule_node_free(node);
3334 inner = isl_union_set_list_get_union_set(data->filters, n - 1);
3335 expansion = isl_schedule_node_expansion_get_expansion(node);
3336 inner = isl_union_set_apply(inner, expansion);
3337
3338 contraction = isl_schedule_node_expansion_get_contraction(node);
3339 contraction = isl_union_pw_multi_aff_gist(contraction,
3340 isl_union_set_copy(inner));
3341
3342 data->filters = isl_union_set_list_add(data->filters, inner);
3343
3344 inner = isl_union_set_list_get_union_set(data->filters, n - 1);
3345 expansion = isl_schedule_node_expansion_get_expansion(node);
3346 expansion = isl_union_map_gist_domain(expansion, inner);
3347 node = isl_schedule_node_expansion_set_contraction_and_expansion(node,
3348 contraction, expansion);
3349
3350 return node;
3351 }
3352
3353 /* Leave the expansion node "node" during a isl_schedule_node_gist traversal.
3354 *
3355 * In particular, remove the element in data->filters that was added by
3356 * gist_enter_expansion and decrement the number of outer expansions.
3357 *
3358 * The expansion has already been simplified in gist_enter_expansion.
3359 * If this simplification results in an identity expansion, then
3360 * it is removed here.
3361 */
gist_leave_expansion(__isl_take isl_schedule_node * node,struct isl_node_gist_data * data)3362 static __isl_give isl_schedule_node *gist_leave_expansion(
3363 __isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
3364 {
3365 isl_size n;
3366 isl_bool identity;
3367 isl_union_map *expansion;
3368
3369 expansion = isl_schedule_node_expansion_get_expansion(node);
3370 identity = isl_union_map_is_identity(expansion);
3371 isl_union_map_free(expansion);
3372
3373 if (identity < 0)
3374 node = isl_schedule_node_free(node);
3375 else if (identity)
3376 node = isl_schedule_node_delete(node);
3377
3378 n = isl_union_set_list_n_union_set(data->filters);
3379 if (n < 0)
3380 return isl_schedule_node_free(node);
3381 data->filters = isl_union_set_list_drop(data->filters, n - 1, 1);
3382
3383 data->n_expansion--;
3384
3385 return node;
3386 }
3387
3388 /* Enter the extension node "node" during a isl_schedule_node_gist traversal.
3389 *
3390 * In particular, add an extra element to data->filters containing
3391 * the union of the previous element with the additional domain elements
3392 * introduced by the extension.
3393 */
gist_enter_extension(__isl_take isl_schedule_node * node,struct isl_node_gist_data * data)3394 static __isl_give isl_schedule_node *gist_enter_extension(
3395 __isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
3396 {
3397 isl_size n;
3398 isl_union_set *inner, *extra;
3399 isl_union_map *extension;
3400
3401 n = isl_union_set_list_n_union_set(data->filters);
3402 if (n < 0)
3403 return isl_schedule_node_free(node);
3404 inner = isl_union_set_list_get_union_set(data->filters, n - 1);
3405 extension = isl_schedule_node_extension_get_extension(node);
3406 extra = isl_union_map_range(extension);
3407 inner = isl_union_set_union(inner, extra);
3408
3409 data->filters = isl_union_set_list_add(data->filters, inner);
3410
3411 return node;
3412 }
3413
3414 /* Can we finish gisting at this node?
3415 * That is, is the filter on the current filter node a subset of
3416 * the original context passed to isl_schedule_node_gist?
3417 * If we have gone through any expansions, then we cannot perform
3418 * this test since the current domain elements are incomparable
3419 * to the domain elements in the original context.
3420 */
gist_done(__isl_keep isl_schedule_node * node,struct isl_node_gist_data * data)3421 static isl_bool gist_done(__isl_keep isl_schedule_node *node,
3422 struct isl_node_gist_data *data)
3423 {
3424 isl_union_set *filter, *outer;
3425 isl_bool subset;
3426
3427 if (data->n_expansion != 0)
3428 return isl_bool_false;
3429
3430 filter = isl_schedule_node_filter_get_filter(node);
3431 outer = isl_union_set_list_get_union_set(data->filters, 0);
3432 subset = isl_union_set_is_subset(filter, outer);
3433 isl_union_set_free(outer);
3434 isl_union_set_free(filter);
3435
3436 return subset;
3437 }
3438
3439 /* Callback for "traverse" to enter a node and to move
3440 * to the deepest initial subtree that should be traversed
3441 * by isl_schedule_node_gist.
3442 *
3443 * The "filters" list is extended by one element each time
3444 * we come across a filter node by the result of intersecting
3445 * the last element in the list with the filter on the filter node.
3446 *
3447 * If the filter on the current filter node is a subset of
3448 * the original context passed to isl_schedule_node_gist,
3449 * then there is no need to go into its subtree since it cannot
3450 * be further simplified by the context. The "filters" list is
3451 * still extended for consistency, but the actual value of the
3452 * added element is immaterial since it will not be used.
3453 *
3454 * Otherwise, the filter on the current filter node is replaced by
3455 * the gist of the original filter with respect to the intersection
3456 * of the original context with the intermediate filters.
3457 *
3458 * If the new element in the "filters" list is empty, then no elements
3459 * can reach the descendants of the current filter node. The subtree
3460 * underneath the filter node is therefore removed.
3461 *
3462 * Each expansion node we come across is handled by
3463 * gist_enter_expansion.
3464 *
3465 * Each extension node we come across is handled by
3466 * gist_enter_extension.
3467 */
gist_enter(__isl_take isl_schedule_node * node,void * user)3468 static __isl_give isl_schedule_node *gist_enter(
3469 __isl_take isl_schedule_node *node, void *user)
3470 {
3471 struct isl_node_gist_data *data = user;
3472
3473 do {
3474 isl_union_set *filter, *inner;
3475 isl_bool done, empty;
3476 isl_size n;
3477
3478 switch (isl_schedule_node_get_type(node)) {
3479 case isl_schedule_node_error:
3480 return isl_schedule_node_free(node);
3481 case isl_schedule_node_expansion:
3482 node = gist_enter_expansion(node, data);
3483 continue;
3484 case isl_schedule_node_extension:
3485 node = gist_enter_extension(node, data);
3486 continue;
3487 case isl_schedule_node_band:
3488 case isl_schedule_node_context:
3489 case isl_schedule_node_domain:
3490 case isl_schedule_node_guard:
3491 case isl_schedule_node_leaf:
3492 case isl_schedule_node_mark:
3493 case isl_schedule_node_sequence:
3494 case isl_schedule_node_set:
3495 continue;
3496 case isl_schedule_node_filter:
3497 break;
3498 }
3499 done = gist_done(node, data);
3500 filter = isl_schedule_node_filter_get_filter(node);
3501 n = isl_union_set_list_n_union_set(data->filters);
3502 if (n < 0 || done < 0 || done) {
3503 data->filters = isl_union_set_list_add(data->filters,
3504 filter);
3505 if (n < 0 || done < 0)
3506 return isl_schedule_node_free(node);
3507 return node;
3508 }
3509 inner = isl_union_set_list_get_union_set(data->filters, n - 1);
3510 filter = isl_union_set_gist(filter, isl_union_set_copy(inner));
3511 node = isl_schedule_node_filter_set_filter(node,
3512 isl_union_set_copy(filter));
3513 filter = isl_union_set_intersect(filter, inner);
3514 empty = isl_union_set_is_empty(filter);
3515 data->filters = isl_union_set_list_add(data->filters, filter);
3516 if (empty < 0)
3517 return isl_schedule_node_free(node);
3518 if (!empty)
3519 continue;
3520 node = isl_schedule_node_child(node, 0);
3521 node = isl_schedule_node_cut(node);
3522 node = isl_schedule_node_parent(node);
3523 return node;
3524 } while (isl_schedule_node_has_children(node) &&
3525 (node = isl_schedule_node_first_child(node)) != NULL);
3526
3527 return node;
3528 }
3529
3530 /* Callback for "traverse" to leave a node for isl_schedule_node_gist.
3531 *
3532 * In particular, if the current node is a filter node, then we remove
3533 * the element on the "filters" list that was added when we entered
3534 * the node. There is no need to compute any gist here, since we
3535 * already did that when we entered the node.
3536 *
3537 * Expansion nodes are handled by gist_leave_expansion.
3538 *
3539 * If the current node is an extension, then remove the element
3540 * in data->filters that was added by gist_enter_extension.
3541 *
3542 * If the current node is a band node, then we compute the gist of
3543 * the band node with respect to the intersection of the original context
3544 * and the intermediate filters.
3545 *
3546 * If the current node is a sequence or set node, then some of
3547 * the filter children may have become empty and so they are removed.
3548 * If only one child is left, then the set or sequence node along with
3549 * the single remaining child filter is removed. The filter can be
3550 * removed because the filters on a sequence or set node are supposed
3551 * to partition the incoming domain instances.
3552 * In principle, it should then be impossible for there to be zero
3553 * remaining children, but should this happen, we replace the entire
3554 * subtree with an empty filter.
3555 */
gist_leave(__isl_take isl_schedule_node * node,void * user)3556 static __isl_give isl_schedule_node *gist_leave(
3557 __isl_take isl_schedule_node *node, void *user)
3558 {
3559 struct isl_node_gist_data *data = user;
3560 isl_schedule_tree *tree;
3561 int i;
3562 isl_size n;
3563 isl_union_set *filter;
3564
3565 switch (isl_schedule_node_get_type(node)) {
3566 case isl_schedule_node_error:
3567 return isl_schedule_node_free(node);
3568 case isl_schedule_node_expansion:
3569 node = gist_leave_expansion(node, data);
3570 break;
3571 case isl_schedule_node_extension:
3572 case isl_schedule_node_filter:
3573 n = isl_union_set_list_n_union_set(data->filters);
3574 if (n < 0)
3575 return isl_schedule_node_free(node);
3576 data->filters = isl_union_set_list_drop(data->filters,
3577 n - 1, 1);
3578 break;
3579 case isl_schedule_node_band:
3580 n = isl_union_set_list_n_union_set(data->filters);
3581 if (n < 0)
3582 return isl_schedule_node_free(node);
3583 filter = isl_union_set_list_get_union_set(data->filters, n - 1);
3584 node = isl_schedule_node_band_gist(node, filter);
3585 break;
3586 case isl_schedule_node_set:
3587 case isl_schedule_node_sequence:
3588 tree = isl_schedule_node_get_tree(node);
3589 n = isl_schedule_tree_n_children(tree);
3590 if (n < 0)
3591 tree = isl_schedule_tree_free(tree);
3592 for (i = n - 1; i >= 0; --i) {
3593 isl_schedule_tree *child;
3594 isl_union_set *filter;
3595 isl_bool empty;
3596
3597 child = isl_schedule_tree_get_child(tree, i);
3598 filter = isl_schedule_tree_filter_get_filter(child);
3599 empty = isl_union_set_is_empty(filter);
3600 isl_union_set_free(filter);
3601 isl_schedule_tree_free(child);
3602 if (empty < 0)
3603 tree = isl_schedule_tree_free(tree);
3604 else if (empty)
3605 tree = isl_schedule_tree_drop_child(tree, i);
3606 }
3607 n = isl_schedule_tree_n_children(tree);
3608 if (n < 0)
3609 tree = isl_schedule_tree_free(tree);
3610 node = isl_schedule_node_graft_tree(node, tree);
3611 if (n == 1) {
3612 node = isl_schedule_node_delete(node);
3613 node = isl_schedule_node_delete(node);
3614 } else if (n == 0) {
3615 isl_space *space;
3616
3617 filter =
3618 isl_union_set_list_get_union_set(data->filters, 0);
3619 space = isl_union_set_get_space(filter);
3620 isl_union_set_free(filter);
3621 filter = isl_union_set_empty(space);
3622 node = isl_schedule_node_cut(node);
3623 node = isl_schedule_node_insert_filter(node, filter);
3624 }
3625 break;
3626 case isl_schedule_node_context:
3627 case isl_schedule_node_domain:
3628 case isl_schedule_node_guard:
3629 case isl_schedule_node_leaf:
3630 case isl_schedule_node_mark:
3631 break;
3632 }
3633
3634 return node;
3635 }
3636
3637 /* Compute the gist of the subtree at "node" with respect to
3638 * the reaching domain elements in "context".
3639 * In particular, compute the gist of all band and filter nodes
3640 * in the subtree with respect to "context". Children of set or sequence
3641 * nodes that end up with an empty filter are removed completely.
3642 *
3643 * We keep track of the intersection of "context" with all outer filters
3644 * of the current node within the subtree in the final element of "filters".
3645 * Initially, this list contains the single element "context" and it is
3646 * extended or shortened each time we enter or leave a filter node.
3647 */
isl_schedule_node_gist(__isl_take isl_schedule_node * node,__isl_take isl_union_set * context)3648 __isl_give isl_schedule_node *isl_schedule_node_gist(
3649 __isl_take isl_schedule_node *node, __isl_take isl_union_set *context)
3650 {
3651 struct isl_node_gist_data data;
3652
3653 data.n_expansion = 0;
3654 data.filters = isl_union_set_list_from_union_set(context);
3655 node = traverse(node, &gist_enter, &gist_leave, &data);
3656 isl_union_set_list_free(data.filters);
3657 return node;
3658 }
3659
3660 /* Intersect the domain of domain node "node" with "domain".
3661 *
3662 * If the domain of "node" is already a subset of "domain",
3663 * then nothing needs to be changed.
3664 *
3665 * Otherwise, we replace the domain of the domain node by the intersection
3666 * and simplify the subtree rooted at "node" with respect to this intersection.
3667 */
isl_schedule_node_domain_intersect_domain(__isl_take isl_schedule_node * node,__isl_take isl_union_set * domain)3668 __isl_give isl_schedule_node *isl_schedule_node_domain_intersect_domain(
3669 __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain)
3670 {
3671 isl_schedule_tree *tree;
3672 isl_union_set *uset;
3673 int is_subset;
3674
3675 if (!node || !domain)
3676 goto error;
3677
3678 uset = isl_schedule_tree_domain_get_domain(node->tree);
3679 is_subset = isl_union_set_is_subset(uset, domain);
3680 isl_union_set_free(uset);
3681 if (is_subset < 0)
3682 goto error;
3683 if (is_subset) {
3684 isl_union_set_free(domain);
3685 return node;
3686 }
3687
3688 tree = isl_schedule_tree_copy(node->tree);
3689 uset = isl_schedule_tree_domain_get_domain(tree);
3690 uset = isl_union_set_intersect(uset, domain);
3691 tree = isl_schedule_tree_domain_set_domain(tree,
3692 isl_union_set_copy(uset));
3693 node = isl_schedule_node_graft_tree(node, tree);
3694
3695 node = isl_schedule_node_child(node, 0);
3696 node = isl_schedule_node_gist(node, uset);
3697 node = isl_schedule_node_parent(node);
3698
3699 return node;
3700 error:
3701 isl_schedule_node_free(node);
3702 isl_union_set_free(domain);
3703 return NULL;
3704 }
3705
3706 /* Replace the domain of domain node "node" with the gist
3707 * of the original domain with respect to the parameter domain "context".
3708 */
isl_schedule_node_domain_gist_params(__isl_take isl_schedule_node * node,__isl_take isl_set * context)3709 __isl_give isl_schedule_node *isl_schedule_node_domain_gist_params(
3710 __isl_take isl_schedule_node *node, __isl_take isl_set *context)
3711 {
3712 isl_union_set *domain;
3713 isl_schedule_tree *tree;
3714
3715 if (!node || !context)
3716 goto error;
3717
3718 tree = isl_schedule_tree_copy(node->tree);
3719 domain = isl_schedule_tree_domain_get_domain(node->tree);
3720 domain = isl_union_set_gist_params(domain, context);
3721 tree = isl_schedule_tree_domain_set_domain(tree, domain);
3722 node = isl_schedule_node_graft_tree(node, tree);
3723
3724 return node;
3725 error:
3726 isl_schedule_node_free(node);
3727 isl_set_free(context);
3728 return NULL;
3729 }
3730
3731 /* Internal data structure for isl_schedule_node_get_subtree_expansion.
3732 * "expansions" contains a list of accumulated expansions
3733 * for each outer expansion, set or sequence node. The first element
3734 * in the list is an identity mapping on the reaching domain elements.
3735 * "res" collects the results.
3736 */
3737 struct isl_subtree_expansion_data {
3738 isl_union_map_list *expansions;
3739 isl_union_map *res;
3740 };
3741
3742 /* Callback for "traverse" to enter a node and to move
3743 * to the deepest initial subtree that should be traversed
3744 * by isl_schedule_node_get_subtree_expansion.
3745 *
3746 * Whenever we come across an expansion node, the last element
3747 * of data->expansions is combined with the expansion
3748 * on the expansion node.
3749 *
3750 * Whenever we come across a filter node that is the child
3751 * of a set or sequence node, data->expansions is extended
3752 * with a new element that restricts the previous element
3753 * to the elements selected by the filter.
3754 * The previous element can then be reused while backtracking.
3755 */
subtree_expansion_enter(__isl_take isl_schedule_node * node,void * user)3756 static __isl_give isl_schedule_node *subtree_expansion_enter(
3757 __isl_take isl_schedule_node *node, void *user)
3758 {
3759 struct isl_subtree_expansion_data *data = user;
3760
3761 do {
3762 enum isl_schedule_node_type type;
3763 isl_union_set *filter;
3764 isl_union_map *inner, *expansion;
3765 isl_size n;
3766
3767 switch (isl_schedule_node_get_type(node)) {
3768 case isl_schedule_node_error:
3769 return isl_schedule_node_free(node);
3770 case isl_schedule_node_filter:
3771 type = isl_schedule_node_get_parent_type(node);
3772 if (type != isl_schedule_node_set &&
3773 type != isl_schedule_node_sequence)
3774 break;
3775 filter = isl_schedule_node_filter_get_filter(node);
3776 n = isl_union_map_list_n_union_map(data->expansions);
3777 if (n < 0)
3778 data->expansions =
3779 isl_union_map_list_free(data->expansions);
3780 inner =
3781 isl_union_map_list_get_union_map(data->expansions,
3782 n - 1);
3783 inner = isl_union_map_intersect_range(inner, filter);
3784 data->expansions =
3785 isl_union_map_list_add(data->expansions, inner);
3786 break;
3787 case isl_schedule_node_expansion:
3788 n = isl_union_map_list_n_union_map(data->expansions);
3789 if (n < 0)
3790 data->expansions =
3791 isl_union_map_list_free(data->expansions);
3792 expansion =
3793 isl_schedule_node_expansion_get_expansion(node);
3794 inner =
3795 isl_union_map_list_get_union_map(data->expansions,
3796 n - 1);
3797 inner = isl_union_map_apply_range(inner, expansion);
3798 data->expansions =
3799 isl_union_map_list_set_union_map(data->expansions,
3800 n - 1, inner);
3801 break;
3802 case isl_schedule_node_band:
3803 case isl_schedule_node_context:
3804 case isl_schedule_node_domain:
3805 case isl_schedule_node_extension:
3806 case isl_schedule_node_guard:
3807 case isl_schedule_node_leaf:
3808 case isl_schedule_node_mark:
3809 case isl_schedule_node_sequence:
3810 case isl_schedule_node_set:
3811 break;
3812 }
3813 } while (isl_schedule_node_has_children(node) &&
3814 (node = isl_schedule_node_first_child(node)) != NULL);
3815
3816 return node;
3817 }
3818
3819 /* Callback for "traverse" to leave a node for
3820 * isl_schedule_node_get_subtree_expansion.
3821 *
3822 * If we come across a filter node that is the child
3823 * of a set or sequence node, then we remove the element
3824 * of data->expansions that was added in subtree_expansion_enter.
3825 *
3826 * If we reach a leaf node, then the accumulated expansion is
3827 * added to data->res.
3828 */
subtree_expansion_leave(__isl_take isl_schedule_node * node,void * user)3829 static __isl_give isl_schedule_node *subtree_expansion_leave(
3830 __isl_take isl_schedule_node *node, void *user)
3831 {
3832 struct isl_subtree_expansion_data *data = user;
3833 isl_size n;
3834 isl_union_map *inner;
3835 enum isl_schedule_node_type type;
3836
3837 switch (isl_schedule_node_get_type(node)) {
3838 case isl_schedule_node_error:
3839 return isl_schedule_node_free(node);
3840 case isl_schedule_node_filter:
3841 type = isl_schedule_node_get_parent_type(node);
3842 if (type != isl_schedule_node_set &&
3843 type != isl_schedule_node_sequence)
3844 break;
3845 n = isl_union_map_list_n_union_map(data->expansions);
3846 if (n < 0)
3847 data->expansions =
3848 isl_union_map_list_free(data->expansions);
3849 data->expansions = isl_union_map_list_drop(data->expansions,
3850 n - 1, 1);
3851 break;
3852 case isl_schedule_node_leaf:
3853 n = isl_union_map_list_n_union_map(data->expansions);
3854 if (n < 0)
3855 data->expansions =
3856 isl_union_map_list_free(data->expansions);
3857 inner = isl_union_map_list_get_union_map(data->expansions,
3858 n - 1);
3859 data->res = isl_union_map_union(data->res, inner);
3860 break;
3861 case isl_schedule_node_band:
3862 case isl_schedule_node_context:
3863 case isl_schedule_node_domain:
3864 case isl_schedule_node_expansion:
3865 case isl_schedule_node_extension:
3866 case isl_schedule_node_guard:
3867 case isl_schedule_node_mark:
3868 case isl_schedule_node_sequence:
3869 case isl_schedule_node_set:
3870 break;
3871 }
3872
3873 return node;
3874 }
3875
3876 /* Return a mapping from the domain elements that reach "node"
3877 * to the corresponding domain elements in the leaves of the subtree
3878 * rooted at "node" obtained by composing the intermediate expansions.
3879 *
3880 * We start out with an identity mapping between the domain elements
3881 * that reach "node" and compose it with all the expansions
3882 * on a path from "node" to a leaf while traversing the subtree.
3883 * Within the children of an a sequence or set node, the
3884 * accumulated expansion is restricted to the elements selected
3885 * by the filter child.
3886 */
isl_schedule_node_get_subtree_expansion(__isl_keep isl_schedule_node * node)3887 __isl_give isl_union_map *isl_schedule_node_get_subtree_expansion(
3888 __isl_keep isl_schedule_node *node)
3889 {
3890 struct isl_subtree_expansion_data data;
3891 isl_space *space;
3892 isl_union_set *domain;
3893 isl_union_map *expansion;
3894
3895 if (!node)
3896 return NULL;
3897
3898 domain = isl_schedule_node_get_universe_domain(node);
3899 space = isl_union_set_get_space(domain);
3900 expansion = isl_union_set_identity(domain);
3901 data.res = isl_union_map_empty(space);
3902 data.expansions = isl_union_map_list_from_union_map(expansion);
3903
3904 node = isl_schedule_node_copy(node);
3905 node = traverse(node, &subtree_expansion_enter,
3906 &subtree_expansion_leave, &data);
3907 if (!node)
3908 data.res = isl_union_map_free(data.res);
3909 isl_schedule_node_free(node);
3910
3911 isl_union_map_list_free(data.expansions);
3912
3913 return data.res;
3914 }
3915
3916 /* Internal data structure for isl_schedule_node_get_subtree_contraction.
3917 * "contractions" contains a list of accumulated contractions
3918 * for each outer expansion, set or sequence node. The first element
3919 * in the list is an identity mapping on the reaching domain elements.
3920 * "res" collects the results.
3921 */
3922 struct isl_subtree_contraction_data {
3923 isl_union_pw_multi_aff_list *contractions;
3924 isl_union_pw_multi_aff *res;
3925 };
3926
3927 /* Callback for "traverse" to enter a node and to move
3928 * to the deepest initial subtree that should be traversed
3929 * by isl_schedule_node_get_subtree_contraction.
3930 *
3931 * Whenever we come across an expansion node, the last element
3932 * of data->contractions is combined with the contraction
3933 * on the expansion node.
3934 *
3935 * Whenever we come across a filter node that is the child
3936 * of a set or sequence node, data->contractions is extended
3937 * with a new element that restricts the previous element
3938 * to the elements selected by the filter.
3939 * The previous element can then be reused while backtracking.
3940 */
subtree_contraction_enter(__isl_take isl_schedule_node * node,void * user)3941 static __isl_give isl_schedule_node *subtree_contraction_enter(
3942 __isl_take isl_schedule_node *node, void *user)
3943 {
3944 struct isl_subtree_contraction_data *data = user;
3945
3946 do {
3947 enum isl_schedule_node_type type;
3948 isl_union_set *filter;
3949 isl_union_pw_multi_aff *inner, *contraction;
3950 isl_size n;
3951
3952 switch (isl_schedule_node_get_type(node)) {
3953 case isl_schedule_node_error:
3954 return isl_schedule_node_free(node);
3955 case isl_schedule_node_filter:
3956 type = isl_schedule_node_get_parent_type(node);
3957 if (type != isl_schedule_node_set &&
3958 type != isl_schedule_node_sequence)
3959 break;
3960 filter = isl_schedule_node_filter_get_filter(node);
3961 n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
3962 data->contractions);
3963 if (n < 0)
3964 data->contractions =
3965 isl_union_pw_multi_aff_list_free(
3966 data->contractions);
3967 inner =
3968 isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
3969 data->contractions, n - 1);
3970 inner = isl_union_pw_multi_aff_intersect_domain(inner,
3971 filter);
3972 data->contractions =
3973 isl_union_pw_multi_aff_list_add(data->contractions,
3974 inner);
3975 break;
3976 case isl_schedule_node_expansion:
3977 n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
3978 data->contractions);
3979 if (n < 0)
3980 data->contractions =
3981 isl_union_pw_multi_aff_list_free(
3982 data->contractions);
3983 contraction =
3984 isl_schedule_node_expansion_get_contraction(node);
3985 inner =
3986 isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
3987 data->contractions, n - 1);
3988 inner =
3989 isl_union_pw_multi_aff_pullback_union_pw_multi_aff(
3990 inner, contraction);
3991 data->contractions =
3992 isl_union_pw_multi_aff_list_set_union_pw_multi_aff(
3993 data->contractions, n - 1, inner);
3994 break;
3995 case isl_schedule_node_band:
3996 case isl_schedule_node_context:
3997 case isl_schedule_node_domain:
3998 case isl_schedule_node_extension:
3999 case isl_schedule_node_guard:
4000 case isl_schedule_node_leaf:
4001 case isl_schedule_node_mark:
4002 case isl_schedule_node_sequence:
4003 case isl_schedule_node_set:
4004 break;
4005 }
4006 } while (isl_schedule_node_has_children(node) &&
4007 (node = isl_schedule_node_first_child(node)) != NULL);
4008
4009 return node;
4010 }
4011
4012 /* Callback for "traverse" to leave a node for
4013 * isl_schedule_node_get_subtree_contraction.
4014 *
4015 * If we come across a filter node that is the child
4016 * of a set or sequence node, then we remove the element
4017 * of data->contractions that was added in subtree_contraction_enter.
4018 *
4019 * If we reach a leaf node, then the accumulated contraction is
4020 * added to data->res.
4021 */
subtree_contraction_leave(__isl_take isl_schedule_node * node,void * user)4022 static __isl_give isl_schedule_node *subtree_contraction_leave(
4023 __isl_take isl_schedule_node *node, void *user)
4024 {
4025 struct isl_subtree_contraction_data *data = user;
4026 isl_size n;
4027 isl_union_pw_multi_aff *inner;
4028 enum isl_schedule_node_type type;
4029
4030 switch (isl_schedule_node_get_type(node)) {
4031 case isl_schedule_node_error:
4032 return isl_schedule_node_free(node);
4033 case isl_schedule_node_filter:
4034 type = isl_schedule_node_get_parent_type(node);
4035 if (type != isl_schedule_node_set &&
4036 type != isl_schedule_node_sequence)
4037 break;
4038 n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
4039 data->contractions);
4040 if (n < 0)
4041 data->contractions = isl_union_pw_multi_aff_list_free(
4042 data->contractions);
4043 data->contractions =
4044 isl_union_pw_multi_aff_list_drop(data->contractions,
4045 n - 1, 1);
4046 break;
4047 case isl_schedule_node_leaf:
4048 n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
4049 data->contractions);
4050 if (n < 0)
4051 data->contractions = isl_union_pw_multi_aff_list_free(
4052 data->contractions);
4053 inner = isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
4054 data->contractions, n - 1);
4055 data->res = isl_union_pw_multi_aff_union_add(data->res, inner);
4056 break;
4057 case isl_schedule_node_band:
4058 case isl_schedule_node_context:
4059 case isl_schedule_node_domain:
4060 case isl_schedule_node_expansion:
4061 case isl_schedule_node_extension:
4062 case isl_schedule_node_guard:
4063 case isl_schedule_node_mark:
4064 case isl_schedule_node_sequence:
4065 case isl_schedule_node_set:
4066 break;
4067 }
4068
4069 return node;
4070 }
4071
4072 /* Return a mapping from the domain elements in the leaves of the subtree
4073 * rooted at "node" to the corresponding domain elements that reach "node"
4074 * obtained by composing the intermediate contractions.
4075 *
4076 * We start out with an identity mapping between the domain elements
4077 * that reach "node" and compose it with all the contractions
4078 * on a path from "node" to a leaf while traversing the subtree.
4079 * Within the children of an a sequence or set node, the
4080 * accumulated contraction is restricted to the elements selected
4081 * by the filter child.
4082 */
isl_schedule_node_get_subtree_contraction(__isl_keep isl_schedule_node * node)4083 __isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction(
4084 __isl_keep isl_schedule_node *node)
4085 {
4086 struct isl_subtree_contraction_data data;
4087 isl_space *space;
4088 isl_union_set *domain;
4089 isl_union_pw_multi_aff *contraction;
4090
4091 if (!node)
4092 return NULL;
4093
4094 domain = isl_schedule_node_get_universe_domain(node);
4095 space = isl_union_set_get_space(domain);
4096 contraction = isl_union_set_identity_union_pw_multi_aff(domain);
4097 data.res = isl_union_pw_multi_aff_empty(space);
4098 data.contractions =
4099 isl_union_pw_multi_aff_list_from_union_pw_multi_aff(contraction);
4100
4101 node = isl_schedule_node_copy(node);
4102 node = traverse(node, &subtree_contraction_enter,
4103 &subtree_contraction_leave, &data);
4104 if (!node)
4105 data.res = isl_union_pw_multi_aff_free(data.res);
4106 isl_schedule_node_free(node);
4107
4108 isl_union_pw_multi_aff_list_free(data.contractions);
4109
4110 return data.res;
4111 }
4112
4113 /* Do the nearest "n" ancestors of "node" have the types given in "types"
4114 * (starting at the parent of "node")?
4115 */
has_ancestors(__isl_keep isl_schedule_node * node,int n,enum isl_schedule_node_type * types)4116 static isl_bool has_ancestors(__isl_keep isl_schedule_node *node,
4117 int n, enum isl_schedule_node_type *types)
4118 {
4119 int i;
4120 isl_size n_ancestor;
4121
4122 if (!node)
4123 return isl_bool_error;
4124
4125 n_ancestor = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
4126 if (n_ancestor < 0)
4127 return isl_bool_error;
4128 if (n_ancestor < n)
4129 return isl_bool_false;
4130
4131 for (i = 0; i < n; ++i) {
4132 isl_schedule_tree *tree;
4133 int correct_type;
4134
4135 tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
4136 n_ancestor - 1 - i);
4137 if (!tree)
4138 return isl_bool_error;
4139 correct_type = isl_schedule_tree_get_type(tree) == types[i];
4140 isl_schedule_tree_free(tree);
4141 if (!correct_type)
4142 return isl_bool_false;
4143 }
4144
4145 return isl_bool_true;
4146 }
4147
4148 /* Given a node "node" that appears in an extension (i.e., it is the child
4149 * of a filter in a sequence inside an extension node), are the spaces
4150 * of the extension specified by "extension" disjoint from those
4151 * of both the original extension and the domain elements that reach
4152 * that original extension?
4153 */
is_disjoint_extension(__isl_keep isl_schedule_node * node,__isl_keep isl_union_map * extension)4154 static int is_disjoint_extension(__isl_keep isl_schedule_node *node,
4155 __isl_keep isl_union_map *extension)
4156 {
4157 isl_union_map *old;
4158 isl_union_set *domain;
4159 int empty;
4160
4161 node = isl_schedule_node_copy(node);
4162 node = isl_schedule_node_parent(node);
4163 node = isl_schedule_node_parent(node);
4164 node = isl_schedule_node_parent(node);
4165 old = isl_schedule_node_extension_get_extension(node);
4166 domain = isl_schedule_node_get_universe_domain(node);
4167 isl_schedule_node_free(node);
4168 old = isl_union_map_universe(old);
4169 domain = isl_union_set_union(domain, isl_union_map_range(old));
4170 extension = isl_union_map_copy(extension);
4171 extension = isl_union_map_intersect_range(extension, domain);
4172 empty = isl_union_map_is_empty(extension);
4173 isl_union_map_free(extension);
4174
4175 return empty;
4176 }
4177
4178 /* Given a node "node" that is governed by an extension node, extend
4179 * that extension node with "extension".
4180 *
4181 * In particular, "node" is the child of a filter in a sequence that
4182 * is in turn a child of an extension node. Extend that extension node
4183 * with "extension".
4184 *
4185 * Return a pointer to the parent of the original node (i.e., a filter).
4186 */
extend_extension(__isl_take isl_schedule_node * node,__isl_take isl_union_map * extension)4187 static __isl_give isl_schedule_node *extend_extension(
4188 __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
4189 {
4190 isl_size pos;
4191 isl_bool disjoint;
4192 isl_union_map *node_extension;
4193
4194 node = isl_schedule_node_parent(node);
4195 pos = isl_schedule_node_get_child_position(node);
4196 if (pos < 0)
4197 node = isl_schedule_node_free(node);
4198 node = isl_schedule_node_parent(node);
4199 node = isl_schedule_node_parent(node);
4200 node_extension = isl_schedule_node_extension_get_extension(node);
4201 disjoint = isl_union_map_is_disjoint(extension, node_extension);
4202 extension = isl_union_map_union(extension, node_extension);
4203 node = isl_schedule_node_extension_set_extension(node, extension);
4204 node = isl_schedule_node_child(node, 0);
4205 node = isl_schedule_node_child(node, pos);
4206
4207 if (disjoint < 0)
4208 return isl_schedule_node_free(node);
4209 if (!node)
4210 return NULL;
4211 if (!disjoint)
4212 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
4213 "extension domain should be disjoint from earlier "
4214 "extensions", return isl_schedule_node_free(node));
4215
4216 return node;
4217 }
4218
4219 /* Return the universe of "uset" if this universe is disjoint from "ref".
4220 * Otherwise, return "uset".
4221 *
4222 * Also check if "uset" itself is disjoint from "ref", reporting
4223 * an error if it is not.
4224 */
replace_by_universe_if_disjoint(__isl_take isl_union_set * uset,__isl_keep isl_union_set * ref)4225 static __isl_give isl_union_set *replace_by_universe_if_disjoint(
4226 __isl_take isl_union_set *uset, __isl_keep isl_union_set *ref)
4227 {
4228 int disjoint;
4229 isl_union_set *universe;
4230
4231 disjoint = isl_union_set_is_disjoint(uset, ref);
4232 if (disjoint < 0)
4233 return isl_union_set_free(uset);
4234 if (!disjoint)
4235 isl_die(isl_union_set_get_ctx(uset), isl_error_invalid,
4236 "extension domain should be disjoint from "
4237 "current domain", return isl_union_set_free(uset));
4238
4239 universe = isl_union_set_universe(isl_union_set_copy(uset));
4240 disjoint = isl_union_set_is_disjoint(universe, ref);
4241 if (disjoint >= 0 && disjoint) {
4242 isl_union_set_free(uset);
4243 return universe;
4244 }
4245 isl_union_set_free(universe);
4246
4247 if (disjoint < 0)
4248 return isl_union_set_free(uset);
4249 return uset;
4250 }
4251
4252 /* Insert an extension node on top of "node" with extension "extension".
4253 * In addition, insert a filter that separates node from the extension
4254 * between the extension node and "node".
4255 * Return a pointer to the inserted filter node.
4256 *
4257 * If "node" already appears in an extension (i.e., if it is the child
4258 * of a filter in a sequence inside an extension node), then extend that
4259 * extension with "extension" instead.
4260 * In this case, a pointer to the original filter node is returned.
4261 * Note that if some of the elements in the new extension live in the
4262 * same space as those of the original extension or the domain elements
4263 * reaching the original extension, then we insert a new extension anyway.
4264 * Otherwise, we would have to adjust the filters in the sequence child
4265 * of the extension to ensure that the elements in the new extension
4266 * are filtered out.
4267 */
insert_extension(__isl_take isl_schedule_node * node,__isl_take isl_union_map * extension)4268 static __isl_give isl_schedule_node *insert_extension(
4269 __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
4270 {
4271 enum isl_schedule_node_type ancestors[] =
4272 { isl_schedule_node_filter, isl_schedule_node_sequence,
4273 isl_schedule_node_extension };
4274 isl_union_set *domain;
4275 isl_union_set *filter;
4276 isl_bool in_ext;
4277
4278 in_ext = has_ancestors(node, 3, ancestors);
4279 if (in_ext < 0)
4280 goto error;
4281 if (in_ext) {
4282 int disjoint;
4283
4284 disjoint = is_disjoint_extension(node, extension);
4285 if (disjoint < 0)
4286 goto error;
4287 if (disjoint)
4288 return extend_extension(node, extension);
4289 }
4290
4291 filter = isl_schedule_node_get_domain(node);
4292 domain = isl_union_map_range(isl_union_map_copy(extension));
4293 filter = replace_by_universe_if_disjoint(filter, domain);
4294 isl_union_set_free(domain);
4295
4296 node = isl_schedule_node_insert_filter(node, filter);
4297 node = isl_schedule_node_insert_extension(node, extension);
4298 node = isl_schedule_node_child(node, 0);
4299 return node;
4300 error:
4301 isl_schedule_node_free(node);
4302 isl_union_map_free(extension);
4303 return NULL;
4304 }
4305
4306 /* Replace the subtree that "node" points to by "tree" (which has
4307 * a sequence root with two children), except if the parent of "node"
4308 * is a sequence as well, in which case "tree" is spliced at the position
4309 * of "node" in its parent.
4310 * Return a pointer to the child of the "tree_pos" (filter) child of "tree"
4311 * in the updated schedule tree.
4312 */
graft_or_splice(__isl_take isl_schedule_node * node,__isl_take isl_schedule_tree * tree,int tree_pos)4313 static __isl_give isl_schedule_node *graft_or_splice(
4314 __isl_take isl_schedule_node *node, __isl_take isl_schedule_tree *tree,
4315 int tree_pos)
4316 {
4317 isl_size pos;
4318
4319 if (isl_schedule_node_get_parent_type(node) ==
4320 isl_schedule_node_sequence) {
4321 pos = isl_schedule_node_get_child_position(node);
4322 if (pos < 0)
4323 node = isl_schedule_node_free(node);
4324 node = isl_schedule_node_parent(node);
4325 node = isl_schedule_node_sequence_splice(node, pos, tree);
4326 } else {
4327 pos = 0;
4328 node = isl_schedule_node_graft_tree(node, tree);
4329 }
4330 node = isl_schedule_node_child(node, pos + tree_pos);
4331 node = isl_schedule_node_child(node, 0);
4332
4333 return node;
4334 }
4335
4336 /* Insert a node "graft" into the schedule tree of "node" such that it
4337 * is executed before (if "before" is set) or after (if "before" is not set)
4338 * the node that "node" points to.
4339 * The root of "graft" is an extension node.
4340 * Return a pointer to the node that "node" pointed to.
4341 *
4342 * We first insert an extension node on top of "node" (or extend
4343 * the extension node if there already is one), with a filter on "node"
4344 * separating it from the extension.
4345 * We then insert a filter in the graft to separate it from the original
4346 * domain elements and combine the original and new tree in a sequence.
4347 * If we have extended an extension node, then the children of this
4348 * sequence are spliced in the sequence of the extended extension
4349 * at the position where "node" appears in the original extension.
4350 * Otherwise, the sequence pair is attached to the new extension node.
4351 */
graft_extension(__isl_take isl_schedule_node * node,__isl_take isl_schedule_node * graft,int before)4352 static __isl_give isl_schedule_node *graft_extension(
4353 __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft,
4354 int before)
4355 {
4356 isl_union_map *extension;
4357 isl_union_set *graft_domain;
4358 isl_union_set *node_domain;
4359 isl_schedule_tree *tree, *tree_graft;
4360
4361 extension = isl_schedule_node_extension_get_extension(graft);
4362 graft_domain = isl_union_map_range(isl_union_map_copy(extension));
4363 node_domain = isl_schedule_node_get_universe_domain(node);
4364 node = insert_extension(node, extension);
4365
4366 graft_domain = replace_by_universe_if_disjoint(graft_domain,
4367 node_domain);
4368 isl_union_set_free(node_domain);
4369
4370 tree = isl_schedule_node_get_tree(node);
4371 if (!isl_schedule_node_has_children(graft)) {
4372 tree_graft = isl_schedule_tree_from_filter(graft_domain);
4373 } else {
4374 graft = isl_schedule_node_child(graft, 0);
4375 tree_graft = isl_schedule_node_get_tree(graft);
4376 tree_graft = isl_schedule_tree_insert_filter(tree_graft,
4377 graft_domain);
4378 }
4379 if (before)
4380 tree = isl_schedule_tree_sequence_pair(tree_graft, tree);
4381 else
4382 tree = isl_schedule_tree_sequence_pair(tree, tree_graft);
4383 node = graft_or_splice(node, tree, before);
4384
4385 isl_schedule_node_free(graft);
4386
4387 return node;
4388 }
4389
4390 /* Replace the root domain node of "node" by an extension node suitable
4391 * for insertion at "pos".
4392 * That is, create an extension node that maps the outer band nodes
4393 * at "pos" to the domain of the root node of "node" and attach
4394 * the child of this root node to the extension node.
4395 */
extension_from_domain(__isl_take isl_schedule_node * node,__isl_keep isl_schedule_node * pos)4396 static __isl_give isl_schedule_node *extension_from_domain(
4397 __isl_take isl_schedule_node *node, __isl_keep isl_schedule_node *pos)
4398 {
4399 isl_union_set *universe;
4400 isl_union_set *domain;
4401 isl_union_map *ext;
4402 isl_size depth;
4403 isl_bool anchored;
4404 isl_space *space;
4405 isl_schedule_node *res;
4406 isl_schedule_tree *tree;
4407
4408 depth = isl_schedule_node_get_schedule_depth(pos);
4409 anchored = isl_schedule_node_is_subtree_anchored(node);
4410 if (depth < 0 || anchored < 0)
4411 return isl_schedule_node_free(node);
4412 if (anchored)
4413 isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
4414 "cannot graft anchored tree with domain root",
4415 return isl_schedule_node_free(node));
4416
4417 domain = isl_schedule_node_domain_get_domain(node);
4418 space = isl_union_set_get_space(domain);
4419 space = isl_space_set_from_params(space);
4420 space = isl_space_add_dims(space, isl_dim_set, depth);
4421 universe = isl_union_set_from_set(isl_set_universe(space));
4422 ext = isl_union_map_from_domain_and_range(universe, domain);
4423 res = isl_schedule_node_from_extension(ext);
4424 node = isl_schedule_node_child(node, 0);
4425 if (!node)
4426 return isl_schedule_node_free(res);
4427 if (!isl_schedule_tree_is_leaf(node->tree)) {
4428 tree = isl_schedule_node_get_tree(node);
4429 res = isl_schedule_node_child(res, 0);
4430 res = isl_schedule_node_graft_tree(res, tree);
4431 res = isl_schedule_node_parent(res);
4432 }
4433 isl_schedule_node_free(node);
4434
4435 return res;
4436 }
4437
4438 /* Insert a node "graft" into the schedule tree of "node" such that it
4439 * is executed before (if "before" is set) or after (if "before" is not set)
4440 * the node that "node" points to.
4441 * The root of "graft" may be either a domain or an extension node.
4442 * In the latter case, the domain of the extension needs to correspond
4443 * to the outer band nodes of "node".
4444 * The elements of the domain or the range of the extension may not
4445 * intersect with the domain elements that reach "node".
4446 * The schedule tree of "graft" may not be anchored.
4447 *
4448 * The schedule tree of "node" is modified to include an extension node
4449 * corresponding to the root node of "graft" as a child of the original
4450 * parent of "node". The original node that "node" points to and the
4451 * child of the root node of "graft" are attached to this extension node
4452 * through a sequence, with appropriate filters and with the child
4453 * of "graft" appearing before or after the original "node".
4454 *
4455 * If "node" already appears inside a sequence that is the child of
4456 * an extension node and if the spaces of the new domain elements
4457 * do not overlap with those of the original domain elements,
4458 * then that extension node is extended with the new extension
4459 * rather than introducing a new segment of extension and sequence nodes.
4460 *
4461 * Return a pointer to the same node in the modified tree that
4462 * "node" pointed to in the original tree.
4463 */
isl_schedule_node_graft_before_or_after(__isl_take isl_schedule_node * node,__isl_take isl_schedule_node * graft,int before)4464 static __isl_give isl_schedule_node *isl_schedule_node_graft_before_or_after(
4465 __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft,
4466 int before)
4467 {
4468 if (!node || !graft)
4469 goto error;
4470 if (check_insert(node) < 0)
4471 goto error;
4472
4473 if (isl_schedule_node_get_type(graft) == isl_schedule_node_domain)
4474 graft = extension_from_domain(graft, node);
4475
4476 if (!graft)
4477 goto error;
4478 if (isl_schedule_node_get_type(graft) != isl_schedule_node_extension)
4479 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
4480 "expecting domain or extension as root of graft",
4481 goto error);
4482
4483 return graft_extension(node, graft, before);
4484 error:
4485 isl_schedule_node_free(node);
4486 isl_schedule_node_free(graft);
4487 return NULL;
4488 }
4489
4490 /* Insert a node "graft" into the schedule tree of "node" such that it
4491 * is executed before the node that "node" points to.
4492 * The root of "graft" may be either a domain or an extension node.
4493 * In the latter case, the domain of the extension needs to correspond
4494 * to the outer band nodes of "node".
4495 * The elements of the domain or the range of the extension may not
4496 * intersect with the domain elements that reach "node".
4497 * The schedule tree of "graft" may not be anchored.
4498 *
4499 * Return a pointer to the same node in the modified tree that
4500 * "node" pointed to in the original tree.
4501 */
isl_schedule_node_graft_before(__isl_take isl_schedule_node * node,__isl_take isl_schedule_node * graft)4502 __isl_give isl_schedule_node *isl_schedule_node_graft_before(
4503 __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft)
4504 {
4505 return isl_schedule_node_graft_before_or_after(node, graft, 1);
4506 }
4507
4508 /* Insert a node "graft" into the schedule tree of "node" such that it
4509 * is executed after the node that "node" points to.
4510 * The root of "graft" may be either a domain or an extension node.
4511 * In the latter case, the domain of the extension needs to correspond
4512 * to the outer band nodes of "node".
4513 * The elements of the domain or the range of the extension may not
4514 * intersect with the domain elements that reach "node".
4515 * The schedule tree of "graft" may not be anchored.
4516 *
4517 * Return a pointer to the same node in the modified tree that
4518 * "node" pointed to in the original tree.
4519 */
isl_schedule_node_graft_after(__isl_take isl_schedule_node * node,__isl_take isl_schedule_node * graft)4520 __isl_give isl_schedule_node *isl_schedule_node_graft_after(
4521 __isl_take isl_schedule_node *node,
4522 __isl_take isl_schedule_node *graft)
4523 {
4524 return isl_schedule_node_graft_before_or_after(node, graft, 0);
4525 }
4526
4527 /* Split the domain elements that reach "node" into those that satisfy
4528 * "filter" and those that do not. Arrange for the first subset to be
4529 * executed before or after the second subset, depending on the value
4530 * of "before".
4531 * Return a pointer to the tree corresponding to the second subset,
4532 * except when this subset is empty in which case the original pointer
4533 * is returned.
4534 * If both subsets are non-empty, then a sequence node is introduced
4535 * to impose the order. If the grandparent of the original node was
4536 * itself a sequence, then the original child is replaced by two children
4537 * in this sequence instead.
4538 * The children in the sequence are copies of the original subtree,
4539 * simplified with respect to their filters.
4540 */
isl_schedule_node_order_before_or_after(__isl_take isl_schedule_node * node,__isl_take isl_union_set * filter,int before)4541 static __isl_give isl_schedule_node *isl_schedule_node_order_before_or_after(
4542 __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter,
4543 int before)
4544 {
4545 enum isl_schedule_node_type ancestors[] =
4546 { isl_schedule_node_filter, isl_schedule_node_sequence };
4547 isl_union_set *node_domain, *node_filter = NULL, *parent_filter;
4548 isl_schedule_node *node2;
4549 isl_schedule_tree *tree1, *tree2;
4550 isl_bool empty1, empty2;
4551 isl_bool in_seq;
4552
4553 if (!node || !filter)
4554 goto error;
4555 if (check_insert(node) < 0)
4556 goto error;
4557
4558 in_seq = has_ancestors(node, 2, ancestors);
4559 if (in_seq < 0)
4560 goto error;
4561 node_domain = isl_schedule_node_get_domain(node);
4562 filter = isl_union_set_gist(filter, isl_union_set_copy(node_domain));
4563 node_filter = isl_union_set_copy(node_domain);
4564 node_filter = isl_union_set_subtract(node_filter,
4565 isl_union_set_copy(filter));
4566 node_filter = isl_union_set_gist(node_filter, node_domain);
4567 empty1 = isl_union_set_is_empty(filter);
4568 empty2 = isl_union_set_is_empty(node_filter);
4569 if (empty1 < 0 || empty2 < 0)
4570 goto error;
4571 if (empty1 || empty2) {
4572 isl_union_set_free(filter);
4573 isl_union_set_free(node_filter);
4574 return node;
4575 }
4576
4577 if (in_seq) {
4578 node = isl_schedule_node_parent(node);
4579 parent_filter = isl_schedule_node_filter_get_filter(node);
4580 node_filter = isl_union_set_intersect(node_filter,
4581 isl_union_set_copy(parent_filter));
4582 filter = isl_union_set_intersect(filter, parent_filter);
4583 }
4584
4585 node2 = isl_schedule_node_copy(node);
4586 node = isl_schedule_node_gist(node, isl_union_set_copy(node_filter));
4587 node2 = isl_schedule_node_gist(node2, isl_union_set_copy(filter));
4588 tree1 = isl_schedule_node_get_tree(node);
4589 tree2 = isl_schedule_node_get_tree(node2);
4590 tree1 = isl_schedule_tree_insert_filter(tree1, node_filter);
4591 tree2 = isl_schedule_tree_insert_filter(tree2, filter);
4592 isl_schedule_node_free(node2);
4593
4594 if (before) {
4595 tree1 = isl_schedule_tree_sequence_pair(tree2, tree1);
4596 node = graft_or_splice(node, tree1, 1);
4597 } else {
4598 tree1 = isl_schedule_tree_sequence_pair(tree1, tree2);
4599 node = graft_or_splice(node, tree1, 0);
4600 }
4601
4602 return node;
4603 error:
4604 isl_schedule_node_free(node);
4605 isl_union_set_free(filter);
4606 isl_union_set_free(node_filter);
4607 return NULL;
4608 }
4609
4610 /* Split the domain elements that reach "node" into those that satisfy
4611 * "filter" and those that do not. Arrange for the first subset to be
4612 * executed before the second subset.
4613 * Return a pointer to the tree corresponding to the second subset,
4614 * except when this subset is empty in which case the original pointer
4615 * is returned.
4616 */
isl_schedule_node_order_before(__isl_take isl_schedule_node * node,__isl_take isl_union_set * filter)4617 __isl_give isl_schedule_node *isl_schedule_node_order_before(
4618 __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
4619 {
4620 return isl_schedule_node_order_before_or_after(node, filter, 1);
4621 }
4622
4623 /* Split the domain elements that reach "node" into those that satisfy
4624 * "filter" and those that do not. Arrange for the first subset to be
4625 * executed after the second subset.
4626 * Return a pointer to the tree corresponding to the second subset,
4627 * except when this subset is empty in which case the original pointer
4628 * is returned.
4629 */
isl_schedule_node_order_after(__isl_take isl_schedule_node * node,__isl_take isl_union_set * filter)4630 __isl_give isl_schedule_node *isl_schedule_node_order_after(
4631 __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
4632 {
4633 return isl_schedule_node_order_before_or_after(node, filter, 0);
4634 }
4635
4636 /* Reset the user pointer on all identifiers of parameters and tuples
4637 * in the schedule node "node".
4638 */
isl_schedule_node_reset_user(__isl_take isl_schedule_node * node)4639 __isl_give isl_schedule_node *isl_schedule_node_reset_user(
4640 __isl_take isl_schedule_node *node)
4641 {
4642 isl_schedule_tree *tree;
4643
4644 tree = isl_schedule_node_get_tree(node);
4645 tree = isl_schedule_tree_reset_user(tree);
4646 node = isl_schedule_node_graft_tree(node, tree);
4647
4648 return node;
4649 }
4650
4651 /* Align the parameters of the schedule node "node" to those of "space".
4652 */
isl_schedule_node_align_params(__isl_take isl_schedule_node * node,__isl_take isl_space * space)4653 __isl_give isl_schedule_node *isl_schedule_node_align_params(
4654 __isl_take isl_schedule_node *node, __isl_take isl_space *space)
4655 {
4656 isl_schedule_tree *tree;
4657
4658 tree = isl_schedule_node_get_tree(node);
4659 tree = isl_schedule_tree_align_params(tree, space);
4660 node = isl_schedule_node_graft_tree(node, tree);
4661
4662 return node;
4663 }
4664
4665 /* Compute the pullback of schedule node "node"
4666 * by the function represented by "upma".
4667 * In other words, plug in "upma" in the iteration domains
4668 * of schedule node "node".
4669 * We currently do not handle expansion nodes.
4670 *
4671 * Note that this is only a helper function for
4672 * isl_schedule_pullback_union_pw_multi_aff. In order to maintain consistency,
4673 * this function should not be called on a single node without also
4674 * calling it on all the other nodes.
4675 */
isl_schedule_node_pullback_union_pw_multi_aff(__isl_take isl_schedule_node * node,__isl_take isl_union_pw_multi_aff * upma)4676 __isl_give isl_schedule_node *isl_schedule_node_pullback_union_pw_multi_aff(
4677 __isl_take isl_schedule_node *node,
4678 __isl_take isl_union_pw_multi_aff *upma)
4679 {
4680 isl_schedule_tree *tree;
4681
4682 tree = isl_schedule_node_get_tree(node);
4683 tree = isl_schedule_tree_pullback_union_pw_multi_aff(tree, upma);
4684 node = isl_schedule_node_graft_tree(node, tree);
4685
4686 return node;
4687 }
4688
4689 /* Internal data structure for isl_schedule_node_expand.
4690 * "tree" is the tree that needs to be plugged in in all the leaves.
4691 * "domain" is the set of domain elements in the original leaves
4692 * to which the tree applies.
4693 */
4694 struct isl_schedule_expand_data {
4695 isl_schedule_tree *tree;
4696 isl_union_set *domain;
4697 };
4698
4699 /* If "node" is a leaf, then plug in data->tree, simplifying it
4700 * within its new context.
4701 *
4702 * If there are any domain elements at the leaf where the tree
4703 * should not be plugged in (i.e., there are elements not in data->domain)
4704 * then first extend the tree to only apply to the elements in data->domain
4705 * by constructing a set node that selects data->tree for elements
4706 * in data->domain and a leaf for the other elements.
4707 */
expand(__isl_take isl_schedule_node * node,void * user)4708 static __isl_give isl_schedule_node *expand(__isl_take isl_schedule_node *node,
4709 void *user)
4710 {
4711 struct isl_schedule_expand_data *data = user;
4712 isl_schedule_tree *tree, *leaf;
4713 isl_union_set *domain, *left;
4714 isl_bool empty;
4715
4716 if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf)
4717 return node;
4718
4719 domain = isl_schedule_node_get_domain(node);
4720 tree = isl_schedule_tree_copy(data->tree);
4721
4722 left = isl_union_set_copy(domain);
4723 left = isl_union_set_subtract(left, isl_union_set_copy(data->domain));
4724 empty = isl_union_set_is_empty(left);
4725 if (empty >= 0 && !empty) {
4726 leaf = isl_schedule_node_get_leaf(node);
4727 leaf = isl_schedule_tree_insert_filter(leaf, left);
4728 left = isl_union_set_copy(data->domain);
4729 tree = isl_schedule_tree_insert_filter(tree, left);
4730 tree = isl_schedule_tree_set_pair(tree, leaf);
4731 } else {
4732 if (empty < 0)
4733 node = isl_schedule_node_free(node);
4734 isl_union_set_free(left);
4735 }
4736
4737 node = isl_schedule_node_graft_tree(node, tree);
4738 node = isl_schedule_node_gist(node, domain);
4739
4740 return node;
4741 }
4742
4743 /* Expand the tree rooted at "node" by extending all leaves
4744 * with an expansion node with as child "tree".
4745 * The expansion is determined by "contraction" and "domain".
4746 * That is, the elements of "domain" are contracted according
4747 * to "contraction". The expansion relation is then the inverse
4748 * of "contraction" with its range intersected with "domain".
4749 *
4750 * Insert the appropriate expansion node on top of "tree" and
4751 * then plug in the result in all leaves of "node".
4752 */
isl_schedule_node_expand(__isl_take isl_schedule_node * node,__isl_take isl_union_pw_multi_aff * contraction,__isl_take isl_union_set * domain,__isl_take isl_schedule_tree * tree)4753 __isl_give isl_schedule_node *isl_schedule_node_expand(
4754 __isl_take isl_schedule_node *node,
4755 __isl_take isl_union_pw_multi_aff *contraction,
4756 __isl_take isl_union_set *domain,
4757 __isl_take isl_schedule_tree *tree)
4758 {
4759 struct isl_schedule_expand_data data;
4760 isl_union_map *expansion;
4761 isl_union_pw_multi_aff *copy;
4762
4763 if (!node || !contraction || !tree)
4764 node = isl_schedule_node_free(node);
4765
4766 copy = isl_union_pw_multi_aff_copy(contraction);
4767 expansion = isl_union_map_from_union_pw_multi_aff(copy);
4768 expansion = isl_union_map_reverse(expansion);
4769 expansion = isl_union_map_intersect_range(expansion, domain);
4770 data.domain = isl_union_map_domain(isl_union_map_copy(expansion));
4771
4772 tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion);
4773 data.tree = tree;
4774
4775 node = isl_schedule_node_map_descendant_bottom_up(node, &expand, &data);
4776 isl_union_set_free(data.domain);
4777 isl_schedule_tree_free(data.tree);
4778 return node;
4779 }
4780
4781 /* Return the position of the subtree containing "node" among the children
4782 * of "ancestor". "node" is assumed to be a descendant of "ancestor".
4783 * In particular, both nodes should point to the same schedule tree.
4784 *
4785 * Return isl_size_error on error.
4786 */
isl_schedule_node_get_ancestor_child_position(__isl_keep isl_schedule_node * node,__isl_keep isl_schedule_node * ancestor)4787 isl_size isl_schedule_node_get_ancestor_child_position(
4788 __isl_keep isl_schedule_node *node,
4789 __isl_keep isl_schedule_node *ancestor)
4790 {
4791 isl_size n1, n2;
4792 isl_schedule_tree *tree;
4793
4794 n1 = isl_schedule_node_get_tree_depth(ancestor);
4795 n2 = isl_schedule_node_get_tree_depth(node);
4796 if (n1 < 0 || n2 < 0)
4797 return isl_size_error;
4798
4799 if (node->schedule != ancestor->schedule)
4800 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
4801 "not a descendant", return isl_size_error);
4802
4803 if (n1 >= n2)
4804 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
4805 "not a descendant", return isl_size_error);
4806 tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, n1);
4807 isl_schedule_tree_free(tree);
4808 if (tree != ancestor->tree)
4809 isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
4810 "not a descendant", return isl_size_error);
4811
4812 return node->child_pos[n1];
4813 }
4814
4815 /* Given two nodes that point to the same schedule tree, return their
4816 * closest shared ancestor.
4817 *
4818 * Since the two nodes point to the same schedule, they share at least
4819 * one ancestor, the root of the schedule. We move down from the root
4820 * to the first ancestor where the respective children have a different
4821 * child position. This is the requested ancestor.
4822 * If there is no ancestor where the children have a different position,
4823 * then one node is an ancestor of the other and then this node is
4824 * the requested ancestor.
4825 */
isl_schedule_node_get_shared_ancestor(__isl_keep isl_schedule_node * node1,__isl_keep isl_schedule_node * node2)4826 __isl_give isl_schedule_node *isl_schedule_node_get_shared_ancestor(
4827 __isl_keep isl_schedule_node *node1,
4828 __isl_keep isl_schedule_node *node2)
4829 {
4830 int i;
4831 isl_size n1, n2;
4832
4833 n1 = isl_schedule_node_get_tree_depth(node1);
4834 n2 = isl_schedule_node_get_tree_depth(node2);
4835 if (n1 < 0 || n2 < 0)
4836 return NULL;
4837 if (node1->schedule != node2->schedule)
4838 isl_die(isl_schedule_node_get_ctx(node1), isl_error_invalid,
4839 "not part of same schedule", return NULL);
4840 if (n2 < n1)
4841 return isl_schedule_node_get_shared_ancestor(node2, node1);
4842 if (n1 == 0)
4843 return isl_schedule_node_copy(node1);
4844 if (isl_schedule_node_is_equal(node1, node2))
4845 return isl_schedule_node_copy(node1);
4846
4847 for (i = 0; i < n1; ++i)
4848 if (node1->child_pos[i] != node2->child_pos[i])
4849 break;
4850
4851 node1 = isl_schedule_node_copy(node1);
4852 return isl_schedule_node_ancestor(node1, n1 - i);
4853 }
4854
4855 /* Print "node" to "p".
4856 */
isl_printer_print_schedule_node(__isl_take isl_printer * p,__isl_keep isl_schedule_node * node)4857 __isl_give isl_printer *isl_printer_print_schedule_node(
4858 __isl_take isl_printer *p, __isl_keep isl_schedule_node *node)
4859 {
4860 isl_size n;
4861
4862 if (!node)
4863 return isl_printer_free(p);
4864 n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
4865 if (n < 0)
4866 return isl_printer_free(p);
4867 return isl_printer_print_schedule_tree_mark(p, node->schedule->root, n,
4868 node->child_pos);
4869 }
4870
isl_schedule_node_dump(__isl_keep isl_schedule_node * node)4871 void isl_schedule_node_dump(__isl_keep isl_schedule_node *node)
4872 {
4873 isl_ctx *ctx;
4874 isl_printer *printer;
4875
4876 if (!node)
4877 return;
4878
4879 ctx = isl_schedule_node_get_ctx(node);
4880 printer = isl_printer_to_file(ctx, stderr);
4881 printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK);
4882 printer = isl_printer_print_schedule_node(printer, node);
4883
4884 isl_printer_free(printer);
4885 }
4886
4887 /* Return a string representation of "node".
4888 * Print the schedule node in block format as it would otherwise
4889 * look identical to the entire schedule.
4890 */
isl_schedule_node_to_str(__isl_keep isl_schedule_node * node)4891 __isl_give char *isl_schedule_node_to_str(__isl_keep isl_schedule_node *node)
4892 {
4893 isl_printer *printer;
4894 char *s;
4895
4896 if (!node)
4897 return NULL;
4898
4899 printer = isl_printer_to_str(isl_schedule_node_get_ctx(node));
4900 printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK);
4901 printer = isl_printer_print_schedule_node(printer, node);
4902 s = isl_printer_get_str(printer);
4903 isl_printer_free(printer);
4904
4905 return s;
4906 }
4907