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 enum adg_arg_type type_i
= type
;
285 if (!strcmp(coa
->call
->name
->s
.c_str(), "&"))
286 type_i
= adg_arg_reference
;
287 add_arguments(fn
, coa
->call
, space
, type_i
);
291 assert(coa
->type
== pdg::call_or_access::t_access
);
292 if (coa
->access
->type
== pdg::access::write
)
293 add_argument(fn
->out
,
294 new_var(coa
->access
, isl_space_copy(space
)), type
);
297 new_var(coa
->access
, isl_space_copy(space
)), type
);
301 /* Add the filters of "node" to "filters", having them refer to "space"
302 * as their iteration domain.
304 static void add_domain_filters(std::vector
<adg_var
*> &filters
, pdg::node
*node
,
305 __isl_keep isl_space
*space
)
307 for (int i
= 0; i
< node
->filters
.size(); ++i
) {
308 pdg::call_or_access
*coa
= node
->filters
[i
];
309 assert(coa
->type
== pdg::call_or_access::t_access
);
310 filters
.push_back(new_var(coa
->access
, isl_space_copy(space
)));
314 /* Set node->function based on "p_node" and the node domain "domain".
315 * In particular, create an adg_function with the given domain
316 * and add input and output arguments.
317 * If any of the input arguments is an affine expression (as opposed to
318 * an array access), then it is added to node->expressions.
320 * If p_node has any filters, then they are converted to filters on node.
322 static void set_function(adg
*adg
, adg_node
*node
, pdg::node
*p_node
,
323 __isl_take isl_set
*domain
)
325 isl_ctx
*ctx
= isl_set_get_ctx(domain
);
327 pdg::statement
*stat
= p_node
->statement
;
328 adg_function
*fn
= new adg_function
;
332 space
= isl_set_get_space(domain
);
333 fn
->domain
= new adg_domain(domain
);
335 add_domain_filters(fn
->domain
->filters
, p_node
, space
);
337 for (int i
= 0; i
< stat
->top_outputs
.size(); ++i
)
338 add_argument(fn
->out
,
339 new_var(stat
->top_outputs
[i
], isl_space_copy(space
)),
342 if (!stat
->top_function
)
345 name
= stat
->top_function
->name
->s
.c_str();
346 fn
->name
= isl_id_alloc(ctx
, name
, NULL
);
348 add_expressions(adg
, node
, p_node
, stat
->top_function
);
349 add_arguments(fn
, stat
->top_function
, space
, adg_arg_value
);
351 isl_space_free(space
);
354 /* Return the domain of "p_node" after scheduling.
355 * "id" represents the name of the corresponding adg_node.
357 * If "p_node" has any filters, then its source iteration domain
358 * is a wrapped map with the filters in the range. The computed
359 * node schedule therefore needs to take this filters into account
360 * and so we pass along the domain space to extended_node_schedule().
361 * In particular, we want the filter values to be preserved.
363 static __isl_give isl_set
*scheduled_domain(pdg::node
*p_node
, adg
*adg
,
364 __isl_take isl_id
*id
)
369 domain
= p_node
->source
->get_isl_set();
370 sched
= extended_node_schedule(p_node
, adg
, id
,
371 isl_set_get_space(domain
));
372 domain
= isl_set_apply(domain
, sched
);
377 /* Construct a schedule for the adg_node (called "id") corresponding to
378 * "p_node". Note that the domain of the adg_node is the result of
379 * applying the p_node schedule to its domain, with some of the
380 * dimensions with constant values removed (through composition with
381 * adg->iterator_map). The schedule for the adg_node then simply
382 * needs to insert those dimensions with constant values back.
384 * If "p_node" has any filters, then its source iteration domain
385 * is a wrapped map with the filters in the range. These filters
386 * need to be removed first.
388 static __isl_give isl_map
*adg_node_schedule(pdg::node
*p_node
, adg
*adg
,
389 __isl_take isl_id
*id
)
394 domain
= p_node
->source
->get_isl_set();
395 if (isl_set_is_wrapping(domain
))
396 domain
= isl_map_domain(isl_set_unwrap(domain
));
397 sched
= p_node
->schedule
->get_isl_map();
398 domain
= isl_set_apply(domain
, sched
);
399 sched
= isl_map_reverse(isl_map_copy(adg
->iterator_map
));
400 sched
= isl_map_intersect_range(sched
, domain
);
401 sched
= isl_map_set_tuple_id(sched
, isl_dim_in
, id
);
406 /* Construct the name of the adg_node corresponding to "p_node".
408 static __isl_give isl_id
*node_id(isl_ctx
*ctx
, pdg::node
*p_node
)
412 snprintf(buf
, sizeof(buf
), "ND_%d", p_node
->nr
);
414 return isl_id_alloc(ctx
, buf
, NULL
);
418 static int intersect_local_space(__isl_take isl_basic_set
*bset
,
422 /* Intersect the local space *user with the local space of "bset".
424 static int intersect_local_space(__isl_take isl_basic_set
*bset
,
428 isl_local_space
**res
= (isl_local_space
**)user
;
430 ls
= isl_basic_set_get_local_space(bset
);
431 isl_basic_set_free(bset
);
433 *res
= isl_local_space_intersect(*res
, ls
);
438 /* Find the position of the first filter in "space".
440 * The given space may be the result of one of more nestings of the form
442 * D -> [D -> local[..]]
444 * and/or a nesting of the form
446 * D -> [D -> [dynamic controls]]
448 * and so we may have to dig down until we find a wrapped space
449 * with an unnamed range. If we find it, the position of the filters
450 * in the original space is equal to the number of input dimensions
451 * of that wrapped space. Otherwise, there are no filters and we
454 static int filter_pos(__isl_take isl_space
*space
)
458 if (!isl_space_is_wrapping(space
)) {
459 isl_space_free(space
);
462 space
= isl_space_unwrap(space
);
463 if (isl_space_has_tuple_id(space
, isl_dim_out
))
464 return filter_pos(isl_space_domain(space
));
465 pos
= isl_space_dim(space
, isl_dim_in
);
466 isl_space_free(space
);
470 /* Set the names of the input dimensions of "aff" according
471 * to "filters", "iterators" and "controls".
473 * The first dimensions correspond to the iterators.
474 * The are followed by the iterators, with the filters intermixed.
476 static __isl_give isl_aff
*set_names(__isl_take isl_aff
*aff
,
477 std::vector
<adg_var
*> &filters
, std::vector
<isl_id
*> &iterators
,
478 std::vector
<adg_expr
*> &controls
)
481 int n_in
= isl_aff_dim(aff
, isl_dim_in
);
486 fp
= filter_pos(isl_aff_get_domain_space(aff
));
489 for (int i
= 0; i
< iterators
.size(); ++i
, ++pos
) {
490 isl_id
*id
= isl_id_copy(iterators
[i
]);
491 aff
= isl_aff_set_dim_id(aff
, isl_dim_in
, pos
, id
);
494 for (ci
= 0; ci
< controls
.size() && pos
< fp
; ++ci
, ++pos
) {
495 isl_id
*id
= isl_id_copy(controls
[ci
]->name
);
496 aff
= isl_aff_set_dim_id(aff
, isl_dim_in
, pos
, id
);
499 for (int i
= 0; i
< nf
; ++i
, ++pos
) {
501 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
503 aff
= isl_aff_set_dim_id(aff
, isl_dim_in
, pos
, id
);
506 for (; ci
< controls
.size() && pos
< n_in
; ++ci
, ++pos
) {
507 isl_id
*id
= isl_id_copy(controls
[ci
]->name
);
508 aff
= isl_aff_set_dim_id(aff
, isl_dim_in
, pos
, id
);
514 /* Set the names of the input dimensions of "pa" according
515 * to "filters", "iterators" and "controls".
517 * The first dimensions correspond to the iterators.
518 * The are followed by the iterators, with the filters intermixed.
520 static __isl_give isl_pw_aff
*set_names(__isl_take isl_pw_aff
*pa
,
521 std::vector
<adg_var
*> &filters
, std::vector
<isl_id
*> &iterators
,
522 std::vector
<adg_expr
*> &controls
)
525 int n_in
= isl_pw_aff_dim(pa
, isl_dim_in
);
530 fp
= filter_pos(isl_pw_aff_get_domain_space(pa
));
533 for (int i
= 0; i
< iterators
.size(); ++i
, ++pos
) {
534 isl_id
*id
= isl_id_copy(iterators
[i
]);
535 pa
= isl_pw_aff_set_dim_id(pa
, isl_dim_in
, pos
, id
);
538 for (ci
= 0; ci
< controls
.size() && pos
< fp
; ++ci
, ++pos
) {
539 isl_id
*id
= isl_id_copy(controls
[ci
]->name
);
540 pa
= isl_pw_aff_set_dim_id(pa
, isl_dim_in
, pos
, id
);
543 for (int i
= 0; i
< nf
; ++i
, ++pos
) {
545 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
547 pa
= isl_pw_aff_set_dim_id(pa
, isl_dim_in
, pos
, id
);
550 for (; ci
< controls
.size() && pos
< n_in
; ++ci
, ++pos
) {
551 isl_id
*id
= isl_id_copy(controls
[ci
]->name
);
552 pa
= isl_pw_aff_set_dim_id(pa
, isl_dim_in
, pos
, id
);
558 /* For each of the local variables in "ls", create a corresponding
559 * adg_expr and add it to "controls".
561 * "filters" contains the filters on the corresponding domain.
562 * These filters appear as the first dimensions in "ls" and "filters" is
563 * used to set the names of those dimensions.
565 static void add_controls(adg
*graph
, std::vector
<adg_var
*> &filters
,
566 std::vector
<adg_expr
*> &controls
,
567 __isl_keep isl_local_space
*ls
)
573 ctx
= isl_local_space_get_ctx(ls
);
574 n_div
= isl_local_space_dim(ls
, isl_dim_div
);
576 for (int i
= 0; i
< n_div
; ++i
) {
580 aff
= isl_aff_floor(isl_local_space_get_div(ls
, i
));
581 aff
= set_names(aff
, filters
, graph
->iterators
, controls
);
582 expr
= graph
->to_control(aff
);
583 controls
.push_back(expr
);
587 /* Apply the lifting "lifting" to domain->bounds and eliminate
588 * redundant existentially quantified variables.
590 static void apply_lifting(adg_domain
*domain
,
591 __isl_take isl_basic_map
*lifting
)
593 domain
->bounds
= isl_set_apply(domain
->bounds
,
594 isl_map_from_basic_map(lifting
));
595 domain
->bounds
= isl_set_detect_equalities(domain
->bounds
);
598 /* Add static controls to "domain" based on the existentially quantified
599 * variables in domain->bounds and set *lifting_p to the lifting that
600 * corresponds to the added static controls. That is, the control variables
601 * correspond to the output variable in the nested map in the range
604 * We first collect all existentially quantified variables in a single
605 * local space. If needed, we then add the corresponding static controls,
606 * construct the corresponding lifting and finally apply it to domain->bounds.
608 static void add_static_controls(adg
*graph
, adg_domain
*domain
,
609 __isl_give isl_basic_map
**lifting_p
)
614 isl_basic_map
*lifting
;
616 space
= isl_set_get_space(domain
->bounds
);
617 ls
= isl_local_space_from_space(space
);
618 isl_set_foreach_basic_set(domain
->bounds
, &intersect_local_space
, &ls
);
620 n_div
= isl_local_space_dim(ls
, isl_dim_div
);
622 isl_local_space_free(ls
);
626 add_controls(graph
, domain
->filters
, domain
->controls
, ls
);
628 lifting
= isl_local_space_lifting(ls
);
630 *lifting_p
= isl_basic_map_copy(lifting
);
632 apply_lifting(domain
, lifting
);
635 /* Add copies of the elements in "src" to the back of "dst".
637 static void copy_controls(std::vector
<adg_expr
*> &dst
,
638 std::vector
<adg_expr
*> &src
)
640 for (int i
= 0; i
< src
.size(); ++i
)
641 dst
.push_back(new adg_expr(*src
[i
]));
644 /* Add static controls to the adg_node "node".
645 * In particular, add static controls to node->domain and if any
646 * were added, copy them to node->function->domain and apply the corresponding
649 static void add_static_controls(adg
*graph
, adg_node
*node
)
651 add_static_controls(graph
, node
->domain
, &node
->lifting
);
656 copy_controls(node
->function
->domain
->controls
, node
->domain
->controls
);
657 apply_lifting(node
->function
->domain
,
658 isl_basic_map_copy(node
->lifting
));
661 /* Add an adg_node corresponding to "p_node" to the graph.
662 * We only construct the domain, the schedule, the expressions
664 * The ports and gates are created during the construction of the edges.
666 * The domain of the function may be subjected to filtering,
667 * but the domain of the node itself may not as it should include
668 * the domains of the ports reading the values of the filters.
669 * The filters are therefore projected out from the node domain.
671 static void add_node(isl_ctx
*ctx
, adg
*graph
, pdg::node
*p_node
)
673 adg_node
*node
= new adg_node
;
676 node
->name
= node_id(ctx
, p_node
);
678 domain
= scheduled_domain(p_node
, graph
, isl_id_copy(node
->name
));
679 node
->schedule
= adg_node_schedule(p_node
, graph
,
680 isl_id_copy(node
->name
));
681 set_function(graph
, node
, p_node
, isl_set_copy(domain
));
682 if (isl_set_is_wrapping(domain
))
683 domain
= isl_map_domain(isl_set_unwrap(domain
));
684 domain
= isl_set_from_basic_set(isl_set_simple_hull(domain
));
685 node
->domain
= new adg_domain(domain
);
687 add_static_controls(graph
, node
);
689 graph
->nodes
.push_back(node
);
692 /* Is "name" of the form __last_*?
694 static bool is_control(const char *name
)
696 const char *prefix
= "__last_";
697 size_t prefix_len
= strlen(prefix
);
699 return !strncmp(name
, prefix
, prefix_len
);
702 /* Is "name" of the form __last_<node_name>*?
704 static bool is_local_control(const char *name
, const char *node_name
)
706 const char *prefix
= "__last_";
707 size_t prefix_len
= strlen(prefix
);
709 if (strncmp(name
, prefix
, prefix_len
) != 0)
711 return !strncmp(name
+ prefix_len
, node_name
, strlen(node_name
));
714 /* Is the name of "id" of the form __last_*?
716 static bool is_control(__isl_keep isl_id
*id
)
718 return is_control(isl_id_get_name(id
));
721 /* Is the name of the i-th parameter of map of the form __last_*?
723 static bool is_control(__isl_keep isl_map
*map
, int i
)
726 if (!isl_map_has_dim_id(map
, isl_dim_param
, i
))
728 name
= isl_map_get_dim_name(map
, isl_dim_param
, i
);
729 return is_control(name
);
732 /* Move all parameters of the form __last_* into the range of a wrapped
733 * map with "domain" as domain.
735 * We first create dimensions in the range of the wrapped map, equating
736 * them to the corresponding parameters.
737 * At the end, we project out the parameters.
739 static void move_filters(adg_domain
*domain
)
746 set
= domain
->bounds
;
747 space
= isl_set_get_space(set
);
748 map
= isl_map_from_domain(set
);
750 nparam
= isl_map_dim(map
, isl_dim_param
);
751 for (int i
= 0; i
< nparam
; ++i
) {
754 if (!is_control(map
, i
))
756 pos
= isl_map_dim(map
, isl_dim_out
);
757 map
= isl_map_add_dims(map
, isl_dim_out
, 1);
758 map
= isl_map_equate(map
, isl_dim_param
, i
, isl_dim_out
, pos
);
759 id
= isl_map_get_dim_id(map
, isl_dim_param
, i
);
760 domain
->filters
.push_back(var_from_id(id
,
761 isl_space_copy(space
)));
764 for (int i
= nparam
- 1; i
>= 0; --i
) {
765 if (!is_control(map
, i
))
767 map
= isl_map_project_out(map
, isl_dim_param
, i
, 1);
770 domain
->bounds
= isl_map_wrap(map
);
771 isl_space_free(space
);
774 /* Auxiliary data used during the construction of edges.
776 * dep is the dependence that is being converted into one or more edges.
777 * container is the dependence that contains information about the buffer
778 * size. It may be dep itself or it may be the pn_union dependence
780 * id is the name of the edge.
781 * from_node is the source node.
782 * to_node is the target node.
783 * rel is the original dependence relation.
784 * rel_maff is that part of rel that corresponds to maff.
785 * range_filters_map is a map that adds back the range filters.
786 * maff is the mapping that will be assigned to edge->map.
787 * in_domain is the domain of the input port.
789 * in_filters are filters that should be added to input ports.
790 * out_filters are filters that should be added to output ports.
792 * edge_nr is the sequence number of the original (non-control) edge.
793 * port_nr is a pointer to the sequence number of the port
795 * lifting is the lifting used on the input port domain
796 * in_controls are the controls for the input port domain. They include
797 * the controls of the node domain and the extra controls induced by
800 struct add_edge_data
{
802 pdg::dependence
*dep
;
803 pdg::dependence
*container
;
808 isl_map
*range_filters_map
;
811 isl_basic_set
*in_domain
;
812 enum adg_edge_type type
;
814 std::vector
<adg_var
*> in_filters
;
815 std::vector
<adg_var
*> out_filters
;
820 isl_basic_map
*lifting
;
821 std::vector
<adg_expr
*> in_controls
;
829 /* Set the name of "port" to
831 * <node>IP_<edge>_<nr>_V_<arg_pos>
833 * <node>OP_<edge>_<nr>_V_<arg_pos>
835 * depending on whether type is adg_port_input or adg_port_output.
836 * The name includes the access->nr so that two edges between the same
837 * pair of nodes that have been combined into a single pn_union edge
838 * would not get identical port names.
840 static void set_port_name(adg_port
*port
, enum adg_port_type type
,
841 __isl_keep isl_id
*node_id
, __isl_keep isl_id
*edge_id
, int nr
)
847 ctx
= isl_id_get_ctx(edge_id
);
849 if (type
== adg_port_input
)
853 snprintf(buf
, sizeof(buf
), "%s%s_%s_%d_V_%d",
854 isl_id_get_name(node_id
), s_type
,
855 isl_id_get_name(edge_id
), nr
, port
->arg_pos
);
856 port
->name
= isl_id_alloc(ctx
, buf
, NULL
);
859 /* Set the binding variable on "port" by calling new_var().
861 static void set_port_var(adg_port
*port
, pdg::access
*access
,
862 __isl_keep isl_basic_set
*bset
)
867 space
= isl_basic_set_get_space(bset
);
868 var
= new_var(access
, space
);
869 port
->vars
.push_back(var
);
872 /* Set the binding variables on "port", one for each element
873 * in "dep_controls". These binding variables are control variables
874 * and they are assumed to of tyoe "int".
876 static void set_port_vars(adg_port
*port
, seq
<str
> &dep_controls
,
877 __isl_keep isl_basic_set
*bset
)
882 ctx
= isl_basic_set_get_ctx(bset
);
883 space
= isl_basic_set_get_space(bset
);
884 for (int i
= 0; i
< dep_controls
.size(); ++i
) {
888 id
= isl_id_alloc(ctx
, dep_controls
[i
]->s
.c_str(), NULL
);
889 var
= var_from_id(id
, isl_space_copy(space
));
890 var
->type
= isl_id_alloc(ctx
, "int", NULL
);
891 port
->vars
.push_back(var
);
893 isl_space_free(space
);
896 /* Create and return a port of type "type" belonging to the node called
897 * "node_id" and attached to the edge called "edge_id".
898 * "access" is the corresponding access.
899 * "nr" is the sequence number.
900 * "dep_controls" contains the names of the control variables.
901 * If the list contains any elements, then the port is connected
902 * to a control edge. Otherwise, it is connected to a data edge.
903 * "bset" represents the iteration domain and "controls" are the controls
904 * that need to be added based on previous liftings.
905 * If "bset" has any existentially quantified variables left, then we
906 * need to apply an additional lifting and append the corresponding
908 * "filters" contains the dynamic controls.
910 static adg_port
*create_port(adg
*graph
, enum adg_port_type type
,
911 seq
<str
> &dep_controls
, pdg::access
*access
,
912 __isl_keep isl_id
*node_id
, __isl_keep isl_id
*edge_id
, int edge_nr
,
913 int nr
, __isl_take isl_basic_set
*bset
,
914 std::vector
<adg_var
*> &filters
, std::vector
<adg_expr
*> &controls
)
916 isl_local_space
*ls
= NULL
;
917 adg_port
*port
= new adg_port
;
919 port
->edge_nr
= edge_nr
;
921 if (isl_basic_set_dim(bset
, isl_dim_div
) != 0) {
922 isl_basic_map
*lifting
;
923 ls
= isl_basic_set_get_local_space(bset
);
924 lifting
= isl_local_space_lifting(isl_local_space_copy(ls
));
925 bset
= isl_basic_set_apply(bset
, lifting
);
926 bset
= isl_basic_set_detect_equalities(bset
);
929 port
->arg_pos
= access
->nr
;
930 set_port_name(port
, type
, node_id
, edge_id
, nr
);
932 port
->node_name
= isl_id_copy(node_id
);
933 port
->edge_name
= isl_id_copy(edge_id
);
934 if (dep_controls
.size() > 0)
935 set_port_vars(port
, dep_controls
, bset
);
937 set_port_var(port
, access
, bset
);
938 port
->domain
= new adg_domain(isl_set_from_basic_set(bset
));
940 for (int i
= 0; i
< filters
.size(); ++i
)
941 port
->domain
->filters
.push_back(new adg_var(*filters
[i
]));
942 for (int i
= 0; i
< controls
.size(); ++i
)
943 port
->domain
->controls
.push_back(new adg_expr(*controls
[i
]));
946 add_controls(graph
, filters
, port
->domain
->controls
, ls
);
947 isl_local_space_free(ls
);
953 /* Add an edge with a convex input port domain (data->in_domain)
954 * and a convex output port domain (bset).
956 * We create the two ports, add them to the appropriate nodes
957 * and add an edge that referes to the nodes and ports.
959 static int add_edge_basic_in(__isl_take isl_basic_set
*bset
, void *user
)
961 add_edge_data
*data
= (add_edge_data
*) user
;
962 adg_edge
*edge
= new adg_edge
;
965 edge
->name
= isl_id_copy(data
->id
);
966 edge
->type
= data
->type
;
967 edge
->map
= isl_multi_aff_copy(data
->maff
);
968 edge
->from_node_name
= isl_id_copy(data
->from_node
->name
);
969 edge
->to_node_name
= isl_id_copy(data
->to_node
->name
);
970 if (data
->container
->value_size
)
971 isl_int_set_si(edge
->value_size
,
972 data
->container
->value_size
->v
);
974 port
= create_port(data
->graph
, adg_port_input
,
975 data
->dep
->to_controls
, data
->dep
->to_access
,
976 data
->to_node
->name
, data
->id
, data
->edge_nr
,
977 *data
->port_nr
, isl_basic_set_copy(data
->in_domain
),
978 data
->in_filters
, data
->in_controls
);
979 data
->to_node
->input_ports
.push_back(port
);
980 edge
->to_port_name
= isl_id_copy(port
->name
);
982 port
= create_port(data
->graph
, adg_port_output
,
983 data
->dep
->from_controls
, data
->dep
->from_access
,
984 data
->from_node
->name
, data
->id
, data
->edge_nr
,
985 *data
->port_nr
, isl_basic_set_copy(bset
),
986 data
->out_filters
, data
->from_node
->domain
->controls
);
987 data
->from_node
->output_ports
.push_back(port
);
988 edge
->from_port_name
= isl_id_copy(port
->name
);
992 data
->graph
->edges
.push_back(edge
);
994 isl_basic_set_free(bset
);
998 /* Does the list of (dynamic) control variables node->function->ctrl
1001 static bool has_control(adg_node
*node
, __isl_keep isl_id
*id
)
1003 for (int i
= 0; i
< node
->function
->ctrl
.size(); ++i
)
1004 if (node
->function
->ctrl
[i
]->name
== id
)
1009 /* Add elements to node->function->ctrl for each element of dep->from_controls,
1010 * provided the element is not in there already.
1012 * We only add elements that correspond to last iterations of "node".
1013 * The given dependence may be the result of reuse detection and may
1014 * therefore propagate controls from one access to the next in some other
1017 * To find out which value should be assigned to the variable,
1018 * we look at the final part of the name of the control variable.
1019 * If this final part is "valid", then the function needs
1020 * to assign the value "1". Otherwise, the final part is
1021 * an integer and then we should assign the value of the
1022 * original corresponding iterator. This value is obtained
1023 * from the schedule of "node".
1025 static void add_control_variables(isl_ctx
*ctx
, adg
*graph
, adg_node
*node
,
1026 pdg::dependence
*dep
)
1029 isl_pw_multi_aff
*pma
;
1031 isl_local_space
*ls
;
1034 if (dep
->from_controls
.size() == 0)
1037 sched
= node_schedule(dep
->from
, graph
, isl_id_copy(node
->name
));
1038 node_name
= strdup(isl_map_get_tuple_name(sched
, isl_dim_in
));
1039 pma
= isl_pw_multi_aff_from_map(isl_map_reverse(sched
));
1040 space
= isl_set_get_space(node
->domain
->bounds
);
1041 ls
= isl_local_space_from_space(space
);
1043 for (int i
= 0; i
< dep
->from_controls
.size(); ++i
) {
1049 const char *name
= dep
->from_controls
[i
]->s
.c_str();
1051 id
= isl_id_alloc(ctx
, name
, NULL
);
1053 if (!is_local_control(name
, node_name
) ||
1054 has_control(node
, id
)) {
1059 name
= strrchr(name
, '_');
1062 expr
= new adg_expr
;
1063 if (!strcmp(name
+ 1, "valid")) {
1064 aff
= isl_aff_zero_on_domain(isl_local_space_copy(ls
));
1065 aff
= isl_aff_add_constant_si(aff
, 1);
1066 pa
= isl_pw_aff_from_aff(aff
);
1068 pos
= atoi(name
+ 1);
1069 pa
= isl_pw_multi_aff_get_pw_aff(pma
, pos
);
1072 pa
= set_names(pa
, node
->domain
->filters
, graph
->iterators
,
1073 node
->domain
->controls
);
1077 node
->function
->ctrl
.push_back(expr
);
1081 isl_local_space_free(ls
);
1082 isl_pw_multi_aff_free(pma
);
1085 /* Add an edge with a convex input port domain (bset).
1086 * We compute the corresponding output port domain by applying
1087 * the dependence relation and apply the required liftings in the from
1088 * domain. The result may in principle be a union again and so we
1089 * split that up as well.
1090 * The lifting required by the input port (computed in add_edge_set)
1091 * is applied to the input port domain. Note that we cannot
1092 * apply this lifting earlier because we need the original input port
1093 * domain (without lifting) to compute the output port domain.
1095 static int add_edge_basic(__isl_take isl_basic_set
*bset
, void *user
)
1098 add_edge_data
*data
= (add_edge_data
*) user
;
1101 data
->in_domain
= isl_basic_set_copy(bset
);
1102 data
->in_domain
= isl_basic_set_apply(data
->in_domain
,
1103 isl_basic_map_copy(data
->lifting
));
1104 data
->in_domain
= isl_basic_set_detect_equalities(data
->in_domain
);
1105 set
= isl_set_from_basic_set(bset
);
1106 set
= isl_set_apply(set
, isl_map_copy(data
->rel_maff
));
1107 if (data
->from_node
->lifting
)
1108 set
= isl_set_apply(set
, isl_map_from_basic_map(
1109 isl_basic_map_copy(data
->from_node
->lifting
)));
1110 set
= isl_set_detect_equalities(set
);
1111 set
= isl_set_compute_divs(set
);
1113 r
= isl_set_foreach_basic_set(set
, &add_edge_basic_in
, data
);
1116 isl_basic_set_free(data
->in_domain
);
1120 /* Save a copy of "map" in data->rel, remove the range filters (if any)
1121 * and return the result. The inverse of the mapping used to remove
1122 * the filters is save in data->range_filters_map so that the filters
1123 * can be reintroduced in insert_range_filters.
1125 static __isl_give isl_map
*remove_range_filters(__isl_take isl_map
*map
,
1126 add_edge_data
*data
)
1130 data
->rel
= isl_map_copy(map
);
1132 space
= isl_space_range(isl_map_get_space(map
));
1133 if (isl_space_is_wrapping(space
)) {
1134 isl_map
*fm
= isl_map_universe(isl_space_unwrap(space
));
1135 fm
= isl_map_domain_map(fm
);
1136 map
= isl_map_apply_range(map
, isl_map_copy(fm
));
1137 data
->range_filters_map
= isl_map_reverse(fm
);
1139 space
= isl_space_map_from_set(space
);
1140 data
->range_filters_map
= isl_map_identity(space
);
1146 /* Reintroduce the filters removed in remove_range_filters.
1148 static __isl_give isl_map
*insert_range_filters(__isl_take isl_map
*map
,
1149 add_edge_data
*data
)
1151 map
= isl_map_apply_range(map
, isl_map_copy(data
->range_filters_map
));
1152 map
= isl_map_intersect(map
, isl_map_copy(data
->rel
));
1157 /* Add edges for the given input port domain (set) and the given
1158 * mapping (maff) to the corresponding output port domain.
1160 * The lifting corresponding to the domain of the destination node
1161 * has already been applied, but "maff" may require additional liftings
1162 * to be applied to the input port domain.
1164 * The given isl_multi_aff is also converted into an isl_basic_map
1165 * for use in the construction of the output port domains
1166 * in add_edge_basic(). After the conversion, we add back the filters
1167 * that were removed right before the call to isl_pw_multi_aff_from_map.
1169 static int add_edge_set(__isl_take isl_set
*set
, __isl_take isl_multi_aff
*maff
,
1172 add_edge_data
*data
= (add_edge_data
*) user
;
1174 isl_local_space
*ls
;
1176 data
->rel_maff
= isl_map_from_basic_map(isl_basic_map_from_multi_aff(
1177 isl_multi_aff_copy(maff
)));
1178 data
->rel_maff
= insert_range_filters(data
->rel_maff
, data
);
1180 maff
= isl_multi_aff_lift(maff
, &ls
);
1181 copy_controls(data
->in_controls
, data
->to_node
->domain
->controls
);
1182 add_controls(data
->graph
, data
->in_filters
, data
->in_controls
, ls
);
1183 data
->lifting
= isl_local_space_lifting(ls
);
1187 r
= isl_set_foreach_basic_set(set
, &add_edge_basic
, data
);
1189 for (int i
= 0; i
< data
->in_controls
.size(); ++i
)
1190 delete data
->in_controls
[i
];
1191 data
->in_controls
.clear();
1192 isl_basic_map_free(data
->lifting
);
1194 isl_map_free(data
->rel_maff
);
1195 isl_multi_aff_free(maff
);
1200 /* Does "rel" have any parameter of the form __last_*?
1202 static bool has_filters(__isl_keep isl_map
*rel
)
1206 nparam
= isl_map_dim(rel
, isl_dim_param
);
1207 for (int i
= 0; i
< nparam
; ++i
)
1208 if (is_control(rel
, i
))
1214 /* Remove all parameters of the form __last_* from "rel".
1216 static __isl_give isl_map
*remove_all_filters(__isl_take isl_map
*rel
)
1220 nparam
= isl_map_dim(rel
, isl_dim_param
);
1221 for (int i
= nparam
- 1; i
>= 0; --i
) {
1222 if (!is_control(rel
, i
))
1224 rel
= isl_map_project_out(rel
, isl_dim_param
, i
, 1);
1230 /* For each parameter of the form __last_*, add it to both
1231 * data->in_filters and data->out_filters, in each case defined
1232 * over the appropriate domain.
1234 static void extract_filters(__isl_take isl_map
*rel
, add_edge_data
*data
)
1241 nparam
= isl_map_dim(rel
, isl_dim_param
);
1242 space
= isl_map_get_space(rel
);
1243 domain
= isl_space_domain(isl_space_copy(space
));
1244 range
= isl_space_range(space
);
1246 for (int i
= 0; i
< nparam
; ++i
) {
1249 if (!is_control(rel
, i
))
1252 id
= isl_map_get_dim_id(rel
, isl_dim_param
, i
);
1253 data
->in_filters
.push_back(var_from_id(isl_id_copy(id
),
1254 isl_space_copy(domain
)));
1255 data
->out_filters
.push_back(var_from_id(id
,
1256 isl_space_copy(range
)));
1259 isl_space_free(domain
);
1260 isl_space_free(range
);
1263 /* Replace both domain and range of "rel" by a wrapped map with the original
1264 * set as domain and dimensions corresponding to the parameters
1265 * referenced by the elements in "filters" as range and then
1266 * remove those parameters.
1267 * The overall effect is that those parameters are moved into
1268 * the ranges of the wrapped maps.
1270 static __isl_give isl_map
*move_filters(__isl_take isl_map
*rel
,
1271 std::vector
<adg_var
*> &filters
)
1276 space
= isl_space_domain(isl_map_get_space(rel
));
1277 space
= isl_space_from_domain(space
);
1278 map
= isl_map_universe(space
);
1279 for (int i
= 0; i
< filters
.size(); ++i
) {
1284 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
1286 param
= isl_map_find_dim_by_id(map
, isl_dim_param
, id
);
1289 pos
= isl_map_dim(map
, isl_dim_out
);
1290 map
= isl_map_add_dims(map
, isl_dim_out
, 1);
1291 map
= isl_map_equate(map
, isl_dim_param
, param
,
1294 map
= isl_map_domain_map(map
);
1296 rel
= isl_map_apply_range(map
, rel
);
1298 for (int i
= 0; i
< filters
.size(); ++i
) {
1302 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
1304 param
= isl_map_find_dim_by_id(rel
, isl_dim_param
, id
);
1307 rel
= isl_map_project_out(rel
, isl_dim_param
, param
, 1);
1310 space
= isl_space_unwrap(isl_space_domain(isl_map_get_space(rel
)));
1311 map
= isl_map_range_map(isl_map_universe(space
));
1312 rel
= isl_map_range_product(rel
, map
);
1317 /* Determine the appropriate adg_edge_type for "dep".
1319 static enum adg_edge_type
dep_type(pdg::dependence
*dep
)
1321 if (dep
->reordering
)
1322 return adg_edge_generic
;
1323 else if (dep
->multiplicity
)
1324 return adg_edge_broadcast
;
1325 else if (dep
->type
== pdg::dependence::pn_shift
)
1326 return adg_edge_shift_register
;
1328 return adg_edge_fifo
;
1331 /* Lift the domain of the relation "rel" according to domain->lifting,
1334 static __isl_give isl_map
*lift(__isl_take isl_map
*rel
, adg_node
*domain
)
1336 if (domain
->lifting
) {
1337 isl_basic_map
*lifting
;
1338 lifting
= isl_basic_map_copy(domain
->lifting
);
1339 rel
= isl_map_apply_domain(rel
,
1340 isl_map_from_basic_map(lifting
));
1341 rel
= isl_map_detect_equalities(rel
);
1347 /* If "dom" is a wrapped map, then add the filters of "node" to "filters".
1349 static void extract_domain_filters(__isl_take isl_space
*space
,
1350 std::vector
<adg_var
*> &filters
, pdg::node
*node
)
1352 if (isl_space_is_wrapping(space
))
1353 add_domain_filters(filters
, node
, space
);
1355 isl_space_free(space
);
1358 /* Extract the filters on the output and input ports from the "from"
1359 * and "to" nodes of "rel".
1361 static void extract_domain_filters(__isl_keep isl_map
*rel
, add_edge_data
*data
)
1365 space
= isl_space_domain(isl_map_get_space(rel
));
1366 extract_domain_filters(space
, data
->out_filters
, data
->dep
->from
);
1368 space
= isl_space_range(isl_map_get_space(rel
));
1369 extract_domain_filters(space
, data
->in_filters
, data
->dep
->to
);
1372 /* Add edges corresponding to "dep" for the relation "rel", which
1373 * may be a subset of dep->relation or dep->controlled_relation.
1374 * "container" contains information about the required buffer sizes.
1375 * "id" is the name that should be given to all created edges.
1376 * "type" is the type of the created edges.
1378 * If "dep" is a control dependence (i.e., if it has elements
1379 * in its from_controls), then add corresponding control variable
1382 * We schedule the domain and range of "rel" and extract the
1383 * domain filters for later use. We currently only allow such filters
1384 * on dependences without control parameters.
1386 * Since "rel" is a subset of dep->relation, it has the source as domain
1387 * and the sink as range. Since the mapping on the edges map iterations
1388 * of the input port to iterations of the corresponding output port,
1389 * we need to invert it. Then we apply the lifting on the
1390 * sink node as the initial static control variables on the input port domain
1391 * need to be the same as those of the node domain. The control variables
1392 * themselves are copied to data->in_controls in add_edge_set().
1394 * If "type" is adg_edge_broadcast, then a given write may be
1395 * associated to several (consecutive) reads in the original
1396 * dependence relation. In the ADG, we only associate the write
1397 * with the first of those reads.
1399 * If there are any dynamic control variables, we extract them
1400 * and move them from the parameters into the domain of the relation.
1401 * For loops however, we simply remove them.
1403 * Finally, we construct an explicit function representation of the
1404 * mapping and consider each convex piece of the domain in turn.
1405 * This is needed because the bounds on the domains have to be convex.
1406 * Before we compute this explicit representation, we first remove
1407 * the range filters as they may not be uniquely defined in terms
1408 * of the source filters.
1410 static void add_edge(PDG
*pdg
, adg
*adg
,
1411 pdg::dependence
*dep
, pdg::dependence
*container
,
1412 __isl_take isl_id
*id
, int edge_nr
, __isl_take isl_map
*rel
,
1413 enum adg_edge_type type
, int *port_nr
)
1416 isl_pw_multi_aff
*pma
;
1418 add_edge_data data
= { adg
, dep
, container
};
1420 ctx
= pdg
->get_isl_ctx();
1424 data
.from_node
= adg
->node_by_id(node_id(ctx
, dep
->from
));
1425 data
.to_node
= adg
->node_by_id(node_id(ctx
, dep
->to
));
1426 data
.port_nr
= port_nr
;
1427 data
.edge_nr
= edge_nr
;
1429 add_control_variables(ctx
, adg
, data
.from_node
, dep
);
1431 rel
= isl_map_apply_domain(rel
,
1432 extended_node_schedule(dep
->from
, adg
,
1433 isl_id_copy(data
.from_node
->name
),
1434 isl_space_domain(isl_map_get_space(rel
))));
1435 rel
= isl_map_apply_range(rel
,
1436 extended_node_schedule(dep
->to
, adg
,
1437 isl_id_copy(data
.to_node
->name
),
1438 isl_space_range(isl_map_get_space(rel
))));
1439 extract_domain_filters(rel
, &data
);
1440 if (data
.out_filters
.size() || data
.in_filters
.size())
1441 assert(!dep
->controlled_relation
);
1442 if (type
== adg_edge_broadcast
)
1443 rel
= isl_map_lexmin(rel
);
1444 rel
= isl_map_reverse(rel
);
1445 rel
= lift(rel
, data
.to_node
);
1446 if (has_filters(rel
)) {
1447 if (dep
->from
!= dep
->to
) {
1448 extract_filters(rel
, &data
);
1449 rel
= move_filters(rel
, data
.in_filters
);
1451 rel
= remove_all_filters(rel
);
1453 rel
= remove_range_filters(rel
, &data
);
1454 pma
= isl_pw_multi_aff_from_map(rel
);
1456 res
= isl_pw_multi_aff_foreach_piece(pma
, &add_edge_set
, &data
);
1458 isl_pw_multi_aff_free(pma
);
1459 isl_map_free(data
.range_filters_map
);
1460 isl_map_free(data
.rel
);
1461 isl_id_free(data
.id
);
1463 for (int i
= 0; i
< data
.in_filters
.size(); ++i
)
1464 delete data
.in_filters
[i
];
1465 for (int i
= 0; i
< data
.out_filters
.size(); ++i
)
1466 delete data
.out_filters
[i
];
1469 /* Create and return an isl_id of the form
1475 * depending on whether "dep" is a control edge.
1477 static __isl_give isl_id
*edge_name(isl_ctx
*ctx
, pdg::dependence
*dep
, int i
)
1480 bool control
= dep
->from_controls
.size() > 0;
1482 snprintf(buf
, sizeof(buf
), "%sED_%d", control
? "C" : "", i
);
1483 return isl_id_alloc(ctx
, buf
, NULL
);
1486 /* Add edges corresponding to "dep".
1487 * "container" contains information about the required buffer sizes.
1488 * "id" is the name that should be given to all created edges.
1490 * The dependence relation is taken from dep->controlled_relation
1491 * if it is not NULL. Otherwise, it is taken from dep->relation.
1493 * We look for pn_hold dependences with overlapping domains.
1494 * A pn_hold dependence signifies that a value available at the source
1495 * iteration should be kept until the next iteration (= target of dependence).
1496 * If there is a pn_hold dependence whose domain overlaps with the target
1497 * of the given dependence, then we turn the entire given dependence into a
1499 * If no such pn_hold dependences were found, then a regular fifo edge
1502 * Obviously, we should only convert fifos into sticky fifos.
1503 * So, if "dep" is not a fifo, we should just construct fifos for
1504 * the associated pn_hold dependences.
1506 static void add_edge(PDG
*pdg
, adg
*adg
,
1507 pdg::dependence
*dep
, pdg::dependence
*container
,
1508 __isl_take isl_id
*id
, int edge_nr
, int *port_nr
)
1512 enum adg_edge_type type
;
1514 ctx
= pdg
->get_isl_ctx();
1516 assert(dep
->type
== pdg::dependence::pn
||
1517 dep
->type
== pdg::dependence::pn_shift
||
1518 dep
->type
== pdg::dependence::pn_part
||
1519 dep
->type
== pdg::dependence::flow
);
1521 if (dep
->controlled_relation
)
1522 rel
= dep
->controlled_relation
->get_isl_map();
1524 rel
= dep
->relation
->get_isl_map();
1526 type
= dep_type(dep
);
1528 for (int j
= 0; j
< pdg
->dependences
.size(); ++j
) {
1529 pdg::dependence
*hold_dep
= pdg
->dependences
[j
];
1533 if (hold_dep
->type
!= pdg::dependence::pn_hold
)
1535 if (dep
->to
!= hold_dep
->from
)
1537 if (dep
->array
!= hold_dep
->array
)
1539 if (dep
->to_access
!= hold_dep
->from_access
)
1542 if (hold_dep
->controlled_relation
)
1543 hold_rel
= hold_dep
->controlled_relation
->get_isl_map();
1545 hold_rel
= hold_dep
->relation
->get_isl_map();
1547 if (type
!= adg_edge_fifo
) {
1548 isl_id
*id
= edge_name(ctx
, pdg
->dependences
[j
], j
);
1549 add_edge(pdg
, adg
, hold_dep
, hold_dep
, id
, edge_nr
,
1550 hold_rel
, adg_edge_fifo
, port_nr
);
1554 hold_rel
= isl_map_intersect_range(isl_map_copy(rel
),
1555 isl_map_domain(hold_rel
));
1556 empty
= isl_map_is_empty(hold_rel
);
1557 isl_map_free(hold_rel
);
1560 type
= adg_edge_sticky_fifo
;
1565 add_edge(pdg
, adg
, dep
, container
, id
, edge_nr
, rel
, type
, port_nr
);
1568 /* Add edges corresponding to "dep".
1569 * If it is a pn_union, then all the corresponding pn_part dependences
1570 * should get the same name.
1572 static void add_union_edge(PDG
*pdg
, adg
*adg
, pdg::dependence
*dep
, int i
)
1578 ctx
= pdg
->get_isl_ctx();
1580 id
= edge_name(ctx
, dep
, i
);
1582 if (dep
->type
!= pdg::dependence::pn_union
) {
1583 add_edge(pdg
, adg
, dep
, dep
, id
, i
, &port_nr
);
1587 for (int j
= 0; j
< pdg
->dependences
.size(); ++j
) {
1588 pdg::dependence
*part_dep
= pdg
->dependences
[j
];
1590 if (part_dep
->type
!= pdg::dependence::pn_part
)
1592 if (part_dep
->container
!= dep
)
1595 add_edge(pdg
, adg
, part_dep
, dep
, isl_id_copy(id
), i
, &port_nr
);
1601 /* Auxiliary data used during the construction of gates.
1603 * nr points to the sequence number.
1604 * from is the source access.
1605 * to is the target access.
1607 struct add_wire_data
{
1615 static int add_wire_basic(__isl_take isl_basic_set
*bset
, void *user
);
1618 /* Add a wire with the convex domain "bset".
1619 * To ensure that the initial control variables of the gate are the same as
1620 * that of the node, we apply the node lifting, if any, and copy the
1621 * static control variables.
1623 * If there are any dynamic control variables among the parameters,
1624 * we move them into the domain of a wrapped map.
1626 static int add_wire_basic(__isl_take isl_basic_set
*bset
, void *user
)
1628 add_wire_data
*data
= (add_wire_data
*) user
;
1631 adg_gate
*gate
= new adg_gate
;
1634 ctx
= isl_basic_set_get_ctx(bset
);
1635 space
= isl_basic_set_get_space(bset
);
1636 snprintf(buf
, sizeof(buf
), "%sID_%d",
1637 isl_id_get_name(data
->node
->name
), (*data
->nr
)++);
1638 gate
->name
= isl_id_alloc(ctx
, buf
, NULL
);
1639 gate
->in
= new_var(data
->from
, isl_space_copy(space
));
1640 gate
->out
= new_var(data
->to
, space
);
1642 gate
->node_name
= isl_id_copy(data
->node
->name
);
1643 gate
->domain
= new adg_domain(isl_set_from_basic_set(bset
));
1644 if (data
->node
->lifting
)
1645 apply_lifting(gate
->domain
,
1646 isl_basic_map_copy(data
->node
->lifting
));
1647 copy_controls(gate
->domain
->controls
, data
->node
->domain
->controls
);
1648 move_filters(gate
->domain
);
1650 data
->node
->gates
.push_back(gate
);
1655 /* Add gates corresponding to the wire "dep".
1656 * "g_nr" is a pointer to the sequence number of gates.
1657 * The gate copies the variable corresponding to the from_access
1658 * to the variable corresponding to the to_access.
1659 * The dependence relation of a wire is always an identity relation,
1660 * so we can just take the domain of this relation.
1661 * We schedule both this domain and the dynamic control variables, if any.
1663 static void add_wire_gate(PDG
*pdg
, adg
*adg
, pdg::dependence
*dep
, int *g_nr
)
1669 struct add_wire_data data
= { g_nr
, dep
->from_access
, dep
->to_access
};
1671 ctx
= pdg
->get_isl_ctx();
1672 rel
= dep
->relation
->get_isl_map();
1673 dom
= isl_map_domain(rel
);
1674 id
= node_id(ctx
, dep
->to
);
1675 data
.node
= adg
->node_by_id(isl_id_copy(id
));
1676 dom
= isl_set_apply(dom
, node_schedule(dep
->from
, adg
, id
));
1677 isl_set_foreach_basic_set(dom
, &add_wire_basic
, &data
);
1681 /* Construct a map that removes some dimensions with known constant
1682 * values from the schedule domain.
1683 * The map is stored in graph->iterator_map.
1684 * Names for the iterators in the domain of this map are stored
1685 * in graph->all_iterators, while the names for the iterators
1686 * that are kept in the range are stored in graph->iterators.
1688 static void compute_iterator_map(adg
*graph
, PDG
*pdg
)
1697 n_all
= pdg
->statement_dimensions
.size();
1699 for (int i
= 0; i
< n_all
; ++i
)
1700 if (!pdg
->statement_dimensions
[i
])
1703 ctx
= pdg
->get_isl_ctx();
1704 space
= isl_space_alloc(ctx
, 0, n_all
, n_it
);
1705 map
= isl_map_universe(space
);
1708 for (int i
= 0; i
< n_all
; ++i
) {
1711 snprintf(buf
, sizeof(buf
), "c%d", i
);
1712 id
= isl_id_alloc(ctx
, buf
, NULL
);
1713 graph
->all_iterators
.push_back(id
);
1714 map
= isl_map_set_dim_id(map
, isl_dim_in
, i
, isl_id_copy(id
));
1716 if (pdg
->statement_dimensions
[i
])
1718 graph
->iterators
.push_back(isl_id_copy(id
));
1719 map
= isl_map_equate(map
, isl_dim_in
, i
, isl_dim_out
, n_it
);
1723 graph
->iterator_map
= map
;
1726 /* Create an adg_param for each element in pdg->params.
1727 * param->val is obtained from pdg->params[i]->value, if any.
1728 * param->lb and param->ub are obtained from the context.
1730 static void extract_params(adg
*adg
, PDG
*pdg
)
1733 isl_local_space
*ls
;
1736 ctx
= pdg
->get_isl_ctx();
1738 ls
= isl_local_space_from_space(isl_set_get_space(adg
->context
));
1740 for (int i
= 0; i
< pdg
->params
.size(); ++i
) {
1741 adg_param
*param
= new adg_param
;
1743 enum isl_lp_result res
;
1745 param
->name
= isl_id_alloc(ctx
, pdg
->params
[i
]->name
->s
.c_str(),
1747 if (pdg
->params
[i
]->value
) {
1748 isl_space
*space
= isl_space_params_alloc(ctx
, 0);
1749 isl_local_space
*ls
= isl_local_space_from_space(space
);
1750 param
->val
= isl_aff_zero_on_domain(ls
);
1751 param
->val
= isl_aff_add_constant_si(param
->val
,
1752 pdg
->params
[i
]->value
->v
);
1755 aff
= isl_aff_zero_on_domain(isl_local_space_copy(ls
));
1756 aff
= isl_aff_add_coefficient_si(aff
, isl_dim_param
, i
, 1);
1757 res
= isl_set_min(adg
->context
, aff
, &v
);
1758 if (res
== isl_lp_ok
) {
1759 isl_space
*space
= isl_space_params_alloc(ctx
, 0);
1760 isl_local_space
*ls
= isl_local_space_from_space(space
);
1761 param
->lb
= isl_aff_zero_on_domain(ls
);
1762 param
->lb
= isl_aff_add_constant(param
->lb
, v
);
1764 res
= isl_set_max(adg
->context
, aff
, &v
);
1765 if (res
== isl_lp_ok
) {
1766 isl_space
*space
= isl_space_params_alloc(ctx
, 0);
1767 isl_local_space
*ls
= isl_local_space_from_space(space
);
1768 param
->ub
= isl_aff_zero_on_domain(ls
);
1769 param
->ub
= isl_aff_add_constant(param
->ub
, v
);
1773 adg
->params
.push_back(param
);
1776 isl_local_space_free(ls
);
1779 /* Comparison function for sorting input ports such that
1780 * first, for input ports that are connected to the same edge,
1781 * an input port corresponding to an earlier argument position comes
1782 * before an input port corresponding to a later argument position, and,
1783 * second, ports that have dynamic control variables in their domains
1784 * come after the ports that set those control variables.
1786 * The first is needed for pn_union channels as their detection allows for
1787 * the case where two (or more) tokens are consumed in the same iteration,
1788 * provided they don't cause reordering when consumed in the order of
1791 * The second is needed to ensure that the values of the dynamic
1792 * control variables are available when they are needed.
1793 * We enforce the second property by sorting the edges based
1794 * on their depth with respect to dependences between input ports.
1795 * It would probably make more sense to perform a topological sort,
1796 * but it seemed easier to implement it this way.
1798 * If two edges are equivalent with respect to the above two criteria,
1799 * then we arbitrarily sort them according to the edge sequence number.
1801 struct less_input_port
:
1802 public std::binary_function
<adg_port
*, adg_port
*, bool> {
1803 std::vector
<adg_port
*> ports
;
1805 less_input_port(std::vector
<adg_port
*> &ports
) : ports(ports
) {}
1807 bool writes(adg_port
*p
, __isl_keep isl_id
*id
);
1808 int depth(adg_port
*p
);
1810 bool operator()(adg_port
*x
, adg_port
*y
) {
1811 int depth_x
, depth_y
;
1813 if (x
->edge_nr
== y
->edge_nr
)
1814 return x
->arg_pos
< y
->arg_pos
;
1818 if (depth_x
!= depth_y
)
1819 return depth_x
< depth_y
;
1820 return x
->edge_nr
< y
->edge_nr
;
1824 /* Does "p" write to the variable "id"?
1826 bool less_input_port::writes(adg_port
*p
, __isl_keep isl_id
*id
)
1828 for (int i
= 0; i
< p
->vars
.size(); ++i
) {
1829 adg_var
*var
= p
->vars
[i
];
1831 var_id
= isl_pw_multi_aff_get_tuple_id(var
->access
,
1833 isl_id_free(var_id
);
1841 /* Return 0 if this port does not involve any filters.
1842 * Otherwise, return 1 plus the maximal depth of any port
1843 * writing a filter used in the domain of the current port.
1844 * We obviously assume here that there are no cyclic dependences.
1846 int less_input_port::depth(adg_port
*p
)
1850 for (int i
= 0; i
< p
->domain
->filters
.size(); ++i
) {
1851 adg_var
*filter
= p
->domain
->filters
[i
];
1853 id
= isl_pw_multi_aff_get_tuple_id(filter
->access
, isl_dim_out
);
1854 for (int j
= 0; j
< ports
.size(); ++j
) {
1856 if (writes(ports
[j
], id
)) {
1857 d
= depth(ports
[j
]) + 1;
1868 /* Sort the input ports of the given node.
1870 static void sort_input_ports(adg_node
*node
)
1872 std::sort(node
->input_ports
.begin(), node
->input_ports
.end(),
1873 less_input_port(node
->input_ports
));
1876 /* Sort the input ports in all nodes of the graph.
1878 static void sort_input_ports(adg
*graph
)
1880 for (int i
= 0; i
< graph
->nodes
.size(); ++i
)
1881 sort_input_ports(graph
->nodes
[i
]);
1884 /* Rename the dynamic control "id" from __last_*_valid to
1888 * and from __last_*_<i> to
1890 * dc<nr>_<node>_c<i>
1892 * "prefix_map" maps previously translated prefixes __last_*
1893 * (within the same node) to their dc<nr>_<node> translations.
1894 * If we can't find the current prefix in this cache, we create
1895 * a new name and add it to the cache.
1897 static __isl_give isl_id
*rename_dynamic_control(__isl_take isl_id
*id
,
1898 const char *node_name
,
1899 std::map
<std::string
, std::string
> &prefix_map
)
1901 const char *name
, *underscore
;
1903 std::ostringstream strm
;
1906 if (!is_control(id
))
1909 name
= isl_id_get_name(id
);
1910 underscore
= strrchr(name
, '_');
1913 old
= string(name
, underscore
- name
);
1914 if (prefix_map
.find(old
) == prefix_map
.end()) {
1915 strm
<< "dc" << prefix_map
.size() << "_" << node_name
;
1916 prefix_map
[old
] = strm
.str();
1920 strm
<< prefix_map
[old
] << "_";
1921 if (!strcmp(underscore
+ 1, "valid"))
1924 strm
<< "c" << underscore
+ 1;
1926 ctx
= isl_id_get_ctx(id
);
1929 return isl_id_alloc(ctx
, strm
.str().c_str(), NULL
);
1932 static void rename_dynamic_controls(adg_expr
*expr
,
1933 const char *node_name
,
1934 std::map
<std::string
, std::string
> &prefix_map
)
1939 n_in
= isl_pw_aff_dim(expr
->expr
, isl_dim_in
);
1940 for (int i
= 0; i
< n_in
; ++i
) {
1942 name
= isl_pw_aff_get_dim_name(expr
->expr
, isl_dim_in
, i
);
1943 if (!is_control(name
))
1945 id
= isl_pw_aff_get_dim_id(expr
->expr
, isl_dim_in
, i
);
1946 id
= rename_dynamic_control(id
, node_name
, prefix_map
);
1947 expr
->expr
= isl_pw_aff_set_dim_id(expr
->expr
,
1951 if (!is_control(expr
->name
))
1953 expr
->name
= rename_dynamic_control(expr
->name
, node_name
, prefix_map
);
1956 static void rename_dynamic_controls(adg_var
*var
,
1957 const char *node_name
,
1958 std::map
<std::string
, std::string
> &prefix_map
)
1963 nparam
= isl_pw_multi_aff_dim(var
->access
, isl_dim_param
);
1964 for (int i
= 0; i
< nparam
; ++i
) {
1966 name
= isl_pw_multi_aff_get_dim_name(var
->access
,
1968 if (!is_control(name
))
1970 id
= isl_pw_multi_aff_get_dim_id(var
->access
,
1972 id
= rename_dynamic_control(id
, node_name
, prefix_map
);
1973 var
->access
= isl_pw_multi_aff_set_dim_id(var
->access
,
1974 isl_dim_param
, i
, id
);
1977 if (!isl_pw_multi_aff_has_tuple_id(var
->access
, isl_dim_out
))
1979 id
= isl_pw_multi_aff_get_tuple_id(var
->access
, isl_dim_out
);
1980 id
= rename_dynamic_control(id
, node_name
, prefix_map
);
1981 var
->access
= isl_pw_multi_aff_set_tuple_id(var
->access
,
1985 static void rename_dynamic_controls(adg_domain
*domain
,
1986 const char *node_name
,
1987 std::map
<std::string
, std::string
> &prefix_map
)
1989 for (int i
= 0; i
< domain
->controls
.size(); ++i
)
1990 rename_dynamic_controls(domain
->controls
[i
], node_name
,
1992 for (int i
= 0; i
< domain
->filters
.size(); ++i
)
1993 rename_dynamic_controls(domain
->filters
[i
], node_name
,
1997 static void rename_dynamic_controls(adg_function
*fn
,
1998 const char *node_name
,
1999 std::map
<std::string
, std::string
> &prefix_map
)
2001 for (int i
= 0; i
< fn
->ctrl
.size(); ++i
)
2002 rename_dynamic_controls(fn
->ctrl
[i
], node_name
, prefix_map
);
2005 static void rename_dynamic_controls(adg_port
*port
,
2006 const char *node_name
,
2007 std::map
<std::string
, std::string
> &prefix_map
)
2009 for (int i
= 0; i
< port
->vars
.size(); ++i
)
2010 rename_dynamic_controls(port
->vars
[i
], node_name
, prefix_map
);
2011 rename_dynamic_controls(port
->domain
, node_name
, prefix_map
);
2014 static void rename_dynamic_controls(adg_gate
*gate
,
2015 const char *node_name
,
2016 std::map
<std::string
, std::string
> &prefix_map
)
2018 rename_dynamic_controls(gate
->in
, node_name
, prefix_map
);
2019 rename_dynamic_controls(gate
->out
, node_name
, prefix_map
);
2020 rename_dynamic_controls(gate
->domain
, node_name
, prefix_map
);
2023 /* Rename all sets of dynamic controls in "node" from __last_*_valid to
2027 * and from __last_*_<i> to
2029 * dc<nr>_<node>_c<i>
2031 * This ensures that each set of dynamic controls has a unique name.
2032 * In particular, if such a set of dynamic controls is transferred from
2033 * one node to another, then they will have different names in the two
2036 static void rename_dynamic_controls(adg_node
*node
)
2038 std::map
<std::string
, std::string
> prefix_map
;
2039 const char *node_name
= isl_id_get_name(node
->name
);
2041 rename_dynamic_controls(node
->function
, node_name
, prefix_map
);
2043 for (int i
= 0; i
< node
->input_ports
.size(); ++i
)
2044 rename_dynamic_controls(node
->input_ports
[i
], node_name
,
2046 for (int i
= 0; i
< node
->output_ports
.size(); ++i
)
2047 rename_dynamic_controls(node
->output_ports
[i
], node_name
,
2049 for (int i
= 0; i
< node
->gates
.size(); ++i
)
2050 rename_dynamic_controls(node
->gates
[i
], node_name
, prefix_map
);
2053 static void rename_dynamic_controls(adg
*graph
)
2055 for (int i
= 0; i
< graph
->nodes
.size(); ++i
)
2056 rename_dynamic_controls(graph
->nodes
[i
]);
2059 /* Convert the pn model "pdg" to an adg model.
2061 static adg
*pdg2adg(PDG
*pdg
)
2067 ctx
= pdg
->get_isl_ctx();
2071 graph
->name
= isl_id_alloc(ctx
, pdg
->name
->s
.c_str(), NULL
);
2072 graph
->context
= pdg
->context
->get_isl_set();
2073 compute_iterator_map(graph
, pdg
);
2075 extract_params(graph
, pdg
);
2077 for (int i
= 0; i
< pdg
->nodes
.size(); ++i
)
2078 add_node(ctx
, graph
, pdg
->nodes
[i
]);
2079 for (int i
= 0; i
< pdg
->dependences
.size(); ++i
) {
2080 pdg::dependence
*dep
= pdg
->dependences
[i
];
2082 if (dep
->type
== pdg::dependence::uninitialized
)
2084 if (dep
->type
== pdg::dependence::pn_part
)
2086 if (dep
->type
== pdg::dependence::pn_hold
)
2088 if (dep
->type
== pdg::dependence::pn_wire
)
2089 add_wire_gate(pdg
, graph
, dep
, &g_nr
);
2091 add_union_edge(pdg
, graph
, dep
, i
);
2094 sort_input_ports(graph
);
2095 rename_dynamic_controls(graph
);
2100 static void set_output_name(isl_ctx
*ctx
, struct options
*options
)
2103 const char *suffix
= options
->xml
? "_adg.xml" : "_adg.yaml";
2105 if (!options
->input
|| !strcmp(options
->input
, "-"))
2107 if (options
->output
)
2110 len
= strlen(options
->input
);
2111 if (len
> 5 && !strcmp(options
->input
+ len
- 5, ".yaml"))
2113 options
->output
= isl_alloc_array(ctx
, char, len
+ strlen(suffix
) + 1);
2114 assert(options
->output
);
2115 strncpy(options
->output
, options
->input
, len
);
2116 strcpy(options
->output
+ len
, suffix
);
2119 /* Convert the input pn model to an adg model and print out the adg
2120 * model in either YAML format or XML format.
2122 int main(int argc
, char *argv
[])
2125 struct options
*options
= options_new_with_defaults();
2128 FILE *in
= stdin
, *out
= stdout
;
2130 ctx
= isl_ctx_alloc();
2131 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
2133 set_output_name(ctx
, options
);
2135 if (options
->input
&& strcmp(options
->input
, "-")) {
2136 in
= fopen(options
->input
, "r");
2139 if (options
->output
&& strcmp(options
->output
, "-")) {
2140 out
= fopen(options
->output
, "w");
2144 pdg
= PDG::Load(in
, ctx
);
2148 adg_print_xml(adg
, out
);
2156 options_free(options
);