2 * Copyright 2011 Leiden University. All rights reserved.
9 #include <isl/union_map.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
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".
46 /* Create and return an adg_var that accesses the variable "id"
49 static adg_var
*var_from_id(__isl_take isl_id
*id
, __isl_take isl_space
*space
)
51 adg_var
*var
= new adg_var
;
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
);
67 /* Create and return an isl_id of the form
69 * in_<access->nr><suffix>
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
,
81 if (access
->type
== pdg::access::write
)
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
103 static __isl_give isl_id
*node_id(__isl_take isl_space
*space
)
107 if (!isl_space_is_wrapping(space
)) {
108 id
= isl_space_get_tuple_id(space
, isl_dim_set
);
109 isl_space_free(space
);
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
)
122 isl_ctx
*ctx
= isl_space_get_ctx(space
);
126 id
= node_id(isl_space_copy(space
));
127 suffix
= isl_id_get_name(id
);
130 id
= access_name(ctx
, access
, suffix
);
131 var
= var_from_id(id
, space
);
133 var
->type
= isl_id_alloc(ctx
,
134 access
->array
->element_type
->s
.c_str(), NULL
);
136 var
->type
= isl_id_alloc(ctx
, "int", NULL
);
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
)
150 sched
= p_node
->schedule
->get_isl_map();
151 sched
= isl_map_apply_range(sched
, isl_map_copy(graph
->iterator_map
));
153 sched
= isl_map_set_tuple_id(sched
, isl_dim_out
, id
);
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
)
171 sched
= node_schedule(p_node
, graph
, id
);
172 if (!isl_space_is_wrapping(space
)) {
173 isl_space_free(space
);
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
);
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
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
)
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
);
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
]));
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
);
239 assert(coa
->type
== pdg::call_or_access::t_access
);
240 if (coa
->access
->type
== pdg::access::write
)
242 if (isl_map_has_tuple_id(coa
->access
->map
->map
, isl_dim_out
))
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
)
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
,
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
);
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
);
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
);
326 pdg::statement
*stat
= p_node
->statement
;
327 adg_function
*fn
= new adg_function
;
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
)),
341 if (!stat
->top_function
)
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
)
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
);
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
)
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
);
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
)
411 snprintf(buf
, sizeof(buf
), "ND_%d", p_node
->nr
);
413 return isl_id_alloc(ctx
, buf
, NULL
);
417 static int intersect_local_space(__isl_take isl_basic_set
*bset
,
421 /* Intersect the local space *user with the local space of "bset".
423 static int intersect_local_space(__isl_take isl_basic_set
*bset
,
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
);
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
453 static int filter_pos(__isl_take isl_space
*space
)
457 if (!isl_space_is_wrapping(space
)) {
458 isl_space_free(space
);
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
);
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
)
480 int n_in
= isl_aff_dim(aff
, isl_dim_in
);
485 fp
= filter_pos(isl_aff_get_domain_space(aff
));
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
) {
500 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
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
);
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
)
524 int n_in
= isl_pw_aff_dim(pa
, isl_dim_in
);
529 fp
= filter_pos(isl_pw_aff_get_domain_space(pa
));
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
) {
544 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
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
);
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
)
572 ctx
= isl_local_space_get_ctx(ls
);
573 n_div
= isl_local_space_dim(ls
, isl_dim_div
);
575 for (int i
= 0; i
< n_div
; ++i
) {
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
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
)
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
);
621 isl_local_space_free(ls
);
625 add_controls(graph
, domain
->filters
, domain
->controls
, ls
);
627 lifting
= isl_local_space_lifting(ls
);
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
648 static void add_static_controls(adg
*graph
, adg_node
*node
)
650 add_static_controls(graph
, node
->domain
, &node
->lifting
);
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
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
;
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)
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
)
725 if (!isl_map_has_dim_id(map
, isl_dim_param
, i
))
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
)
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
) {
753 if (!is_control(map
, i
))
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
))
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
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
799 struct add_edge_data
{
801 pdg::dependence
*dep
;
802 pdg::dependence
*container
;
807 isl_map
*range_filters_map
;
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
;
819 isl_basic_map
*lifting
;
820 std::vector
<adg_expr
*> in_controls
;
828 /* Set the name of "port" to
830 * <node>IP_<edge>_<nr>_V_<arg_pos>
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
)
846 ctx
= isl_id_get_ctx(edge_id
);
848 if (type
== adg_port_input
)
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
)
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
)
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
) {
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
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
);
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
]));
945 add_controls(graph
, filters
, port
->domain
->controls
, ls
);
946 isl_local_space_free(ls
);
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
;
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
);
991 data
->graph
->edges
.push_back(edge
);
993 isl_basic_set_free(bset
);
997 /* Does the list of (dynamic) control variables node->function->ctrl
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
)
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
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
)
1028 isl_pw_multi_aff
*pma
;
1030 isl_local_space
*ls
;
1033 if (dep
->from_controls
.size() == 0)
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
) {
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
)) {
1058 name
= strrchr(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
);
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
);
1076 node
->function
->ctrl
.push_back(expr
);
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
)
1097 add_edge_data
*data
= (add_edge_data
*) user
;
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
);
1115 isl_basic_set_free(data
->in_domain
);
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
)
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
);
1138 space
= isl_space_map_from_set(space
);
1139 data
->range_filters_map
= isl_map_identity(space
);
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
));
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
,
1171 add_edge_data
*data
= (add_edge_data
*) user
;
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
);
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
);
1193 isl_map_free(data
->rel_maff
);
1194 isl_multi_aff_free(maff
);
1199 /* Does "rel" have any parameter of the form __last_*?
1201 static bool has_filters(__isl_keep isl_map
*rel
)
1205 nparam
= isl_map_dim(rel
, isl_dim_param
);
1206 for (int i
= 0; i
< nparam
; ++i
)
1207 if (is_control(rel
, i
))
1213 /* Remove all parameters of the form __last_* from "rel".
1215 static __isl_give isl_map
*remove_all_filters(__isl_take isl_map
*rel
)
1219 nparam
= isl_map_dim(rel
, isl_dim_param
);
1220 for (int i
= nparam
- 1; i
>= 0; --i
) {
1221 if (!is_control(rel
, i
))
1223 rel
= isl_map_project_out(rel
, isl_dim_param
, i
, 1);
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
)
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
) {
1248 if (!is_control(rel
, i
))
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
)
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
) {
1283 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
1285 param
= isl_map_find_dim_by_id(map
, isl_dim_param
, 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
,
1293 map
= isl_map_domain_map(map
);
1295 rel
= isl_map_apply_range(map
, rel
);
1297 for (int i
= 0; i
< filters
.size(); ++i
) {
1301 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
1303 param
= isl_map_find_dim_by_id(rel
, isl_dim_param
, 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
);
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
;
1327 return adg_edge_fifo
;
1330 /* Lift the domain of the relation "rel" according to domain->lifting,
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
);
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
)
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
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
)
1415 isl_pw_multi_aff
*pma
;
1417 add_edge_data data
= { adg
, dep
, container
};
1419 ctx
= pdg
->get_isl_ctx();
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
);
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
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
)
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
1498 * If no such pn_hold dependences were found, then a regular fifo edge
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
)
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();
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
];
1532 if (hold_dep
->type
!= pdg::dependence::pn_hold
)
1534 if (dep
->to
!= hold_dep
->from
)
1536 if (dep
->array
!= hold_dep
->array
)
1538 if (dep
->to_access
!= hold_dep
->from_access
)
1541 if (hold_dep
->controlled_relation
)
1542 hold_rel
= hold_dep
->controlled_relation
->get_isl_map();
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
);
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
);
1559 type
= adg_edge_sticky_fifo
;
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
)
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
);
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
)
1591 if (part_dep
->container
!= dep
)
1594 add_edge(pdg
, adg
, part_dep
, dep
, isl_id_copy(id
), i
, &port_nr
);
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
{
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
;
1630 adg_gate
*gate
= new adg_gate
;
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
);
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
)
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
);
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
)
1696 n_all
= pdg
->statement_dimensions
.size();
1698 for (int i
= 0; i
< n_all
; ++i
)
1699 if (!pdg
->statement_dimensions
[i
])
1702 ctx
= pdg
->get_isl_ctx();
1703 space
= isl_space_alloc(ctx
, 0, n_all
, n_it
);
1704 map
= isl_map_universe(space
);
1707 for (int i
= 0; i
< n_all
; ++i
) {
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
])
1717 graph
->iterators
.push_back(isl_id_copy(id
));
1718 map
= isl_map_equate(map
, isl_dim_in
, i
, isl_dim_out
, 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
)
1732 isl_local_space
*ls
;
1735 ctx
= pdg
->get_isl_ctx();
1737 ls
= isl_local_space_from_space(isl_set_get_space(adg
->context
));
1739 for (int i
= 0; i
< pdg
->params
.size(); ++i
) {
1740 adg_param
*param
= new adg_param
;
1742 enum isl_lp_result res
;
1744 param
->name
= isl_id_alloc(ctx
, pdg
->params
[i
]->name
->s
.c_str(),
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
);
1772 adg
->params
.push_back(param
);
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
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
;
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
];
1830 var_id
= isl_pw_multi_aff_get_tuple_id(var
->access
,
1832 isl_id_free(var_id
);
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
)
1849 for (int i
= 0; i
< p
->domain
->filters
.size(); ++i
) {
1850 adg_var
*filter
= p
->domain
->filters
[i
];
1852 id
= isl_pw_multi_aff_get_tuple_id(filter
->access
, isl_dim_out
);
1853 for (int j
= 0; j
< ports
.size(); ++j
) {
1855 if (writes(ports
[j
], id
)) {
1856 d
= depth(ports
[j
]) + 1;
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
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
;
1902 std::ostringstream strm
;
1905 if (!is_control(id
))
1908 name
= isl_id_get_name(id
);
1909 underscore
= strrchr(name
, '_');
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();
1919 strm
<< prefix_map
[old
] << "_";
1920 if (!strcmp(underscore
+ 1, "valid"))
1923 strm
<< "c" << underscore
+ 1;
1925 ctx
= isl_id_get_ctx(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
)
1938 n_in
= isl_pw_aff_dim(expr
->expr
, isl_dim_in
);
1939 for (int i
= 0; i
< n_in
; ++i
) {
1941 name
= isl_pw_aff_get_dim_name(expr
->expr
, isl_dim_in
, i
);
1942 if (!is_control(name
))
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
,
1950 if (!is_control(expr
->name
))
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
)
1962 nparam
= isl_pw_multi_aff_dim(var
->access
, isl_dim_param
);
1963 for (int i
= 0; i
< nparam
; ++i
) {
1965 name
= isl_pw_multi_aff_get_dim_name(var
->access
,
1967 if (!is_control(name
))
1969 id
= isl_pw_multi_aff_get_dim_id(var
->access
,
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
))
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
,
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
,
1991 for (int i
= 0; i
< domain
->filters
.size(); ++i
)
1992 rename_dynamic_controls(domain
->filters
[i
], node_name
,
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
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
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
,
2045 for (int i
= 0; i
< node
->output_ports
.size(); ++i
)
2046 rename_dynamic_controls(node
->output_ports
[i
], node_name
,
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
)
2066 ctx
= pdg
->get_isl_ctx();
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
)
2083 if (dep
->type
== pdg::dependence::pn_part
)
2085 if (dep
->type
== pdg::dependence::pn_hold
)
2087 if (dep
->type
== pdg::dependence::pn_wire
)
2088 add_wire_gate(pdg
, graph
, dep
, &g_nr
);
2090 add_union_edge(pdg
, graph
, dep
, i
);
2093 sort_input_ports(graph
);
2094 rename_dynamic_controls(graph
);
2099 static void set_output_name(isl_ctx
*ctx
, struct options
*options
)
2102 const char *suffix
= options
->xml
? "_adg.xml" : "_adg.yaml";
2104 if (!options
->input
|| !strcmp(options
->input
, "-"))
2106 if (options
->output
)
2109 len
= strlen(options
->input
);
2110 if (len
> 5 && !strcmp(options
->input
+ len
- 5, ".yaml"))
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
[])
2124 struct options
*options
= options_new_with_defaults();
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");
2138 if (options
->output
&& strcmp(options
->output
, "-")) {
2139 out
= fopen(options
->output
, "w");
2143 pdg
= PDG::Load(in
, ctx
);
2147 adg_print_xml(adg
, out
);
2155 options_free(options
);