update pet for more relaxed pet_expr_is_equal index comparison
[ppn.git] / pn2adg.cc
blobb3dc1dcf34b4583e8e49cc40dd9bd5d3e06419bc
1 /*
2 * Copyright 2011 Leiden University. All rights reserved.
3 */
5 #include <sstream>
7 #include <isl/ctx.h>
8 #include <isl/id.h>
9 #include <isl/space.h>
10 #include <isl/local_space.h>
11 #include <isl/aff.h>
12 #include <isl/ilp.h>
13 #include <isl/set.h>
14 #include <isl/map.h>
15 #include <isl/union_map.h>
16 #include <isa/pdg.h>
17 #include <isa/adg.h>
19 #include "adg_xml.h"
20 #include "pn2adg_options.h"
22 /* Convert the result of "pn" to and "adg".
23 * The result may be written out as either a YAML or an XML file.
25 * We essentially create an adg node for each pn node and an adg edge
26 * for each pn dependence. The ports are obtained by projecting the
27 * dependence relationss on their domains and ranges.
28 * There are however various extra constraints on the resulting adg which
29 * make the tranformation not quite as trivial as it sounds.
32 /* ESPAM requirements:
34 * The static control variables that appear in the domain of a node
35 * should also appear in the domains of the function, ports and invars
36 * of that node.
38 * The name of a static control should uniquely define the expression
39 * of the static control throughout the entire graph. That is, two
40 * static controls with the same name should have the same expression.
42 * The bounds on the domains should not involve any unions.
44 * An edge of type adg_edge_broadcast should only associate a write
45 * with the first corresponding read.
47 * The name of a control edge should start with "CED".
50 using pdg::PDG;
52 /* Create and return an adg_var that accesses the variable "id"
53 * on the given space.
55 static adg_var *var_from_id(__isl_take isl_id *id, __isl_take isl_space *space)
57 adg_var *var = new adg_var;
58 isl_ctx *ctx;
59 isl_set *dom;
60 isl_multi_aff *maff;
61 isl_aff_list *list;
63 ctx = isl_space_get_ctx(space);
64 dom = isl_set_universe(isl_space_copy(space));
65 space = isl_space_from_domain(space);
66 space = isl_space_set_tuple_id(space, isl_dim_out, id);
67 list = isl_aff_list_alloc(ctx, 0);
68 maff = isl_multi_aff_from_aff_list(space, list);
69 var->access = isl_pw_multi_aff_alloc(dom, maff);
70 return var;
73 /* Create and return an isl_id of the form
75 * in_<access->nr><suffix>
76 * or
77 * out_<access->nr><suffix>
79 * depending on whether access represents a read or a write.
81 static __isl_give isl_id *access_name(isl_ctx *ctx, pdg::access *access,
82 const char *suffix)
84 char buf[100];
85 const char *type;
87 if (access->type == pdg::access::write)
88 type = "out";
89 else
90 type = "in";
91 snprintf(buf, sizeof(buf), "%s_%d%s", type, access->nr, suffix);
93 return isl_id_alloc(ctx, buf, NULL);
96 /* Extract the isl_id of the node from "space".
98 * The given space may be the result of one of more nestings of the form
100 * D -> [D -> local[..]]
102 * and/or a nesting of the form
104 * D -> [D -> [dynamic controls]]
106 * and so we may have to dig down to obtain the space that corresponds
107 * to the node.
109 static __isl_give isl_id *node_id(__isl_take isl_space *space)
111 isl_id *id;
113 if (!isl_space_is_wrapping(space)) {
114 id = isl_space_get_tuple_id(space, isl_dim_set);
115 isl_space_free(space);
116 return id;
118 return node_id(isl_space_domain(isl_space_unwrap(space)));
121 /* Create and return an adg_var corresponding to "access" on "space".
122 * If there is no array associated to the access, then we assume
123 * it is an iterator and assign it type "int".
125 static adg_var *new_var(pdg::access *access, __isl_take isl_space *space)
127 adg_var *var;
128 isl_ctx *ctx = isl_space_get_ctx(space);
129 isl_id *id;
130 const char *suffix;
132 id = node_id(isl_space_copy(space));
133 suffix = isl_id_get_name(id);
134 isl_id_free(id);
136 id = access_name(ctx, access, suffix);
137 var = var_from_id(id, space);
138 if (access->array)
139 var->type = isl_id_alloc(ctx,
140 access->array->element_type->s.c_str(), NULL);
141 else
142 var->type = isl_id_alloc(ctx, "int", NULL);
143 return var;
146 /* Return the schedule for "p_node", mapping the iteration domain
147 * of "p_node" to the domain (with given "id") of the corresponding adg node.
148 * In practice, we compose the pdg schedule with the mapping that
149 * removes the dimensions with constant values.
151 static __isl_give isl_map *node_schedule(pdg::node *p_node, adg *graph,
152 __isl_take isl_id *id)
154 isl_map *sched;
156 sched = p_node->schedule->get_isl_map();
157 sched = isl_map_apply_range(sched, isl_map_copy(graph->iterator_map));
158 if (id)
159 sched = isl_map_set_tuple_id(sched, isl_dim_out, id);
161 return sched;
164 /* Return the schedule for "p_node", mapping the iteration domain
165 * of "p_node" to the domain (with given "id") of the corresponding adg node.
167 * If "space" represents the space to which the schedule needs to be applied.
168 * If it is a wrapped map, then we assume that the actual node schedule
169 * applies to the domain of this wrapped map and we adjust the schedule
170 * to preserve the range of this wrapped map.
172 static __isl_give isl_map *extended_node_schedule(pdg::node *p_node, adg *graph,
173 __isl_take isl_id *id, __isl_take isl_space *space)
175 isl_map *sched;
177 sched = node_schedule(p_node, graph, id);
178 if (!isl_space_is_wrapping(space)) {
179 isl_space_free(space);
180 } else {
181 isl_map *map;
182 space = isl_space_unwrap(space);
183 space = isl_space_map_from_set(isl_space_range(space));
184 map = isl_map_identity(space);
185 sched = isl_map_product(sched, map);
188 return sched;
191 /* Construct an isl_pw_aff from a map with a single output dimension.
192 * The map needs to be single-valued in order to obtain a meaningful
193 * result.
195 static __isl_give isl_pw_aff *pw_aff_from_map(__isl_take isl_map *map)
197 assert(isl_map_is_single_valued(map));
198 return isl_map_dim_max(map, 0);
201 /* Create and return an adg_expr that corresponds to the given access
202 * from the given node. id represents the name of the corresponding adg_node.
204 * The access is assumed to be an access to the set of integers.
205 * That is, the range is a nameless 1D space and the value is equal
206 * to the index expression.
208 static adg_expr *new_expression(adg *graph, pdg::node *node,
209 pdg::access *access, __isl_take isl_id *id)
211 isl_map *sched;
212 isl_map *map;
213 adg_expr *expr;
215 sched = node_schedule(node, graph, isl_id_copy(id));
216 map = access->map->get_isl_map();
217 map = isl_map_apply_domain(map, sched);
218 expr = new adg_expr;
219 expr->name = access_name(isl_map_get_ctx(map), access,
220 isl_id_get_name(id));
221 expr->expr = pw_aff_from_map(map);
222 expr->expr = isl_pw_aff_coalesce(expr->expr);
223 for (int i = 0; i < graph->iterators.size(); ++i)
224 expr->expr = isl_pw_aff_set_dim_id(expr->expr,
225 isl_dim_in, i, isl_id_copy(graph->iterators[i]));
227 isl_id_free(id);
228 return expr;
231 /* Add an adg_expr to node->expressions for each access to the "set of
232 * integers" from "call" (recursively) within the corresponding node p_node.
234 static void add_expressions(adg *graph, adg_node *node, pdg::node *p_node,
235 pdg::function_call *call)
237 for (int i = 0; i < call->arguments.size(); ++i) {
238 pdg::call_or_access *coa = call->arguments[i];
240 if (coa->type == pdg::call_or_access::t_call) {
241 add_expressions(graph, node, p_node, coa->call);
242 continue;
245 assert(coa->type == pdg::call_or_access::t_access);
246 if (coa->access->type == pdg::access::write)
247 continue;
248 if (isl_map_has_tuple_id(coa->access->map->map, isl_dim_out))
249 continue;
250 node->expressions.push_back(new_expression(graph, p_node,
251 coa->access, isl_id_copy(node->name)));
255 /* Append "var" with argument type "type" to "args",
256 * provided such a combination doesn't appear in the list already.
258 static void add_argument(std::vector<adg_arg *> &args, adg_var *var,
259 enum adg_arg_type type)
261 adg_arg *arg;
263 for (int i = 0; i < args.size(); ++i) {
264 if (type == args[i]->type &&
265 isl_pw_multi_aff_plain_is_equal(args[i]->var->access,
266 var->access)) {
267 delete var;
268 return;
272 arg = new adg_arg;
273 arg->var = var;
274 arg->type = type;
276 args.push_back(arg);
279 /* Recursively add an element to fn->out or fn->in for each argument of "call".
280 * If we come across any "&", then we replace "type" by adg_arg_reference.
282 static void add_arguments(adg_function *fn,
283 pdg::function_call *call, __isl_keep isl_space *space,
284 enum adg_arg_type type)
286 for (int i = 0; i < call->arguments.size(); ++i) {
287 pdg::call_or_access *coa = call->arguments[i];
289 if (coa->type == pdg::call_or_access::t_call) {
290 enum adg_arg_type type_i = type;
291 if (!strcmp(coa->call->name->s.c_str(), "&"))
292 type_i = adg_arg_reference;
293 add_arguments(fn, coa->call, space, type_i);
294 continue;
297 assert(coa->type == pdg::call_or_access::t_access);
298 if (coa->access->type == pdg::access::write)
299 add_argument(fn->out,
300 new_var(coa->access, isl_space_copy(space)), type);
301 else
302 add_argument(fn->in,
303 new_var(coa->access, isl_space_copy(space)), type);
307 /* Add the filters of "node" to "filters", having them refer to "space"
308 * as their iteration domain.
310 static void add_domain_filters(std::vector<adg_var *> &filters, pdg::node *node,
311 __isl_keep isl_space *space)
313 for (int i = 0; i < node->filters.size(); ++i) {
314 pdg::call_or_access *coa = node->filters[i];
315 assert(coa->type == pdg::call_or_access::t_access);
316 filters.push_back(new_var(coa->access, isl_space_copy(space)));
320 /* Set node->function based on "p_node" and the node domain "domain".
321 * In particular, create an adg_function with the given domain
322 * and add input and output arguments.
323 * If any of the input arguments is an affine expression (as opposed to
324 * an array access), then it is added to node->expressions.
326 * If p_node has any filters, then they are converted to filters on node.
328 static void set_function(adg *adg, adg_node *node, pdg::node *p_node,
329 __isl_take isl_set *domain)
331 isl_ctx *ctx = isl_set_get_ctx(domain);
332 const char *name;
333 pdg::statement *stat = p_node->statement;
334 adg_function *fn = new adg_function;
335 isl_space *space;
336 node->function = fn;
338 space = isl_set_get_space(domain);
339 fn->domain = new adg_domain(domain);
341 add_domain_filters(fn->domain->filters, p_node, space);
343 for (int i = 0; i < stat->top_outputs.size(); ++i)
344 add_argument(fn->out,
345 new_var(stat->top_outputs[i], isl_space_copy(space)),
346 adg_arg_return);
348 if (!stat->top_function)
349 return;
351 name = stat->top_function->name->s.c_str();
352 fn->name = isl_id_alloc(ctx, name, NULL);
354 add_expressions(adg, node, p_node, stat->top_function);
355 add_arguments(fn, stat->top_function, space, adg_arg_value);
357 isl_space_free(space);
360 /* Return the domain of "p_node" after scheduling.
361 * "id" represents the name of the corresponding adg_node.
363 * If "p_node" has any filters, then its source iteration domain
364 * is a wrapped map with the filters in the range. The computed
365 * node schedule therefore needs to take this filters into account
366 * and so we pass along the domain space to extended_node_schedule().
367 * In particular, we want the filter values to be preserved.
369 static __isl_give isl_set *scheduled_domain(pdg::node *p_node, adg *adg,
370 __isl_take isl_id *id)
372 isl_set *domain;
373 isl_map *sched;
375 domain = p_node->source->get_isl_set();
376 sched = extended_node_schedule(p_node, adg, id,
377 isl_set_get_space(domain));
378 domain = isl_set_apply(domain, sched);
380 return domain;
383 /* Construct a schedule for the adg_node (called "id") corresponding to
384 * "p_node". Note that the domain of the adg_node is the result of
385 * applying the p_node schedule to its domain, with some of the
386 * dimensions with constant values removed (through composition with
387 * adg->iterator_map). The schedule for the adg_node then simply
388 * needs to insert those dimensions with constant values back.
390 * If "p_node" has any filters, then its source iteration domain
391 * is a wrapped map with the filters in the range. These filters
392 * need to be removed first.
394 static __isl_give isl_map *adg_node_schedule(pdg::node *p_node, adg *adg,
395 __isl_take isl_id *id)
397 isl_set *domain;
398 isl_map *sched;
400 domain = p_node->source->get_isl_set();
401 if (isl_set_is_wrapping(domain))
402 domain = isl_map_domain(isl_set_unwrap(domain));
403 sched = p_node->schedule->get_isl_map();
404 domain = isl_set_apply(domain, sched);
405 sched = isl_map_reverse(isl_map_copy(adg->iterator_map));
406 sched = isl_map_intersect_range(sched, domain);
407 sched = isl_map_set_tuple_id(sched, isl_dim_in, id);
409 return sched;
412 /* Construct the name of the adg_node corresponding to "p_node".
414 static __isl_give isl_id *node_id(isl_ctx *ctx, pdg::node *p_node)
416 char buf[10];
418 snprintf(buf, sizeof(buf), "ND_%d", p_node->nr);
420 return isl_id_alloc(ctx, buf, NULL);
423 extern "C" {
424 static isl_stat intersect_local_space(__isl_take isl_basic_set *bset,
425 void *user);
428 /* Intersect the local space *user with the local space of "bset".
430 static isl_stat intersect_local_space(__isl_take isl_basic_set *bset,
431 void *user)
433 isl_local_space *ls;
434 isl_local_space **res = (isl_local_space **)user;
436 ls = isl_basic_set_get_local_space(bset);
437 isl_basic_set_free(bset);
439 *res = isl_local_space_intersect(*res, ls);
441 return isl_stat_ok;
444 /* Find the position of the first filter in "space".
446 * The given space may be the result of one of more nestings of the form
448 * D -> [D -> local[..]]
450 * and/or a nesting of the form
452 * D -> [D -> [dynamic controls]]
454 * and so we may have to dig down until we find a wrapped space
455 * with an unnamed range. If we find it, the position of the filters
456 * in the original space is equal to the number of input dimensions
457 * of that wrapped space. Otherwise, there are no filters and we
458 * return 0.
460 static int filter_pos(__isl_take isl_space *space)
462 int pos;
464 if (!isl_space_is_wrapping(space)) {
465 isl_space_free(space);
466 return 0;
468 space = isl_space_unwrap(space);
469 if (isl_space_has_tuple_id(space, isl_dim_out))
470 return filter_pos(isl_space_domain(space));
471 pos = isl_space_dim(space, isl_dim_in);
472 isl_space_free(space);
473 return pos;
476 /* Set the names of the input dimensions of "aff" according
477 * to "filters", "iterators" and "controls".
479 * The first dimensions correspond to the iterators.
480 * The are followed by the iterators, with the filters intermixed.
482 static __isl_give isl_aff *set_names(__isl_take isl_aff *aff,
483 std::vector<adg_var *> &filters, std::vector<isl_id *> &iterators,
484 std::vector<adg_expr *> &controls)
486 int pos = 0;
487 int n_in = isl_aff_dim(aff, isl_dim_in);
488 int fp;
489 int nf;
490 int ci;
492 fp = filter_pos(isl_aff_get_domain_space(aff));
493 nf = filters.size();
495 for (int i = 0; i < iterators.size(); ++i, ++pos) {
496 isl_id *id = isl_id_copy(iterators[i]);
497 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
500 for (ci = 0; ci < controls.size() && pos < fp; ++ci, ++pos) {
501 isl_id *id = isl_id_copy(controls[ci]->name);
502 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
505 for (int i = 0; i < nf; ++i, ++pos) {
506 isl_id *id;
507 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
508 isl_dim_out);
509 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
512 for (; ci < controls.size() && pos < n_in; ++ci, ++pos) {
513 isl_id *id = isl_id_copy(controls[ci]->name);
514 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
517 return aff;
520 /* Set the names of the input dimensions of "pa" according
521 * to "filters", "iterators" and "controls".
523 * The first dimensions correspond to the iterators.
524 * The are followed by the iterators, with the filters intermixed.
526 static __isl_give isl_pw_aff *set_names(__isl_take isl_pw_aff *pa,
527 std::vector<adg_var *> &filters, std::vector<isl_id *> &iterators,
528 std::vector<adg_expr *> &controls)
530 int pos = 0;
531 int n_in = isl_pw_aff_dim(pa, isl_dim_in);
532 int fp;
533 int nf;
534 int ci;
536 fp = filter_pos(isl_pw_aff_get_domain_space(pa));
537 nf = filters.size();
539 for (int i = 0; i < iterators.size(); ++i, ++pos) {
540 isl_id *id = isl_id_copy(iterators[i]);
541 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
544 for (ci = 0; ci < controls.size() && pos < fp; ++ci, ++pos) {
545 isl_id *id = isl_id_copy(controls[ci]->name);
546 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
549 for (int i = 0; i < nf; ++i, ++pos) {
550 isl_id *id;
551 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
552 isl_dim_out);
553 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
556 for (; ci < controls.size() && pos < n_in; ++ci, ++pos) {
557 isl_id *id = isl_id_copy(controls[ci]->name);
558 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
561 return pa;
564 /* For each of the local variables in "ls", create a corresponding
565 * adg_expr and add it to "controls".
567 * "filters" contains the filters on the corresponding domain.
568 * These filters appear as the first dimensions in "ls" and "filters" is
569 * used to set the names of those dimensions.
571 static void add_controls(adg *graph, std::vector<adg_var *> &filters,
572 std::vector<adg_expr *> &controls,
573 __isl_keep isl_local_space *ls)
575 isl_ctx *ctx;
576 int n_div;
577 int n;
579 ctx = isl_local_space_get_ctx(ls);
580 n_div = isl_local_space_dim(ls, isl_dim_div);
581 n = controls.size();
582 for (int i = 0; i < n_div; ++i) {
583 isl_aff *aff;
584 adg_expr *expr;
586 aff = isl_aff_floor(isl_local_space_get_div(ls, i));
587 aff = set_names(aff, filters, graph->iterators, controls);
588 expr = graph->to_control(aff);
589 controls.push_back(expr);
593 /* Apply the lifting "lifting" to domain->bounds and eliminate
594 * redundant existentially quantified variables.
596 static void apply_lifting(adg_domain *domain,
597 __isl_take isl_basic_map *lifting)
599 domain->bounds = isl_set_apply(domain->bounds,
600 isl_map_from_basic_map(lifting));
601 domain->bounds = isl_set_detect_equalities(domain->bounds);
604 /* Add static controls to "domain" based on the existentially quantified
605 * variables in domain->bounds and set *lifting_p to the lifting that
606 * corresponds to the added static controls. That is, the control variables
607 * correspond to the output variable in the nested map in the range
608 * of the lifting.
610 * We first collect all existentially quantified variables in a single
611 * local space. If needed, we then add the corresponding static controls,
612 * construct the corresponding lifting and finally apply it to domain->bounds.
614 static void add_static_controls(adg *graph, adg_domain *domain,
615 __isl_give isl_basic_map **lifting_p)
617 isl_space *space;
618 isl_local_space *ls;
619 int n_div;
620 isl_basic_map *lifting;
622 space = isl_set_get_space(domain->bounds);
623 ls = isl_local_space_from_space(space);
624 isl_set_foreach_basic_set(domain->bounds, &intersect_local_space, &ls);
626 n_div = isl_local_space_dim(ls, isl_dim_div);
627 if (n_div == 0) {
628 isl_local_space_free(ls);
629 return;
632 add_controls(graph, domain->filters, domain->controls, ls);
634 lifting = isl_local_space_lifting(ls);
635 if (lifting_p)
636 *lifting_p = isl_basic_map_copy(lifting);
638 apply_lifting(domain, lifting);
641 /* Add copies of the elements in "src" to the back of "dst".
643 static void copy_controls(std::vector<adg_expr *> &dst,
644 std::vector<adg_expr *> &src)
646 for (int i = 0; i < src.size(); ++i)
647 dst.push_back(new adg_expr(*src[i]));
650 /* Add static controls to the adg_node "node".
651 * In particular, add static controls to node->domain and if any
652 * were added, copy them to node->function->domain and apply the corresponding
653 * lifting.
655 static void add_static_controls(adg *graph, adg_node *node)
657 add_static_controls(graph, node->domain, &node->lifting);
659 if (!node->lifting)
660 return;
662 copy_controls(node->function->domain->controls, node->domain->controls);
663 apply_lifting(node->function->domain,
664 isl_basic_map_copy(node->lifting));
667 /* Add an adg_node corresponding to "p_node" to the graph.
668 * We only construct the domain, the schedule, the expressions
669 * and the function.
670 * The ports and gates are created during the construction of the edges.
672 * The domain of the function may be subjected to filtering,
673 * but the domain of the node itself may not as it should include
674 * the domains of the ports reading the values of the filters.
675 * The filters are therefore projected out from the node domain.
677 static void add_node(isl_ctx *ctx, adg *graph, pdg::node *p_node)
679 adg_node *node = new adg_node;
680 isl_set *domain;
682 node->name = node_id(ctx, p_node);
684 domain = scheduled_domain(p_node, graph, isl_id_copy(node->name));
685 node->schedule = adg_node_schedule(p_node, graph,
686 isl_id_copy(node->name));
687 set_function(graph, node, p_node, isl_set_copy(domain));
688 if (isl_set_is_wrapping(domain))
689 domain = isl_map_domain(isl_set_unwrap(domain));
690 domain = isl_set_from_basic_set(isl_set_simple_hull(domain));
691 node->domain = new adg_domain(domain);
693 add_static_controls(graph, node);
695 graph->nodes.push_back(node);
698 /* Is "name" of the form __last_*?
700 static bool is_control(const char *name)
702 const char *prefix = "__last_";
703 size_t prefix_len = strlen(prefix);
705 return !strncmp(name, prefix, prefix_len);
708 /* Is "name" of the form __last_<node_name>*?
710 static bool is_local_control(const char *name, const char *node_name)
712 const char *prefix = "__last_";
713 size_t prefix_len = strlen(prefix);
715 if (strncmp(name, prefix, prefix_len) != 0)
716 return 0;
717 return !strncmp(name + prefix_len, node_name, strlen(node_name));
720 /* Is the name of "id" of the form __last_*?
722 static bool is_control(__isl_keep isl_id *id)
724 return is_control(isl_id_get_name(id));
727 /* Is the name of the i-th parameter of map of the form __last_*?
729 static bool is_control(__isl_keep isl_map *map, int i)
731 const char *name;
732 if (!isl_map_has_dim_id(map, isl_dim_param, i))
733 return false;
734 name = isl_map_get_dim_name(map, isl_dim_param, i);
735 return is_control(name);
738 /* Move all parameters of the form __last_* into the range of a wrapped
739 * map with "domain" as domain.
741 * We first create dimensions in the range of the wrapped map, equating
742 * them to the corresponding parameters.
743 * At the end, we project out the parameters.
745 static void move_filters(adg_domain *domain)
747 isl_space *space;
748 isl_set *set;
749 isl_map *map;
750 int nparam;
752 set = domain->bounds;
753 space = isl_set_get_space(set);
754 map = isl_map_from_domain(set);
756 nparam = isl_map_dim(map, isl_dim_param);
757 for (int i = 0; i < nparam; ++i) {
758 int pos;
759 isl_id *id;
760 if (!is_control(map, i))
761 continue;
762 pos = isl_map_dim(map, isl_dim_out);
763 map = isl_map_add_dims(map, isl_dim_out, 1);
764 map = isl_map_equate(map, isl_dim_param, i, isl_dim_out, pos);
765 id = isl_map_get_dim_id(map, isl_dim_param, i);
766 domain->filters.push_back(var_from_id(id,
767 isl_space_copy(space)));
770 for (int i = nparam - 1; i >= 0; --i) {
771 if (!is_control(map, i))
772 continue;
773 map = isl_map_project_out(map, isl_dim_param, i, 1);
776 domain->bounds = isl_map_wrap(map);
777 isl_space_free(space);
780 /* Auxiliary data used during the construction of edges.
782 * dep is the dependence that is being converted into one or more edges.
783 * container is the dependence that contains information about the buffer
784 * size. It may be dep itself or it may be the pn_union dependence
785 * containing dep.
786 * id is the name of the edge.
787 * from_node is the source node.
788 * to_node is the target node.
789 * rel is the original dependence relation.
790 * rel_maff is that part of rel that corresponds to maff.
791 * range_filters_map is a map that adds back the range filters.
792 * maff is the mapping that will be assigned to edge->map.
793 * in_domain is the domain of the input port.
795 * in_filters are filters that should be added to input ports.
796 * out_filters are filters that should be added to output ports.
798 * edge_nr is the sequence number of the original (non-control) edge.
799 * port_nr is a pointer to the sequence number of the port
801 * lifting is the lifting used on the input port domain
802 * in_controls are the controls for the input port domain. They include
803 * the controls of the node domain and the extra controls induced by
804 * lifting.
806 struct add_edge_data {
807 adg *graph;
808 pdg::dependence *dep;
809 pdg::dependence *container;
810 isl_id *id;
811 adg_node *from_node;
812 adg_node *to_node;
813 isl_map *rel;
814 isl_map *range_filters_map;
815 isl_map *rel_maff;
816 isl_multi_aff *maff;
817 isl_basic_set *in_domain;
818 enum adg_edge_type type;
820 std::vector<adg_var *> in_filters;
821 std::vector<adg_var *> out_filters;
823 int edge_nr;
824 int *port_nr;
826 isl_basic_map *lifting;
827 std::vector<adg_expr *> in_controls;
830 enum adg_port_type {
831 adg_port_input,
832 adg_port_output
835 /* Set the name of "port" to
837 * <node>IP_<edge>_<nr>_V_<arg_pos>
838 * or
839 * <node>OP_<edge>_<nr>_V_<arg_pos>
841 * depending on whether type is adg_port_input or adg_port_output.
842 * The name includes the access->nr so that two edges between the same
843 * pair of nodes that have been combined into a single pn_union edge
844 * would not get identical port names.
846 static void set_port_name(adg_port *port, enum adg_port_type type,
847 __isl_keep isl_id *node_id, __isl_keep isl_id *edge_id, int nr)
849 char buf[100];
850 isl_ctx *ctx;
851 const char *s_type;
853 ctx = isl_id_get_ctx(edge_id);
855 if (type == adg_port_input)
856 s_type = "IP";
857 else
858 s_type = "OP";
859 snprintf(buf, sizeof(buf), "%s%s_%s_%d_V_%d",
860 isl_id_get_name(node_id), s_type,
861 isl_id_get_name(edge_id), nr, port->arg_pos);
862 port->name = isl_id_alloc(ctx, buf, NULL);
865 /* Set the binding variable on "port" by calling new_var().
867 static void set_port_var(adg_port *port, pdg::access *access,
868 __isl_keep isl_basic_set *bset)
870 adg_var *var;
871 isl_space *space;
873 space = isl_basic_set_get_space(bset);
874 var = new_var(access, space);
875 port->vars.push_back(var);
878 /* Set the binding variables on "port", one for each element
879 * in "dep_controls". These binding variables are control variables
880 * and they are assumed to of tyoe "int".
882 static void set_port_vars(adg_port *port, seq<str> &dep_controls,
883 __isl_keep isl_basic_set *bset)
885 isl_ctx *ctx;
886 isl_space *space;
888 ctx = isl_basic_set_get_ctx(bset);
889 space = isl_basic_set_get_space(bset);
890 for (int i = 0; i < dep_controls.size(); ++i) {
891 isl_id *id;
892 adg_var *var;
894 id = isl_id_alloc(ctx, dep_controls[i]->s.c_str(), NULL);
895 var = var_from_id(id, isl_space_copy(space));
896 var->type = isl_id_alloc(ctx, "int", NULL);
897 port->vars.push_back(var);
899 isl_space_free(space);
902 /* Create and return a port of type "type" belonging to the node called
903 * "node_id" and attached to the edge called "edge_id".
904 * "access" is the corresponding access.
905 * "nr" is the sequence number.
906 * "dep_controls" contains the names of the control variables.
907 * If the list contains any elements, then the port is connected
908 * to a control edge. Otherwise, it is connected to a data edge.
909 * "bset" represents the iteration domain and "controls" are the controls
910 * that need to be added based on previous liftings.
911 * If "bset" has any existentially quantified variables left, then we
912 * need to apply an additional lifting and append the corresponding
913 * control variables.
914 * "filters" contains the dynamic controls.
916 static adg_port *create_port(adg *graph, enum adg_port_type type,
917 seq<str> &dep_controls, pdg::access *access,
918 __isl_keep isl_id *node_id, __isl_keep isl_id *edge_id, int edge_nr,
919 int nr, __isl_take isl_basic_set *bset,
920 std::vector<adg_var *> &filters, std::vector<adg_expr *> &controls)
922 isl_local_space *ls = NULL;
923 adg_port *port = new adg_port;
925 port->edge_nr = edge_nr;
927 if (isl_basic_set_dim(bset, isl_dim_div) != 0) {
928 isl_basic_map *lifting;
929 ls = isl_basic_set_get_local_space(bset);
930 lifting = isl_local_space_lifting(isl_local_space_copy(ls));
931 bset = isl_basic_set_apply(bset, lifting);
932 bset = isl_basic_set_detect_equalities(bset);
935 port->arg_pos = access->nr;
936 set_port_name(port, type, node_id, edge_id, nr);
938 port->node_name = isl_id_copy(node_id);
939 port->edge_name = isl_id_copy(edge_id);
940 if (dep_controls.size() > 0)
941 set_port_vars(port, dep_controls, bset);
942 else
943 set_port_var(port, access, bset);
944 port->domain = new adg_domain(isl_set_from_basic_set(bset));
946 for (int i = 0; i < filters.size(); ++i)
947 port->domain->filters.push_back(new adg_var(*filters[i]));
948 for (int i = 0; i < controls.size(); ++i)
949 port->domain->controls.push_back(new adg_expr(*controls[i]));
951 if (ls) {
952 add_controls(graph, filters, port->domain->controls, ls);
953 isl_local_space_free(ls);
956 return port;
959 /* Add an edge with a convex input port domain (data->in_domain)
960 * and a convex output port domain (bset).
962 * We create the two ports, add them to the appropriate nodes
963 * and add an edge that referes to the nodes and ports.
965 static isl_stat add_edge_basic_in(__isl_take isl_basic_set *bset, void *user)
967 add_edge_data *data = (add_edge_data *) user;
968 adg_edge *edge = new adg_edge;
969 adg_port *port;
971 edge->name = isl_id_copy(data->id);
972 edge->type = data->type;
973 edge->map = isl_multi_aff_copy(data->maff);
974 edge->from_node_name = isl_id_copy(data->from_node->name);
975 edge->to_node_name = isl_id_copy(data->to_node->name);
976 if (data->container->value_size) {
977 isl_ctx *ctx = isl_basic_set_get_ctx(bset);
978 edge->value_size = isl_val_int_from_si(ctx,
979 data->container->value_size->v);
982 port = create_port(data->graph, adg_port_input,
983 data->dep->to_controls, data->dep->to_access,
984 data->to_node->name, data->id, data->edge_nr,
985 *data->port_nr, isl_basic_set_copy(data->in_domain),
986 data->in_filters, data->in_controls);
987 data->to_node->input_ports.push_back(port);
988 edge->to_port_name = isl_id_copy(port->name);
990 port = create_port(data->graph, adg_port_output,
991 data->dep->from_controls, data->dep->from_access,
992 data->from_node->name, data->id, data->edge_nr,
993 *data->port_nr, isl_basic_set_copy(bset),
994 data->out_filters, data->from_node->domain->controls);
995 data->from_node->output_ports.push_back(port);
996 edge->from_port_name = isl_id_copy(port->name);
998 (*data->port_nr)++;
1000 data->graph->edges.push_back(edge);
1002 isl_basic_set_free(bset);
1003 return isl_stat_ok;
1006 /* Does the list of (dynamic) control variables node->function->ctrl
1007 * include "id"?
1009 static bool has_control(adg_node *node, __isl_keep isl_id *id)
1011 for (int i = 0; i < node->function->ctrl.size(); ++i)
1012 if (node->function->ctrl[i]->name == id)
1013 return true;
1014 return false;
1017 /* Add elements to node->function->ctrl for each element of dep->from_controls,
1018 * provided the element is not in there already.
1020 * We only add elements that correspond to last iterations of "node".
1021 * The given dependence may be the result of reuse detection and may
1022 * therefore propagate controls from one access to the next in some other
1023 * node.
1025 * To find out which value should be assigned to the variable,
1026 * we look at the final part of the name of the control variable.
1027 * If this final part is "valid", then the function needs
1028 * to assign the value "1". Otherwise, the final part is
1029 * an integer and then we should assign the value of the
1030 * original corresponding iterator. This value is obtained
1031 * from the schedule of "node".
1033 static void add_control_variables(isl_ctx *ctx, adg *graph, adg_node *node,
1034 pdg::dependence *dep)
1036 isl_map *sched;
1037 isl_pw_multi_aff *pma;
1038 isl_space *space;
1039 isl_local_space *ls;
1040 char *node_name;
1042 if (dep->from_controls.size() == 0)
1043 return;
1045 sched = node_schedule(dep->from, graph, isl_id_copy(node->name));
1046 node_name = strdup(isl_map_get_tuple_name(sched, isl_dim_in));
1047 pma = isl_pw_multi_aff_from_map(isl_map_reverse(sched));
1048 space = isl_set_get_space(node->domain->bounds);
1049 ls = isl_local_space_from_space(space);
1051 for (int i = 0; i < dep->from_controls.size(); ++i) {
1052 adg_expr *expr;
1053 isl_aff *aff;
1054 isl_pw_aff *pa;
1055 int pos;
1056 isl_id *id;
1057 const char *name = dep->from_controls[i]->s.c_str();
1059 id = isl_id_alloc(ctx, name, NULL);
1061 if (!is_local_control(name, node_name) ||
1062 has_control(node, id)) {
1063 isl_id_free(id);
1064 continue;
1067 name = strrchr(name, '_');
1068 assert(name);
1070 expr = new adg_expr;
1071 if (!strcmp(name + 1, "valid")) {
1072 aff = isl_aff_zero_on_domain(isl_local_space_copy(ls));
1073 aff = isl_aff_add_constant_si(aff, 1);
1074 pa = isl_pw_aff_from_aff(aff);
1075 } else {
1076 pos = atoi(name + 1);
1077 pa = isl_pw_multi_aff_get_pw_aff(pma, pos);
1080 pa = set_names(pa, node->domain->filters, graph->iterators,
1081 node->domain->controls);
1083 expr->name = id;
1084 expr->expr = pa;
1085 node->function->ctrl.push_back(expr);
1088 free(node_name);
1089 isl_local_space_free(ls);
1090 isl_pw_multi_aff_free(pma);
1093 /* Add an edge with a convex input port domain (bset).
1094 * We compute the corresponding output port domain by applying
1095 * the dependence relation and apply the required liftings in the from
1096 * domain. The result may in principle be a union again and so we
1097 * split that up as well.
1098 * The lifting required by the input port (computed in add_edge_set)
1099 * is applied to the input port domain. Note that we cannot
1100 * apply this lifting earlier because we need the original input port
1101 * domain (without lifting) to compute the output port domain.
1103 static isl_stat add_edge_basic(__isl_take isl_basic_set *bset, void *user)
1105 isl_stat r;
1106 add_edge_data *data = (add_edge_data *) user;
1107 isl_set *set;
1109 data->in_domain = isl_basic_set_copy(bset);
1110 data->in_domain = isl_basic_set_apply(data->in_domain,
1111 isl_basic_map_copy(data->lifting));
1112 data->in_domain = isl_basic_set_detect_equalities(data->in_domain);
1113 set = isl_set_from_basic_set(bset);
1114 set = isl_set_apply(set, isl_map_copy(data->rel_maff));
1115 if (data->from_node->lifting)
1116 set = isl_set_apply(set, isl_map_from_basic_map(
1117 isl_basic_map_copy(data->from_node->lifting)));
1118 set = isl_set_detect_equalities(set);
1119 set = isl_set_compute_divs(set);
1121 r = isl_set_foreach_basic_set(set, &add_edge_basic_in, data);
1123 isl_set_free(set);
1124 isl_basic_set_free(data->in_domain);
1125 return r;
1128 /* Save a copy of "map" in data->rel, remove the range filters (if any)
1129 * and return the result. The inverse of the mapping used to remove
1130 * the filters is save in data->range_filters_map so that the filters
1131 * can be reintroduced in insert_range_filters.
1133 static __isl_give isl_map *remove_range_filters(__isl_take isl_map *map,
1134 add_edge_data *data)
1136 isl_space *space;
1138 data->rel = isl_map_copy(map);
1140 space = isl_space_range(isl_map_get_space(map));
1141 if (isl_space_is_wrapping(space)) {
1142 isl_map *fm = isl_map_universe(isl_space_unwrap(space));
1143 fm = isl_map_domain_map(fm);
1144 map = isl_map_apply_range(map, isl_map_copy(fm));
1145 data->range_filters_map = isl_map_reverse(fm);
1146 } else {
1147 space = isl_space_map_from_set(space);
1148 data->range_filters_map = isl_map_identity(space);
1151 return map;
1154 /* Reintroduce the filters removed in remove_range_filters.
1156 static __isl_give isl_map *insert_range_filters(__isl_take isl_map *map,
1157 add_edge_data *data)
1159 map = isl_map_apply_range(map, isl_map_copy(data->range_filters_map));
1160 map = isl_map_intersect(map, isl_map_copy(data->rel));
1162 return map;
1165 /* Add edges for the given input port domain (set) and the given
1166 * mapping (maff) to the corresponding output port domain.
1168 * The lifting corresponding to the domain of the destination node
1169 * has already been applied, but "maff" may require additional liftings
1170 * to be applied to the input port domain.
1172 * The given isl_multi_aff is also converted into an isl_basic_map
1173 * for use in the construction of the output port domains
1174 * in add_edge_basic(). After the conversion, we add back the filters
1175 * that were removed right before the call to isl_pw_multi_aff_from_map.
1177 static isl_stat add_edge_set(__isl_take isl_set *set,
1178 __isl_take isl_multi_aff *maff, void *user)
1180 add_edge_data *data = (add_edge_data *) user;
1181 isl_stat r;
1182 isl_local_space *ls;
1184 data->rel_maff = isl_map_from_basic_map(isl_basic_map_from_multi_aff(
1185 isl_multi_aff_copy(maff)));
1186 data->rel_maff = insert_range_filters(data->rel_maff, data);
1188 maff = isl_multi_aff_lift(maff, &ls);
1189 copy_controls(data->in_controls, data->to_node->domain->controls);
1190 add_controls(data->graph, data->in_filters, data->in_controls, ls);
1191 data->lifting = isl_local_space_lifting(ls);
1193 data->maff = maff;
1195 r = isl_set_foreach_basic_set(set, &add_edge_basic, data);
1197 for (int i = 0; i < data->in_controls.size(); ++i)
1198 delete data->in_controls[i];
1199 data->in_controls.clear();
1200 isl_basic_map_free(data->lifting);
1201 isl_set_free(set);
1202 isl_map_free(data->rel_maff);
1203 isl_multi_aff_free(maff);
1205 return r;
1208 /* Does "rel" have any parameter of the form __last_*?
1210 static bool has_filters(__isl_keep isl_map *rel)
1212 int nparam;
1214 nparam = isl_map_dim(rel, isl_dim_param);
1215 for (int i = 0; i < nparam; ++i)
1216 if (is_control(rel, i))
1217 return true;
1219 return false;
1222 /* Remove all parameters of the form __last_* from "rel".
1224 static __isl_give isl_map *remove_all_filters(__isl_take isl_map *rel)
1226 int nparam;
1228 nparam = isl_map_dim(rel, isl_dim_param);
1229 for (int i = nparam - 1; i >= 0; --i) {
1230 if (!is_control(rel, i))
1231 continue;
1232 rel = isl_map_project_out(rel, isl_dim_param, i, 1);
1235 return rel;
1238 /* For each parameter of the form __last_*, add it to both
1239 * data->in_filters and data->out_filters, in each case defined
1240 * over the appropriate domain.
1242 static void extract_filters(__isl_take isl_map *rel, add_edge_data *data)
1244 isl_space *domain;
1245 isl_space *range;
1246 isl_space *space;
1247 int nparam;
1249 nparam = isl_map_dim(rel, isl_dim_param);
1250 space = isl_map_get_space(rel);
1251 domain = isl_space_domain(isl_space_copy(space));
1252 range = isl_space_range(space);
1254 for (int i = 0; i < nparam; ++i) {
1255 isl_id *id;
1257 if (!is_control(rel, i))
1258 continue;
1260 id = isl_map_get_dim_id(rel, isl_dim_param, i);
1261 data->in_filters.push_back(var_from_id(isl_id_copy(id),
1262 isl_space_copy(domain)));
1263 data->out_filters.push_back(var_from_id(id,
1264 isl_space_copy(range)));
1267 isl_space_free(domain);
1268 isl_space_free(range);
1271 /* Replace both domain and range of "rel" by a wrapped map with the original
1272 * set as domain and dimensions corresponding to the parameters
1273 * referenced by the elements in "filters" as range and then
1274 * remove those parameters.
1275 * The overall effect is that those parameters are moved into
1276 * the ranges of the wrapped maps.
1278 static __isl_give isl_map *move_filters(__isl_take isl_map *rel,
1279 std::vector<adg_var *> &filters)
1281 isl_space *space;
1282 isl_map *map;
1284 space = isl_space_domain(isl_map_get_space(rel));
1285 space = isl_space_from_domain(space);
1286 map = isl_map_universe(space);
1287 for (int i = 0; i < filters.size(); ++i) {
1288 int param;
1289 int pos;
1290 isl_id *id;
1292 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
1293 isl_dim_out);
1294 param = isl_map_find_dim_by_id(map, isl_dim_param, id);
1295 isl_id_free(id);
1297 pos = isl_map_dim(map, isl_dim_out);
1298 map = isl_map_add_dims(map, isl_dim_out, 1);
1299 map = isl_map_equate(map, isl_dim_param, param,
1300 isl_dim_out, pos);
1302 map = isl_map_domain_map(map);
1304 rel = isl_map_apply_range(map, rel);
1306 for (int i = 0; i < filters.size(); ++i) {
1307 int param;
1308 isl_id *id;
1310 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
1311 isl_dim_out);
1312 param = isl_map_find_dim_by_id(rel, isl_dim_param, id);
1313 isl_id_free(id);
1315 rel = isl_map_project_out(rel, isl_dim_param, param, 1);
1318 space = isl_space_unwrap(isl_space_domain(isl_map_get_space(rel)));
1319 map = isl_map_range_map(isl_map_universe(space));
1320 rel = isl_map_range_product(rel, map);
1322 return rel;
1325 /* Determine the appropriate adg_edge_type for "dep".
1327 static enum adg_edge_type dep_type(pdg::dependence *dep)
1329 if (dep->reordering)
1330 return adg_edge_generic;
1331 else if (dep->multiplicity)
1332 return adg_edge_broadcast;
1333 else if (dep->type == pdg::dependence::pn_shift)
1334 return adg_edge_shift_register;
1335 else
1336 return adg_edge_fifo;
1339 /* Lift the domain of the relation "rel" according to domain->lifting,
1340 * if any.
1342 static __isl_give isl_map *lift(__isl_take isl_map *rel, adg_node *domain)
1344 if (domain->lifting) {
1345 isl_basic_map *lifting;
1346 lifting = isl_basic_map_copy(domain->lifting);
1347 rel = isl_map_apply_domain(rel,
1348 isl_map_from_basic_map(lifting));
1349 rel = isl_map_detect_equalities(rel);
1352 return rel;
1355 /* If "dom" is a wrapped map, then add the filters of "node" to "filters".
1357 static void extract_domain_filters(__isl_take isl_space *space,
1358 std::vector<adg_var *> &filters, pdg::node *node)
1360 if (isl_space_is_wrapping(space))
1361 add_domain_filters(filters, node, space);
1363 isl_space_free(space);
1366 /* Extract the filters on the output and input ports from the "from"
1367 * and "to" nodes of "rel".
1369 static void extract_domain_filters(__isl_keep isl_map *rel, add_edge_data *data)
1371 isl_space *space;
1373 space = isl_space_domain(isl_map_get_space(rel));
1374 extract_domain_filters(space, data->out_filters, data->dep->from);
1376 space = isl_space_range(isl_map_get_space(rel));
1377 extract_domain_filters(space, data->in_filters, data->dep->to);
1380 /* Add edges corresponding to "dep" for the relation "rel", which
1381 * may be a subset of dep->relation or dep->controlled_relation.
1382 * "container" contains information about the required buffer sizes.
1383 * "id" is the name that should be given to all created edges.
1384 * "type" is the type of the created edges.
1386 * If "dep" is a control dependence (i.e., if it has elements
1387 * in its from_controls), then add corresponding control variable
1388 * to the from node.
1390 * We schedule the domain and range of "rel" and extract the
1391 * domain filters for later use. We currently only allow such filters
1392 * on dependences without control parameters.
1394 * Since "rel" is a subset of dep->relation, it has the source as domain
1395 * and the sink as range. Since the mapping on the edges map iterations
1396 * of the input port to iterations of the corresponding output port,
1397 * we need to invert it. Then we apply the lifting on the
1398 * sink node as the initial static control variables on the input port domain
1399 * need to be the same as those of the node domain. The control variables
1400 * themselves are copied to data->in_controls in add_edge_set().
1402 * If "type" is adg_edge_broadcast, then a given write may be
1403 * associated to several (consecutive) reads in the original
1404 * dependence relation. In the ADG, we only associate the write
1405 * with the first of those reads.
1407 * If there are any dynamic control variables, we extract them
1408 * and move them from the parameters into the domain of the relation.
1409 * For loops however, we simply remove them.
1411 * Finally, we construct an explicit function representation of the
1412 * mapping and consider each convex piece of the domain in turn.
1413 * This is needed because the bounds on the domains have to be convex.
1414 * Before we compute this explicit representation, we first remove
1415 * the range filters as they may not be uniquely defined in terms
1416 * of the source filters.
1418 static void add_edge(PDG *pdg, adg *adg,
1419 pdg::dependence *dep, pdg::dependence *container,
1420 __isl_take isl_id *id, int edge_nr, __isl_take isl_map *rel,
1421 enum adg_edge_type type, int *port_nr)
1423 isl_ctx *ctx;
1424 isl_pw_multi_aff *pma;
1425 int res;
1426 add_edge_data data = { adg, dep, container };
1428 ctx = pdg->get_isl_ctx();
1430 data.type = type;
1431 data.id = id;
1432 data.from_node = adg->node_by_id(node_id(ctx, dep->from));
1433 data.to_node = adg->node_by_id(node_id(ctx, dep->to));
1434 data.port_nr = port_nr;
1435 data.edge_nr = edge_nr;
1437 add_control_variables(ctx, adg, data.from_node, dep);
1439 rel = isl_map_apply_domain(rel,
1440 extended_node_schedule(dep->from, adg,
1441 isl_id_copy(data.from_node->name),
1442 isl_space_domain(isl_map_get_space(rel))));
1443 rel = isl_map_apply_range(rel,
1444 extended_node_schedule(dep->to, adg,
1445 isl_id_copy(data.to_node->name),
1446 isl_space_range(isl_map_get_space(rel))));
1447 extract_domain_filters(rel, &data);
1448 if (data.out_filters.size() || data.in_filters.size())
1449 assert(!dep->controlled_relation);
1450 if (type == adg_edge_broadcast)
1451 rel = isl_map_lexmin(rel);
1452 rel = isl_map_reverse(rel);
1453 rel = lift(rel, data.to_node);
1454 if (has_filters(rel)) {
1455 if (dep->from != dep->to) {
1456 extract_filters(rel, &data);
1457 rel = move_filters(rel, data.in_filters);
1458 } else
1459 rel = remove_all_filters(rel);
1461 rel = remove_range_filters(rel, &data);
1462 pma = isl_pw_multi_aff_from_map(rel);
1464 res = isl_pw_multi_aff_foreach_piece(pma, &add_edge_set, &data);
1466 isl_pw_multi_aff_free(pma);
1467 isl_map_free(data.range_filters_map);
1468 isl_map_free(data.rel);
1469 isl_id_free(data.id);
1471 for (int i = 0; i < data.in_filters.size(); ++i)
1472 delete data.in_filters[i];
1473 for (int i = 0; i < data.out_filters.size(); ++i)
1474 delete data.out_filters[i];
1477 /* Create and return an isl_id of the form
1479 * ED_<i>
1480 * or
1481 * CED_<i>
1483 * depending on whether "dep" is a control edge.
1485 static __isl_give isl_id *edge_name(isl_ctx *ctx, pdg::dependence *dep, int i)
1487 char buf[19];
1488 bool control = dep->from_controls.size() > 0;
1490 snprintf(buf, sizeof(buf), "%sED_%d", control ? "C" : "", i);
1491 return isl_id_alloc(ctx, buf, NULL);
1494 /* Add edges corresponding to "dep".
1495 * "container" contains information about the required buffer sizes.
1496 * "id" is the name that should be given to all created edges.
1498 * The dependence relation is taken from dep->controlled_relation
1499 * if it is not NULL. Otherwise, it is taken from dep->relation.
1501 * We look for pn_hold dependences with overlapping domains.
1502 * A pn_hold dependence signifies that a value available at the source
1503 * iteration should be kept until the next iteration (= target of dependence).
1504 * If there is a pn_hold dependence whose domain overlaps with the target
1505 * of the given dependence, then we turn the entire given dependence into a
1506 * "sticky" fifo.
1507 * If no such pn_hold dependences were found, then a regular fifo edge
1508 * is created.
1510 * Obviously, we should only convert fifos into sticky fifos.
1511 * So, if "dep" is not a fifo, we should just construct fifos for
1512 * the associated pn_hold dependences.
1514 static void add_edge(PDG *pdg, adg *adg,
1515 pdg::dependence *dep, pdg::dependence *container,
1516 __isl_take isl_id *id, int edge_nr, int *port_nr)
1518 isl_ctx *ctx;
1519 isl_map *rel;
1520 enum adg_edge_type type;
1522 ctx = pdg->get_isl_ctx();
1524 assert(dep->type == pdg::dependence::pn ||
1525 dep->type == pdg::dependence::pn_shift ||
1526 dep->type == pdg::dependence::pn_part ||
1527 dep->type == pdg::dependence::flow);
1529 if (dep->controlled_relation)
1530 rel = dep->controlled_relation->get_isl_map();
1531 else
1532 rel = dep->relation->get_isl_map();
1534 type = dep_type(dep);
1536 for (int j = 0; j < pdg->dependences.size(); ++j) {
1537 pdg::dependence *hold_dep = pdg->dependences[j];
1538 isl_map *hold_rel;
1539 bool empty;
1541 if (hold_dep->type != pdg::dependence::pn_hold)
1542 continue;
1543 if (dep->to != hold_dep->from)
1544 continue;
1545 if (dep->array != hold_dep->array)
1546 continue;
1547 if (dep->to_access != hold_dep->from_access)
1548 continue;
1550 if (hold_dep->controlled_relation)
1551 hold_rel = hold_dep->controlled_relation->get_isl_map();
1552 else
1553 hold_rel = hold_dep->relation->get_isl_map();
1555 if (type != adg_edge_fifo) {
1556 isl_id *id = edge_name(ctx, pdg->dependences[j], j);
1557 add_edge(pdg, adg, hold_dep, hold_dep, id, edge_nr,
1558 hold_rel, adg_edge_fifo, port_nr);
1559 continue;
1562 hold_rel = isl_map_intersect_range(isl_map_copy(rel),
1563 isl_map_domain(hold_rel));
1564 empty = isl_map_is_empty(hold_rel);
1565 isl_map_free(hold_rel);
1567 if (!empty) {
1568 type = adg_edge_sticky_fifo;
1569 break;
1573 add_edge(pdg, adg, dep, container, id, edge_nr, rel, type, port_nr);
1576 /* Add edges corresponding to "dep".
1577 * If it is a pn_union, then all the corresponding pn_part dependences
1578 * should get the same name.
1580 static void add_union_edge(PDG *pdg, adg *adg, pdg::dependence *dep, int i)
1582 isl_ctx *ctx;
1583 isl_id *id;
1584 int port_nr = 0;
1586 ctx = pdg->get_isl_ctx();
1588 id = edge_name(ctx, dep, i);
1590 if (dep->type != pdg::dependence::pn_union) {
1591 add_edge(pdg, adg, dep, dep, id, i, &port_nr);
1592 return;
1595 for (int j = 0; j < pdg->dependences.size(); ++j) {
1596 pdg::dependence *part_dep = pdg->dependences[j];
1598 if (part_dep->type != pdg::dependence::pn_part)
1599 continue;
1600 if (part_dep->container != dep)
1601 continue;
1603 add_edge(pdg, adg, part_dep, dep, isl_id_copy(id), i, &port_nr);
1606 isl_id_free(id);
1609 /* Auxiliary data used during the construction of gates.
1611 * nr points to the sequence number.
1612 * from is the source access.
1613 * to is the target access.
1615 struct add_wire_data {
1616 int *nr;
1617 pdg::access *from;
1618 pdg::access *to;
1619 adg_node *node;
1622 extern "C" {
1623 static isl_stat add_wire_basic(__isl_take isl_basic_set *bset,
1624 void *user);
1627 /* Add a wire with the convex domain "bset".
1628 * To ensure that the initial control variables of the gate are the same as
1629 * that of the node, we apply the node lifting, if any, and copy the
1630 * static control variables.
1632 * If there are any dynamic control variables among the parameters,
1633 * we move them into the domain of a wrapped map.
1635 static isl_stat add_wire_basic(__isl_take isl_basic_set *bset, void *user)
1637 add_wire_data *data = (add_wire_data *) user;
1638 isl_ctx *ctx;
1639 isl_space *space;
1640 adg_gate *gate = new adg_gate;
1641 char buf[100];
1643 ctx = isl_basic_set_get_ctx(bset);
1644 space = isl_basic_set_get_space(bset);
1645 snprintf(buf, sizeof(buf), "%sID_%d",
1646 isl_id_get_name(data->node->name), (*data->nr)++);
1647 gate->name = isl_id_alloc(ctx, buf, NULL);
1648 gate->in = new_var(data->from, isl_space_copy(space));
1649 gate->out = new_var(data->to, space);
1651 gate->node_name = isl_id_copy(data->node->name);
1652 gate->domain = new adg_domain(isl_set_from_basic_set(bset));
1653 if (data->node->lifting)
1654 apply_lifting(gate->domain,
1655 isl_basic_map_copy(data->node->lifting));
1656 copy_controls(gate->domain->controls, data->node->domain->controls);
1657 move_filters(gate->domain);
1659 data->node->gates.push_back(gate);
1661 return isl_stat_ok;
1664 /* Add gates corresponding to the wire "dep".
1665 * "g_nr" is a pointer to the sequence number of gates.
1666 * The gate copies the variable corresponding to the from_access
1667 * to the variable corresponding to the to_access.
1668 * The dependence relation of a wire is always an identity relation,
1669 * so we can just take the domain of this relation.
1670 * We schedule both this domain and the dynamic control variables, if any.
1672 static void add_wire_gate(PDG *pdg, adg *adg, pdg::dependence *dep, int *g_nr)
1674 isl_map *rel;
1675 isl_set *dom;
1676 isl_ctx *ctx;
1677 isl_id *id;
1678 struct add_wire_data data = { g_nr, dep->from_access, dep->to_access };
1680 ctx = pdg->get_isl_ctx();
1681 rel = dep->relation->get_isl_map();
1682 dom = isl_map_domain(rel);
1683 id = node_id(ctx, dep->to);
1684 data.node = adg->node_by_id(isl_id_copy(id));
1685 dom = isl_set_apply(dom, node_schedule(dep->from, adg, id));
1686 isl_set_foreach_basic_set(dom, &add_wire_basic, &data);
1687 isl_set_free(dom);
1690 /* Construct a map that removes some dimensions with known constant
1691 * values from the schedule domain.
1692 * The map is stored in graph->iterator_map.
1693 * Names for the iterators in the domain of this map are stored
1694 * in graph->all_iterators, while the names for the iterators
1695 * that are kept in the range are stored in graph->iterators.
1697 static void compute_iterator_map(adg *graph, PDG *pdg)
1699 int n_it;
1700 int n_all;
1701 isl_ctx *ctx;
1702 isl_space *space;
1703 isl_map *map;
1704 char buf[20];
1706 n_all = pdg->statement_dimensions.size();
1707 n_it = 0;
1708 for (int i = 0; i < n_all; ++i)
1709 if (!pdg->statement_dimensions[i])
1710 n_it++;
1712 ctx = pdg->get_isl_ctx();
1713 space = isl_space_alloc(ctx, 0, n_all, n_it);
1714 map = isl_map_universe(space);
1716 n_it = 0;
1717 for (int i = 0; i < n_all; ++i) {
1718 isl_id *id;
1720 snprintf(buf, sizeof(buf), "c%d", i);
1721 id = isl_id_alloc(ctx, buf, NULL);
1722 graph->all_iterators.push_back(id);
1723 map = isl_map_set_dim_id(map, isl_dim_in, i, isl_id_copy(id));
1725 if (pdg->statement_dimensions[i])
1726 continue;
1727 graph->iterators.push_back(isl_id_copy(id));
1728 map = isl_map_equate(map, isl_dim_in, i, isl_dim_out, n_it);
1729 n_it++;
1732 graph->iterator_map = map;
1735 /* Create an adg_param for each element in pdg->params.
1736 * param->val is obtained from pdg->params[i]->value, if any.
1737 * param->lb and param->ub are obtained from the context.
1739 static void extract_params(adg *adg, PDG *pdg)
1741 isl_ctx *ctx;
1742 isl_local_space *ls;
1744 ctx = pdg->get_isl_ctx();
1746 ls = isl_local_space_from_space(isl_set_get_space(adg->context));
1747 for (int i = 0; i < pdg->params.size(); ++i) {
1748 adg_param *param = new adg_param;
1749 isl_aff *aff;
1750 isl_val *v;
1752 param->name = isl_id_alloc(ctx, pdg->params[i]->name->s.c_str(),
1753 NULL);
1754 if (pdg->params[i]->value) {
1755 isl_space *space = isl_space_params_alloc(ctx, 0);
1756 isl_local_space *ls = isl_local_space_from_space(space);
1757 param->val = isl_aff_zero_on_domain(ls);
1758 param->val = isl_aff_add_constant_si(param->val,
1759 pdg->params[i]->value->v);
1762 aff = isl_aff_zero_on_domain(isl_local_space_copy(ls));
1763 aff = isl_aff_add_coefficient_si(aff, isl_dim_param, i, 1);
1764 v = isl_set_min_val(adg->context, aff);
1765 if (isl_val_is_int(v)) {
1766 isl_space *space = isl_space_params_alloc(ctx, 0);
1767 isl_local_space *ls = isl_local_space_from_space(space);
1768 param->lb = isl_aff_zero_on_domain(ls);
1769 param->lb = isl_aff_add_constant_val(param->lb, v);
1770 } else
1771 isl_val_free(v);
1772 v = isl_set_max_val(adg->context, aff);
1773 if (isl_val_is_int(v)) {
1774 isl_space *space = isl_space_params_alloc(ctx, 0);
1775 isl_local_space *ls = isl_local_space_from_space(space);
1776 param->ub = isl_aff_zero_on_domain(ls);
1777 param->ub = isl_aff_add_constant_val(param->ub, v);
1778 } else
1779 isl_val_free(v);
1780 isl_aff_free(aff);
1782 adg->params.push_back(param);
1784 isl_local_space_free(ls);
1787 /* Comparison function for sorting input ports such that
1788 * first, for input ports that are connected to the same edge,
1789 * an input port corresponding to an earlier argument position comes
1790 * before an input port corresponding to a later argument position, and,
1791 * second, ports that have dynamic control variables in their domains
1792 * come after the ports that set those control variables.
1794 * The first is needed for pn_union channels as their detection allows for
1795 * the case where two (or more) tokens are consumed in the same iteration,
1796 * provided they don't cause reordering when consumed in the order of
1797 * the arguments.
1799 * The second is needed to ensure that the values of the dynamic
1800 * control variables are available when they are needed.
1801 * We enforce the second property by sorting the edges based
1802 * on their depth with respect to dependences between input ports.
1803 * It would probably make more sense to perform a topological sort,
1804 * but it seemed easier to implement it this way.
1806 * If two edges are equivalent with respect to the above two criteria,
1807 * then we arbitrarily sort them according to the edge sequence number.
1809 struct less_input_port :
1810 public std::binary_function<adg_port *, adg_port *, bool> {
1811 std::vector<adg_port *> ports;
1813 less_input_port(std::vector<adg_port *> &ports) : ports(ports) {}
1815 bool writes(adg_port *p, __isl_keep isl_id *id);
1816 int depth(adg_port *p);
1818 bool operator()(adg_port *x, adg_port *y) {
1819 int depth_x, depth_y;
1821 if (x->edge_nr == y->edge_nr)
1822 return x->arg_pos < y->arg_pos;
1824 depth_x = depth(x);
1825 depth_y = depth(y);
1826 if (depth_x != depth_y)
1827 return depth_x < depth_y;
1828 return x->edge_nr < y->edge_nr;
1832 /* Does "p" write to the variable "id"?
1834 bool less_input_port::writes(adg_port *p, __isl_keep isl_id *id)
1836 for (int i = 0; i < p->vars.size(); ++i) {
1837 adg_var *var = p->vars[i];
1838 isl_id *var_id;
1839 var_id = isl_pw_multi_aff_get_tuple_id(var->access,
1840 isl_dim_out);
1841 isl_id_free(var_id);
1842 if (var_id == id)
1843 return true;
1846 return false;
1849 /* Return 0 if this port does not involve any filters.
1850 * Otherwise, return 1 plus the maximal depth of any port
1851 * writing a filter used in the domain of the current port.
1852 * We obviously assume here that there are no cyclic dependences.
1854 int less_input_port::depth(adg_port *p)
1856 int max_depth = 0;
1858 for (int i = 0; i < p->domain->filters.size(); ++i) {
1859 adg_var *filter = p->domain->filters[i];
1860 isl_id *id;
1861 id = isl_pw_multi_aff_get_tuple_id(filter->access, isl_dim_out);
1862 for (int j = 0; j < ports.size(); ++j) {
1863 int d;
1864 if (writes(ports[j], id)) {
1865 d = depth(ports[j]) + 1;
1866 if (d > max_depth)
1867 max_depth = d;
1870 isl_id_free(id);
1873 return max_depth;
1876 /* Sort the input ports of the given node.
1878 static void sort_input_ports(adg_node *node)
1880 std::sort(node->input_ports.begin(), node->input_ports.end(),
1881 less_input_port(node->input_ports));
1884 /* Sort the input ports in all nodes of the graph.
1886 static void sort_input_ports(adg *graph)
1888 for (int i = 0; i < graph->nodes.size(); ++i)
1889 sort_input_ports(graph->nodes[i]);
1892 /* Rename the dynamic control "id" from __last_*_valid to
1894 * dc<nr>_<node>_b
1896 * and from __last_*_<i> to
1898 * dc<nr>_<node>_c<i>
1900 * "prefix_map" maps previously translated prefixes __last_*
1901 * (within the same node) to their dc<nr>_<node> translations.
1902 * If we can't find the current prefix in this cache, we create
1903 * a new name and add it to the cache.
1905 static __isl_give isl_id *rename_dynamic_control(__isl_take isl_id *id,
1906 const char *node_name,
1907 std::map<std::string, std::string> &prefix_map)
1909 const char *name, *underscore;
1910 string old;
1911 std::ostringstream strm;
1912 isl_ctx *ctx;
1914 if (!is_control(id))
1915 return id;
1917 name = isl_id_get_name(id);
1918 underscore = strrchr(name, '_');
1919 assert(underscore);
1921 old = string(name, underscore - name);
1922 if (prefix_map.find(old) == prefix_map.end()) {
1923 strm << "dc" << prefix_map.size() << "_" << node_name;
1924 prefix_map[old] = strm.str();
1927 strm.str("");
1928 strm << prefix_map[old] << "_";
1929 if (!strcmp(underscore + 1, "valid"))
1930 strm << "b";
1931 else
1932 strm << "c" << underscore + 1;
1934 ctx = isl_id_get_ctx(id);
1935 isl_id_free(id);
1937 return isl_id_alloc(ctx, strm.str().c_str(), NULL);
1940 static void rename_dynamic_controls(adg_expr *expr,
1941 const char *node_name,
1942 std::map<std::string, std::string> &prefix_map)
1944 isl_id *id;
1945 int n_in;
1947 n_in = isl_pw_aff_dim(expr->expr, isl_dim_in);
1948 for (int i = 0; i < n_in; ++i) {
1949 const char *name;
1950 name = isl_pw_aff_get_dim_name(expr->expr, isl_dim_in, i);
1951 if (!is_control(name))
1952 continue;
1953 id = isl_pw_aff_get_dim_id(expr->expr, isl_dim_in, i);
1954 id = rename_dynamic_control(id, node_name, prefix_map);
1955 expr->expr = isl_pw_aff_set_dim_id(expr->expr,
1956 isl_dim_in, i, id);
1959 if (!is_control(expr->name))
1960 return;
1961 expr->name = rename_dynamic_control(expr->name, node_name, prefix_map);
1964 static void rename_dynamic_controls(adg_var *var,
1965 const char *node_name,
1966 std::map<std::string, std::string> &prefix_map)
1968 isl_id *id;
1969 int nparam;
1971 nparam = isl_pw_multi_aff_dim(var->access, isl_dim_param);
1972 for (int i = 0; i < nparam; ++i) {
1973 const char *name;
1974 name = isl_pw_multi_aff_get_dim_name(var->access,
1975 isl_dim_param, i);
1976 if (!is_control(name))
1977 continue;
1978 id = isl_pw_multi_aff_get_dim_id(var->access,
1979 isl_dim_param, i);
1980 id = rename_dynamic_control(id, node_name, prefix_map);
1981 var->access = isl_pw_multi_aff_set_dim_id(var->access,
1982 isl_dim_param, i, id);
1985 if (!isl_pw_multi_aff_has_tuple_id(var->access, isl_dim_out))
1986 return;
1987 id = isl_pw_multi_aff_get_tuple_id(var->access, isl_dim_out);
1988 id = rename_dynamic_control(id, node_name, prefix_map);
1989 var->access = isl_pw_multi_aff_set_tuple_id(var->access,
1990 isl_dim_out, id);
1993 static void rename_dynamic_controls(adg_domain *domain,
1994 const char *node_name,
1995 std::map<std::string, std::string> &prefix_map)
1997 for (int i = 0; i < domain->controls.size(); ++i)
1998 rename_dynamic_controls(domain->controls[i], node_name,
1999 prefix_map);
2000 for (int i = 0; i < domain->filters.size(); ++i)
2001 rename_dynamic_controls(domain->filters[i], node_name,
2002 prefix_map);
2005 static void rename_dynamic_controls(adg_function *fn,
2006 const char *node_name,
2007 std::map<std::string, std::string> &prefix_map)
2009 for (int i = 0; i < fn->ctrl.size(); ++i)
2010 rename_dynamic_controls(fn->ctrl[i], node_name, prefix_map);
2013 static void rename_dynamic_controls(adg_port *port,
2014 const char *node_name,
2015 std::map<std::string, std::string> &prefix_map)
2017 for (int i = 0; i < port->vars.size(); ++i)
2018 rename_dynamic_controls(port->vars[i], node_name, prefix_map);
2019 rename_dynamic_controls(port->domain, node_name, prefix_map);
2022 static void rename_dynamic_controls(adg_gate *gate,
2023 const char *node_name,
2024 std::map<std::string, std::string> &prefix_map)
2026 rename_dynamic_controls(gate->in, node_name, prefix_map);
2027 rename_dynamic_controls(gate->out, node_name, prefix_map);
2028 rename_dynamic_controls(gate->domain, node_name, prefix_map);
2031 /* Rename all sets of dynamic controls in "node" from __last_*_valid to
2033 * dc<nr>_<node>_b
2035 * and from __last_*_<i> to
2037 * dc<nr>_<node>_c<i>
2039 * This ensures that each set of dynamic controls has a unique name.
2040 * In particular, if such a set of dynamic controls is transferred from
2041 * one node to another, then they will have different names in the two
2042 * nodes.
2044 static void rename_dynamic_controls(adg_node *node)
2046 std::map<std::string, std::string> prefix_map;
2047 const char *node_name = isl_id_get_name(node->name);
2049 rename_dynamic_controls(node->function, node_name, prefix_map);
2051 for (int i = 0; i < node->input_ports.size(); ++i)
2052 rename_dynamic_controls(node->input_ports[i], node_name,
2053 prefix_map);
2054 for (int i = 0; i < node->output_ports.size(); ++i)
2055 rename_dynamic_controls(node->output_ports[i], node_name,
2056 prefix_map);
2057 for (int i = 0; i < node->gates.size(); ++i)
2058 rename_dynamic_controls(node->gates[i], node_name, prefix_map);
2061 static void rename_dynamic_controls(adg *graph)
2063 for (int i = 0; i < graph->nodes.size(); ++i)
2064 rename_dynamic_controls(graph->nodes[i]);
2067 /* Convert the pn model "pdg" to an adg model.
2069 static adg *pdg2adg(PDG *pdg)
2071 isl_ctx *ctx;
2072 adg *graph;
2073 int g_nr = 0;
2075 ctx = pdg->get_isl_ctx();
2077 graph = new adg;
2079 graph->name = isl_id_alloc(ctx, pdg->name->s.c_str(), NULL);
2080 graph->context = pdg->context->get_isl_set();
2081 compute_iterator_map(graph, pdg);
2083 extract_params(graph, pdg);
2085 for (int i = 0; i < pdg->nodes.size(); ++i)
2086 add_node(ctx, graph, pdg->nodes[i]);
2087 for (int i = 0; i < pdg->dependences.size(); ++i) {
2088 pdg::dependence *dep = pdg->dependences[i];
2090 if (dep->type == pdg::dependence::uninitialized)
2091 continue;
2092 if (dep->type == pdg::dependence::pn_part)
2093 continue;
2094 if (dep->type == pdg::dependence::pn_hold)
2095 continue;
2096 if (dep->type == pdg::dependence::pn_wire)
2097 add_wire_gate(pdg, graph, dep, &g_nr);
2098 else
2099 add_union_edge(pdg, graph, dep, i);
2102 sort_input_ports(graph);
2103 rename_dynamic_controls(graph);
2105 return graph;
2108 static void set_output_name(isl_ctx *ctx, struct options *options)
2110 int len;
2111 const char *suffix = options->xml ? "_adg.xml" : "_adg.yaml";
2113 if (!options->input || !strcmp(options->input, "-"))
2114 return;
2115 if (options->output)
2116 return;
2118 len = strlen(options->input);
2119 if (len > 5 && !strcmp(options->input + len - 5, ".yaml"))
2120 len -= 5;
2121 options->output = isl_alloc_array(ctx, char, len + strlen(suffix) + 1);
2122 assert(options->output);
2123 strncpy(options->output, options->input, len);
2124 strcpy(options->output + len, suffix);
2127 /* Convert the input pn model to an adg model and print out the adg
2128 * model in either YAML format or XML format.
2130 int main(int argc, char *argv[])
2132 isl_ctx *ctx;
2133 struct options *options = options_new_with_defaults();
2134 PDG *pdg;
2135 adg *adg;
2136 FILE *in = stdin, *out = stdout;
2138 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
2139 ctx = isl_ctx_alloc_with_options(&options_args, options);
2141 set_output_name(ctx, options);
2143 if (options->input && strcmp(options->input, "-")) {
2144 in = fopen(options->input, "r");
2145 assert(in);
2147 if (options->output && strcmp(options->output, "-")) {
2148 out = fopen(options->output, "w");
2149 assert(out);
2152 pdg = PDG::Load(in, ctx);
2154 adg = pdg2adg(pdg);
2155 if (options->xml)
2156 adg_print_xml(adg, out);
2157 else
2158 adg->print(out);
2159 delete adg;
2161 pdg->free();
2162 delete pdg;
2163 isl_ctx_free(ctx);
2165 return 0;