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 isl_stat
intersect_local_space(__isl_take isl_basic_set
*bset
,
422 /* Intersect the local space *user with the local space of "bset".
424 static isl_stat
intersect_local_space(__isl_take isl_basic_set
*bset
,
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 isl_stat
add_edge_basic_in(__isl_take isl_basic_set
*bset
, void *user
)
961 add_edge_data
*data
= (add_edge_data
*) user
;
962 adg_edge
*edge
= new adg_edge
;
965 edge
->name
= isl_id_copy(data
->id
);
966 edge
->type
= data
->type
;
967 edge
->map
= isl_multi_aff_copy(data
->maff
);
968 edge
->from_node_name
= isl_id_copy(data
->from_node
->name
);
969 edge
->to_node_name
= isl_id_copy(data
->to_node
->name
);
970 if (data
->container
->value_size
) {
971 isl_ctx
*ctx
= isl_basic_set_get_ctx(bset
);
972 edge
->value_size
= isl_val_int_from_si(ctx
,
973 data
->container
->value_size
->v
);
976 port
= create_port(data
->graph
, adg_port_input
,
977 data
->dep
->to_controls
, data
->dep
->to_access
,
978 data
->to_node
->name
, data
->id
, data
->edge_nr
,
979 *data
->port_nr
, isl_basic_set_copy(data
->in_domain
),
980 data
->in_filters
, data
->in_controls
);
981 data
->to_node
->input_ports
.push_back(port
);
982 edge
->to_port_name
= isl_id_copy(port
->name
);
984 port
= create_port(data
->graph
, adg_port_output
,
985 data
->dep
->from_controls
, data
->dep
->from_access
,
986 data
->from_node
->name
, data
->id
, data
->edge_nr
,
987 *data
->port_nr
, isl_basic_set_copy(bset
),
988 data
->out_filters
, data
->from_node
->domain
->controls
);
989 data
->from_node
->output_ports
.push_back(port
);
990 edge
->from_port_name
= isl_id_copy(port
->name
);
994 data
->graph
->edges
.push_back(edge
);
996 isl_basic_set_free(bset
);
1000 /* Does the list of (dynamic) control variables node->function->ctrl
1003 static bool has_control(adg_node
*node
, __isl_keep isl_id
*id
)
1005 for (int i
= 0; i
< node
->function
->ctrl
.size(); ++i
)
1006 if (node
->function
->ctrl
[i
]->name
== id
)
1011 /* Add elements to node->function->ctrl for each element of dep->from_controls,
1012 * provided the element is not in there already.
1014 * We only add elements that correspond to last iterations of "node".
1015 * The given dependence may be the result of reuse detection and may
1016 * therefore propagate controls from one access to the next in some other
1019 * To find out which value should be assigned to the variable,
1020 * we look at the final part of the name of the control variable.
1021 * If this final part is "valid", then the function needs
1022 * to assign the value "1". Otherwise, the final part is
1023 * an integer and then we should assign the value of the
1024 * original corresponding iterator. This value is obtained
1025 * from the schedule of "node".
1027 static void add_control_variables(isl_ctx
*ctx
, adg
*graph
, adg_node
*node
,
1028 pdg::dependence
*dep
)
1031 isl_pw_multi_aff
*pma
;
1033 isl_local_space
*ls
;
1036 if (dep
->from_controls
.size() == 0)
1039 sched
= node_schedule(dep
->from
, graph
, isl_id_copy(node
->name
));
1040 node_name
= strdup(isl_map_get_tuple_name(sched
, isl_dim_in
));
1041 pma
= isl_pw_multi_aff_from_map(isl_map_reverse(sched
));
1042 space
= isl_set_get_space(node
->domain
->bounds
);
1043 ls
= isl_local_space_from_space(space
);
1045 for (int i
= 0; i
< dep
->from_controls
.size(); ++i
) {
1051 const char *name
= dep
->from_controls
[i
]->s
.c_str();
1053 id
= isl_id_alloc(ctx
, name
, NULL
);
1055 if (!is_local_control(name
, node_name
) ||
1056 has_control(node
, id
)) {
1061 name
= strrchr(name
, '_');
1064 expr
= new adg_expr
;
1065 if (!strcmp(name
+ 1, "valid")) {
1066 aff
= isl_aff_zero_on_domain(isl_local_space_copy(ls
));
1067 aff
= isl_aff_add_constant_si(aff
, 1);
1068 pa
= isl_pw_aff_from_aff(aff
);
1070 pos
= atoi(name
+ 1);
1071 pa
= isl_pw_multi_aff_get_pw_aff(pma
, pos
);
1074 pa
= set_names(pa
, node
->domain
->filters
, graph
->iterators
,
1075 node
->domain
->controls
);
1079 node
->function
->ctrl
.push_back(expr
);
1083 isl_local_space_free(ls
);
1084 isl_pw_multi_aff_free(pma
);
1087 /* Add an edge with a convex input port domain (bset).
1088 * We compute the corresponding output port domain by applying
1089 * the dependence relation and apply the required liftings in the from
1090 * domain. The result may in principle be a union again and so we
1091 * split that up as well.
1092 * The lifting required by the input port (computed in add_edge_set)
1093 * is applied to the input port domain. Note that we cannot
1094 * apply this lifting earlier because we need the original input port
1095 * domain (without lifting) to compute the output port domain.
1097 static isl_stat
add_edge_basic(__isl_take isl_basic_set
*bset
, void *user
)
1100 add_edge_data
*data
= (add_edge_data
*) user
;
1103 data
->in_domain
= isl_basic_set_copy(bset
);
1104 data
->in_domain
= isl_basic_set_apply(data
->in_domain
,
1105 isl_basic_map_copy(data
->lifting
));
1106 data
->in_domain
= isl_basic_set_detect_equalities(data
->in_domain
);
1107 set
= isl_set_from_basic_set(bset
);
1108 set
= isl_set_apply(set
, isl_map_copy(data
->rel_maff
));
1109 if (data
->from_node
->lifting
)
1110 set
= isl_set_apply(set
, isl_map_from_basic_map(
1111 isl_basic_map_copy(data
->from_node
->lifting
)));
1112 set
= isl_set_detect_equalities(set
);
1113 set
= isl_set_compute_divs(set
);
1115 r
= isl_set_foreach_basic_set(set
, &add_edge_basic_in
, data
);
1118 isl_basic_set_free(data
->in_domain
);
1122 /* Save a copy of "map" in data->rel, remove the range filters (if any)
1123 * and return the result. The inverse of the mapping used to remove
1124 * the filters is save in data->range_filters_map so that the filters
1125 * can be reintroduced in insert_range_filters.
1127 static __isl_give isl_map
*remove_range_filters(__isl_take isl_map
*map
,
1128 add_edge_data
*data
)
1132 data
->rel
= isl_map_copy(map
);
1134 space
= isl_space_range(isl_map_get_space(map
));
1135 if (isl_space_is_wrapping(space
)) {
1136 isl_map
*fm
= isl_map_universe(isl_space_unwrap(space
));
1137 fm
= isl_map_domain_map(fm
);
1138 map
= isl_map_apply_range(map
, isl_map_copy(fm
));
1139 data
->range_filters_map
= isl_map_reverse(fm
);
1141 space
= isl_space_map_from_set(space
);
1142 data
->range_filters_map
= isl_map_identity(space
);
1148 /* Reintroduce the filters removed in remove_range_filters.
1150 static __isl_give isl_map
*insert_range_filters(__isl_take isl_map
*map
,
1151 add_edge_data
*data
)
1153 map
= isl_map_apply_range(map
, isl_map_copy(data
->range_filters_map
));
1154 map
= isl_map_intersect(map
, isl_map_copy(data
->rel
));
1159 /* Add edges for the given input port domain (set) and the given
1160 * mapping (maff) to the corresponding output port domain.
1162 * The lifting corresponding to the domain of the destination node
1163 * has already been applied, but "maff" may require additional liftings
1164 * to be applied to the input port domain.
1166 * The given isl_multi_aff is also converted into an isl_basic_map
1167 * for use in the construction of the output port domains
1168 * in add_edge_basic(). After the conversion, we add back the filters
1169 * that were removed right before the call to isl_pw_multi_aff_from_map.
1171 static isl_stat
add_edge_set(__isl_take isl_set
*set
,
1172 __isl_take isl_multi_aff
*maff
, void *user
)
1174 add_edge_data
*data
= (add_edge_data
*) user
;
1176 isl_local_space
*ls
;
1178 data
->rel_maff
= isl_map_from_basic_map(isl_basic_map_from_multi_aff(
1179 isl_multi_aff_copy(maff
)));
1180 data
->rel_maff
= insert_range_filters(data
->rel_maff
, data
);
1182 maff
= isl_multi_aff_lift(maff
, &ls
);
1183 copy_controls(data
->in_controls
, data
->to_node
->domain
->controls
);
1184 add_controls(data
->graph
, data
->in_filters
, data
->in_controls
, ls
);
1185 data
->lifting
= isl_local_space_lifting(ls
);
1189 r
= isl_set_foreach_basic_set(set
, &add_edge_basic
, data
);
1191 for (int i
= 0; i
< data
->in_controls
.size(); ++i
)
1192 delete data
->in_controls
[i
];
1193 data
->in_controls
.clear();
1194 isl_basic_map_free(data
->lifting
);
1196 isl_map_free(data
->rel_maff
);
1197 isl_multi_aff_free(maff
);
1202 /* Does "rel" have any parameter of the form __last_*?
1204 static bool has_filters(__isl_keep isl_map
*rel
)
1208 nparam
= isl_map_dim(rel
, isl_dim_param
);
1209 for (int i
= 0; i
< nparam
; ++i
)
1210 if (is_control(rel
, i
))
1216 /* Remove all parameters of the form __last_* from "rel".
1218 static __isl_give isl_map
*remove_all_filters(__isl_take isl_map
*rel
)
1222 nparam
= isl_map_dim(rel
, isl_dim_param
);
1223 for (int i
= nparam
- 1; i
>= 0; --i
) {
1224 if (!is_control(rel
, i
))
1226 rel
= isl_map_project_out(rel
, isl_dim_param
, i
, 1);
1232 /* For each parameter of the form __last_*, add it to both
1233 * data->in_filters and data->out_filters, in each case defined
1234 * over the appropriate domain.
1236 static void extract_filters(__isl_take isl_map
*rel
, add_edge_data
*data
)
1243 nparam
= isl_map_dim(rel
, isl_dim_param
);
1244 space
= isl_map_get_space(rel
);
1245 domain
= isl_space_domain(isl_space_copy(space
));
1246 range
= isl_space_range(space
);
1248 for (int i
= 0; i
< nparam
; ++i
) {
1251 if (!is_control(rel
, i
))
1254 id
= isl_map_get_dim_id(rel
, isl_dim_param
, i
);
1255 data
->in_filters
.push_back(var_from_id(isl_id_copy(id
),
1256 isl_space_copy(domain
)));
1257 data
->out_filters
.push_back(var_from_id(id
,
1258 isl_space_copy(range
)));
1261 isl_space_free(domain
);
1262 isl_space_free(range
);
1265 /* Replace both domain and range of "rel" by a wrapped map with the original
1266 * set as domain and dimensions corresponding to the parameters
1267 * referenced by the elements in "filters" as range and then
1268 * remove those parameters.
1269 * The overall effect is that those parameters are moved into
1270 * the ranges of the wrapped maps.
1272 static __isl_give isl_map
*move_filters(__isl_take isl_map
*rel
,
1273 std::vector
<adg_var
*> &filters
)
1278 space
= isl_space_domain(isl_map_get_space(rel
));
1279 space
= isl_space_from_domain(space
);
1280 map
= isl_map_universe(space
);
1281 for (int i
= 0; i
< filters
.size(); ++i
) {
1286 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
1288 param
= isl_map_find_dim_by_id(map
, isl_dim_param
, id
);
1291 pos
= isl_map_dim(map
, isl_dim_out
);
1292 map
= isl_map_add_dims(map
, isl_dim_out
, 1);
1293 map
= isl_map_equate(map
, isl_dim_param
, param
,
1296 map
= isl_map_domain_map(map
);
1298 rel
= isl_map_apply_range(map
, rel
);
1300 for (int i
= 0; i
< filters
.size(); ++i
) {
1304 id
= isl_pw_multi_aff_get_tuple_id(filters
[i
]->access
,
1306 param
= isl_map_find_dim_by_id(rel
, isl_dim_param
, id
);
1309 rel
= isl_map_project_out(rel
, isl_dim_param
, param
, 1);
1312 space
= isl_space_unwrap(isl_space_domain(isl_map_get_space(rel
)));
1313 map
= isl_map_range_map(isl_map_universe(space
));
1314 rel
= isl_map_range_product(rel
, map
);
1319 /* Determine the appropriate adg_edge_type for "dep".
1321 static enum adg_edge_type
dep_type(pdg::dependence
*dep
)
1323 if (dep
->reordering
)
1324 return adg_edge_generic
;
1325 else if (dep
->multiplicity
)
1326 return adg_edge_broadcast
;
1327 else if (dep
->type
== pdg::dependence::pn_shift
)
1328 return adg_edge_shift_register
;
1330 return adg_edge_fifo
;
1333 /* Lift the domain of the relation "rel" according to domain->lifting,
1336 static __isl_give isl_map
*lift(__isl_take isl_map
*rel
, adg_node
*domain
)
1338 if (domain
->lifting
) {
1339 isl_basic_map
*lifting
;
1340 lifting
= isl_basic_map_copy(domain
->lifting
);
1341 rel
= isl_map_apply_domain(rel
,
1342 isl_map_from_basic_map(lifting
));
1343 rel
= isl_map_detect_equalities(rel
);
1349 /* If "dom" is a wrapped map, then add the filters of "node" to "filters".
1351 static void extract_domain_filters(__isl_take isl_space
*space
,
1352 std::vector
<adg_var
*> &filters
, pdg::node
*node
)
1354 if (isl_space_is_wrapping(space
))
1355 add_domain_filters(filters
, node
, space
);
1357 isl_space_free(space
);
1360 /* Extract the filters on the output and input ports from the "from"
1361 * and "to" nodes of "rel".
1363 static void extract_domain_filters(__isl_keep isl_map
*rel
, add_edge_data
*data
)
1367 space
= isl_space_domain(isl_map_get_space(rel
));
1368 extract_domain_filters(space
, data
->out_filters
, data
->dep
->from
);
1370 space
= isl_space_range(isl_map_get_space(rel
));
1371 extract_domain_filters(space
, data
->in_filters
, data
->dep
->to
);
1374 /* Add edges corresponding to "dep" for the relation "rel", which
1375 * may be a subset of dep->relation or dep->controlled_relation.
1376 * "container" contains information about the required buffer sizes.
1377 * "id" is the name that should be given to all created edges.
1378 * "type" is the type of the created edges.
1380 * If "dep" is a control dependence (i.e., if it has elements
1381 * in its from_controls), then add corresponding control variable
1384 * We schedule the domain and range of "rel" and extract the
1385 * domain filters for later use. We currently only allow such filters
1386 * on dependences without control parameters.
1388 * Since "rel" is a subset of dep->relation, it has the source as domain
1389 * and the sink as range. Since the mapping on the edges map iterations
1390 * of the input port to iterations of the corresponding output port,
1391 * we need to invert it. Then we apply the lifting on the
1392 * sink node as the initial static control variables on the input port domain
1393 * need to be the same as those of the node domain. The control variables
1394 * themselves are copied to data->in_controls in add_edge_set().
1396 * If "type" is adg_edge_broadcast, then a given write may be
1397 * associated to several (consecutive) reads in the original
1398 * dependence relation. In the ADG, we only associate the write
1399 * with the first of those reads.
1401 * If there are any dynamic control variables, we extract them
1402 * and move them from the parameters into the domain of the relation.
1403 * For loops however, we simply remove them.
1405 * Finally, we construct an explicit function representation of the
1406 * mapping and consider each convex piece of the domain in turn.
1407 * This is needed because the bounds on the domains have to be convex.
1408 * Before we compute this explicit representation, we first remove
1409 * the range filters as they may not be uniquely defined in terms
1410 * of the source filters.
1412 static void add_edge(PDG
*pdg
, adg
*adg
,
1413 pdg::dependence
*dep
, pdg::dependence
*container
,
1414 __isl_take isl_id
*id
, int edge_nr
, __isl_take isl_map
*rel
,
1415 enum adg_edge_type type
, int *port_nr
)
1418 isl_pw_multi_aff
*pma
;
1420 add_edge_data data
= { adg
, dep
, container
};
1422 ctx
= pdg
->get_isl_ctx();
1426 data
.from_node
= adg
->node_by_id(node_id(ctx
, dep
->from
));
1427 data
.to_node
= adg
->node_by_id(node_id(ctx
, dep
->to
));
1428 data
.port_nr
= port_nr
;
1429 data
.edge_nr
= edge_nr
;
1431 add_control_variables(ctx
, adg
, data
.from_node
, dep
);
1433 rel
= isl_map_apply_domain(rel
,
1434 extended_node_schedule(dep
->from
, adg
,
1435 isl_id_copy(data
.from_node
->name
),
1436 isl_space_domain(isl_map_get_space(rel
))));
1437 rel
= isl_map_apply_range(rel
,
1438 extended_node_schedule(dep
->to
, adg
,
1439 isl_id_copy(data
.to_node
->name
),
1440 isl_space_range(isl_map_get_space(rel
))));
1441 extract_domain_filters(rel
, &data
);
1442 if (data
.out_filters
.size() || data
.in_filters
.size())
1443 assert(!dep
->controlled_relation
);
1444 if (type
== adg_edge_broadcast
)
1445 rel
= isl_map_lexmin(rel
);
1446 rel
= isl_map_reverse(rel
);
1447 rel
= lift(rel
, data
.to_node
);
1448 if (has_filters(rel
)) {
1449 if (dep
->from
!= dep
->to
) {
1450 extract_filters(rel
, &data
);
1451 rel
= move_filters(rel
, data
.in_filters
);
1453 rel
= remove_all_filters(rel
);
1455 rel
= remove_range_filters(rel
, &data
);
1456 pma
= isl_pw_multi_aff_from_map(rel
);
1458 res
= isl_pw_multi_aff_foreach_piece(pma
, &add_edge_set
, &data
);
1460 isl_pw_multi_aff_free(pma
);
1461 isl_map_free(data
.range_filters_map
);
1462 isl_map_free(data
.rel
);
1463 isl_id_free(data
.id
);
1465 for (int i
= 0; i
< data
.in_filters
.size(); ++i
)
1466 delete data
.in_filters
[i
];
1467 for (int i
= 0; i
< data
.out_filters
.size(); ++i
)
1468 delete data
.out_filters
[i
];
1471 /* Create and return an isl_id of the form
1477 * depending on whether "dep" is a control edge.
1479 static __isl_give isl_id
*edge_name(isl_ctx
*ctx
, pdg::dependence
*dep
, int i
)
1482 bool control
= dep
->from_controls
.size() > 0;
1484 snprintf(buf
, sizeof(buf
), "%sED_%d", control
? "C" : "", i
);
1485 return isl_id_alloc(ctx
, buf
, NULL
);
1488 /* Add edges corresponding to "dep".
1489 * "container" contains information about the required buffer sizes.
1490 * "id" is the name that should be given to all created edges.
1492 * The dependence relation is taken from dep->controlled_relation
1493 * if it is not NULL. Otherwise, it is taken from dep->relation.
1495 * We look for pn_hold dependences with overlapping domains.
1496 * A pn_hold dependence signifies that a value available at the source
1497 * iteration should be kept until the next iteration (= target of dependence).
1498 * If there is a pn_hold dependence whose domain overlaps with the target
1499 * of the given dependence, then we turn the entire given dependence into a
1501 * If no such pn_hold dependences were found, then a regular fifo edge
1504 * Obviously, we should only convert fifos into sticky fifos.
1505 * So, if "dep" is not a fifo, we should just construct fifos for
1506 * the associated pn_hold dependences.
1508 static void add_edge(PDG
*pdg
, adg
*adg
,
1509 pdg::dependence
*dep
, pdg::dependence
*container
,
1510 __isl_take isl_id
*id
, int edge_nr
, int *port_nr
)
1514 enum adg_edge_type type
;
1516 ctx
= pdg
->get_isl_ctx();
1518 assert(dep
->type
== pdg::dependence::pn
||
1519 dep
->type
== pdg::dependence::pn_shift
||
1520 dep
->type
== pdg::dependence::pn_part
||
1521 dep
->type
== pdg::dependence::flow
);
1523 if (dep
->controlled_relation
)
1524 rel
= dep
->controlled_relation
->get_isl_map();
1526 rel
= dep
->relation
->get_isl_map();
1528 type
= dep_type(dep
);
1530 for (int j
= 0; j
< pdg
->dependences
.size(); ++j
) {
1531 pdg::dependence
*hold_dep
= pdg
->dependences
[j
];
1535 if (hold_dep
->type
!= pdg::dependence::pn_hold
)
1537 if (dep
->to
!= hold_dep
->from
)
1539 if (dep
->array
!= hold_dep
->array
)
1541 if (dep
->to_access
!= hold_dep
->from_access
)
1544 if (hold_dep
->controlled_relation
)
1545 hold_rel
= hold_dep
->controlled_relation
->get_isl_map();
1547 hold_rel
= hold_dep
->relation
->get_isl_map();
1549 if (type
!= adg_edge_fifo
) {
1550 isl_id
*id
= edge_name(ctx
, pdg
->dependences
[j
], j
);
1551 add_edge(pdg
, adg
, hold_dep
, hold_dep
, id
, edge_nr
,
1552 hold_rel
, adg_edge_fifo
, port_nr
);
1556 hold_rel
= isl_map_intersect_range(isl_map_copy(rel
),
1557 isl_map_domain(hold_rel
));
1558 empty
= isl_map_is_empty(hold_rel
);
1559 isl_map_free(hold_rel
);
1562 type
= adg_edge_sticky_fifo
;
1567 add_edge(pdg
, adg
, dep
, container
, id
, edge_nr
, rel
, type
, port_nr
);
1570 /* Add edges corresponding to "dep".
1571 * If it is a pn_union, then all the corresponding pn_part dependences
1572 * should get the same name.
1574 static void add_union_edge(PDG
*pdg
, adg
*adg
, pdg::dependence
*dep
, int i
)
1580 ctx
= pdg
->get_isl_ctx();
1582 id
= edge_name(ctx
, dep
, i
);
1584 if (dep
->type
!= pdg::dependence::pn_union
) {
1585 add_edge(pdg
, adg
, dep
, dep
, id
, i
, &port_nr
);
1589 for (int j
= 0; j
< pdg
->dependences
.size(); ++j
) {
1590 pdg::dependence
*part_dep
= pdg
->dependences
[j
];
1592 if (part_dep
->type
!= pdg::dependence::pn_part
)
1594 if (part_dep
->container
!= dep
)
1597 add_edge(pdg
, adg
, part_dep
, dep
, isl_id_copy(id
), i
, &port_nr
);
1603 /* Auxiliary data used during the construction of gates.
1605 * nr points to the sequence number.
1606 * from is the source access.
1607 * to is the target access.
1609 struct add_wire_data
{
1617 static isl_stat
add_wire_basic(__isl_take isl_basic_set
*bset
,
1621 /* Add a wire with the convex domain "bset".
1622 * To ensure that the initial control variables of the gate are the same as
1623 * that of the node, we apply the node lifting, if any, and copy the
1624 * static control variables.
1626 * If there are any dynamic control variables among the parameters,
1627 * we move them into the domain of a wrapped map.
1629 static isl_stat
add_wire_basic(__isl_take isl_basic_set
*bset
, void *user
)
1631 add_wire_data
*data
= (add_wire_data
*) user
;
1634 adg_gate
*gate
= new adg_gate
;
1637 ctx
= isl_basic_set_get_ctx(bset
);
1638 space
= isl_basic_set_get_space(bset
);
1639 snprintf(buf
, sizeof(buf
), "%sID_%d",
1640 isl_id_get_name(data
->node
->name
), (*data
->nr
)++);
1641 gate
->name
= isl_id_alloc(ctx
, buf
, NULL
);
1642 gate
->in
= new_var(data
->from
, isl_space_copy(space
));
1643 gate
->out
= new_var(data
->to
, space
);
1645 gate
->node_name
= isl_id_copy(data
->node
->name
);
1646 gate
->domain
= new adg_domain(isl_set_from_basic_set(bset
));
1647 if (data
->node
->lifting
)
1648 apply_lifting(gate
->domain
,
1649 isl_basic_map_copy(data
->node
->lifting
));
1650 copy_controls(gate
->domain
->controls
, data
->node
->domain
->controls
);
1651 move_filters(gate
->domain
);
1653 data
->node
->gates
.push_back(gate
);
1658 /* Add gates corresponding to the wire "dep".
1659 * "g_nr" is a pointer to the sequence number of gates.
1660 * The gate copies the variable corresponding to the from_access
1661 * to the variable corresponding to the to_access.
1662 * The dependence relation of a wire is always an identity relation,
1663 * so we can just take the domain of this relation.
1664 * We schedule both this domain and the dynamic control variables, if any.
1666 static void add_wire_gate(PDG
*pdg
, adg
*adg
, pdg::dependence
*dep
, int *g_nr
)
1672 struct add_wire_data data
= { g_nr
, dep
->from_access
, dep
->to_access
};
1674 ctx
= pdg
->get_isl_ctx();
1675 rel
= dep
->relation
->get_isl_map();
1676 dom
= isl_map_domain(rel
);
1677 id
= node_id(ctx
, dep
->to
);
1678 data
.node
= adg
->node_by_id(isl_id_copy(id
));
1679 dom
= isl_set_apply(dom
, node_schedule(dep
->from
, adg
, id
));
1680 isl_set_foreach_basic_set(dom
, &add_wire_basic
, &data
);
1684 /* Construct a map that removes some dimensions with known constant
1685 * values from the schedule domain.
1686 * The map is stored in graph->iterator_map.
1687 * Names for the iterators in the domain of this map are stored
1688 * in graph->all_iterators, while the names for the iterators
1689 * that are kept in the range are stored in graph->iterators.
1691 static void compute_iterator_map(adg
*graph
, PDG
*pdg
)
1700 n_all
= pdg
->statement_dimensions
.size();
1702 for (int i
= 0; i
< n_all
; ++i
)
1703 if (!pdg
->statement_dimensions
[i
])
1706 ctx
= pdg
->get_isl_ctx();
1707 space
= isl_space_alloc(ctx
, 0, n_all
, n_it
);
1708 map
= isl_map_universe(space
);
1711 for (int i
= 0; i
< n_all
; ++i
) {
1714 snprintf(buf
, sizeof(buf
), "c%d", i
);
1715 id
= isl_id_alloc(ctx
, buf
, NULL
);
1716 graph
->all_iterators
.push_back(id
);
1717 map
= isl_map_set_dim_id(map
, isl_dim_in
, i
, isl_id_copy(id
));
1719 if (pdg
->statement_dimensions
[i
])
1721 graph
->iterators
.push_back(isl_id_copy(id
));
1722 map
= isl_map_equate(map
, isl_dim_in
, i
, isl_dim_out
, n_it
);
1726 graph
->iterator_map
= map
;
1729 /* Create an adg_param for each element in pdg->params.
1730 * param->val is obtained from pdg->params[i]->value, if any.
1731 * param->lb and param->ub are obtained from the context.
1733 static void extract_params(adg
*adg
, PDG
*pdg
)
1736 isl_local_space
*ls
;
1738 ctx
= pdg
->get_isl_ctx();
1740 ls
= isl_local_space_from_space(isl_set_get_space(adg
->context
));
1741 for (int i
= 0; i
< pdg
->params
.size(); ++i
) {
1742 adg_param
*param
= new adg_param
;
1746 param
->name
= isl_id_alloc(ctx
, pdg
->params
[i
]->name
->s
.c_str(),
1748 if (pdg
->params
[i
]->value
) {
1749 isl_space
*space
= isl_space_params_alloc(ctx
, 0);
1750 isl_local_space
*ls
= isl_local_space_from_space(space
);
1751 param
->val
= isl_aff_zero_on_domain(ls
);
1752 param
->val
= isl_aff_add_constant_si(param
->val
,
1753 pdg
->params
[i
]->value
->v
);
1756 aff
= isl_aff_zero_on_domain(isl_local_space_copy(ls
));
1757 aff
= isl_aff_add_coefficient_si(aff
, isl_dim_param
, i
, 1);
1758 v
= isl_set_min_val(adg
->context
, aff
);
1759 if (isl_val_is_int(v
)) {
1760 isl_space
*space
= isl_space_params_alloc(ctx
, 0);
1761 isl_local_space
*ls
= isl_local_space_from_space(space
);
1762 param
->lb
= isl_aff_zero_on_domain(ls
);
1763 param
->lb
= isl_aff_add_constant_val(param
->lb
, v
);
1766 v
= isl_set_max_val(adg
->context
, aff
);
1767 if (isl_val_is_int(v
)) {
1768 isl_space
*space
= isl_space_params_alloc(ctx
, 0);
1769 isl_local_space
*ls
= isl_local_space_from_space(space
);
1770 param
->ub
= isl_aff_zero_on_domain(ls
);
1771 param
->ub
= isl_aff_add_constant_val(param
->ub
, v
);
1776 adg
->params
.push_back(param
);
1778 isl_local_space_free(ls
);
1781 /* Comparison function for sorting input ports such that
1782 * first, for input ports that are connected to the same edge,
1783 * an input port corresponding to an earlier argument position comes
1784 * before an input port corresponding to a later argument position, and,
1785 * second, ports that have dynamic control variables in their domains
1786 * come after the ports that set those control variables.
1788 * The first is needed for pn_union channels as their detection allows for
1789 * the case where two (or more) tokens are consumed in the same iteration,
1790 * provided they don't cause reordering when consumed in the order of
1793 * The second is needed to ensure that the values of the dynamic
1794 * control variables are available when they are needed.
1795 * We enforce the second property by sorting the edges based
1796 * on their depth with respect to dependences between input ports.
1797 * It would probably make more sense to perform a topological sort,
1798 * but it seemed easier to implement it this way.
1800 * If two edges are equivalent with respect to the above two criteria,
1801 * then we arbitrarily sort them according to the edge sequence number.
1803 struct less_input_port
:
1804 public std::binary_function
<adg_port
*, adg_port
*, bool> {
1805 std::vector
<adg_port
*> ports
;
1807 less_input_port(std::vector
<adg_port
*> &ports
) : ports(ports
) {}
1809 bool writes(adg_port
*p
, __isl_keep isl_id
*id
);
1810 int depth(adg_port
*p
);
1812 bool operator()(adg_port
*x
, adg_port
*y
) {
1813 int depth_x
, depth_y
;
1815 if (x
->edge_nr
== y
->edge_nr
)
1816 return x
->arg_pos
< y
->arg_pos
;
1820 if (depth_x
!= depth_y
)
1821 return depth_x
< depth_y
;
1822 return x
->edge_nr
< y
->edge_nr
;
1826 /* Does "p" write to the variable "id"?
1828 bool less_input_port::writes(adg_port
*p
, __isl_keep isl_id
*id
)
1830 for (int i
= 0; i
< p
->vars
.size(); ++i
) {
1831 adg_var
*var
= p
->vars
[i
];
1833 var_id
= isl_pw_multi_aff_get_tuple_id(var
->access
,
1835 isl_id_free(var_id
);
1843 /* Return 0 if this port does not involve any filters.
1844 * Otherwise, return 1 plus the maximal depth of any port
1845 * writing a filter used in the domain of the current port.
1846 * We obviously assume here that there are no cyclic dependences.
1848 int less_input_port::depth(adg_port
*p
)
1852 for (int i
= 0; i
< p
->domain
->filters
.size(); ++i
) {
1853 adg_var
*filter
= p
->domain
->filters
[i
];
1855 id
= isl_pw_multi_aff_get_tuple_id(filter
->access
, isl_dim_out
);
1856 for (int j
= 0; j
< ports
.size(); ++j
) {
1858 if (writes(ports
[j
], id
)) {
1859 d
= depth(ports
[j
]) + 1;
1870 /* Sort the input ports of the given node.
1872 static void sort_input_ports(adg_node
*node
)
1874 std::sort(node
->input_ports
.begin(), node
->input_ports
.end(),
1875 less_input_port(node
->input_ports
));
1878 /* Sort the input ports in all nodes of the graph.
1880 static void sort_input_ports(adg
*graph
)
1882 for (int i
= 0; i
< graph
->nodes
.size(); ++i
)
1883 sort_input_ports(graph
->nodes
[i
]);
1886 /* Rename the dynamic control "id" from __last_*_valid to
1890 * and from __last_*_<i> to
1892 * dc<nr>_<node>_c<i>
1894 * "prefix_map" maps previously translated prefixes __last_*
1895 * (within the same node) to their dc<nr>_<node> translations.
1896 * If we can't find the current prefix in this cache, we create
1897 * a new name and add it to the cache.
1899 static __isl_give isl_id
*rename_dynamic_control(__isl_take isl_id
*id
,
1900 const char *node_name
,
1901 std::map
<std::string
, std::string
> &prefix_map
)
1903 const char *name
, *underscore
;
1905 std::ostringstream strm
;
1908 if (!is_control(id
))
1911 name
= isl_id_get_name(id
);
1912 underscore
= strrchr(name
, '_');
1915 old
= string(name
, underscore
- name
);
1916 if (prefix_map
.find(old
) == prefix_map
.end()) {
1917 strm
<< "dc" << prefix_map
.size() << "_" << node_name
;
1918 prefix_map
[old
] = strm
.str();
1922 strm
<< prefix_map
[old
] << "_";
1923 if (!strcmp(underscore
+ 1, "valid"))
1926 strm
<< "c" << underscore
+ 1;
1928 ctx
= isl_id_get_ctx(id
);
1931 return isl_id_alloc(ctx
, strm
.str().c_str(), NULL
);
1934 static void rename_dynamic_controls(adg_expr
*expr
,
1935 const char *node_name
,
1936 std::map
<std::string
, std::string
> &prefix_map
)
1941 n_in
= isl_pw_aff_dim(expr
->expr
, isl_dim_in
);
1942 for (int i
= 0; i
< n_in
; ++i
) {
1944 name
= isl_pw_aff_get_dim_name(expr
->expr
, isl_dim_in
, i
);
1945 if (!is_control(name
))
1947 id
= isl_pw_aff_get_dim_id(expr
->expr
, isl_dim_in
, i
);
1948 id
= rename_dynamic_control(id
, node_name
, prefix_map
);
1949 expr
->expr
= isl_pw_aff_set_dim_id(expr
->expr
,
1953 if (!is_control(expr
->name
))
1955 expr
->name
= rename_dynamic_control(expr
->name
, node_name
, prefix_map
);
1958 static void rename_dynamic_controls(adg_var
*var
,
1959 const char *node_name
,
1960 std::map
<std::string
, std::string
> &prefix_map
)
1965 nparam
= isl_pw_multi_aff_dim(var
->access
, isl_dim_param
);
1966 for (int i
= 0; i
< nparam
; ++i
) {
1968 name
= isl_pw_multi_aff_get_dim_name(var
->access
,
1970 if (!is_control(name
))
1972 id
= isl_pw_multi_aff_get_dim_id(var
->access
,
1974 id
= rename_dynamic_control(id
, node_name
, prefix_map
);
1975 var
->access
= isl_pw_multi_aff_set_dim_id(var
->access
,
1976 isl_dim_param
, i
, id
);
1979 if (!isl_pw_multi_aff_has_tuple_id(var
->access
, isl_dim_out
))
1981 id
= isl_pw_multi_aff_get_tuple_id(var
->access
, isl_dim_out
);
1982 id
= rename_dynamic_control(id
, node_name
, prefix_map
);
1983 var
->access
= isl_pw_multi_aff_set_tuple_id(var
->access
,
1987 static void rename_dynamic_controls(adg_domain
*domain
,
1988 const char *node_name
,
1989 std::map
<std::string
, std::string
> &prefix_map
)
1991 for (int i
= 0; i
< domain
->controls
.size(); ++i
)
1992 rename_dynamic_controls(domain
->controls
[i
], node_name
,
1994 for (int i
= 0; i
< domain
->filters
.size(); ++i
)
1995 rename_dynamic_controls(domain
->filters
[i
], node_name
,
1999 static void rename_dynamic_controls(adg_function
*fn
,
2000 const char *node_name
,
2001 std::map
<std::string
, std::string
> &prefix_map
)
2003 for (int i
= 0; i
< fn
->ctrl
.size(); ++i
)
2004 rename_dynamic_controls(fn
->ctrl
[i
], node_name
, prefix_map
);
2007 static void rename_dynamic_controls(adg_port
*port
,
2008 const char *node_name
,
2009 std::map
<std::string
, std::string
> &prefix_map
)
2011 for (int i
= 0; i
< port
->vars
.size(); ++i
)
2012 rename_dynamic_controls(port
->vars
[i
], node_name
, prefix_map
);
2013 rename_dynamic_controls(port
->domain
, node_name
, prefix_map
);
2016 static void rename_dynamic_controls(adg_gate
*gate
,
2017 const char *node_name
,
2018 std::map
<std::string
, std::string
> &prefix_map
)
2020 rename_dynamic_controls(gate
->in
, node_name
, prefix_map
);
2021 rename_dynamic_controls(gate
->out
, node_name
, prefix_map
);
2022 rename_dynamic_controls(gate
->domain
, node_name
, prefix_map
);
2025 /* Rename all sets of dynamic controls in "node" from __last_*_valid to
2029 * and from __last_*_<i> to
2031 * dc<nr>_<node>_c<i>
2033 * This ensures that each set of dynamic controls has a unique name.
2034 * In particular, if such a set of dynamic controls is transferred from
2035 * one node to another, then they will have different names in the two
2038 static void rename_dynamic_controls(adg_node
*node
)
2040 std::map
<std::string
, std::string
> prefix_map
;
2041 const char *node_name
= isl_id_get_name(node
->name
);
2043 rename_dynamic_controls(node
->function
, node_name
, prefix_map
);
2045 for (int i
= 0; i
< node
->input_ports
.size(); ++i
)
2046 rename_dynamic_controls(node
->input_ports
[i
], node_name
,
2048 for (int i
= 0; i
< node
->output_ports
.size(); ++i
)
2049 rename_dynamic_controls(node
->output_ports
[i
], node_name
,
2051 for (int i
= 0; i
< node
->gates
.size(); ++i
)
2052 rename_dynamic_controls(node
->gates
[i
], node_name
, prefix_map
);
2055 static void rename_dynamic_controls(adg
*graph
)
2057 for (int i
= 0; i
< graph
->nodes
.size(); ++i
)
2058 rename_dynamic_controls(graph
->nodes
[i
]);
2061 /* Convert the pn model "pdg" to an adg model.
2063 static adg
*pdg2adg(PDG
*pdg
)
2069 ctx
= pdg
->get_isl_ctx();
2073 graph
->name
= isl_id_alloc(ctx
, pdg
->name
->s
.c_str(), NULL
);
2074 graph
->context
= pdg
->context
->get_isl_set();
2075 compute_iterator_map(graph
, pdg
);
2077 extract_params(graph
, pdg
);
2079 for (int i
= 0; i
< pdg
->nodes
.size(); ++i
)
2080 add_node(ctx
, graph
, pdg
->nodes
[i
]);
2081 for (int i
= 0; i
< pdg
->dependences
.size(); ++i
) {
2082 pdg::dependence
*dep
= pdg
->dependences
[i
];
2084 if (dep
->type
== pdg::dependence::uninitialized
)
2086 if (dep
->type
== pdg::dependence::pn_part
)
2088 if (dep
->type
== pdg::dependence::pn_hold
)
2090 if (dep
->type
== pdg::dependence::pn_wire
)
2091 add_wire_gate(pdg
, graph
, dep
, &g_nr
);
2093 add_union_edge(pdg
, graph
, dep
, i
);
2096 sort_input_ports(graph
);
2097 rename_dynamic_controls(graph
);
2102 static void set_output_name(isl_ctx
*ctx
, struct options
*options
)
2105 const char *suffix
= options
->xml
? "_adg.xml" : "_adg.yaml";
2107 if (!options
->input
|| !strcmp(options
->input
, "-"))
2109 if (options
->output
)
2112 len
= strlen(options
->input
);
2113 if (len
> 5 && !strcmp(options
->input
+ len
- 5, ".yaml"))
2115 options
->output
= isl_alloc_array(ctx
, char, len
+ strlen(suffix
) + 1);
2116 assert(options
->output
);
2117 strncpy(options
->output
, options
->input
, len
);
2118 strcpy(options
->output
+ len
, suffix
);
2121 /* Convert the input pn model to an adg model and print out the adg
2122 * model in either YAML format or XML format.
2124 int main(int argc
, char *argv
[])
2127 struct options
*options
= options_new_with_defaults();
2130 FILE *in
= stdin
, *out
= stdout
;
2132 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
2133 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
2135 set_output_name(ctx
, options
);
2137 if (options
->input
&& strcmp(options
->input
, "-")) {
2138 in
= fopen(options
->input
, "r");
2141 if (options
->output
&& strcmp(options
->output
, "-")) {
2142 out
= fopen(options
->output
, "w");
2146 pdg
= PDG::Load(in
, ctx
);
2150 adg_print_xml(adg
, out
);