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