update isl for fix in isl_map_plain_is_disjoint
[ppn.git] / pn2adg.cc
blob93bb0e1d7b737bd8d92019f8aed1dc9dfef4e27a
1 /*
2 * Copyright 2011 Leiden University. All rights reserved.
3 */
5 #include <sstream>
7 #include <isl/aff.h>
8 #include <isl/ilp.h>
9 #include <isl/union_map.h>
10 #include <isa/pdg.h>
11 #include <isa/adg.h>
13 #include "adg_xml.h"
14 #include "pn2adg_options.h"
16 /* Convert the result of "pn" to and "adg".
17 * The result may be written out as either a YAML or an XML file.
19 * We essentially create an adg node for each pn node and an adg edge
20 * for each pn dependence. The ports are obtained by projecting the
21 * dependence relationss on their domains and ranges.
22 * There are however various extra constraints on the resulting adg which
23 * make the tranformation not quite as trivial as it sounds.
26 /* ESPAM requirements:
28 * The static control variables that appear in the domain of a node
29 * should also appear in the domains of the function, ports and invars
30 * of that node.
32 * The name of a static control should uniquely define the expression
33 * of the static control throughout the entire graph. That is, two
34 * static controls with the same name should have the same expression.
36 * The bounds on the domains should not involve any unions.
38 * An edge of type adg_edge_broadcast should only associate a write
39 * with the first corresponding read.
41 * The name of a control edge should start with "CED".
44 using pdg::PDG;
46 /* Create and return an adg_var that accesses the variable "id"
47 * on the given space.
49 static adg_var *var_from_id(__isl_take isl_id *id, __isl_take isl_space *space)
51 adg_var *var = new adg_var;
52 isl_ctx *ctx;
53 isl_set *dom;
54 isl_multi_aff *maff;
55 isl_aff_list *list;
57 ctx = isl_space_get_ctx(space);
58 dom = isl_set_universe(isl_space_copy(space));
59 space = isl_space_from_domain(space);
60 space = isl_space_set_tuple_id(space, isl_dim_out, id);
61 list = isl_aff_list_alloc(ctx, 0);
62 maff = isl_multi_aff_from_aff_list(space, list);
63 var->access = isl_pw_multi_aff_alloc(dom, maff);
64 return var;
67 /* Create and return an isl_id of the form
69 * in_<access->nr><suffix>
70 * or
71 * out_<access->nr><suffix>
73 * depending on whether access represents a read or a write.
75 static __isl_give isl_id *access_name(isl_ctx *ctx, pdg::access *access,
76 const char *suffix)
78 char buf[100];
79 const char *type;
81 if (access->type == pdg::access::write)
82 type = "out";
83 else
84 type = "in";
85 snprintf(buf, sizeof(buf), "%s_%d%s", type, access->nr, suffix);
87 return isl_id_alloc(ctx, buf, NULL);
90 /* Extract the isl_id of the node from "space".
92 * The given space may be the result of one of more nestings of the form
94 * D -> [D -> local[..]]
96 * and/or a nesting of the form
98 * D -> [D -> [dynamic controls]]
100 * and so we may have to dig down to obtain the space that corresponds
101 * to the node.
103 static __isl_give isl_id *node_id(__isl_take isl_space *space)
105 isl_id *id;
107 if (!isl_space_is_wrapping(space)) {
108 id = isl_space_get_tuple_id(space, isl_dim_set);
109 isl_space_free(space);
110 return id;
112 return node_id(isl_space_domain(isl_space_unwrap(space)));
115 /* Create and return an adg_var corresponding to "access" on "space".
116 * If there is no array associated to the access, then we assume
117 * it is an iterator and assign it type "int".
119 static adg_var *new_var(pdg::access *access, __isl_take isl_space *space)
121 adg_var *var;
122 isl_ctx *ctx = isl_space_get_ctx(space);
123 isl_id *id;
124 const char *suffix;
126 id = node_id(isl_space_copy(space));
127 suffix = isl_id_get_name(id);
128 isl_id_free(id);
130 id = access_name(ctx, access, suffix);
131 var = var_from_id(id, space);
132 if (access->array)
133 var->type = isl_id_alloc(ctx,
134 access->array->element_type->s.c_str(), NULL);
135 else
136 var->type = isl_id_alloc(ctx, "int", NULL);
137 return var;
140 /* Return the schedule for "p_node", mapping the iteration domain
141 * of "p_node" to the domain (with given "id") of the corresponding adg node.
142 * In practice, we compose the pdg schedule with the mapping that
143 * removes the dimensions with constant values.
145 static __isl_give isl_map *node_schedule(pdg::node *p_node, adg *graph,
146 __isl_take isl_id *id)
148 isl_map *sched;
150 sched = p_node->schedule->get_isl_map();
151 sched = isl_map_apply_range(sched, isl_map_copy(graph->iterator_map));
152 if (id)
153 sched = isl_map_set_tuple_id(sched, isl_dim_out, id);
155 return sched;
158 /* Return the schedule for "p_node", mapping the iteration domain
159 * of "p_node" to the domain (with given "id") of the corresponding adg node.
161 * If "space" represents the space to which the schedule needs to be applied.
162 * If it is a wrapped map, then we assume that the actual node schedule
163 * applies to the domain of this wrapped map and we adjust the schedule
164 * to preserve the range of this wrapped map.
166 static __isl_give isl_map *extended_node_schedule(pdg::node *p_node, adg *graph,
167 __isl_take isl_id *id, __isl_take isl_space *space)
169 isl_map *sched;
171 sched = node_schedule(p_node, graph, id);
172 if (!isl_space_is_wrapping(space)) {
173 isl_space_free(space);
174 } else {
175 isl_map *map;
176 space = isl_space_unwrap(space);
177 space = isl_space_map_from_set(isl_space_range(space));
178 map = isl_map_identity(space);
179 sched = isl_map_product(sched, map);
182 return sched;
185 /* Construct an isl_pw_aff from a map with a single output dimension.
186 * The map needs to be single-valued in order to obtain a meaningful
187 * result.
189 static __isl_give isl_pw_aff *pw_aff_from_map(__isl_take isl_map *map)
191 assert(isl_map_is_single_valued(map));
192 return isl_map_dim_max(map, 0);
195 /* Create and return an adg_expr that corresponds to the given access
196 * from the given node. id represents the name of the corresponding adg_node.
198 * The access is assumed to be an access to the set of integers.
199 * That is, the range is a nameless 1D space and the value is equal
200 * to the index expression.
202 static adg_expr *new_expression(adg *graph, pdg::node *node,
203 pdg::access *access, __isl_take isl_id *id)
205 isl_map *sched;
206 isl_map *map;
207 adg_expr *expr;
209 sched = node_schedule(node, graph, isl_id_copy(id));
210 map = access->map->get_isl_map();
211 map = isl_map_apply_domain(map, sched);
212 expr = new adg_expr;
213 expr->name = access_name(isl_map_get_ctx(map), access,
214 isl_id_get_name(id));
215 expr->expr = pw_aff_from_map(map);
216 expr->expr = isl_pw_aff_coalesce(expr->expr);
217 for (int i = 0; i < graph->iterators.size(); ++i)
218 expr->expr = isl_pw_aff_set_dim_id(expr->expr,
219 isl_dim_in, i, isl_id_copy(graph->iterators[i]));
221 isl_id_free(id);
222 return expr;
225 /* Add an adg_expr to node->expressions for each access to the "set of
226 * integers" from "call" (recursively) within the corresponding node p_node.
228 static void add_expressions(adg *graph, adg_node *node, pdg::node *p_node,
229 pdg::function_call *call)
231 for (int i = 0; i < call->arguments.size(); ++i) {
232 pdg::call_or_access *coa = call->arguments[i];
234 if (coa->type == pdg::call_or_access::t_call) {
235 add_expressions(graph, node, p_node, coa->call);
236 continue;
239 assert(coa->type == pdg::call_or_access::t_access);
240 if (coa->access->type == pdg::access::write)
241 continue;
242 if (isl_map_has_tuple_id(coa->access->map->map, isl_dim_out))
243 continue;
244 node->expressions.push_back(new_expression(graph, p_node,
245 coa->access, isl_id_copy(node->name)));
249 /* Append "var" with argument type "type" to "args",
250 * provided such a combination doesn't appear in the list already.
252 static void add_argument(std::vector<adg_arg *> &args, adg_var *var,
253 enum adg_arg_type type)
255 adg_arg *arg;
257 for (int i = 0; i < args.size(); ++i) {
258 if (type == args[i]->type &&
259 isl_pw_multi_aff_plain_is_equal(args[i]->var->access,
260 var->access)) {
261 delete var;
262 return;
266 arg = new adg_arg;
267 arg->var = var;
268 arg->type = type;
270 args.push_back(arg);
273 /* Recursively add an element to fn->out or fn->in for each argument of "call".
274 * If we come across any "&", then we replace "type" by adg_arg_reference.
276 static void add_arguments(adg_function *fn,
277 pdg::function_call *call, __isl_keep isl_space *space,
278 enum adg_arg_type type)
280 for (int i = 0; i < call->arguments.size(); ++i) {
281 pdg::call_or_access *coa = call->arguments[i];
283 if (coa->type == pdg::call_or_access::t_call) {
284 enum adg_arg_type type_i = type;
285 if (!strcmp(coa->call->name->s.c_str(), "&"))
286 type_i = adg_arg_reference;
287 add_arguments(fn, coa->call, space, type_i);
288 continue;
291 assert(coa->type == pdg::call_or_access::t_access);
292 if (coa->access->type == pdg::access::write)
293 add_argument(fn->out,
294 new_var(coa->access, isl_space_copy(space)), type);
295 else
296 add_argument(fn->in,
297 new_var(coa->access, isl_space_copy(space)), type);
301 /* Add the filters of "node" to "filters", having them refer to "space"
302 * as their iteration domain.
304 static void add_domain_filters(std::vector<adg_var *> &filters, pdg::node *node,
305 __isl_keep isl_space *space)
307 for (int i = 0; i < node->filters.size(); ++i) {
308 pdg::call_or_access *coa = node->filters[i];
309 assert(coa->type == pdg::call_or_access::t_access);
310 filters.push_back(new_var(coa->access, isl_space_copy(space)));
314 /* Set node->function based on "p_node" and the node domain "domain".
315 * In particular, create an adg_function with the given domain
316 * and add input and output arguments.
317 * If any of the input arguments is an affine expression (as opposed to
318 * an array access), then it is added to node->expressions.
320 * If p_node has any filters, then they are converted to filters on node.
322 static void set_function(adg *adg, adg_node *node, pdg::node *p_node,
323 __isl_take isl_set *domain)
325 isl_ctx *ctx = isl_set_get_ctx(domain);
326 const char *name;
327 pdg::statement *stat = p_node->statement;
328 adg_function *fn = new adg_function;
329 isl_space *space;
330 node->function = fn;
332 space = isl_set_get_space(domain);
333 fn->domain = new adg_domain(domain);
335 add_domain_filters(fn->domain->filters, p_node, space);
337 for (int i = 0; i < stat->top_outputs.size(); ++i)
338 add_argument(fn->out,
339 new_var(stat->top_outputs[i], isl_space_copy(space)),
340 adg_arg_return);
342 if (!stat->top_function)
343 return;
345 name = stat->top_function->name->s.c_str();
346 fn->name = isl_id_alloc(ctx, name, NULL);
348 add_expressions(adg, node, p_node, stat->top_function);
349 add_arguments(fn, stat->top_function, space, adg_arg_value);
351 isl_space_free(space);
354 /* Return the domain of "p_node" after scheduling.
355 * "id" represents the name of the corresponding adg_node.
357 * If "p_node" has any filters, then its source iteration domain
358 * is a wrapped map with the filters in the range. The computed
359 * node schedule therefore needs to take this filters into account
360 * and so we pass along the domain space to extended_node_schedule().
361 * In particular, we want the filter values to be preserved.
363 static __isl_give isl_set *scheduled_domain(pdg::node *p_node, adg *adg,
364 __isl_take isl_id *id)
366 isl_set *domain;
367 isl_map *sched;
369 domain = p_node->source->get_isl_set();
370 sched = extended_node_schedule(p_node, adg, id,
371 isl_set_get_space(domain));
372 domain = isl_set_apply(domain, sched);
374 return domain;
377 /* Construct a schedule for the adg_node (called "id") corresponding to
378 * "p_node". Note that the domain of the adg_node is the result of
379 * applying the p_node schedule to its domain, with some of the
380 * dimensions with constant values removed (through composition with
381 * adg->iterator_map). The schedule for the adg_node then simply
382 * needs to insert those dimensions with constant values back.
384 * If "p_node" has any filters, then its source iteration domain
385 * is a wrapped map with the filters in the range. These filters
386 * need to be removed first.
388 static __isl_give isl_map *adg_node_schedule(pdg::node *p_node, adg *adg,
389 __isl_take isl_id *id)
391 isl_set *domain;
392 isl_map *sched;
394 domain = p_node->source->get_isl_set();
395 if (isl_set_is_wrapping(domain))
396 domain = isl_map_domain(isl_set_unwrap(domain));
397 sched = p_node->schedule->get_isl_map();
398 domain = isl_set_apply(domain, sched);
399 sched = isl_map_reverse(isl_map_copy(adg->iterator_map));
400 sched = isl_map_intersect_range(sched, domain);
401 sched = isl_map_set_tuple_id(sched, isl_dim_in, id);
403 return sched;
406 /* Construct the name of the adg_node corresponding to "p_node".
408 static __isl_give isl_id *node_id(isl_ctx *ctx, pdg::node *p_node)
410 char buf[10];
412 snprintf(buf, sizeof(buf), "ND_%d", p_node->nr);
414 return isl_id_alloc(ctx, buf, NULL);
417 extern "C" {
418 static int intersect_local_space(__isl_take isl_basic_set *bset,
419 void *user);
422 /* Intersect the local space *user with the local space of "bset".
424 static int intersect_local_space(__isl_take isl_basic_set *bset,
425 void *user)
427 isl_local_space *ls;
428 isl_local_space **res = (isl_local_space **)user;
430 ls = isl_basic_set_get_local_space(bset);
431 isl_basic_set_free(bset);
433 *res = isl_local_space_intersect(*res, ls);
435 return 0;
438 /* Find the position of the first filter in "space".
440 * The given space may be the result of one of more nestings of the form
442 * D -> [D -> local[..]]
444 * and/or a nesting of the form
446 * D -> [D -> [dynamic controls]]
448 * and so we may have to dig down until we find a wrapped space
449 * with an unnamed range. If we find it, the position of the filters
450 * in the original space is equal to the number of input dimensions
451 * of that wrapped space. Otherwise, there are no filters and we
452 * return 0.
454 static int filter_pos(__isl_take isl_space *space)
456 int pos;
458 if (!isl_space_is_wrapping(space)) {
459 isl_space_free(space);
460 return 0;
462 space = isl_space_unwrap(space);
463 if (isl_space_has_tuple_id(space, isl_dim_out))
464 return filter_pos(isl_space_domain(space));
465 pos = isl_space_dim(space, isl_dim_in);
466 isl_space_free(space);
467 return pos;
470 /* Set the names of the input dimensions of "aff" according
471 * to "filters", "iterators" and "controls".
473 * The first dimensions correspond to the iterators.
474 * The are followed by the iterators, with the filters intermixed.
476 static __isl_give isl_aff *set_names(__isl_take isl_aff *aff,
477 std::vector<adg_var *> &filters, std::vector<isl_id *> &iterators,
478 std::vector<adg_expr *> &controls)
480 int pos = 0;
481 int n_in = isl_aff_dim(aff, isl_dim_in);
482 int fp;
483 int nf;
484 int ci;
486 fp = filter_pos(isl_aff_get_domain_space(aff));
487 nf = filters.size();
489 for (int i = 0; i < iterators.size(); ++i, ++pos) {
490 isl_id *id = isl_id_copy(iterators[i]);
491 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
494 for (ci = 0; ci < controls.size() && pos < fp; ++ci, ++pos) {
495 isl_id *id = isl_id_copy(controls[ci]->name);
496 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
499 for (int i = 0; i < nf; ++i, ++pos) {
500 isl_id *id;
501 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
502 isl_dim_out);
503 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
506 for (; ci < controls.size() && pos < n_in; ++ci, ++pos) {
507 isl_id *id = isl_id_copy(controls[ci]->name);
508 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
511 return aff;
514 /* Set the names of the input dimensions of "pa" according
515 * to "filters", "iterators" and "controls".
517 * The first dimensions correspond to the iterators.
518 * The are followed by the iterators, with the filters intermixed.
520 static __isl_give isl_pw_aff *set_names(__isl_take isl_pw_aff *pa,
521 std::vector<adg_var *> &filters, std::vector<isl_id *> &iterators,
522 std::vector<adg_expr *> &controls)
524 int pos = 0;
525 int n_in = isl_pw_aff_dim(pa, isl_dim_in);
526 int fp;
527 int nf;
528 int ci;
530 fp = filter_pos(isl_pw_aff_get_domain_space(pa));
531 nf = filters.size();
533 for (int i = 0; i < iterators.size(); ++i, ++pos) {
534 isl_id *id = isl_id_copy(iterators[i]);
535 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
538 for (ci = 0; ci < controls.size() && pos < fp; ++ci, ++pos) {
539 isl_id *id = isl_id_copy(controls[ci]->name);
540 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
543 for (int i = 0; i < nf; ++i, ++pos) {
544 isl_id *id;
545 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
546 isl_dim_out);
547 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
550 for (; ci < controls.size() && pos < n_in; ++ci, ++pos) {
551 isl_id *id = isl_id_copy(controls[ci]->name);
552 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
555 return pa;
558 /* For each of the local variables in "ls", create a corresponding
559 * adg_expr and add it to "controls".
561 * "filters" contains the filters on the corresponding domain.
562 * These filters appear as the first dimensions in "ls" and "filters" is
563 * used to set the names of those dimensions.
565 static void add_controls(adg *graph, std::vector<adg_var *> &filters,
566 std::vector<adg_expr *> &controls,
567 __isl_keep isl_local_space *ls)
569 isl_ctx *ctx;
570 int n_div;
571 int n;
573 ctx = isl_local_space_get_ctx(ls);
574 n_div = isl_local_space_dim(ls, isl_dim_div);
575 n = controls.size();
576 for (int i = 0; i < n_div; ++i) {
577 isl_aff *aff;
578 adg_expr *expr;
580 aff = isl_aff_floor(isl_local_space_get_div(ls, i));
581 aff = set_names(aff, filters, graph->iterators, controls);
582 expr = graph->to_control(aff);
583 controls.push_back(expr);
587 /* Apply the lifting "lifting" to domain->bounds and eliminate
588 * redundant existentially quantified variables.
590 static void apply_lifting(adg_domain *domain,
591 __isl_take isl_basic_map *lifting)
593 domain->bounds = isl_set_apply(domain->bounds,
594 isl_map_from_basic_map(lifting));
595 domain->bounds = isl_set_detect_equalities(domain->bounds);
598 /* Add static controls to "domain" based on the existentially quantified
599 * variables in domain->bounds and set *lifting_p to the lifting that
600 * corresponds to the added static controls. That is, the control variables
601 * correspond to the output variable in the nested map in the range
602 * of the lifting.
604 * We first collect all existentially quantified variables in a single
605 * local space. If needed, we then add the corresponding static controls,
606 * construct the corresponding lifting and finally apply it to domain->bounds.
608 static void add_static_controls(adg *graph, adg_domain *domain,
609 __isl_give isl_basic_map **lifting_p)
611 isl_space *space;
612 isl_local_space *ls;
613 int n_div;
614 isl_basic_map *lifting;
616 space = isl_set_get_space(domain->bounds);
617 ls = isl_local_space_from_space(space);
618 isl_set_foreach_basic_set(domain->bounds, &intersect_local_space, &ls);
620 n_div = isl_local_space_dim(ls, isl_dim_div);
621 if (n_div == 0) {
622 isl_local_space_free(ls);
623 return;
626 add_controls(graph, domain->filters, domain->controls, ls);
628 lifting = isl_local_space_lifting(ls);
629 if (lifting_p)
630 *lifting_p = isl_basic_map_copy(lifting);
632 apply_lifting(domain, lifting);
635 /* Add copies of the elements in "src" to the back of "dst".
637 static void copy_controls(std::vector<adg_expr *> &dst,
638 std::vector<adg_expr *> &src)
640 for (int i = 0; i < src.size(); ++i)
641 dst.push_back(new adg_expr(*src[i]));
644 /* Add static controls to the adg_node "node".
645 * In particular, add static controls to node->domain and if any
646 * were added, copy them to node->function->domain and apply the corresponding
647 * lifting.
649 static void add_static_controls(adg *graph, adg_node *node)
651 add_static_controls(graph, node->domain, &node->lifting);
653 if (!node->lifting)
654 return;
656 copy_controls(node->function->domain->controls, node->domain->controls);
657 apply_lifting(node->function->domain,
658 isl_basic_map_copy(node->lifting));
661 /* Add an adg_node corresponding to "p_node" to the graph.
662 * We only construct the domain, the schedule, the expressions
663 * and the function.
664 * The ports and gates are created during the construction of the edges.
666 * The domain of the function may be subjected to filtering,
667 * but the domain of the node itself may not as it should include
668 * the domains of the ports reading the values of the filters.
669 * The filters are therefore projected out from the node domain.
671 static void add_node(isl_ctx *ctx, adg *graph, pdg::node *p_node)
673 adg_node *node = new adg_node;
674 isl_set *domain;
676 node->name = node_id(ctx, p_node);
678 domain = scheduled_domain(p_node, graph, isl_id_copy(node->name));
679 node->schedule = adg_node_schedule(p_node, graph,
680 isl_id_copy(node->name));
681 set_function(graph, node, p_node, isl_set_copy(domain));
682 if (isl_set_is_wrapping(domain))
683 domain = isl_map_domain(isl_set_unwrap(domain));
684 domain = isl_set_from_basic_set(isl_set_simple_hull(domain));
685 node->domain = new adg_domain(domain);
687 add_static_controls(graph, node);
689 graph->nodes.push_back(node);
692 /* Is "name" of the form __last_*?
694 static bool is_control(const char *name)
696 const char *prefix = "__last_";
697 size_t prefix_len = strlen(prefix);
699 return !strncmp(name, prefix, prefix_len);
702 /* Is "name" of the form __last_<node_name>*?
704 static bool is_local_control(const char *name, const char *node_name)
706 const char *prefix = "__last_";
707 size_t prefix_len = strlen(prefix);
709 if (strncmp(name, prefix, prefix_len) != 0)
710 return 0;
711 return !strncmp(name + prefix_len, node_name, strlen(node_name));
714 /* Is the name of "id" of the form __last_*?
716 static bool is_control(__isl_keep isl_id *id)
718 return is_control(isl_id_get_name(id));
721 /* Is the name of the i-th parameter of map of the form __last_*?
723 static bool is_control(__isl_keep isl_map *map, int i)
725 const char *name;
726 if (!isl_map_has_dim_id(map, isl_dim_param, i))
727 return false;
728 name = isl_map_get_dim_name(map, isl_dim_param, i);
729 return is_control(name);
732 /* Move all parameters of the form __last_* into the range of a wrapped
733 * map with "domain" as domain.
735 * We first create dimensions in the range of the wrapped map, equating
736 * them to the corresponding parameters.
737 * At the end, we project out the parameters.
739 static void move_filters(adg_domain *domain)
741 isl_space *space;
742 isl_set *set;
743 isl_map *map;
744 int nparam;
746 set = domain->bounds;
747 space = isl_set_get_space(set);
748 map = isl_map_from_domain(set);
750 nparam = isl_map_dim(map, isl_dim_param);
751 for (int i = 0; i < nparam; ++i) {
752 int pos;
753 isl_id *id;
754 if (!is_control(map, i))
755 continue;
756 pos = isl_map_dim(map, isl_dim_out);
757 map = isl_map_add_dims(map, isl_dim_out, 1);
758 map = isl_map_equate(map, isl_dim_param, i, isl_dim_out, pos);
759 id = isl_map_get_dim_id(map, isl_dim_param, i);
760 domain->filters.push_back(var_from_id(id,
761 isl_space_copy(space)));
764 for (int i = nparam - 1; i >= 0; --i) {
765 if (!is_control(map, i))
766 continue;
767 map = isl_map_project_out(map, isl_dim_param, i, 1);
770 domain->bounds = isl_map_wrap(map);
771 isl_space_free(space);
774 /* Auxiliary data used during the construction of edges.
776 * dep is the dependence that is being converted into one or more edges.
777 * container is the dependence that contains information about the buffer
778 * size. It may be dep itself or it may be the pn_union dependence
779 * containing dep.
780 * id is the name of the edge.
781 * from_node is the source node.
782 * to_node is the target node.
783 * rel is the original dependence relation.
784 * rel_maff is that part of rel that corresponds to maff.
785 * range_filters_map is a map that adds back the range filters.
786 * maff is the mapping that will be assigned to edge->map.
787 * in_domain is the domain of the input port.
789 * in_filters are filters that should be added to input ports.
790 * out_filters are filters that should be added to output ports.
792 * edge_nr is the sequence number of the original (non-control) edge.
793 * port_nr is a pointer to the sequence number of the port
795 * lifting is the lifting used on the input port domain
796 * in_controls are the controls for the input port domain. They include
797 * the controls of the node domain and the extra controls induced by
798 * lifting.
800 struct add_edge_data {
801 adg *graph;
802 pdg::dependence *dep;
803 pdg::dependence *container;
804 isl_id *id;
805 adg_node *from_node;
806 adg_node *to_node;
807 isl_map *rel;
808 isl_map *range_filters_map;
809 isl_map *rel_maff;
810 isl_multi_aff *maff;
811 isl_basic_set *in_domain;
812 enum adg_edge_type type;
814 std::vector<adg_var *> in_filters;
815 std::vector<adg_var *> out_filters;
817 int edge_nr;
818 int *port_nr;
820 isl_basic_map *lifting;
821 std::vector<adg_expr *> in_controls;
824 enum adg_port_type {
825 adg_port_input,
826 adg_port_output
829 /* Set the name of "port" to
831 * <node>IP_<edge>_<nr>_V_<arg_pos>
832 * or
833 * <node>OP_<edge>_<nr>_V_<arg_pos>
835 * depending on whether type is adg_port_input or adg_port_output.
836 * The name includes the access->nr so that two edges between the same
837 * pair of nodes that have been combined into a single pn_union edge
838 * would not get identical port names.
840 static void set_port_name(adg_port *port, enum adg_port_type type,
841 __isl_keep isl_id *node_id, __isl_keep isl_id *edge_id, int nr)
843 char buf[100];
844 isl_ctx *ctx;
845 const char *s_type;
847 ctx = isl_id_get_ctx(edge_id);
849 if (type == adg_port_input)
850 s_type = "IP";
851 else
852 s_type = "OP";
853 snprintf(buf, sizeof(buf), "%s%s_%s_%d_V_%d",
854 isl_id_get_name(node_id), s_type,
855 isl_id_get_name(edge_id), nr, port->arg_pos);
856 port->name = isl_id_alloc(ctx, buf, NULL);
859 /* Set the binding variable on "port" by calling new_var().
861 static void set_port_var(adg_port *port, pdg::access *access,
862 __isl_keep isl_basic_set *bset)
864 adg_var *var;
865 isl_space *space;
867 space = isl_basic_set_get_space(bset);
868 var = new_var(access, space);
869 port->vars.push_back(var);
872 /* Set the binding variables on "port", one for each element
873 * in "dep_controls". These binding variables are control variables
874 * and they are assumed to of tyoe "int".
876 static void set_port_vars(adg_port *port, seq<str> &dep_controls,
877 __isl_keep isl_basic_set *bset)
879 isl_ctx *ctx;
880 isl_space *space;
882 ctx = isl_basic_set_get_ctx(bset);
883 space = isl_basic_set_get_space(bset);
884 for (int i = 0; i < dep_controls.size(); ++i) {
885 isl_id *id;
886 adg_var *var;
888 id = isl_id_alloc(ctx, dep_controls[i]->s.c_str(), NULL);
889 var = var_from_id(id, isl_space_copy(space));
890 var->type = isl_id_alloc(ctx, "int", NULL);
891 port->vars.push_back(var);
893 isl_space_free(space);
896 /* Create and return a port of type "type" belonging to the node called
897 * "node_id" and attached to the edge called "edge_id".
898 * "access" is the corresponding access.
899 * "nr" is the sequence number.
900 * "dep_controls" contains the names of the control variables.
901 * If the list contains any elements, then the port is connected
902 * to a control edge. Otherwise, it is connected to a data edge.
903 * "bset" represents the iteration domain and "controls" are the controls
904 * that need to be added based on previous liftings.
905 * If "bset" has any existentially quantified variables left, then we
906 * need to apply an additional lifting and append the corresponding
907 * control variables.
908 * "filters" contains the dynamic controls.
910 static adg_port *create_port(adg *graph, enum adg_port_type type,
911 seq<str> &dep_controls, pdg::access *access,
912 __isl_keep isl_id *node_id, __isl_keep isl_id *edge_id, int edge_nr,
913 int nr, __isl_take isl_basic_set *bset,
914 std::vector<adg_var *> &filters, std::vector<adg_expr *> &controls)
916 isl_local_space *ls = NULL;
917 adg_port *port = new adg_port;
919 port->edge_nr = edge_nr;
921 if (isl_basic_set_dim(bset, isl_dim_div) != 0) {
922 isl_basic_map *lifting;
923 ls = isl_basic_set_get_local_space(bset);
924 lifting = isl_local_space_lifting(isl_local_space_copy(ls));
925 bset = isl_basic_set_apply(bset, lifting);
926 bset = isl_basic_set_detect_equalities(bset);
929 port->arg_pos = access->nr;
930 set_port_name(port, type, node_id, edge_id, nr);
932 port->node_name = isl_id_copy(node_id);
933 port->edge_name = isl_id_copy(edge_id);
934 if (dep_controls.size() > 0)
935 set_port_vars(port, dep_controls, bset);
936 else
937 set_port_var(port, access, bset);
938 port->domain = new adg_domain(isl_set_from_basic_set(bset));
940 for (int i = 0; i < filters.size(); ++i)
941 port->domain->filters.push_back(new adg_var(*filters[i]));
942 for (int i = 0; i < controls.size(); ++i)
943 port->domain->controls.push_back(new adg_expr(*controls[i]));
945 if (ls) {
946 add_controls(graph, filters, port->domain->controls, ls);
947 isl_local_space_free(ls);
950 return port;
953 /* Add an edge with a convex input port domain (data->in_domain)
954 * and a convex output port domain (bset).
956 * We create the two ports, add them to the appropriate nodes
957 * and add an edge that referes to the nodes and ports.
959 static int add_edge_basic_in(__isl_take isl_basic_set *bset, void *user)
961 add_edge_data *data = (add_edge_data *) user;
962 adg_edge *edge = new adg_edge;
963 adg_port *port;
965 edge->name = isl_id_copy(data->id);
966 edge->type = data->type;
967 edge->map = isl_multi_aff_copy(data->maff);
968 edge->from_node_name = isl_id_copy(data->from_node->name);
969 edge->to_node_name = isl_id_copy(data->to_node->name);
970 if (data->container->value_size)
971 isl_int_set_si(edge->value_size,
972 data->container->value_size->v);
974 port = create_port(data->graph, adg_port_input,
975 data->dep->to_controls, data->dep->to_access,
976 data->to_node->name, data->id, data->edge_nr,
977 *data->port_nr, isl_basic_set_copy(data->in_domain),
978 data->in_filters, data->in_controls);
979 data->to_node->input_ports.push_back(port);
980 edge->to_port_name = isl_id_copy(port->name);
982 port = create_port(data->graph, adg_port_output,
983 data->dep->from_controls, data->dep->from_access,
984 data->from_node->name, data->id, data->edge_nr,
985 *data->port_nr, isl_basic_set_copy(bset),
986 data->out_filters, data->from_node->domain->controls);
987 data->from_node->output_ports.push_back(port);
988 edge->from_port_name = isl_id_copy(port->name);
990 (*data->port_nr)++;
992 data->graph->edges.push_back(edge);
994 isl_basic_set_free(bset);
995 return 0;
998 /* Does the list of (dynamic) control variables node->function->ctrl
999 * include "id"?
1001 static bool has_control(adg_node *node, __isl_keep isl_id *id)
1003 for (int i = 0; i < node->function->ctrl.size(); ++i)
1004 if (node->function->ctrl[i]->name == id)
1005 return true;
1006 return false;
1009 /* Add elements to node->function->ctrl for each element of dep->from_controls,
1010 * provided the element is not in there already.
1012 * We only add elements that correspond to last iterations of "node".
1013 * The given dependence may be the result of reuse detection and may
1014 * therefore propagate controls from one access to the next in some other
1015 * node.
1017 * To find out which value should be assigned to the variable,
1018 * we look at the final part of the name of the control variable.
1019 * If this final part is "valid", then the function needs
1020 * to assign the value "1". Otherwise, the final part is
1021 * an integer and then we should assign the value of the
1022 * original corresponding iterator. This value is obtained
1023 * from the schedule of "node".
1025 static void add_control_variables(isl_ctx *ctx, adg *graph, adg_node *node,
1026 pdg::dependence *dep)
1028 isl_map *sched;
1029 isl_pw_multi_aff *pma;
1030 isl_space *space;
1031 isl_local_space *ls;
1032 char *node_name;
1034 if (dep->from_controls.size() == 0)
1035 return;
1037 sched = node_schedule(dep->from, graph, isl_id_copy(node->name));
1038 node_name = strdup(isl_map_get_tuple_name(sched, isl_dim_in));
1039 pma = isl_pw_multi_aff_from_map(isl_map_reverse(sched));
1040 space = isl_set_get_space(node->domain->bounds);
1041 ls = isl_local_space_from_space(space);
1043 for (int i = 0; i < dep->from_controls.size(); ++i) {
1044 adg_expr *expr;
1045 isl_aff *aff;
1046 isl_pw_aff *pa;
1047 int pos;
1048 isl_id *id;
1049 const char *name = dep->from_controls[i]->s.c_str();
1051 id = isl_id_alloc(ctx, name, NULL);
1053 if (!is_local_control(name, node_name) ||
1054 has_control(node, id)) {
1055 isl_id_free(id);
1056 continue;
1059 name = strrchr(name, '_');
1060 assert(name);
1062 expr = new adg_expr;
1063 if (!strcmp(name + 1, "valid")) {
1064 aff = isl_aff_zero_on_domain(isl_local_space_copy(ls));
1065 aff = isl_aff_add_constant_si(aff, 1);
1066 pa = isl_pw_aff_from_aff(aff);
1067 } else {
1068 pos = atoi(name + 1);
1069 pa = isl_pw_multi_aff_get_pw_aff(pma, pos);
1072 pa = set_names(pa, node->domain->filters, graph->iterators,
1073 node->domain->controls);
1075 expr->name = id;
1076 expr->expr = pa;
1077 node->function->ctrl.push_back(expr);
1080 free(node_name);
1081 isl_local_space_free(ls);
1082 isl_pw_multi_aff_free(pma);
1085 /* Add an edge with a convex input port domain (bset).
1086 * We compute the corresponding output port domain by applying
1087 * the dependence relation and apply the required liftings in the from
1088 * domain. The result may in principle be a union again and so we
1089 * split that up as well.
1090 * The lifting required by the input port (computed in add_edge_set)
1091 * is applied to the input port domain. Note that we cannot
1092 * apply this lifting earlier because we need the original input port
1093 * domain (without lifting) to compute the output port domain.
1095 static int add_edge_basic(__isl_take isl_basic_set *bset, void *user)
1097 int r;
1098 add_edge_data *data = (add_edge_data *) user;
1099 isl_set *set;
1101 data->in_domain = isl_basic_set_copy(bset);
1102 data->in_domain = isl_basic_set_apply(data->in_domain,
1103 isl_basic_map_copy(data->lifting));
1104 data->in_domain = isl_basic_set_detect_equalities(data->in_domain);
1105 set = isl_set_from_basic_set(bset);
1106 set = isl_set_apply(set, isl_map_copy(data->rel_maff));
1107 if (data->from_node->lifting)
1108 set = isl_set_apply(set, isl_map_from_basic_map(
1109 isl_basic_map_copy(data->from_node->lifting)));
1110 set = isl_set_detect_equalities(set);
1111 set = isl_set_compute_divs(set);
1113 r = isl_set_foreach_basic_set(set, &add_edge_basic_in, data);
1115 isl_set_free(set);
1116 isl_basic_set_free(data->in_domain);
1117 return r;
1120 /* Save a copy of "map" in data->rel, remove the range filters (if any)
1121 * and return the result. The inverse of the mapping used to remove
1122 * the filters is save in data->range_filters_map so that the filters
1123 * can be reintroduced in insert_range_filters.
1125 static __isl_give isl_map *remove_range_filters(__isl_take isl_map *map,
1126 add_edge_data *data)
1128 isl_space *space;
1130 data->rel = isl_map_copy(map);
1132 space = isl_space_range(isl_map_get_space(map));
1133 if (isl_space_is_wrapping(space)) {
1134 isl_map *fm = isl_map_universe(isl_space_unwrap(space));
1135 fm = isl_map_domain_map(fm);
1136 map = isl_map_apply_range(map, isl_map_copy(fm));
1137 data->range_filters_map = isl_map_reverse(fm);
1138 } else {
1139 space = isl_space_map_from_set(space);
1140 data->range_filters_map = isl_map_identity(space);
1143 return map;
1146 /* Reintroduce the filters removed in remove_range_filters.
1148 static __isl_give isl_map *insert_range_filters(__isl_take isl_map *map,
1149 add_edge_data *data)
1151 map = isl_map_apply_range(map, isl_map_copy(data->range_filters_map));
1152 map = isl_map_intersect(map, isl_map_copy(data->rel));
1154 return map;
1157 /* Add edges for the given input port domain (set) and the given
1158 * mapping (maff) to the corresponding output port domain.
1160 * The lifting corresponding to the domain of the destination node
1161 * has already been applied, but "maff" may require additional liftings
1162 * to be applied to the input port domain.
1164 * The given isl_multi_aff is also converted into an isl_basic_map
1165 * for use in the construction of the output port domains
1166 * in add_edge_basic(). After the conversion, we add back the filters
1167 * that were removed right before the call to isl_pw_multi_aff_from_map.
1169 static int add_edge_set(__isl_take isl_set *set, __isl_take isl_multi_aff *maff,
1170 void *user)
1172 add_edge_data *data = (add_edge_data *) user;
1173 int r;
1174 isl_local_space *ls;
1176 data->rel_maff = isl_map_from_basic_map(isl_basic_map_from_multi_aff(
1177 isl_multi_aff_copy(maff)));
1178 data->rel_maff = insert_range_filters(data->rel_maff, data);
1180 maff = isl_multi_aff_lift(maff, &ls);
1181 copy_controls(data->in_controls, data->to_node->domain->controls);
1182 add_controls(data->graph, data->in_filters, data->in_controls, ls);
1183 data->lifting = isl_local_space_lifting(ls);
1185 data->maff = maff;
1187 r = isl_set_foreach_basic_set(set, &add_edge_basic, data);
1189 for (int i = 0; i < data->in_controls.size(); ++i)
1190 delete data->in_controls[i];
1191 data->in_controls.clear();
1192 isl_basic_map_free(data->lifting);
1193 isl_set_free(set);
1194 isl_map_free(data->rel_maff);
1195 isl_multi_aff_free(maff);
1197 return r;
1200 /* Does "rel" have any parameter of the form __last_*?
1202 static bool has_filters(__isl_keep isl_map *rel)
1204 int nparam;
1206 nparam = isl_map_dim(rel, isl_dim_param);
1207 for (int i = 0; i < nparam; ++i)
1208 if (is_control(rel, i))
1209 return true;
1211 return false;
1214 /* Remove all parameters of the form __last_* from "rel".
1216 static __isl_give isl_map *remove_all_filters(__isl_take isl_map *rel)
1218 int nparam;
1220 nparam = isl_map_dim(rel, isl_dim_param);
1221 for (int i = nparam - 1; i >= 0; --i) {
1222 if (!is_control(rel, i))
1223 continue;
1224 rel = isl_map_project_out(rel, isl_dim_param, i, 1);
1227 return rel;
1230 /* For each parameter of the form __last_*, add it to both
1231 * data->in_filters and data->out_filters, in each case defined
1232 * over the appropriate domain.
1234 static void extract_filters(__isl_take isl_map *rel, add_edge_data *data)
1236 isl_space *domain;
1237 isl_space *range;
1238 isl_space *space;
1239 int nparam;
1241 nparam = isl_map_dim(rel, isl_dim_param);
1242 space = isl_map_get_space(rel);
1243 domain = isl_space_domain(isl_space_copy(space));
1244 range = isl_space_range(space);
1246 for (int i = 0; i < nparam; ++i) {
1247 isl_id *id;
1249 if (!is_control(rel, i))
1250 continue;
1252 id = isl_map_get_dim_id(rel, isl_dim_param, i);
1253 data->in_filters.push_back(var_from_id(isl_id_copy(id),
1254 isl_space_copy(domain)));
1255 data->out_filters.push_back(var_from_id(id,
1256 isl_space_copy(range)));
1259 isl_space_free(domain);
1260 isl_space_free(range);
1263 /* Replace both domain and range of "rel" by a wrapped map with the original
1264 * set as domain and dimensions corresponding to the parameters
1265 * referenced by the elements in "filters" as range and then
1266 * remove those parameters.
1267 * The overall effect is that those parameters are moved into
1268 * the ranges of the wrapped maps.
1270 static __isl_give isl_map *move_filters(__isl_take isl_map *rel,
1271 std::vector<adg_var *> &filters)
1273 isl_space *space;
1274 isl_map *map;
1276 space = isl_space_domain(isl_map_get_space(rel));
1277 space = isl_space_from_domain(space);
1278 map = isl_map_universe(space);
1279 for (int i = 0; i < filters.size(); ++i) {
1280 int param;
1281 int pos;
1282 isl_id *id;
1284 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
1285 isl_dim_out);
1286 param = isl_map_find_dim_by_id(map, isl_dim_param, id);
1287 isl_id_free(id);
1289 pos = isl_map_dim(map, isl_dim_out);
1290 map = isl_map_add_dims(map, isl_dim_out, 1);
1291 map = isl_map_equate(map, isl_dim_param, param,
1292 isl_dim_out, pos);
1294 map = isl_map_domain_map(map);
1296 rel = isl_map_apply_range(map, rel);
1298 for (int i = 0; i < filters.size(); ++i) {
1299 int param;
1300 isl_id *id;
1302 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
1303 isl_dim_out);
1304 param = isl_map_find_dim_by_id(rel, isl_dim_param, id);
1305 isl_id_free(id);
1307 rel = isl_map_project_out(rel, isl_dim_param, param, 1);
1310 space = isl_space_unwrap(isl_space_domain(isl_map_get_space(rel)));
1311 map = isl_map_range_map(isl_map_universe(space));
1312 rel = isl_map_range_product(rel, map);
1314 return rel;
1317 /* Determine the appropriate adg_edge_type for "dep".
1319 static enum adg_edge_type dep_type(pdg::dependence *dep)
1321 if (dep->reordering)
1322 return adg_edge_generic;
1323 else if (dep->multiplicity)
1324 return adg_edge_broadcast;
1325 else if (dep->type == pdg::dependence::pn_shift)
1326 return adg_edge_shift_register;
1327 else
1328 return adg_edge_fifo;
1331 /* Lift the domain of the relation "rel" according to domain->lifting,
1332 * if any.
1334 static __isl_give isl_map *lift(__isl_take isl_map *rel, adg_node *domain)
1336 if (domain->lifting) {
1337 isl_basic_map *lifting;
1338 lifting = isl_basic_map_copy(domain->lifting);
1339 rel = isl_map_apply_domain(rel,
1340 isl_map_from_basic_map(lifting));
1341 rel = isl_map_detect_equalities(rel);
1344 return rel;
1347 /* If "dom" is a wrapped map, then add the filters of "node" to "filters".
1349 static void extract_domain_filters(__isl_take isl_space *space,
1350 std::vector<adg_var *> &filters, pdg::node *node)
1352 if (isl_space_is_wrapping(space))
1353 add_domain_filters(filters, node, space);
1355 isl_space_free(space);
1358 /* Extract the filters on the output and input ports from the "from"
1359 * and "to" nodes of "rel".
1361 static void extract_domain_filters(__isl_keep isl_map *rel, add_edge_data *data)
1363 isl_space *space;
1365 space = isl_space_domain(isl_map_get_space(rel));
1366 extract_domain_filters(space, data->out_filters, data->dep->from);
1368 space = isl_space_range(isl_map_get_space(rel));
1369 extract_domain_filters(space, data->in_filters, data->dep->to);
1372 /* Add edges corresponding to "dep" for the relation "rel", which
1373 * may be a subset of dep->relation or dep->controlled_relation.
1374 * "container" contains information about the required buffer sizes.
1375 * "id" is the name that should be given to all created edges.
1376 * "type" is the type of the created edges.
1378 * If "dep" is a control dependence (i.e., if it has elements
1379 * in its from_controls), then add corresponding control variable
1380 * to the from node.
1382 * We schedule the domain and range of "rel" and extract the
1383 * domain filters for later use. We currently only allow such filters
1384 * on dependences without control parameters.
1386 * Since "rel" is a subset of dep->relation, it has the source as domain
1387 * and the sink as range. Since the mapping on the edges map iterations
1388 * of the input port to iterations of the corresponding output port,
1389 * we need to invert it. Then we apply the lifting on the
1390 * sink node as the initial static control variables on the input port domain
1391 * need to be the same as those of the node domain. The control variables
1392 * themselves are copied to data->in_controls in add_edge_set().
1394 * If "type" is adg_edge_broadcast, then a given write may be
1395 * associated to several (consecutive) reads in the original
1396 * dependence relation. In the ADG, we only associate the write
1397 * with the first of those reads.
1399 * If there are any dynamic control variables, we extract them
1400 * and move them from the parameters into the domain of the relation.
1401 * For loops however, we simply remove them.
1403 * Finally, we construct an explicit function representation of the
1404 * mapping and consider each convex piece of the domain in turn.
1405 * This is needed because the bounds on the domains have to be convex.
1406 * Before we compute this explicit representation, we first remove
1407 * the range filters as they may not be uniquely defined in terms
1408 * of the source filters.
1410 static void add_edge(PDG *pdg, adg *adg,
1411 pdg::dependence *dep, pdg::dependence *container,
1412 __isl_take isl_id *id, int edge_nr, __isl_take isl_map *rel,
1413 enum adg_edge_type type, int *port_nr)
1415 isl_ctx *ctx;
1416 isl_pw_multi_aff *pma;
1417 int res;
1418 add_edge_data data = { adg, dep, container };
1420 ctx = pdg->get_isl_ctx();
1422 data.type = type;
1423 data.id = id;
1424 data.from_node = adg->node_by_id(node_id(ctx, dep->from));
1425 data.to_node = adg->node_by_id(node_id(ctx, dep->to));
1426 data.port_nr = port_nr;
1427 data.edge_nr = edge_nr;
1429 add_control_variables(ctx, adg, data.from_node, dep);
1431 rel = isl_map_apply_domain(rel,
1432 extended_node_schedule(dep->from, adg,
1433 isl_id_copy(data.from_node->name),
1434 isl_space_domain(isl_map_get_space(rel))));
1435 rel = isl_map_apply_range(rel,
1436 extended_node_schedule(dep->to, adg,
1437 isl_id_copy(data.to_node->name),
1438 isl_space_range(isl_map_get_space(rel))));
1439 extract_domain_filters(rel, &data);
1440 if (data.out_filters.size() || data.in_filters.size())
1441 assert(!dep->controlled_relation);
1442 if (type == adg_edge_broadcast)
1443 rel = isl_map_lexmin(rel);
1444 rel = isl_map_reverse(rel);
1445 rel = lift(rel, data.to_node);
1446 if (has_filters(rel)) {
1447 if (dep->from != dep->to) {
1448 extract_filters(rel, &data);
1449 rel = move_filters(rel, data.in_filters);
1450 } else
1451 rel = remove_all_filters(rel);
1453 rel = remove_range_filters(rel, &data);
1454 pma = isl_pw_multi_aff_from_map(rel);
1456 res = isl_pw_multi_aff_foreach_piece(pma, &add_edge_set, &data);
1458 isl_pw_multi_aff_free(pma);
1459 isl_map_free(data.range_filters_map);
1460 isl_map_free(data.rel);
1461 isl_id_free(data.id);
1463 for (int i = 0; i < data.in_filters.size(); ++i)
1464 delete data.in_filters[i];
1465 for (int i = 0; i < data.out_filters.size(); ++i)
1466 delete data.out_filters[i];
1469 /* Create and return an isl_id of the form
1471 * ED_<i>
1472 * or
1473 * CED_<i>
1475 * depending on whether "dep" is a control edge.
1477 static __isl_give isl_id *edge_name(isl_ctx *ctx, pdg::dependence *dep, int i)
1479 char buf[10];
1480 bool control = dep->from_controls.size() > 0;
1482 snprintf(buf, sizeof(buf), "%sED_%d", control ? "C" : "", i);
1483 return isl_id_alloc(ctx, buf, NULL);
1486 /* Add edges corresponding to "dep".
1487 * "container" contains information about the required buffer sizes.
1488 * "id" is the name that should be given to all created edges.
1490 * The dependence relation is taken from dep->controlled_relation
1491 * if it is not NULL. Otherwise, it is taken from dep->relation.
1493 * We look for pn_hold dependences with overlapping domains.
1494 * A pn_hold dependence signifies that a value available at the source
1495 * iteration should be kept until the next iteration (= target of dependence).
1496 * If there is a pn_hold dependence whose domain overlaps with the target
1497 * of the given dependence, then we turn the entire given dependence into a
1498 * "sticky" fifo.
1499 * If no such pn_hold dependences were found, then a regular fifo edge
1500 * is created.
1502 * Obviously, we should only convert fifos into sticky fifos.
1503 * So, if "dep" is not a fifo, we should just construct fifos for
1504 * the associated pn_hold dependences.
1506 static void add_edge(PDG *pdg, adg *adg,
1507 pdg::dependence *dep, pdg::dependence *container,
1508 __isl_take isl_id *id, int edge_nr, int *port_nr)
1510 isl_ctx *ctx;
1511 isl_map *rel;
1512 enum adg_edge_type type;
1514 ctx = pdg->get_isl_ctx();
1516 assert(dep->type == pdg::dependence::pn ||
1517 dep->type == pdg::dependence::pn_shift ||
1518 dep->type == pdg::dependence::pn_part ||
1519 dep->type == pdg::dependence::flow);
1521 if (dep->controlled_relation)
1522 rel = dep->controlled_relation->get_isl_map();
1523 else
1524 rel = dep->relation->get_isl_map();
1526 type = dep_type(dep);
1528 for (int j = 0; j < pdg->dependences.size(); ++j) {
1529 pdg::dependence *hold_dep = pdg->dependences[j];
1530 isl_map *hold_rel;
1531 bool empty;
1533 if (hold_dep->type != pdg::dependence::pn_hold)
1534 continue;
1535 if (dep->to != hold_dep->from)
1536 continue;
1537 if (dep->array != hold_dep->array)
1538 continue;
1539 if (dep->to_access != hold_dep->from_access)
1540 continue;
1542 if (hold_dep->controlled_relation)
1543 hold_rel = hold_dep->controlled_relation->get_isl_map();
1544 else
1545 hold_rel = hold_dep->relation->get_isl_map();
1547 if (type != adg_edge_fifo) {
1548 isl_id *id = edge_name(ctx, pdg->dependences[j], j);
1549 add_edge(pdg, adg, hold_dep, hold_dep, id, edge_nr,
1550 hold_rel, adg_edge_fifo, port_nr);
1551 continue;
1554 hold_rel = isl_map_intersect_range(isl_map_copy(rel),
1555 isl_map_domain(hold_rel));
1556 empty = isl_map_is_empty(hold_rel);
1557 isl_map_free(hold_rel);
1559 if (!empty) {
1560 type = adg_edge_sticky_fifo;
1561 break;
1565 add_edge(pdg, adg, dep, container, id, edge_nr, rel, type, port_nr);
1568 /* Add edges corresponding to "dep".
1569 * If it is a pn_union, then all the corresponding pn_part dependences
1570 * should get the same name.
1572 static void add_union_edge(PDG *pdg, adg *adg, pdg::dependence *dep, int i)
1574 isl_ctx *ctx;
1575 isl_id *id;
1576 int port_nr = 0;
1578 ctx = pdg->get_isl_ctx();
1580 id = edge_name(ctx, dep, i);
1582 if (dep->type != pdg::dependence::pn_union) {
1583 add_edge(pdg, adg, dep, dep, id, i, &port_nr);
1584 return;
1587 for (int j = 0; j < pdg->dependences.size(); ++j) {
1588 pdg::dependence *part_dep = pdg->dependences[j];
1590 if (part_dep->type != pdg::dependence::pn_part)
1591 continue;
1592 if (part_dep->container != dep)
1593 continue;
1595 add_edge(pdg, adg, part_dep, dep, isl_id_copy(id), i, &port_nr);
1598 isl_id_free(id);
1601 /* Auxiliary data used during the construction of gates.
1603 * nr points to the sequence number.
1604 * from is the source access.
1605 * to is the target access.
1607 struct add_wire_data {
1608 int *nr;
1609 pdg::access *from;
1610 pdg::access *to;
1611 adg_node *node;
1614 extern "C" {
1615 static int add_wire_basic(__isl_take isl_basic_set *bset, void *user);
1618 /* Add a wire with the convex domain "bset".
1619 * To ensure that the initial control variables of the gate are the same as
1620 * that of the node, we apply the node lifting, if any, and copy the
1621 * static control variables.
1623 * If there are any dynamic control variables among the parameters,
1624 * we move them into the domain of a wrapped map.
1626 static int add_wire_basic(__isl_take isl_basic_set *bset, void *user)
1628 add_wire_data *data = (add_wire_data *) user;
1629 isl_ctx *ctx;
1630 isl_space *space;
1631 adg_gate *gate = new adg_gate;
1632 char buf[100];
1634 ctx = isl_basic_set_get_ctx(bset);
1635 space = isl_basic_set_get_space(bset);
1636 snprintf(buf, sizeof(buf), "%sID_%d",
1637 isl_id_get_name(data->node->name), (*data->nr)++);
1638 gate->name = isl_id_alloc(ctx, buf, NULL);
1639 gate->in = new_var(data->from, isl_space_copy(space));
1640 gate->out = new_var(data->to, space);
1642 gate->node_name = isl_id_copy(data->node->name);
1643 gate->domain = new adg_domain(isl_set_from_basic_set(bset));
1644 if (data->node->lifting)
1645 apply_lifting(gate->domain,
1646 isl_basic_map_copy(data->node->lifting));
1647 copy_controls(gate->domain->controls, data->node->domain->controls);
1648 move_filters(gate->domain);
1650 data->node->gates.push_back(gate);
1652 return 0;
1655 /* Add gates corresponding to the wire "dep".
1656 * "g_nr" is a pointer to the sequence number of gates.
1657 * The gate copies the variable corresponding to the from_access
1658 * to the variable corresponding to the to_access.
1659 * The dependence relation of a wire is always an identity relation,
1660 * so we can just take the domain of this relation.
1661 * We schedule both this domain and the dynamic control variables, if any.
1663 static void add_wire_gate(PDG *pdg, adg *adg, pdg::dependence *dep, int *g_nr)
1665 isl_map *rel;
1666 isl_set *dom;
1667 isl_ctx *ctx;
1668 isl_id *id;
1669 struct add_wire_data data = { g_nr, dep->from_access, dep->to_access };
1671 ctx = pdg->get_isl_ctx();
1672 rel = dep->relation->get_isl_map();
1673 dom = isl_map_domain(rel);
1674 id = node_id(ctx, dep->to);
1675 data.node = adg->node_by_id(isl_id_copy(id));
1676 dom = isl_set_apply(dom, node_schedule(dep->from, adg, id));
1677 isl_set_foreach_basic_set(dom, &add_wire_basic, &data);
1678 isl_set_free(dom);
1681 /* Construct a map that removes some dimensions with known constant
1682 * values from the schedule domain.
1683 * The map is stored in graph->iterator_map.
1684 * Names for the iterators in the domain of this map are stored
1685 * in graph->all_iterators, while the names for the iterators
1686 * that are kept in the range are stored in graph->iterators.
1688 static void compute_iterator_map(adg *graph, PDG *pdg)
1690 int n_it;
1691 int n_all;
1692 isl_ctx *ctx;
1693 isl_space *space;
1694 isl_map *map;
1695 char buf[20];
1697 n_all = pdg->statement_dimensions.size();
1698 n_it = 0;
1699 for (int i = 0; i < n_all; ++i)
1700 if (!pdg->statement_dimensions[i])
1701 n_it++;
1703 ctx = pdg->get_isl_ctx();
1704 space = isl_space_alloc(ctx, 0, n_all, n_it);
1705 map = isl_map_universe(space);
1707 n_it = 0;
1708 for (int i = 0; i < n_all; ++i) {
1709 isl_id *id;
1711 snprintf(buf, sizeof(buf), "c%d", i);
1712 id = isl_id_alloc(ctx, buf, NULL);
1713 graph->all_iterators.push_back(id);
1714 map = isl_map_set_dim_id(map, isl_dim_in, i, isl_id_copy(id));
1716 if (pdg->statement_dimensions[i])
1717 continue;
1718 graph->iterators.push_back(isl_id_copy(id));
1719 map = isl_map_equate(map, isl_dim_in, i, isl_dim_out, n_it);
1720 n_it++;
1723 graph->iterator_map = map;
1726 /* Create an adg_param for each element in pdg->params.
1727 * param->val is obtained from pdg->params[i]->value, if any.
1728 * param->lb and param->ub are obtained from the context.
1730 static void extract_params(adg *adg, PDG *pdg)
1732 isl_ctx *ctx;
1733 isl_local_space *ls;
1734 isl_int v;
1736 ctx = pdg->get_isl_ctx();
1738 ls = isl_local_space_from_space(isl_set_get_space(adg->context));
1739 isl_int_init(v);
1740 for (int i = 0; i < pdg->params.size(); ++i) {
1741 adg_param *param = new adg_param;
1742 isl_aff *aff;
1743 enum isl_lp_result res;
1745 param->name = isl_id_alloc(ctx, pdg->params[i]->name->s.c_str(),
1746 NULL);
1747 if (pdg->params[i]->value) {
1748 isl_space *space = isl_space_params_alloc(ctx, 0);
1749 isl_local_space *ls = isl_local_space_from_space(space);
1750 param->val = isl_aff_zero_on_domain(ls);
1751 param->val = isl_aff_add_constant_si(param->val,
1752 pdg->params[i]->value->v);
1755 aff = isl_aff_zero_on_domain(isl_local_space_copy(ls));
1756 aff = isl_aff_add_coefficient_si(aff, isl_dim_param, i, 1);
1757 res = isl_set_min(adg->context, aff, &v);
1758 if (res == isl_lp_ok) {
1759 isl_space *space = isl_space_params_alloc(ctx, 0);
1760 isl_local_space *ls = isl_local_space_from_space(space);
1761 param->lb = isl_aff_zero_on_domain(ls);
1762 param->lb = isl_aff_add_constant(param->lb, v);
1764 res = isl_set_max(adg->context, aff, &v);
1765 if (res == isl_lp_ok) {
1766 isl_space *space = isl_space_params_alloc(ctx, 0);
1767 isl_local_space *ls = isl_local_space_from_space(space);
1768 param->ub = isl_aff_zero_on_domain(ls);
1769 param->ub = isl_aff_add_constant(param->ub, v);
1771 isl_aff_free(aff);
1773 adg->params.push_back(param);
1775 isl_int_clear(v);
1776 isl_local_space_free(ls);
1779 /* Comparison function for sorting input ports such that
1780 * first, for input ports that are connected to the same edge,
1781 * an input port corresponding to an earlier argument position comes
1782 * before an input port corresponding to a later argument position, and,
1783 * second, ports that have dynamic control variables in their domains
1784 * come after the ports that set those control variables.
1786 * The first is needed for pn_union channels as their detection allows for
1787 * the case where two (or more) tokens are consumed in the same iteration,
1788 * provided they don't cause reordering when consumed in the order of
1789 * the arguments.
1791 * The second is needed to ensure that the values of the dynamic
1792 * control variables are available when they are needed.
1793 * We enforce the second property by sorting the edges based
1794 * on their depth with respect to dependences between input ports.
1795 * It would probably make more sense to perform a topological sort,
1796 * but it seemed easier to implement it this way.
1798 * If two edges are equivalent with respect to the above two criteria,
1799 * then we arbitrarily sort them according to the edge sequence number.
1801 struct less_input_port :
1802 public std::binary_function<adg_port *, adg_port *, bool> {
1803 std::vector<adg_port *> ports;
1805 less_input_port(std::vector<adg_port *> &ports) : ports(ports) {}
1807 bool writes(adg_port *p, __isl_keep isl_id *id);
1808 int depth(adg_port *p);
1810 bool operator()(adg_port *x, adg_port *y) {
1811 int depth_x, depth_y;
1813 if (x->edge_nr == y->edge_nr)
1814 return x->arg_pos < y->arg_pos;
1816 depth_x = depth(x);
1817 depth_y = depth(y);
1818 if (depth_x != depth_y)
1819 return depth_x < depth_y;
1820 return x->edge_nr < y->edge_nr;
1824 /* Does "p" write to the variable "id"?
1826 bool less_input_port::writes(adg_port *p, __isl_keep isl_id *id)
1828 for (int i = 0; i < p->vars.size(); ++i) {
1829 adg_var *var = p->vars[i];
1830 isl_id *var_id;
1831 var_id = isl_pw_multi_aff_get_tuple_id(var->access,
1832 isl_dim_out);
1833 isl_id_free(var_id);
1834 if (var_id == id)
1835 return true;
1838 return false;
1841 /* Return 0 if this port does not involve any filters.
1842 * Otherwise, return 1 plus the maximal depth of any port
1843 * writing a filter used in the domain of the current port.
1844 * We obviously assume here that there are no cyclic dependences.
1846 int less_input_port::depth(adg_port *p)
1848 int max_depth = 0;
1850 for (int i = 0; i < p->domain->filters.size(); ++i) {
1851 adg_var *filter = p->domain->filters[i];
1852 isl_id *id;
1853 id = isl_pw_multi_aff_get_tuple_id(filter->access, isl_dim_out);
1854 for (int j = 0; j < ports.size(); ++j) {
1855 int d;
1856 if (writes(ports[j], id)) {
1857 d = depth(ports[j]) + 1;
1858 if (d > max_depth)
1859 max_depth = d;
1862 isl_id_free(id);
1865 return max_depth;
1868 /* Sort the input ports of the given node.
1870 static void sort_input_ports(adg_node *node)
1872 std::sort(node->input_ports.begin(), node->input_ports.end(),
1873 less_input_port(node->input_ports));
1876 /* Sort the input ports in all nodes of the graph.
1878 static void sort_input_ports(adg *graph)
1880 for (int i = 0; i < graph->nodes.size(); ++i)
1881 sort_input_ports(graph->nodes[i]);
1884 /* Rename the dynamic control "id" from __last_*_valid to
1886 * dc<nr>_<node>_b
1888 * and from __last_*_<i> to
1890 * dc<nr>_<node>_c<i>
1892 * "prefix_map" maps previously translated prefixes __last_*
1893 * (within the same node) to their dc<nr>_<node> translations.
1894 * If we can't find the current prefix in this cache, we create
1895 * a new name and add it to the cache.
1897 static __isl_give isl_id *rename_dynamic_control(__isl_take isl_id *id,
1898 const char *node_name,
1899 std::map<std::string, std::string> &prefix_map)
1901 const char *name, *underscore;
1902 string old;
1903 std::ostringstream strm;
1904 isl_ctx *ctx;
1906 if (!is_control(id))
1907 return id;
1909 name = isl_id_get_name(id);
1910 underscore = strrchr(name, '_');
1911 assert(underscore);
1913 old = string(name, underscore - name);
1914 if (prefix_map.find(old) == prefix_map.end()) {
1915 strm << "dc" << prefix_map.size() << "_" << node_name;
1916 prefix_map[old] = strm.str();
1919 strm.str("");
1920 strm << prefix_map[old] << "_";
1921 if (!strcmp(underscore + 1, "valid"))
1922 strm << "b";
1923 else
1924 strm << "c" << underscore + 1;
1926 ctx = isl_id_get_ctx(id);
1927 isl_id_free(id);
1929 return isl_id_alloc(ctx, strm.str().c_str(), NULL);
1932 static void rename_dynamic_controls(adg_expr *expr,
1933 const char *node_name,
1934 std::map<std::string, std::string> &prefix_map)
1936 isl_id *id;
1937 int n_in;
1939 n_in = isl_pw_aff_dim(expr->expr, isl_dim_in);
1940 for (int i = 0; i < n_in; ++i) {
1941 const char *name;
1942 name = isl_pw_aff_get_dim_name(expr->expr, isl_dim_in, i);
1943 if (!is_control(name))
1944 continue;
1945 id = isl_pw_aff_get_dim_id(expr->expr, isl_dim_in, i);
1946 id = rename_dynamic_control(id, node_name, prefix_map);
1947 expr->expr = isl_pw_aff_set_dim_id(expr->expr,
1948 isl_dim_in, i, id);
1951 if (!is_control(expr->name))
1952 return;
1953 expr->name = rename_dynamic_control(expr->name, node_name, prefix_map);
1956 static void rename_dynamic_controls(adg_var *var,
1957 const char *node_name,
1958 std::map<std::string, std::string> &prefix_map)
1960 isl_id *id;
1961 int nparam;
1963 nparam = isl_pw_multi_aff_dim(var->access, isl_dim_param);
1964 for (int i = 0; i < nparam; ++i) {
1965 const char *name;
1966 name = isl_pw_multi_aff_get_dim_name(var->access,
1967 isl_dim_param, i);
1968 if (!is_control(name))
1969 continue;
1970 id = isl_pw_multi_aff_get_dim_id(var->access,
1971 isl_dim_param, i);
1972 id = rename_dynamic_control(id, node_name, prefix_map);
1973 var->access = isl_pw_multi_aff_set_dim_id(var->access,
1974 isl_dim_param, i, id);
1977 if (!isl_pw_multi_aff_has_tuple_id(var->access, isl_dim_out))
1978 return;
1979 id = isl_pw_multi_aff_get_tuple_id(var->access, isl_dim_out);
1980 id = rename_dynamic_control(id, node_name, prefix_map);
1981 var->access = isl_pw_multi_aff_set_tuple_id(var->access,
1982 isl_dim_out, id);
1985 static void rename_dynamic_controls(adg_domain *domain,
1986 const char *node_name,
1987 std::map<std::string, std::string> &prefix_map)
1989 for (int i = 0; i < domain->controls.size(); ++i)
1990 rename_dynamic_controls(domain->controls[i], node_name,
1991 prefix_map);
1992 for (int i = 0; i < domain->filters.size(); ++i)
1993 rename_dynamic_controls(domain->filters[i], node_name,
1994 prefix_map);
1997 static void rename_dynamic_controls(adg_function *fn,
1998 const char *node_name,
1999 std::map<std::string, std::string> &prefix_map)
2001 for (int i = 0; i < fn->ctrl.size(); ++i)
2002 rename_dynamic_controls(fn->ctrl[i], node_name, prefix_map);
2005 static void rename_dynamic_controls(adg_port *port,
2006 const char *node_name,
2007 std::map<std::string, std::string> &prefix_map)
2009 for (int i = 0; i < port->vars.size(); ++i)
2010 rename_dynamic_controls(port->vars[i], node_name, prefix_map);
2011 rename_dynamic_controls(port->domain, node_name, prefix_map);
2014 static void rename_dynamic_controls(adg_gate *gate,
2015 const char *node_name,
2016 std::map<std::string, std::string> &prefix_map)
2018 rename_dynamic_controls(gate->in, node_name, prefix_map);
2019 rename_dynamic_controls(gate->out, node_name, prefix_map);
2020 rename_dynamic_controls(gate->domain, node_name, prefix_map);
2023 /* Rename all sets of dynamic controls in "node" from __last_*_valid to
2025 * dc<nr>_<node>_b
2027 * and from __last_*_<i> to
2029 * dc<nr>_<node>_c<i>
2031 * This ensures that each set of dynamic controls has a unique name.
2032 * In particular, if such a set of dynamic controls is transferred from
2033 * one node to another, then they will have different names in the two
2034 * nodes.
2036 static void rename_dynamic_controls(adg_node *node)
2038 std::map<std::string, std::string> prefix_map;
2039 const char *node_name = isl_id_get_name(node->name);
2041 rename_dynamic_controls(node->function, node_name, prefix_map);
2043 for (int i = 0; i < node->input_ports.size(); ++i)
2044 rename_dynamic_controls(node->input_ports[i], node_name,
2045 prefix_map);
2046 for (int i = 0; i < node->output_ports.size(); ++i)
2047 rename_dynamic_controls(node->output_ports[i], node_name,
2048 prefix_map);
2049 for (int i = 0; i < node->gates.size(); ++i)
2050 rename_dynamic_controls(node->gates[i], node_name, prefix_map);
2053 static void rename_dynamic_controls(adg *graph)
2055 for (int i = 0; i < graph->nodes.size(); ++i)
2056 rename_dynamic_controls(graph->nodes[i]);
2059 /* Convert the pn model "pdg" to an adg model.
2061 static adg *pdg2adg(PDG *pdg)
2063 isl_ctx *ctx;
2064 adg *graph;
2065 int g_nr = 0;
2067 ctx = pdg->get_isl_ctx();
2069 graph = new adg;
2071 graph->name = isl_id_alloc(ctx, pdg->name->s.c_str(), NULL);
2072 graph->context = pdg->context->get_isl_set();
2073 compute_iterator_map(graph, pdg);
2075 extract_params(graph, pdg);
2077 for (int i = 0; i < pdg->nodes.size(); ++i)
2078 add_node(ctx, graph, pdg->nodes[i]);
2079 for (int i = 0; i < pdg->dependences.size(); ++i) {
2080 pdg::dependence *dep = pdg->dependences[i];
2082 if (dep->type == pdg::dependence::uninitialized)
2083 continue;
2084 if (dep->type == pdg::dependence::pn_part)
2085 continue;
2086 if (dep->type == pdg::dependence::pn_hold)
2087 continue;
2088 if (dep->type == pdg::dependence::pn_wire)
2089 add_wire_gate(pdg, graph, dep, &g_nr);
2090 else
2091 add_union_edge(pdg, graph, dep, i);
2094 sort_input_ports(graph);
2095 rename_dynamic_controls(graph);
2097 return graph;
2100 static void set_output_name(isl_ctx *ctx, struct options *options)
2102 int len;
2103 const char *suffix = options->xml ? "_adg.xml" : "_adg.yaml";
2105 if (!options->input || !strcmp(options->input, "-"))
2106 return;
2107 if (options->output)
2108 return;
2110 len = strlen(options->input);
2111 if (len > 5 && !strcmp(options->input + len - 5, ".yaml"))
2112 len -= 5;
2113 options->output = isl_alloc_array(ctx, char, len + strlen(suffix) + 1);
2114 assert(options->output);
2115 strncpy(options->output, options->input, len);
2116 strcpy(options->output + len, suffix);
2119 /* Convert the input pn model to an adg model and print out the adg
2120 * model in either YAML format or XML format.
2122 int main(int argc, char *argv[])
2124 isl_ctx *ctx;
2125 struct options *options = options_new_with_defaults();
2126 PDG *pdg;
2127 adg *adg;
2128 FILE *in = stdin, *out = stdout;
2130 ctx = isl_ctx_alloc();
2131 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
2133 set_output_name(ctx, options);
2135 if (options->input && strcmp(options->input, "-")) {
2136 in = fopen(options->input, "r");
2137 assert(in);
2139 if (options->output && strcmp(options->output, "-")) {
2140 out = fopen(options->output, "w");
2141 assert(out);
2144 pdg = PDG::Load(in, ctx);
2146 adg = pdg2adg(pdg);
2147 if (options->xml)
2148 adg_print_xml(adg, out);
2149 else
2150 adg->print(out);
2151 delete adg;
2153 pdg->free();
2154 delete pdg;
2155 isl_ctx_free(ctx);
2156 options_free(options);
2158 return 0;