da: only add parameters up to deepest filter
[ppn.git] / pn2adg.cc
blob3e6ad9f0885618fe7e4e7445e7a192abd3d01078
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 if (!strcmp(coa->call->name->s.c_str(), "&"))
285 type = adg_arg_reference;
286 add_arguments(fn, coa->call, space, type);
287 continue;
290 assert(coa->type == pdg::call_or_access::t_access);
291 if (coa->access->type == pdg::access::write)
292 add_argument(fn->out,
293 new_var(coa->access, isl_space_copy(space)), type);
294 else
295 add_argument(fn->in,
296 new_var(coa->access, isl_space_copy(space)), type);
300 /* Add the filters of "node" to "filters", having them refer to "space"
301 * as their iteration domain.
303 static void add_domain_filters(std::vector<adg_var *> &filters, pdg::node *node,
304 __isl_keep isl_space *space)
306 for (int i = 0; i < node->filters.size(); ++i) {
307 pdg::call_or_access *coa = node->filters[i];
308 assert(coa->type == pdg::call_or_access::t_access);
309 filters.push_back(new_var(coa->access, isl_space_copy(space)));
313 /* Set node->function based on "p_node" and the node domain "domain".
314 * In particular, create an adg_function with the given domain
315 * and add input and output arguments.
316 * If any of the input arguments is an affine expression (as opposed to
317 * an array access), then it is added to node->expressions.
319 * If p_node has any filters, then they are converted to filters on node.
321 static void set_function(adg *adg, adg_node *node, pdg::node *p_node,
322 __isl_take isl_set *domain)
324 isl_ctx *ctx = isl_set_get_ctx(domain);
325 const char *name;
326 pdg::statement *stat = p_node->statement;
327 adg_function *fn = new adg_function;
328 isl_space *space;
329 node->function = fn;
331 space = isl_set_get_space(domain);
332 fn->domain = new adg_domain(domain);
334 add_domain_filters(fn->domain->filters, p_node, space);
336 for (int i = 0; i < stat->top_outputs.size(); ++i)
337 add_argument(fn->out,
338 new_var(stat->top_outputs[i], isl_space_copy(space)),
339 adg_arg_return);
341 if (!stat->top_function)
342 return;
344 name = stat->top_function->name->s.c_str();
345 fn->name = isl_id_alloc(ctx, name, NULL);
347 add_expressions(adg, node, p_node, stat->top_function);
348 add_arguments(fn, stat->top_function, space, adg_arg_value);
350 isl_space_free(space);
353 /* Return the domain of "p_node" after scheduling.
354 * "id" represents the name of the corresponding adg_node.
356 * If "p_node" has any filters, then its source iteration domain
357 * is a wrapped map with the filters in the range. The computed
358 * node schedule therefore needs to take this filters into account
359 * and so we pass along the domain space to extended_node_schedule().
360 * In particular, we want the filter values to be preserved.
362 static __isl_give isl_set *scheduled_domain(pdg::node *p_node, adg *adg,
363 __isl_take isl_id *id)
365 isl_set *domain;
366 isl_map *sched;
368 domain = p_node->source->get_isl_set();
369 sched = extended_node_schedule(p_node, adg, id,
370 isl_set_get_space(domain));
371 domain = isl_set_apply(domain, sched);
373 return domain;
376 /* Construct a schedule for the adg_node (called "id") corresponding to
377 * "p_node". Note that the domain of the adg_node is the result of
378 * applying the p_node schedule to its domain, with some of the
379 * dimensions with constant values removed (through composition with
380 * adg->iterator_map). The schedule for the adg_node then simply
381 * needs to insert those dimensions with constant values back.
383 * If "p_node" has any filters, then its source iteration domain
384 * is a wrapped map with the filters in the range. These filters
385 * need to be removed first.
387 static __isl_give isl_map *adg_node_schedule(pdg::node *p_node, adg *adg,
388 __isl_take isl_id *id)
390 isl_set *domain;
391 isl_map *sched;
393 domain = p_node->source->get_isl_set();
394 if (isl_set_is_wrapping(domain))
395 domain = isl_map_domain(isl_set_unwrap(domain));
396 sched = p_node->schedule->get_isl_map();
397 domain = isl_set_apply(domain, sched);
398 sched = isl_map_reverse(isl_map_copy(adg->iterator_map));
399 sched = isl_map_intersect_range(sched, domain);
400 sched = isl_map_set_tuple_id(sched, isl_dim_in, id);
402 return sched;
405 /* Construct the name of the adg_node corresponding to "p_node".
407 static __isl_give isl_id *node_id(isl_ctx *ctx, pdg::node *p_node)
409 char buf[10];
411 snprintf(buf, sizeof(buf), "ND_%d", p_node->nr);
413 return isl_id_alloc(ctx, buf, NULL);
416 extern "C" {
417 static int intersect_local_space(__isl_take isl_basic_set *bset,
418 void *user);
421 /* Intersect the local space *user with the local space of "bset".
423 static int intersect_local_space(__isl_take isl_basic_set *bset,
424 void *user)
426 isl_local_space *ls;
427 isl_local_space **res = (isl_local_space **)user;
429 ls = isl_basic_set_get_local_space(bset);
430 isl_basic_set_free(bset);
432 *res = isl_local_space_intersect(*res, ls);
434 return 0;
437 /* Find the position of the first filter in "space".
439 * The given space may be the result of one of more nestings of the form
441 * D -> [D -> local[..]]
443 * and/or a nesting of the form
445 * D -> [D -> [dynamic controls]]
447 * and so we may have to dig down until we find a wrapped space
448 * with an unnamed range. If we find it, the position of the filters
449 * in the original space is equal to the number of input dimensions
450 * of that wrapped space. Otherwise, there are no filters and we
451 * return 0.
453 static int filter_pos(__isl_take isl_space *space)
455 int pos;
457 if (!isl_space_is_wrapping(space)) {
458 isl_space_free(space);
459 return 0;
461 space = isl_space_unwrap(space);
462 if (isl_space_has_tuple_id(space, isl_dim_out))
463 return filter_pos(isl_space_domain(space));
464 pos = isl_space_dim(space, isl_dim_in);
465 isl_space_free(space);
466 return pos;
469 /* Set the names of the input dimensions of "aff" according
470 * to "filters", "iterators" and "controls".
472 * The first dimensions correspond to the iterators.
473 * The are followed by the iterators, with the filters intermixed.
475 static __isl_give isl_aff *set_names(__isl_take isl_aff *aff,
476 std::vector<adg_var *> &filters, std::vector<isl_id *> &iterators,
477 std::vector<adg_expr *> &controls)
479 int pos = 0;
480 int n_in = isl_aff_dim(aff, isl_dim_in);
481 int fp;
482 int nf;
483 int ci;
485 fp = filter_pos(isl_aff_get_domain_space(aff));
486 nf = filters.size();
488 for (int i = 0; i < iterators.size(); ++i, ++pos) {
489 isl_id *id = isl_id_copy(iterators[i]);
490 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
493 for (ci = 0; ci < controls.size() && pos < fp; ++ci, ++pos) {
494 isl_id *id = isl_id_copy(controls[ci]->name);
495 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
498 for (int i = 0; i < nf; ++i, ++pos) {
499 isl_id *id;
500 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
501 isl_dim_out);
502 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
505 for (; ci < controls.size() && pos < n_in; ++ci, ++pos) {
506 isl_id *id = isl_id_copy(controls[ci]->name);
507 aff = isl_aff_set_dim_id(aff, isl_dim_in, pos, id);
510 return aff;
513 /* Set the names of the input dimensions of "pa" according
514 * to "filters", "iterators" and "controls".
516 * The first dimensions correspond to the iterators.
517 * The are followed by the iterators, with the filters intermixed.
519 static __isl_give isl_pw_aff *set_names(__isl_take isl_pw_aff *pa,
520 std::vector<adg_var *> &filters, std::vector<isl_id *> &iterators,
521 std::vector<adg_expr *> &controls)
523 int pos = 0;
524 int n_in = isl_pw_aff_dim(pa, isl_dim_in);
525 int fp;
526 int nf;
527 int ci;
529 fp = filter_pos(isl_pw_aff_get_domain_space(pa));
530 nf = filters.size();
532 for (int i = 0; i < iterators.size(); ++i, ++pos) {
533 isl_id *id = isl_id_copy(iterators[i]);
534 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
537 for (ci = 0; ci < controls.size() && pos < fp; ++ci, ++pos) {
538 isl_id *id = isl_id_copy(controls[ci]->name);
539 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
542 for (int i = 0; i < nf; ++i, ++pos) {
543 isl_id *id;
544 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
545 isl_dim_out);
546 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
549 for (; ci < controls.size() && pos < n_in; ++ci, ++pos) {
550 isl_id *id = isl_id_copy(controls[ci]->name);
551 pa = isl_pw_aff_set_dim_id(pa, isl_dim_in, pos, id);
554 return pa;
557 /* For each of the local variables in "ls", create a corresponding
558 * adg_expr and add it to "controls".
560 * "filters" contains the filters on the corresponding domain.
561 * These filters appear as the first dimensions in "ls" and "filters" is
562 * used to set the names of those dimensions.
564 static void add_controls(adg *graph, std::vector<adg_var *> &filters,
565 std::vector<adg_expr *> &controls,
566 __isl_keep isl_local_space *ls)
568 isl_ctx *ctx;
569 int n_div;
570 int n;
572 ctx = isl_local_space_get_ctx(ls);
573 n_div = isl_local_space_dim(ls, isl_dim_div);
574 n = controls.size();
575 for (int i = 0; i < n_div; ++i) {
576 isl_aff *aff;
577 adg_expr *expr;
579 aff = isl_aff_floor(isl_local_space_get_div(ls, i));
580 aff = set_names(aff, filters, graph->iterators, controls);
581 expr = graph->to_control(aff);
582 controls.push_back(expr);
586 /* Apply the lifting "lifting" to domain->bounds and eliminate
587 * redundant existentially quantified variables.
589 static void apply_lifting(adg_domain *domain,
590 __isl_take isl_basic_map *lifting)
592 domain->bounds = isl_set_apply(domain->bounds,
593 isl_map_from_basic_map(lifting));
594 domain->bounds = isl_set_detect_equalities(domain->bounds);
597 /* Add static controls to "domain" based on the existentially quantified
598 * variables in domain->bounds and set *lifting_p to the lifting that
599 * corresponds to the added static controls. That is, the control variables
600 * correspond to the output variable in the nested map in the range
601 * of the lifting.
603 * We first collect all existentially quantified variables in a single
604 * local space. If needed, we then add the corresponding static controls,
605 * construct the corresponding lifting and finally apply it to domain->bounds.
607 static void add_static_controls(adg *graph, adg_domain *domain,
608 __isl_give isl_basic_map **lifting_p)
610 isl_space *space;
611 isl_local_space *ls;
612 int n_div;
613 isl_basic_map *lifting;
615 space = isl_set_get_space(domain->bounds);
616 ls = isl_local_space_from_space(space);
617 isl_set_foreach_basic_set(domain->bounds, &intersect_local_space, &ls);
619 n_div = isl_local_space_dim(ls, isl_dim_div);
620 if (n_div == 0) {
621 isl_local_space_free(ls);
622 return;
625 add_controls(graph, domain->filters, domain->controls, ls);
627 lifting = isl_local_space_lifting(ls);
628 if (lifting_p)
629 *lifting_p = isl_basic_map_copy(lifting);
631 apply_lifting(domain, lifting);
634 /* Add copies of the elements in "src" to the back of "dst".
636 static void copy_controls(std::vector<adg_expr *> &dst,
637 std::vector<adg_expr *> &src)
639 for (int i = 0; i < src.size(); ++i)
640 dst.push_back(new adg_expr(*src[i]));
643 /* Add static controls to the adg_node "node".
644 * In particular, add static controls to node->domain and if any
645 * were added, copy them to node->function->domain and apply the corresponding
646 * lifting.
648 static void add_static_controls(adg *graph, adg_node *node)
650 add_static_controls(graph, node->domain, &node->lifting);
652 if (!node->lifting)
653 return;
655 copy_controls(node->function->domain->controls, node->domain->controls);
656 apply_lifting(node->function->domain,
657 isl_basic_map_copy(node->lifting));
660 /* Add an adg_node corresponding to "p_node" to the graph.
661 * We only construct the domain, the schedule, the expressions
662 * and the function.
663 * The ports and gates are created during the construction of the edges.
665 * The domain of the function may be subjected to filtering,
666 * but the domain of the node itself may not as it should include
667 * the domains of the ports reading the values of the filters.
668 * The filters are therefore projected out from the node domain.
670 static void add_node(isl_ctx *ctx, adg *graph, pdg::node *p_node)
672 adg_node *node = new adg_node;
673 isl_set *domain;
675 node->name = node_id(ctx, p_node);
677 domain = scheduled_domain(p_node, graph, isl_id_copy(node->name));
678 node->schedule = adg_node_schedule(p_node, graph,
679 isl_id_copy(node->name));
680 set_function(graph, node, p_node, isl_set_copy(domain));
681 if (isl_set_is_wrapping(domain))
682 domain = isl_map_domain(isl_set_unwrap(domain));
683 domain = isl_set_from_basic_set(isl_set_simple_hull(domain));
684 node->domain = new adg_domain(domain);
686 add_static_controls(graph, node);
688 graph->nodes.push_back(node);
691 /* Is "name" of the form __last_*?
693 static bool is_control(const char *name)
695 const char *prefix = "__last_";
696 size_t prefix_len = strlen(prefix);
698 return !strncmp(name, prefix, prefix_len);
701 /* Is "name" of the form __last_<node_name>*?
703 static bool is_local_control(const char *name, const char *node_name)
705 const char *prefix = "__last_";
706 size_t prefix_len = strlen(prefix);
708 if (strncmp(name, prefix, prefix_len) != 0)
709 return 0;
710 return !strncmp(name + prefix_len, node_name, strlen(node_name));
713 /* Is the name of "id" of the form __last_*?
715 static bool is_control(__isl_keep isl_id *id)
717 return is_control(isl_id_get_name(id));
720 /* Is the name of the i-th parameter of map of the form __last_*?
722 static bool is_control(__isl_keep isl_map *map, int i)
724 const char *name;
725 if (!isl_map_has_dim_id(map, isl_dim_param, i))
726 return false;
727 name = isl_map_get_dim_name(map, isl_dim_param, i);
728 return is_control(name);
731 /* Move all parameters of the form __last_* into the range of a wrapped
732 * map with "domain" as domain.
734 * We first create dimensions in the range of the wrapped map, equating
735 * them to the corresponding parameters.
736 * At the end, we project out the parameters.
738 static void move_filters(adg_domain *domain)
740 isl_space *space;
741 isl_set *set;
742 isl_map *map;
743 int nparam;
745 set = domain->bounds;
746 space = isl_set_get_space(set);
747 map = isl_map_from_domain(set);
749 nparam = isl_map_dim(map, isl_dim_param);
750 for (int i = 0; i < nparam; ++i) {
751 int pos;
752 isl_id *id;
753 if (!is_control(map, i))
754 continue;
755 pos = isl_map_dim(map, isl_dim_out);
756 map = isl_map_add_dims(map, isl_dim_out, 1);
757 map = isl_map_equate(map, isl_dim_param, i, isl_dim_out, pos);
758 id = isl_map_get_dim_id(map, isl_dim_param, i);
759 domain->filters.push_back(var_from_id(id,
760 isl_space_copy(space)));
763 for (int i = nparam - 1; i >= 0; --i) {
764 if (!is_control(map, i))
765 continue;
766 map = isl_map_project_out(map, isl_dim_param, i, 1);
769 domain->bounds = isl_map_wrap(map);
770 isl_space_free(space);
773 /* Auxiliary data used during the construction of edges.
775 * dep is the dependence that is being converted into one or more edges.
776 * container is the dependence that contains information about the buffer
777 * size. It may be dep itself or it may be the pn_union dependence
778 * containing dep.
779 * id is the name of the edge.
780 * from_node is the source node.
781 * to_node is the target node.
782 * rel is the original dependence relation.
783 * rel_maff is that part of rel that corresponds to maff.
784 * range_filters_map is a map that adds back the range filters.
785 * maff is the mapping that will be assigned to edge->map.
786 * in_domain is the domain of the input port.
788 * in_filters are filters that should be added to input ports.
789 * out_filters are filters that should be added to output ports.
791 * edge_nr is the sequence number of the original (non-control) edge.
792 * port_nr is a pointer to the sequence number of the port
794 * lifting is the lifting used on the input port domain
795 * in_controls are the controls for the input port domain. They include
796 * the controls of the node domain and the extra controls induced by
797 * lifting.
799 struct add_edge_data {
800 adg *graph;
801 pdg::dependence *dep;
802 pdg::dependence *container;
803 isl_id *id;
804 adg_node *from_node;
805 adg_node *to_node;
806 isl_map *rel;
807 isl_map *range_filters_map;
808 isl_map *rel_maff;
809 isl_multi_aff *maff;
810 isl_basic_set *in_domain;
811 enum adg_edge_type type;
813 std::vector<adg_var *> in_filters;
814 std::vector<adg_var *> out_filters;
816 int edge_nr;
817 int *port_nr;
819 isl_basic_map *lifting;
820 std::vector<adg_expr *> in_controls;
823 enum adg_port_type {
824 adg_port_input,
825 adg_port_output
828 /* Set the name of "port" to
830 * <node>IP_<edge>_<nr>_V_<arg_pos>
831 * or
832 * <node>OP_<edge>_<nr>_V_<arg_pos>
834 * depending on whether type is adg_port_input or adg_port_output.
835 * The name includes the access->nr so that two edges between the same
836 * pair of nodes that have been combined into a single pn_union edge
837 * would not get identical port names.
839 static void set_port_name(adg_port *port, enum adg_port_type type,
840 __isl_keep isl_id *node_id, __isl_keep isl_id *edge_id, int nr)
842 char buf[100];
843 isl_ctx *ctx;
844 const char *s_type;
846 ctx = isl_id_get_ctx(edge_id);
848 if (type == adg_port_input)
849 s_type = "IP";
850 else
851 s_type = "OP";
852 snprintf(buf, sizeof(buf), "%s%s_%s_%d_V_%d",
853 isl_id_get_name(node_id), s_type,
854 isl_id_get_name(edge_id), nr, port->arg_pos);
855 port->name = isl_id_alloc(ctx, buf, NULL);
858 /* Set the binding variable on "port" by calling new_var().
860 static void set_port_var(adg_port *port, pdg::access *access,
861 __isl_keep isl_basic_set *bset)
863 adg_var *var;
864 isl_space *space;
866 space = isl_basic_set_get_space(bset);
867 var = new_var(access, space);
868 port->vars.push_back(var);
871 /* Set the binding variables on "port", one for each element
872 * in "dep_controls". These binding variables are control variables
873 * and they are assumed to of tyoe "int".
875 static void set_port_vars(adg_port *port, seq<str> &dep_controls,
876 __isl_keep isl_basic_set *bset)
878 isl_ctx *ctx;
879 isl_space *space;
881 ctx = isl_basic_set_get_ctx(bset);
882 space = isl_basic_set_get_space(bset);
883 for (int i = 0; i < dep_controls.size(); ++i) {
884 isl_id *id;
885 adg_var *var;
887 id = isl_id_alloc(ctx, dep_controls[i]->s.c_str(), NULL);
888 var = var_from_id(id, isl_space_copy(space));
889 var->type = isl_id_alloc(ctx, "int", NULL);
890 port->vars.push_back(var);
892 isl_space_free(space);
895 /* Create and return a port of type "type" belonging to the node called
896 * "node_id" and attached to the edge called "edge_id".
897 * "access" is the corresponding access.
898 * "nr" is the sequence number.
899 * "dep_controls" contains the names of the control variables.
900 * If the list contains any elements, then the port is connected
901 * to a control edge. Otherwise, it is connected to a data edge.
902 * "bset" represents the iteration domain and "controls" are the controls
903 * that need to be added based on previous liftings.
904 * If "bset" has any existentially quantified variables left, then we
905 * need to apply an additional lifting and append the corresponding
906 * control variables.
907 * "filters" contains the dynamic controls.
909 static adg_port *create_port(adg *graph, enum adg_port_type type,
910 seq<str> &dep_controls, pdg::access *access,
911 __isl_keep isl_id *node_id, __isl_keep isl_id *edge_id, int edge_nr,
912 int nr, __isl_take isl_basic_set *bset,
913 std::vector<adg_var *> &filters, std::vector<adg_expr *> &controls)
915 isl_local_space *ls = NULL;
916 adg_port *port = new adg_port;
918 port->edge_nr = edge_nr;
920 if (isl_basic_set_dim(bset, isl_dim_div) != 0) {
921 isl_basic_map *lifting;
922 ls = isl_basic_set_get_local_space(bset);
923 lifting = isl_local_space_lifting(isl_local_space_copy(ls));
924 bset = isl_basic_set_apply(bset, lifting);
925 bset = isl_basic_set_detect_equalities(bset);
928 port->arg_pos = access->nr;
929 set_port_name(port, type, node_id, edge_id, nr);
931 port->node_name = isl_id_copy(node_id);
932 port->edge_name = isl_id_copy(edge_id);
933 if (dep_controls.size() > 0)
934 set_port_vars(port, dep_controls, bset);
935 else
936 set_port_var(port, access, bset);
937 port->domain = new adg_domain(isl_set_from_basic_set(bset));
939 for (int i = 0; i < filters.size(); ++i)
940 port->domain->filters.push_back(new adg_var(*filters[i]));
941 for (int i = 0; i < controls.size(); ++i)
942 port->domain->controls.push_back(new adg_expr(*controls[i]));
944 if (ls) {
945 add_controls(graph, filters, port->domain->controls, ls);
946 isl_local_space_free(ls);
949 return port;
952 /* Add an edge with a convex input port domain (data->in_domain)
953 * and a convex output port domain (bset).
955 * We create the two ports, add them to the appropriate nodes
956 * and add an edge that referes to the nodes and ports.
958 static int add_edge_basic_in(__isl_take isl_basic_set *bset, void *user)
960 add_edge_data *data = (add_edge_data *) user;
961 adg_edge *edge = new adg_edge;
962 adg_port *port;
964 edge->name = isl_id_copy(data->id);
965 edge->type = data->type;
966 edge->map = isl_multi_aff_copy(data->maff);
967 edge->from_node_name = isl_id_copy(data->from_node->name);
968 edge->to_node_name = isl_id_copy(data->to_node->name);
969 if (data->container->value_size)
970 isl_int_set_si(edge->value_size,
971 data->container->value_size->v);
973 port = create_port(data->graph, adg_port_input,
974 data->dep->to_controls, data->dep->to_access,
975 data->to_node->name, data->id, data->edge_nr,
976 *data->port_nr, isl_basic_set_copy(data->in_domain),
977 data->in_filters, data->in_controls);
978 data->to_node->input_ports.push_back(port);
979 edge->to_port_name = isl_id_copy(port->name);
981 port = create_port(data->graph, adg_port_output,
982 data->dep->from_controls, data->dep->from_access,
983 data->from_node->name, data->id, data->edge_nr,
984 *data->port_nr, isl_basic_set_copy(bset),
985 data->out_filters, data->from_node->domain->controls);
986 data->from_node->output_ports.push_back(port);
987 edge->from_port_name = isl_id_copy(port->name);
989 (*data->port_nr)++;
991 data->graph->edges.push_back(edge);
993 isl_basic_set_free(bset);
994 return 0;
997 /* Does the list of (dynamic) control variables node->function->ctrl
998 * include "id"?
1000 static bool has_control(adg_node *node, __isl_keep isl_id *id)
1002 for (int i = 0; i < node->function->ctrl.size(); ++i)
1003 if (node->function->ctrl[i]->name == id)
1004 return true;
1005 return false;
1008 /* Add elements to node->function->ctrl for each element of dep->from_controls,
1009 * provided the element is not in there already.
1011 * We only add elements that correspond to last iterations of "node".
1012 * The given dependence may be the result of reuse detection and may
1013 * therefore propagate controls from one access to the next in some other
1014 * node.
1016 * To find out which value should be assigned to the variable,
1017 * we look at the final part of the name of the control variable.
1018 * If this final part is "valid", then the function needs
1019 * to assign the value "1". Otherwise, the final part is
1020 * an integer and then we should assign the value of the
1021 * original corresponding iterator. This value is obtained
1022 * from the schedule of "node".
1024 static void add_control_variables(isl_ctx *ctx, adg *graph, adg_node *node,
1025 pdg::dependence *dep)
1027 isl_map *sched;
1028 isl_pw_multi_aff *pma;
1029 isl_space *space;
1030 isl_local_space *ls;
1031 char *node_name;
1033 if (dep->from_controls.size() == 0)
1034 return;
1036 sched = node_schedule(dep->from, graph, isl_id_copy(node->name));
1037 node_name = strdup(isl_map_get_tuple_name(sched, isl_dim_in));
1038 pma = isl_pw_multi_aff_from_map(isl_map_reverse(sched));
1039 space = isl_set_get_space(node->domain->bounds);
1040 ls = isl_local_space_from_space(space);
1042 for (int i = 0; i < dep->from_controls.size(); ++i) {
1043 adg_expr *expr;
1044 isl_aff *aff;
1045 isl_pw_aff *pa;
1046 int pos;
1047 isl_id *id;
1048 const char *name = dep->from_controls[i]->s.c_str();
1050 id = isl_id_alloc(ctx, name, NULL);
1052 if (!is_local_control(name, node_name) ||
1053 has_control(node, id)) {
1054 isl_id_free(id);
1055 continue;
1058 name = strrchr(name, '_');
1059 assert(name);
1061 expr = new adg_expr;
1062 if (!strcmp(name + 1, "valid")) {
1063 aff = isl_aff_zero_on_domain(isl_local_space_copy(ls));
1064 aff = isl_aff_add_constant_si(aff, 1);
1065 pa = isl_pw_aff_from_aff(aff);
1066 } else {
1067 pos = atoi(name + 1);
1068 pa = isl_pw_multi_aff_get_pw_aff(pma, pos);
1071 pa = set_names(pa, node->domain->filters, graph->iterators,
1072 node->domain->controls);
1074 expr->name = id;
1075 expr->expr = pa;
1076 node->function->ctrl.push_back(expr);
1079 free(node_name);
1080 isl_local_space_free(ls);
1081 isl_pw_multi_aff_free(pma);
1084 /* Add an edge with a convex input port domain (bset).
1085 * We compute the corresponding output port domain by applying
1086 * the dependence relation and apply the required liftings in the from
1087 * domain. The result may in principle be a union again and so we
1088 * split that up as well.
1089 * The lifting required by the input port (computed in add_edge_set)
1090 * is applied to the input port domain. Note that we cannot
1091 * apply this lifting earlier because we need the original input port
1092 * domain (without lifting) to compute the output port domain.
1094 static int add_edge_basic(__isl_take isl_basic_set *bset, void *user)
1096 int r;
1097 add_edge_data *data = (add_edge_data *) user;
1098 isl_set *set;
1100 data->in_domain = isl_basic_set_copy(bset);
1101 data->in_domain = isl_basic_set_apply(data->in_domain,
1102 isl_basic_map_copy(data->lifting));
1103 data->in_domain = isl_basic_set_detect_equalities(data->in_domain);
1104 set = isl_set_from_basic_set(bset);
1105 set = isl_set_apply(set, isl_map_copy(data->rel_maff));
1106 if (data->from_node->lifting)
1107 set = isl_set_apply(set, isl_map_from_basic_map(
1108 isl_basic_map_copy(data->from_node->lifting)));
1109 set = isl_set_detect_equalities(set);
1110 set = isl_set_compute_divs(set);
1112 r = isl_set_foreach_basic_set(set, &add_edge_basic_in, data);
1114 isl_set_free(set);
1115 isl_basic_set_free(data->in_domain);
1116 return r;
1119 /* Save a copy of "map" in data->rel, remove the range filters (if any)
1120 * and return the result. The inverse of the mapping used to remove
1121 * the filters is save in data->range_filters_map so that the filters
1122 * can be reintroduced in insert_range_filters.
1124 static __isl_give isl_map *remove_range_filters(__isl_take isl_map *map,
1125 add_edge_data *data)
1127 isl_space *space;
1129 data->rel = isl_map_copy(map);
1131 space = isl_space_range(isl_map_get_space(map));
1132 if (isl_space_is_wrapping(space)) {
1133 isl_map *fm = isl_map_universe(isl_space_unwrap(space));
1134 fm = isl_map_domain_map(fm);
1135 map = isl_map_apply_range(map, isl_map_copy(fm));
1136 data->range_filters_map = isl_map_reverse(fm);
1137 } else {
1138 space = isl_space_map_from_set(space);
1139 data->range_filters_map = isl_map_identity(space);
1142 return map;
1145 /* Reintroduce the filters removed in remove_range_filters.
1147 static __isl_give isl_map *insert_range_filters(__isl_take isl_map *map,
1148 add_edge_data *data)
1150 map = isl_map_apply_range(map, isl_map_copy(data->range_filters_map));
1151 map = isl_map_intersect(map, isl_map_copy(data->rel));
1153 return map;
1156 /* Add edges for the given input port domain (set) and the given
1157 * mapping (maff) to the corresponding output port domain.
1159 * The lifting corresponding to the domain of the destination node
1160 * has already been applied, but "maff" may require additional liftings
1161 * to be applied to the input port domain.
1163 * The given isl_multi_aff is also converted into an isl_basic_map
1164 * for use in the construction of the output port domains
1165 * in add_edge_basic(). After the conversion, we add back the filters
1166 * that were removed right before the call to isl_pw_multi_aff_from_map.
1168 static int add_edge_set(__isl_take isl_set *set, __isl_take isl_multi_aff *maff,
1169 void *user)
1171 add_edge_data *data = (add_edge_data *) user;
1172 int r;
1173 isl_local_space *ls;
1175 data->rel_maff = isl_map_from_basic_map(isl_basic_map_from_multi_aff(
1176 isl_multi_aff_copy(maff)));
1177 data->rel_maff = insert_range_filters(data->rel_maff, data);
1179 maff = isl_multi_aff_lift(maff, &ls);
1180 copy_controls(data->in_controls, data->to_node->domain->controls);
1181 add_controls(data->graph, data->in_filters, data->in_controls, ls);
1182 data->lifting = isl_local_space_lifting(ls);
1184 data->maff = maff;
1186 r = isl_set_foreach_basic_set(set, &add_edge_basic, data);
1188 for (int i = 0; i < data->in_controls.size(); ++i)
1189 delete data->in_controls[i];
1190 data->in_controls.clear();
1191 isl_basic_map_free(data->lifting);
1192 isl_set_free(set);
1193 isl_map_free(data->rel_maff);
1194 isl_multi_aff_free(maff);
1196 return r;
1199 /* Does "rel" have any parameter of the form __last_*?
1201 static bool has_filters(__isl_keep isl_map *rel)
1203 int nparam;
1205 nparam = isl_map_dim(rel, isl_dim_param);
1206 for (int i = 0; i < nparam; ++i)
1207 if (is_control(rel, i))
1208 return true;
1210 return false;
1213 /* Remove all parameters of the form __last_* from "rel".
1215 static __isl_give isl_map *remove_all_filters(__isl_take isl_map *rel)
1217 int nparam;
1219 nparam = isl_map_dim(rel, isl_dim_param);
1220 for (int i = nparam - 1; i >= 0; --i) {
1221 if (!is_control(rel, i))
1222 continue;
1223 rel = isl_map_project_out(rel, isl_dim_param, i, 1);
1226 return rel;
1229 /* For each parameter of the form __last_*, add it to both
1230 * data->in_filters and data->out_filters, in each case defined
1231 * over the appropriate domain.
1233 static void extract_filters(__isl_take isl_map *rel, add_edge_data *data)
1235 isl_space *domain;
1236 isl_space *range;
1237 isl_space *space;
1238 int nparam;
1240 nparam = isl_map_dim(rel, isl_dim_param);
1241 space = isl_map_get_space(rel);
1242 domain = isl_space_domain(isl_space_copy(space));
1243 range = isl_space_range(space);
1245 for (int i = 0; i < nparam; ++i) {
1246 isl_id *id;
1248 if (!is_control(rel, i))
1249 continue;
1251 id = isl_map_get_dim_id(rel, isl_dim_param, i);
1252 data->in_filters.push_back(var_from_id(isl_id_copy(id),
1253 isl_space_copy(domain)));
1254 data->out_filters.push_back(var_from_id(id,
1255 isl_space_copy(range)));
1258 isl_space_free(domain);
1259 isl_space_free(range);
1262 /* Replace both domain and range of "rel" by a wrapped map with the original
1263 * set as domain and dimensions corresponding to the parameters
1264 * referenced by the elements in "filters" as range and then
1265 * remove those parameters.
1266 * The overall effect is that those parameters are moved into
1267 * the ranges of the wrapped maps.
1269 static __isl_give isl_map *move_filters(__isl_take isl_map *rel,
1270 std::vector<adg_var *> &filters)
1272 isl_space *space;
1273 isl_map *map;
1275 space = isl_space_domain(isl_map_get_space(rel));
1276 space = isl_space_from_domain(space);
1277 map = isl_map_universe(space);
1278 for (int i = 0; i < filters.size(); ++i) {
1279 int param;
1280 int pos;
1281 isl_id *id;
1283 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
1284 isl_dim_out);
1285 param = isl_map_find_dim_by_id(map, isl_dim_param, id);
1286 isl_id_free(id);
1288 pos = isl_map_dim(map, isl_dim_out);
1289 map = isl_map_add_dims(map, isl_dim_out, 1);
1290 map = isl_map_equate(map, isl_dim_param, param,
1291 isl_dim_out, pos);
1293 map = isl_map_domain_map(map);
1295 rel = isl_map_apply_range(map, rel);
1297 for (int i = 0; i < filters.size(); ++i) {
1298 int param;
1299 isl_id *id;
1301 id = isl_pw_multi_aff_get_tuple_id(filters[i]->access,
1302 isl_dim_out);
1303 param = isl_map_find_dim_by_id(rel, isl_dim_param, id);
1304 isl_id_free(id);
1306 rel = isl_map_project_out(rel, isl_dim_param, param, 1);
1309 space = isl_space_unwrap(isl_space_domain(isl_map_get_space(rel)));
1310 map = isl_map_range_map(isl_map_universe(space));
1311 rel = isl_map_range_product(rel, map);
1313 return rel;
1316 /* Determine the appropriate adg_edge_type for "dep".
1318 static enum adg_edge_type dep_type(pdg::dependence *dep)
1320 if (dep->reordering)
1321 return adg_edge_generic;
1322 else if (dep->multiplicity)
1323 return adg_edge_broadcast;
1324 else if (dep->type == pdg::dependence::pn_shift)
1325 return adg_edge_shift_register;
1326 else
1327 return adg_edge_fifo;
1330 /* Lift the domain of the relation "rel" according to domain->lifting,
1331 * if any.
1333 static __isl_give isl_map *lift(__isl_take isl_map *rel, adg_node *domain)
1335 if (domain->lifting) {
1336 isl_basic_map *lifting;
1337 lifting = isl_basic_map_copy(domain->lifting);
1338 rel = isl_map_apply_domain(rel,
1339 isl_map_from_basic_map(lifting));
1340 rel = isl_map_detect_equalities(rel);
1343 return rel;
1346 /* If "dom" is a wrapped map, then add the filters of "node" to "filters".
1348 static void extract_domain_filters(__isl_take isl_space *space,
1349 std::vector<adg_var *> &filters, pdg::node *node)
1351 if (isl_space_is_wrapping(space))
1352 add_domain_filters(filters, node, space);
1354 isl_space_free(space);
1357 /* Extract the filters on the output and input ports from the "from"
1358 * and "to" nodes of "rel".
1360 static void extract_domain_filters(__isl_keep isl_map *rel, add_edge_data *data)
1362 isl_space *space;
1364 space = isl_space_domain(isl_map_get_space(rel));
1365 extract_domain_filters(space, data->out_filters, data->dep->from);
1367 space = isl_space_range(isl_map_get_space(rel));
1368 extract_domain_filters(space, data->in_filters, data->dep->to);
1371 /* Add edges corresponding to "dep" for the relation "rel", which
1372 * may be a subset of dep->relation or dep->controlled_relation.
1373 * "container" contains information about the required buffer sizes.
1374 * "id" is the name that should be given to all created edges.
1375 * "type" is the type of the created edges.
1377 * If "dep" is a control dependence (i.e., if it has elements
1378 * in its from_controls), then add corresponding control variable
1379 * to the from node.
1381 * We schedule the domain and range of "rel" and extract the
1382 * domain filters for later use. We currently only allow such filters
1383 * on dependences without control parameters.
1385 * Since "rel" is a subset of dep->relation, it has the source as domain
1386 * and the sink as range. Since the mapping on the edges map iterations
1387 * of the input port to iterations of the corresponding output port,
1388 * we need to invert it. Then we apply the lifting on the
1389 * sink node as the initial static control variables on the input port domain
1390 * need to be the same as those of the node domain. The control variables
1391 * themselves are copied to data->in_controls in add_edge_set().
1393 * If "type" is adg_edge_broadcast, then a given write may be
1394 * associated to several (consecutive) reads in the original
1395 * dependence relation. In the ADG, we only associate the write
1396 * with the first of those reads.
1398 * If there are any dynamic control variables, we extract them
1399 * and move them from the parameters into the domain of the relation.
1400 * For loops however, we simply remove them.
1402 * Finally, we construct an explicit function representation of the
1403 * mapping and consider each convex piece of the domain in turn.
1404 * This is needed because the bounds on the domains have to be convex.
1405 * Before we compute this explicit representation, we first remove
1406 * the range filters as they may not be uniquely defined in terms
1407 * of the source filters.
1409 static void add_edge(PDG *pdg, adg *adg,
1410 pdg::dependence *dep, pdg::dependence *container,
1411 __isl_take isl_id *id, int edge_nr, __isl_take isl_map *rel,
1412 enum adg_edge_type type, int *port_nr)
1414 isl_ctx *ctx;
1415 isl_pw_multi_aff *pma;
1416 int res;
1417 add_edge_data data = { adg, dep, container };
1419 ctx = pdg->get_isl_ctx();
1421 data.type = type;
1422 data.id = id;
1423 data.from_node = adg->node_by_id(node_id(ctx, dep->from));
1424 data.to_node = adg->node_by_id(node_id(ctx, dep->to));
1425 data.port_nr = port_nr;
1426 data.edge_nr = edge_nr;
1428 add_control_variables(ctx, adg, data.from_node, dep);
1430 rel = isl_map_apply_domain(rel,
1431 extended_node_schedule(dep->from, adg,
1432 isl_id_copy(data.from_node->name),
1433 isl_space_domain(isl_map_get_space(rel))));
1434 rel = isl_map_apply_range(rel,
1435 extended_node_schedule(dep->to, adg,
1436 isl_id_copy(data.to_node->name),
1437 isl_space_range(isl_map_get_space(rel))));
1438 extract_domain_filters(rel, &data);
1439 if (data.out_filters.size() || data.in_filters.size())
1440 assert(!dep->controlled_relation);
1441 if (type == adg_edge_broadcast)
1442 rel = isl_map_lexmin(rel);
1443 rel = isl_map_reverse(rel);
1444 rel = lift(rel, data.to_node);
1445 if (has_filters(rel)) {
1446 if (dep->from != dep->to) {
1447 extract_filters(rel, &data);
1448 rel = move_filters(rel, data.in_filters);
1449 } else
1450 rel = remove_all_filters(rel);
1452 rel = remove_range_filters(rel, &data);
1453 pma = isl_pw_multi_aff_from_map(rel);
1455 res = isl_pw_multi_aff_foreach_piece(pma, &add_edge_set, &data);
1457 isl_pw_multi_aff_free(pma);
1458 isl_map_free(data.range_filters_map);
1459 isl_map_free(data.rel);
1460 isl_id_free(data.id);
1462 for (int i = 0; i < data.in_filters.size(); ++i)
1463 delete data.in_filters[i];
1464 for (int i = 0; i < data.out_filters.size(); ++i)
1465 delete data.out_filters[i];
1468 /* Create and return an isl_id of the form
1470 * ED_<i>
1471 * or
1472 * CED_<i>
1474 * depending on whether "dep" is a control edge.
1476 static __isl_give isl_id *edge_name(isl_ctx *ctx, pdg::dependence *dep, int i)
1478 char buf[10];
1479 bool control = dep->from_controls.size() > 0;
1481 snprintf(buf, sizeof(buf), "%sED_%d", control ? "C" : "", i);
1482 return isl_id_alloc(ctx, buf, NULL);
1485 /* Add edges corresponding to "dep".
1486 * "container" contains information about the required buffer sizes.
1487 * "id" is the name that should be given to all created edges.
1489 * The dependence relation is taken from dep->controlled_relation
1490 * if it is not NULL. Otherwise, it is taken from dep->relation.
1492 * We look for pn_hold dependences with overlapping domains.
1493 * A pn_hold dependence signifies that a value available at the source
1494 * iteration should be kept until the next iteration (= target of dependence).
1495 * If there is a pn_hold dependence whose domain overlaps with the target
1496 * of the given dependence, then we turn the entire given dependence into a
1497 * "sticky" fifo.
1498 * If no such pn_hold dependences were found, then a regular fifo edge
1499 * is created.
1501 * Obviously, we should only convert fifos into sticky fifos.
1502 * So, if "dep" is not a fifo, we should just construct fifos for
1503 * the associated pn_hold dependences.
1505 static void add_edge(PDG *pdg, adg *adg,
1506 pdg::dependence *dep, pdg::dependence *container,
1507 __isl_take isl_id *id, int edge_nr, int *port_nr)
1509 isl_ctx *ctx;
1510 isl_map *rel;
1511 enum adg_edge_type type;
1513 ctx = pdg->get_isl_ctx();
1515 assert(dep->type == pdg::dependence::pn ||
1516 dep->type == pdg::dependence::pn_shift ||
1517 dep->type == pdg::dependence::pn_part ||
1518 dep->type == pdg::dependence::flow);
1520 if (dep->controlled_relation)
1521 rel = dep->controlled_relation->get_isl_map();
1522 else
1523 rel = dep->relation->get_isl_map();
1525 type = dep_type(dep);
1527 for (int j = 0; j < pdg->dependences.size(); ++j) {
1528 pdg::dependence *hold_dep = pdg->dependences[j];
1529 isl_map *hold_rel;
1530 bool empty;
1532 if (hold_dep->type != pdg::dependence::pn_hold)
1533 continue;
1534 if (dep->to != hold_dep->from)
1535 continue;
1536 if (dep->array != hold_dep->array)
1537 continue;
1538 if (dep->to_access != hold_dep->from_access)
1539 continue;
1541 if (hold_dep->controlled_relation)
1542 hold_rel = hold_dep->controlled_relation->get_isl_map();
1543 else
1544 hold_rel = hold_dep->relation->get_isl_map();
1546 if (type != adg_edge_fifo) {
1547 isl_id *id = edge_name(ctx, pdg->dependences[j], j);
1548 add_edge(pdg, adg, hold_dep, hold_dep, id, edge_nr,
1549 hold_rel, adg_edge_fifo, port_nr);
1550 continue;
1553 hold_rel = isl_map_intersect_range(isl_map_copy(rel),
1554 isl_map_domain(hold_rel));
1555 empty = isl_map_is_empty(hold_rel);
1556 isl_map_free(hold_rel);
1558 if (!empty) {
1559 type = adg_edge_sticky_fifo;
1560 break;
1564 add_edge(pdg, adg, dep, container, id, edge_nr, rel, type, port_nr);
1567 /* Add edges corresponding to "dep".
1568 * If it is a pn_union, then all the corresponding pn_part dependences
1569 * should get the same name.
1571 static void add_union_edge(PDG *pdg, adg *adg, pdg::dependence *dep, int i)
1573 isl_ctx *ctx;
1574 isl_id *id;
1575 int port_nr = 0;
1577 ctx = pdg->get_isl_ctx();
1579 id = edge_name(ctx, dep, i);
1581 if (dep->type != pdg::dependence::pn_union) {
1582 add_edge(pdg, adg, dep, dep, id, i, &port_nr);
1583 return;
1586 for (int j = 0; j < pdg->dependences.size(); ++j) {
1587 pdg::dependence *part_dep = pdg->dependences[j];
1589 if (part_dep->type != pdg::dependence::pn_part)
1590 continue;
1591 if (part_dep->container != dep)
1592 continue;
1594 add_edge(pdg, adg, part_dep, dep, isl_id_copy(id), i, &port_nr);
1597 isl_id_free(id);
1600 /* Auxiliary data used during the construction of gates.
1602 * nr points to the sequence number.
1603 * from is the source access.
1604 * to is the target access.
1606 struct add_wire_data {
1607 int *nr;
1608 pdg::access *from;
1609 pdg::access *to;
1610 adg_node *node;
1613 extern "C" {
1614 static int add_wire_basic(__isl_take isl_basic_set *bset, void *user);
1617 /* Add a wire with the convex domain "bset".
1618 * To ensure that the initial control variables of the gate are the same as
1619 * that of the node, we apply the node lifting, if any, and copy the
1620 * static control variables.
1622 * If there are any dynamic control variables among the parameters,
1623 * we move them into the domain of a wrapped map.
1625 static int add_wire_basic(__isl_take isl_basic_set *bset, void *user)
1627 add_wire_data *data = (add_wire_data *) user;
1628 isl_ctx *ctx;
1629 isl_space *space;
1630 adg_gate *gate = new adg_gate;
1631 char buf[100];
1633 ctx = isl_basic_set_get_ctx(bset);
1634 space = isl_basic_set_get_space(bset);
1635 snprintf(buf, sizeof(buf), "%sID_%d",
1636 isl_id_get_name(data->node->name), (*data->nr)++);
1637 gate->name = isl_id_alloc(ctx, buf, NULL);
1638 gate->in = new_var(data->from, isl_space_copy(space));
1639 gate->out = new_var(data->to, space);
1641 gate->node_name = isl_id_copy(data->node->name);
1642 gate->domain = new adg_domain(isl_set_from_basic_set(bset));
1643 if (data->node->lifting)
1644 apply_lifting(gate->domain,
1645 isl_basic_map_copy(data->node->lifting));
1646 copy_controls(gate->domain->controls, data->node->domain->controls);
1647 move_filters(gate->domain);
1649 data->node->gates.push_back(gate);
1651 return 0;
1654 /* Add gates corresponding to the wire "dep".
1655 * "g_nr" is a pointer to the sequence number of gates.
1656 * The gate copies the variable corresponding to the from_access
1657 * to the variable corresponding to the to_access.
1658 * The dependence relation of a wire is always an identity relation,
1659 * so we can just take the domain of this relation.
1660 * We schedule both this domain and the dynamic control variables, if any.
1662 static void add_wire_gate(PDG *pdg, adg *adg, pdg::dependence *dep, int *g_nr)
1664 isl_map *rel;
1665 isl_set *dom;
1666 isl_ctx *ctx;
1667 isl_id *id;
1668 struct add_wire_data data = { g_nr, dep->from_access, dep->to_access };
1670 ctx = pdg->get_isl_ctx();
1671 rel = dep->relation->get_isl_map();
1672 dom = isl_map_domain(rel);
1673 id = node_id(ctx, dep->to);
1674 data.node = adg->node_by_id(isl_id_copy(id));
1675 dom = isl_set_apply(dom, node_schedule(dep->from, adg, id));
1676 isl_set_foreach_basic_set(dom, &add_wire_basic, &data);
1677 isl_set_free(dom);
1680 /* Construct a map that removes some dimensions with known constant
1681 * values from the schedule domain.
1682 * The map is stored in graph->iterator_map.
1683 * Names for the iterators in the domain of this map are stored
1684 * in graph->all_iterators, while the names for the iterators
1685 * that are kept in the range are stored in graph->iterators.
1687 static void compute_iterator_map(adg *graph, PDG *pdg)
1689 int n_it;
1690 int n_all;
1691 isl_ctx *ctx;
1692 isl_space *space;
1693 isl_map *map;
1694 char buf[20];
1696 n_all = pdg->statement_dimensions.size();
1697 n_it = 0;
1698 for (int i = 0; i < n_all; ++i)
1699 if (!pdg->statement_dimensions[i])
1700 n_it++;
1702 ctx = pdg->get_isl_ctx();
1703 space = isl_space_alloc(ctx, 0, n_all, n_it);
1704 map = isl_map_universe(space);
1706 n_it = 0;
1707 for (int i = 0; i < n_all; ++i) {
1708 isl_id *id;
1710 snprintf(buf, sizeof(buf), "c%d", i);
1711 id = isl_id_alloc(ctx, buf, NULL);
1712 graph->all_iterators.push_back(id);
1713 map = isl_map_set_dim_id(map, isl_dim_in, i, isl_id_copy(id));
1715 if (pdg->statement_dimensions[i])
1716 continue;
1717 graph->iterators.push_back(isl_id_copy(id));
1718 map = isl_map_equate(map, isl_dim_in, i, isl_dim_out, n_it);
1719 n_it++;
1722 graph->iterator_map = map;
1725 /* Create an adg_param for each element in pdg->params.
1726 * param->val is obtained from pdg->params[i]->value, if any.
1727 * param->lb and param->ub are obtained from the context.
1729 static void extract_params(adg *adg, PDG *pdg)
1731 isl_ctx *ctx;
1732 isl_local_space *ls;
1733 isl_int v;
1735 ctx = pdg->get_isl_ctx();
1737 ls = isl_local_space_from_space(isl_set_get_space(adg->context));
1738 isl_int_init(v);
1739 for (int i = 0; i < pdg->params.size(); ++i) {
1740 adg_param *param = new adg_param;
1741 isl_aff *aff;
1742 enum isl_lp_result res;
1744 param->name = isl_id_alloc(ctx, pdg->params[i]->name->s.c_str(),
1745 NULL);
1746 if (pdg->params[i]->value) {
1747 isl_space *space = isl_space_params_alloc(ctx, 0);
1748 isl_local_space *ls = isl_local_space_from_space(space);
1749 param->val = isl_aff_zero_on_domain(ls);
1750 param->val = isl_aff_add_constant_si(param->val,
1751 pdg->params[i]->value->v);
1754 aff = isl_aff_zero_on_domain(isl_local_space_copy(ls));
1755 aff = isl_aff_add_coefficient_si(aff, isl_dim_param, i, 1);
1756 res = isl_set_min(adg->context, aff, &v);
1757 if (res == isl_lp_ok) {
1758 isl_space *space = isl_space_params_alloc(ctx, 0);
1759 isl_local_space *ls = isl_local_space_from_space(space);
1760 param->lb = isl_aff_zero_on_domain(ls);
1761 param->lb = isl_aff_add_constant(param->lb, v);
1763 res = isl_set_max(adg->context, aff, &v);
1764 if (res == isl_lp_ok) {
1765 isl_space *space = isl_space_params_alloc(ctx, 0);
1766 isl_local_space *ls = isl_local_space_from_space(space);
1767 param->ub = isl_aff_zero_on_domain(ls);
1768 param->ub = isl_aff_add_constant(param->ub, v);
1770 isl_aff_free(aff);
1772 adg->params.push_back(param);
1774 isl_int_clear(v);
1775 isl_local_space_free(ls);
1778 /* Comparison function for sorting input ports such that
1779 * first, for input ports that are connected to the same edge,
1780 * an input port corresponding to an earlier argument position comes
1781 * before an input port corresponding to a later argument position, and,
1782 * second, ports that have dynamic control variables in their domains
1783 * come after the ports that set those control variables.
1785 * The first is needed for pn_union channels as their detection allows for
1786 * the case where two (or more) tokens are consumed in the same iteration,
1787 * provided they don't cause reordering when consumed in the order of
1788 * the arguments.
1790 * The second is needed to ensure that the values of the dynamic
1791 * control variables are available when they are needed.
1792 * We enforce the second property by sorting the edges based
1793 * on their depth with respect to dependences between input ports.
1794 * It would probably make more sense to perform a topological sort,
1795 * but it seemed easier to implement it this way.
1797 * If two edges are equivalent with respect to the above two criteria,
1798 * then we arbitrarily sort them according to the edge sequence number.
1800 struct less_input_port :
1801 public std::binary_function<adg_port *, adg_port *, bool> {
1802 std::vector<adg_port *> ports;
1804 less_input_port(std::vector<adg_port *> &ports) : ports(ports) {}
1806 bool writes(adg_port *p, __isl_keep isl_id *id);
1807 int depth(adg_port *p);
1809 bool operator()(adg_port *x, adg_port *y) {
1810 int depth_x, depth_y;
1812 if (x->edge_nr == y->edge_nr)
1813 return x->arg_pos < y->arg_pos;
1815 depth_x = depth(x);
1816 depth_y = depth(y);
1817 if (depth_x != depth_y)
1818 return depth_x < depth_y;
1819 return x->edge_nr < y->edge_nr;
1823 /* Does "p" write to the variable "id"?
1825 bool less_input_port::writes(adg_port *p, __isl_keep isl_id *id)
1827 for (int i = 0; i < p->vars.size(); ++i) {
1828 adg_var *var = p->vars[i];
1829 isl_id *var_id;
1830 var_id = isl_pw_multi_aff_get_tuple_id(var->access,
1831 isl_dim_out);
1832 isl_id_free(var_id);
1833 if (var_id == id)
1834 return true;
1837 return false;
1840 /* Return 0 if this port does not involve any filters.
1841 * Otherwise, return 1 plus the maximal depth of any port
1842 * writing a filter used in the domain of the current port.
1843 * We obviously assume here that there are no cyclic dependences.
1845 int less_input_port::depth(adg_port *p)
1847 int max_depth = 0;
1849 for (int i = 0; i < p->domain->filters.size(); ++i) {
1850 adg_var *filter = p->domain->filters[i];
1851 isl_id *id;
1852 id = isl_pw_multi_aff_get_tuple_id(filter->access, isl_dim_out);
1853 for (int j = 0; j < ports.size(); ++j) {
1854 int d;
1855 if (writes(ports[j], id)) {
1856 d = depth(ports[j]) + 1;
1857 if (d > max_depth)
1858 max_depth = d;
1861 isl_id_free(id);
1864 return max_depth;
1867 /* Sort the input ports of the given node.
1869 static void sort_input_ports(adg_node *node)
1871 std::sort(node->input_ports.begin(), node->input_ports.end(),
1872 less_input_port(node->input_ports));
1875 /* Sort the input ports in all nodes of the graph.
1877 static void sort_input_ports(adg *graph)
1879 for (int i = 0; i < graph->nodes.size(); ++i)
1880 sort_input_ports(graph->nodes[i]);
1883 /* Rename the dynamic control "id" from __last_*_valid to
1885 * dc<nr>_<node>_b
1887 * and from __last_*_<i> to
1889 * dc<nr>_<node>_c<i>
1891 * "prefix_map" maps previously translated prefixes __last_*
1892 * (within the same node) to their dc<nr>_<node> translations.
1893 * If we can't find the current prefix in this cache, we create
1894 * a new name and add it to the cache.
1896 static __isl_give isl_id *rename_dynamic_control(__isl_take isl_id *id,
1897 const char *node_name,
1898 std::map<std::string, std::string> &prefix_map)
1900 const char *name, *underscore;
1901 string old;
1902 std::ostringstream strm;
1903 isl_ctx *ctx;
1905 if (!is_control(id))
1906 return id;
1908 name = isl_id_get_name(id);
1909 underscore = strrchr(name, '_');
1910 assert(underscore);
1912 old = string(name, underscore - name);
1913 if (prefix_map.find(old) == prefix_map.end()) {
1914 strm << "dc" << prefix_map.size() << "_" << node_name;
1915 prefix_map[old] = strm.str();
1918 strm.str("");
1919 strm << prefix_map[old] << "_";
1920 if (!strcmp(underscore + 1, "valid"))
1921 strm << "b";
1922 else
1923 strm << "c" << underscore + 1;
1925 ctx = isl_id_get_ctx(id);
1926 isl_id_free(id);
1928 return isl_id_alloc(ctx, strm.str().c_str(), NULL);
1931 static void rename_dynamic_controls(adg_expr *expr,
1932 const char *node_name,
1933 std::map<std::string, std::string> &prefix_map)
1935 isl_id *id;
1936 int n_in;
1938 n_in = isl_pw_aff_dim(expr->expr, isl_dim_in);
1939 for (int i = 0; i < n_in; ++i) {
1940 const char *name;
1941 name = isl_pw_aff_get_dim_name(expr->expr, isl_dim_in, i);
1942 if (!is_control(name))
1943 continue;
1944 id = isl_pw_aff_get_dim_id(expr->expr, isl_dim_in, i);
1945 id = rename_dynamic_control(id, node_name, prefix_map);
1946 expr->expr = isl_pw_aff_set_dim_id(expr->expr,
1947 isl_dim_in, i, id);
1950 if (!is_control(expr->name))
1951 return;
1952 expr->name = rename_dynamic_control(expr->name, node_name, prefix_map);
1955 static void rename_dynamic_controls(adg_var *var,
1956 const char *node_name,
1957 std::map<std::string, std::string> &prefix_map)
1959 isl_id *id;
1960 int nparam;
1962 nparam = isl_pw_multi_aff_dim(var->access, isl_dim_param);
1963 for (int i = 0; i < nparam; ++i) {
1964 const char *name;
1965 name = isl_pw_multi_aff_get_dim_name(var->access,
1966 isl_dim_param, i);
1967 if (!is_control(name))
1968 continue;
1969 id = isl_pw_multi_aff_get_dim_id(var->access,
1970 isl_dim_param, i);
1971 id = rename_dynamic_control(id, node_name, prefix_map);
1972 var->access = isl_pw_multi_aff_set_dim_id(var->access,
1973 isl_dim_param, i, id);
1976 if (!isl_pw_multi_aff_has_tuple_id(var->access, isl_dim_out))
1977 return;
1978 id = isl_pw_multi_aff_get_tuple_id(var->access, isl_dim_out);
1979 id = rename_dynamic_control(id, node_name, prefix_map);
1980 var->access = isl_pw_multi_aff_set_tuple_id(var->access,
1981 isl_dim_out, id);
1984 static void rename_dynamic_controls(adg_domain *domain,
1985 const char *node_name,
1986 std::map<std::string, std::string> &prefix_map)
1988 for (int i = 0; i < domain->controls.size(); ++i)
1989 rename_dynamic_controls(domain->controls[i], node_name,
1990 prefix_map);
1991 for (int i = 0; i < domain->filters.size(); ++i)
1992 rename_dynamic_controls(domain->filters[i], node_name,
1993 prefix_map);
1996 static void rename_dynamic_controls(adg_function *fn,
1997 const char *node_name,
1998 std::map<std::string, std::string> &prefix_map)
2000 for (int i = 0; i < fn->ctrl.size(); ++i)
2001 rename_dynamic_controls(fn->ctrl[i], node_name, prefix_map);
2004 static void rename_dynamic_controls(adg_port *port,
2005 const char *node_name,
2006 std::map<std::string, std::string> &prefix_map)
2008 for (int i = 0; i < port->vars.size(); ++i)
2009 rename_dynamic_controls(port->vars[i], node_name, prefix_map);
2010 rename_dynamic_controls(port->domain, node_name, prefix_map);
2013 static void rename_dynamic_controls(adg_gate *gate,
2014 const char *node_name,
2015 std::map<std::string, std::string> &prefix_map)
2017 rename_dynamic_controls(gate->in, node_name, prefix_map);
2018 rename_dynamic_controls(gate->out, node_name, prefix_map);
2019 rename_dynamic_controls(gate->domain, node_name, prefix_map);
2022 /* Rename all sets of dynamic controls in "node" from __last_*_valid to
2024 * dc<nr>_<node>_b
2026 * and from __last_*_<i> to
2028 * dc<nr>_<node>_c<i>
2030 * This ensures that each set of dynamic controls has a unique name.
2031 * In particular, if such a set of dynamic controls is transferred from
2032 * one node to another, then they will have different names in the two
2033 * nodes.
2035 static void rename_dynamic_controls(adg_node *node)
2037 std::map<std::string, std::string> prefix_map;
2038 const char *node_name = isl_id_get_name(node->name);
2040 rename_dynamic_controls(node->function, node_name, prefix_map);
2042 for (int i = 0; i < node->input_ports.size(); ++i)
2043 rename_dynamic_controls(node->input_ports[i], node_name,
2044 prefix_map);
2045 for (int i = 0; i < node->output_ports.size(); ++i)
2046 rename_dynamic_controls(node->output_ports[i], node_name,
2047 prefix_map);
2048 for (int i = 0; i < node->gates.size(); ++i)
2049 rename_dynamic_controls(node->gates[i], node_name, prefix_map);
2052 static void rename_dynamic_controls(adg *graph)
2054 for (int i = 0; i < graph->nodes.size(); ++i)
2055 rename_dynamic_controls(graph->nodes[i]);
2058 /* Convert the pn model "pdg" to an adg model.
2060 static adg *pdg2adg(PDG *pdg)
2062 isl_ctx *ctx;
2063 adg *graph;
2064 int g_nr = 0;
2066 ctx = pdg->get_isl_ctx();
2068 graph = new adg;
2070 graph->name = isl_id_alloc(ctx, pdg->name->s.c_str(), NULL);
2071 graph->context = pdg->context->get_isl_set();
2072 compute_iterator_map(graph, pdg);
2074 extract_params(graph, pdg);
2076 for (int i = 0; i < pdg->nodes.size(); ++i)
2077 add_node(ctx, graph, pdg->nodes[i]);
2078 for (int i = 0; i < pdg->dependences.size(); ++i) {
2079 pdg::dependence *dep = pdg->dependences[i];
2081 if (dep->type == pdg::dependence::uninitialized)
2082 continue;
2083 if (dep->type == pdg::dependence::pn_part)
2084 continue;
2085 if (dep->type == pdg::dependence::pn_hold)
2086 continue;
2087 if (dep->type == pdg::dependence::pn_wire)
2088 add_wire_gate(pdg, graph, dep, &g_nr);
2089 else
2090 add_union_edge(pdg, graph, dep, i);
2093 sort_input_ports(graph);
2094 rename_dynamic_controls(graph);
2096 return graph;
2099 static void set_output_name(isl_ctx *ctx, struct options *options)
2101 int len;
2102 const char *suffix = options->xml ? "_adg.xml" : "_adg.yaml";
2104 if (!options->input || !strcmp(options->input, "-"))
2105 return;
2106 if (options->output)
2107 return;
2109 len = strlen(options->input);
2110 if (len > 5 && !strcmp(options->input + len - 5, ".yaml"))
2111 len -= 5;
2112 options->output = isl_alloc_array(ctx, char, len + strlen(suffix) + 1);
2113 assert(options->output);
2114 strncpy(options->output, options->input, len);
2115 strcpy(options->output + len, suffix);
2118 /* Convert the input pn model to an adg model and print out the adg
2119 * model in either YAML format or XML format.
2121 int main(int argc, char *argv[])
2123 isl_ctx *ctx;
2124 struct options *options = options_new_with_defaults();
2125 PDG *pdg;
2126 adg *adg;
2127 FILE *in = stdin, *out = stdout;
2129 ctx = isl_ctx_alloc();
2130 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
2132 set_output_name(ctx, options);
2134 if (options->input && strcmp(options->input, "-")) {
2135 in = fopen(options->input, "r");
2136 assert(in);
2138 if (options->output && strcmp(options->output, "-")) {
2139 out = fopen(options->output, "w");
2140 assert(out);
2143 pdg = PDG::Load(in, ctx);
2145 adg = pdg2adg(pdg);
2146 if (options->xml)
2147 adg_print_xml(adg, out);
2148 else
2149 adg->print(out);
2150 delete adg;
2152 pdg->free();
2153 delete pdg;
2154 isl_ctx_free(ctx);
2155 options_free(options);
2157 return 0;