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