1 #include <isl/constraint.h>
3 #include "dependence_graph.h"
11 case normal
: s
= ""; break;
12 case expansion
: s
= "expansion "; break;
14 fprintf(stderr
, "\tpos: %d, source: %d,%s %s[%p]\n",
15 pos
, source
->location
, source
->operation
, s
, source
);
16 ctx
= isl_map_get_ctx(relation
);
17 prn
= isl_printer_to_file(ctx
, stderr
);
18 prn
= isl_printer_set_indent(prn
, 8);
19 prn
= isl_printer_print_map(prn
, relation
);
20 prn
= isl_printer_end_line(prn
);
21 isl_printer_free(prn
);
24 void computation::dump(void)
35 fprintf(stderr
, "op: %d,%s/%d %s[%p]\n",
36 location
, operation
, arity
, s
, this);
37 ctx
= isl_set_get_ctx(domain
);
38 prn
= isl_printer_to_file(ctx
, stderr
);
39 prn
= isl_printer_print_set(prn
, domain
);
40 prn
= isl_printer_end_line(prn
);
41 isl_printer_free(prn
);
43 for (int i
= 0; i
< edges
.size(); ++i
) {
44 fprintf(stderr
, "edge %d:\n", i
);
49 void dependence_graph::dump(void)
51 for (int i
= 0; i
< vertices
.size(); ++i
)
56 static void add_input_edges(isl_ctx
*ctx
, int pos
, pdg::dependence
*dep
,
57 computation
*comp
, computation
*source
)
59 pdg::access
*a
= dep
->to_access
;
61 isl_map
*map
= a
->map
->get_isl_map(ctx
);
62 isl_map
*relation
= dep
->relation
->get_isl_map(ctx
);
63 isl_set
*domain
= isl_map_range(relation
);
64 map
= isl_map_intersect_domain(map
, domain
);
70 comp
->edges
.push_back(e
);
73 /* For each basic map in relation, add an edge to comp with
74 * given source and pos.
76 static void add_split_edge(computation
*comp
, computation
*source
,
77 int pos
, isl_map
*relation
, std::vector
<computation
**> *missing
,
78 enum edge::type type
= edge::normal
)
84 missing
->push_back(&e
->source
);
87 e
->relation
= relation
;
89 comp
->edges
.push_back(e
);
92 /* For each (flow) dependence with write access a, and an
93 * edge to computation comp.
94 * Note that in the pdg, the dependences have as "input" the source (read)
95 * and as "output" the sink (write), while in the dependence_graph,
96 * we want edges that point from sink to source.
98 * If the (read) access has an extension, i.e., if the access
99 * reads a row or an even larger chunk of an array, then we introduces
100 * an extra "expansion" edge to an extra node that corresponds to the
101 * reading of the whole array. The iteration domain of this extra node
102 * has extra dimensions, as many as were missing from the access.
103 * The extra edge maps each iteration of the statement in which
104 * the read appears to all iterations in the extended iteration domain
105 * with the same values for the first iterators.
106 * The remaining iterators range over the elements of the array chunk.
107 * All dependences in which the given access is involved are attached
108 * to the new node, where we need to be careful to use the extended_relation
109 * instead of the projected "standard" relations.
110 * The operation associated with the new node is the
111 * empty string since the new node can be treated as a copy operation.
112 * The edge connecting the original computation and the new node
113 * is marked as an "expansion" edge because it needs to be treated
116 static void add_dep_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
117 std::map
<pdg::access
*, computation
*> &s2c
,
118 std::map
<pdg::array
*, computation
*> &a2c
,
119 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
120 int pos
, pdg::access
*a
, computation
*comp
)
122 isl_ctx
*ctx
= pdg
->get_isl_ctx();
124 isl_map
*ext
= a
->extension
->get_isl_map(ctx
);
126 computation
*child
= new computation();
127 child
->domain
= isl_set_apply(isl_set_copy(comp
->domain
),
129 child
->operation
= strdup("");
131 child
->location
= comp
->location
;
132 dg
->vertices
.push_back(child
);
134 add_split_edge(comp
, child
, pos
, ext
, NULL
, edge::expansion
);
135 comp
->is_expanded
= 1;
139 for (int i
= 0; i
< pdg
->dependences
.size(); ++i
) {
140 pdg::dependence
*dep
= pdg
->dependences
[i
];
141 if (dep
->to_access
!= a
)
144 if (dep
->type
== pdg::dependence::uninitialized
) {
145 add_input_edges(ctx
, pos
, dep
, comp
, a2c
[dep
->array
]);
148 assert(dep
->type
== pdg::dependence::flow
);
151 if (!dep
->extended_relation
)
152 relation
= dep
->relation
->get_isl_map(ctx
);
154 relation
= dep
->extended_relation
->get_isl_map(ctx
);
155 relation
= isl_map_reverse(relation
);
156 add_split_edge(comp
, s2c
[dep
->from_access
], pos
, relation
,
157 &missing
[dep
->from_access
]);
161 /* Each access without an associated array corresponds to a read
162 * of an (affine combination of) iterator value(s).
163 * The expression in terms of the iterators is given by the access map.
164 * For each of these accesses, we add an edge to a special
165 * "input computation" that is not associated to any particular
166 * (input) array. The map of the edge is the access map,
167 * with the domain intersected with the domain of the current computation.
168 * This ensures that corresponding reads are considered equal iff the
169 * value they read is the same for both programs.
171 * In the special case where the affine expression is a mere integer constant
172 * that appears as an argument to a #test operation, the domain of "comp"
173 * may reference an argument that may not be present in "a" if it was
174 * created by c2pdg from an integer expression.
175 * In this case, we not only need to intersect the domain of
176 * the access relation, but also introduce a reference to the argument.
178 static void add_iterator_edges(pdg::PDG
*pdg
,
179 std::map
<pdg::array
*, computation
*> &a2c
,
181 pdg::access
*a
, computation
*comp
)
183 isl_ctx
*ctx
= pdg
->get_isl_ctx();
184 computation
*Z
= a2c
[NULL
];
185 isl_map
*map
= a
->map
->get_isl_map(ctx
);
186 isl_set
*domain
= isl_set_copy(comp
->domain
);
187 if (isl_set_is_wrapping(domain
) && !isl_map_domain_is_wrapping(map
)) {
189 domain_map
= isl_map_domain_map(isl_set_unwrap(domain
));
190 map
= isl_map_apply_range(domain_map
, map
);
192 map
= isl_map_intersect_domain(map
, domain
);
193 add_split_edge(comp
, Z
, pos
, map
, NULL
);
196 static void add_access_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
197 std::map
<pdg::access
*, computation
*> &s2c
,
198 std::map
<pdg::array
*, computation
*> &a2c
,
199 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
200 int pos
, pdg::access
*a
, computation
*comp
)
203 add_dep_edges(dg
, pdg
, s2c
, a2c
, missing
, pos
, a
, comp
);
205 add_iterator_edges(pdg
, a2c
, pos
, a
, comp
);
208 static int add_edges_at_pos(dependence_graph
*dg
, pdg::PDG
*pdg
,
209 std::map
<pdg::access
*, computation
*> &s2c
,
210 std::map
<pdg::array
*, computation
*> &a2c
,
211 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
212 int pos
, pdg::call_or_access
*coa
, computation
*comp
);
214 /* Add a new copy computation to dependence graph dg with given
215 * domain and location and return a pointer to the copy computation.
217 static computation
*add_copy_computation(dependence_graph
*dg
,
218 isl_set
*domain
, int location
)
221 copy
= new computation();
222 copy
->domain
= domain
;
223 copy
->operation
= strdup("");
225 copy
->location
= location
;
226 dg
->vertices
.push_back(copy
);
230 /* Create a "#NEST computation, with n + 1 arguments, where n is the
231 * number of nested accesses.
232 * The first n arguments correspond to the nested accesses and
233 * edges for these arguments are added as usual.
234 * Note that if the access "a" appears in a "#test" function,
235 * then some of the nested accesses will already have been
236 * added by an outer #NEST computation and should therefore be skipped.
237 * The final argument corresponds to the expansion and for this position
238 * a single expansion edge is added to the given copy computation.
239 * The mapping on the expansion edge is the identity
240 * mapping on the domain, with the range extended by one dimension for
241 * each of nested accesses. The range of values in each of these dimensions
242 * is given by the value_bounds on the corresponding array.
243 * The iteration domain of the given copy computation is adjusted accordingly.
245 static computation
*add_nest_computation(dependence_graph
*dg
, pdg::PDG
*pdg
,
246 std::map
<pdg::access
*, computation
*> &s2c
,
247 std::map
<pdg::array
*, computation
*> &a2c
,
248 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
249 pdg::access
*a
, computation
*copy
)
251 isl_ctx
*ctx
= pdg
->get_isl_ctx();
252 computation
*nest
= new computation();
253 nest
->domain
= isl_set_copy(copy
->domain
);
254 nest
->operation
= strdup("#NEST");
255 nest
->arity
= a
->nested
.size() + 1;
256 nest
->location
= copy
->location
;
257 dg
->vertices
.push_back(nest
);
259 int nested_dim
= a
->map
->input() - isl_set_dim(copy
->domain
, isl_dim_set
);
260 assert(nested_dim
> 0 && nested_dim
<= a
->nested
.size());
261 int skip_dim
= a
->nested
.size() - nested_dim
;
268 dim
= isl_set_get_space(copy
->domain
);
269 dim
= isl_space_drop_dims(dim
, isl_dim_set
,
270 0, isl_space_dim(dim
, isl_dim_set
));
271 bounds
= isl_set_universe(dim
);
273 for (int i
= skip_dim
; i
< a
->nested
.size(); ++i
) {
274 pdg::call_or_access
*coa
= a
->nested
[i
];
276 if (add_edges_at_pos(dg
, pdg
, s2c
, a2c
,
277 missing
, pos
, coa
, nest
))
280 assert(coa
->type
== pdg::call_or_access::t_access
);
281 pdg::access
*nested
= coa
->access
;
282 assert(nested
->array
->value_bounds
);
284 bounds_i
= nested
->array
->value_bounds
->get_isl_set(ctx
);
286 bounds
= isl_set_flat_product(bounds
, bounds_i
);
289 dim
= isl_set_get_space(copy
->domain
);
290 copy
->domain
= isl_set_product(copy
->domain
, isl_set_copy(bounds
));
291 bounds
= isl_set_product(isl_set_universe(dim
), bounds
);
292 ext
= isl_map_reverse(isl_map_domain_map(isl_set_unwrap(bounds
)));
294 add_split_edge(nest
, copy
, pos
, ext
, NULL
, edge::expansion
);
295 nest
->is_expanded
= 1;
300 /* Add an edge to "comp" at position "pos" according to access "a",
301 * where "a" is has nested accesses.
302 * We first create a copy computation where the actual access
303 * will be attached. Then we create a "#NEST" computation that
304 * sits between "comp" and the copy computation.
306 static void add_nested_access_edge(dependence_graph
*dg
, pdg::PDG
*pdg
,
307 std::map
<pdg::access
*, computation
*> &s2c
,
308 std::map
<pdg::array
*, computation
*> &a2c
,
309 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
310 int pos
, pdg::access
*a
, computation
*comp
)
313 copy
= add_copy_computation(dg
, isl_set_copy(comp
->domain
),
317 nest
= add_nest_computation(dg
, pdg
, s2c
, a2c
, missing
, a
, copy
);
322 isl_space
*dim
= isl_space_map_from_set(isl_set_get_space(comp
->domain
));
323 e
->relation
= isl_map_identity(dim
);
324 comp
->edges
.push_back(e
);
326 add_access_edges(dg
, pdg
, s2c
, a2c
, missing
, 0, a
, copy
);
329 /* "Invocations" of integer constants are replaced by accesses
330 * to the "input computation", so we can treat them like and combine them
331 * with iterator accesses.
333 static void add_constant_edge(pdg::PDG
*pdg
,
334 std::map
<pdg::array
*, computation
*> &a2c
,
335 int pos
, long cst
, computation
*comp
)
337 isl_ctx
*ctx
= pdg
->get_isl_ctx();
338 computation
*Z
= a2c
[NULL
];
340 isl_map
*map
= isl_map_from_range(isl_set_copy(comp
->domain
));
341 map
= isl_map_reverse(map
);
342 map
= isl_map_extend(map
, isl_map_n_param(map
),
343 isl_map_n_in(map
), 1);
344 map
= isl_map_fix_si(map
, isl_dim_out
, 0, cst
);
345 add_split_edge(comp
, Z
, pos
, map
, NULL
);
348 static void add_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
349 std::map
<pdg::access
*, computation
*> &s2c
,
350 std::map
<pdg::array
*, computation
*> &a2c
,
351 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
352 pdg::function_call
*call
, computation
*comp
);
354 /* Scale the single output dimension of bmap by a factor of f */
355 static __isl_give isl_map
*scale(__isl_take isl_map
*map
, __isl_take isl_val
*f
)
357 struct isl_constraint
*c
;
358 isl_basic_map
*scaling
;
361 dim
= isl_map_get_space(map
);
362 dim
= isl_space_range(dim
);
363 dim
= isl_space_map_from_set(dim
);
364 scaling
= isl_basic_map_universe(dim
);
365 c
= isl_equality_alloc(isl_basic_map_get_local_space(scaling
));
366 c
= isl_constraint_set_coefficient_val(c
, isl_dim_in
, 0, f
);
367 c
= isl_constraint_set_coefficient_si(c
, isl_dim_out
, 0, -1);
368 scaling
= isl_basic_map_add_constraint(scaling
, c
);
370 map
= isl_map_apply_range(map
, isl_map_from_basic_map(scaling
));
375 /* Compute the mapping along the given edge to the Z computation,
376 * where the Z computation may be either the source of the edge
377 * or it may be the source of the single edge leaving the copy
378 * computation that is the source of the given edge.
380 static isl_map
*map_to_Z(edge
*edge
, computation
*Z
)
382 if (edge
->source
== Z
)
383 return isl_map_copy(edge
->relation
);
384 assert(edge
->source
->is_copy());
385 assert(edge
->source
->edges
.size() == 1);
386 assert(edge
->source
->edges
[0]->source
== Z
);
387 return isl_map_apply_range(
388 isl_map_copy(edge
->relation
),
389 isl_map_copy(edge
->source
->edges
[0]->relation
));
392 static isl_map
*affine_expression_div(computation
*comp
, computation
*Z
)
396 isl_map
*rel
= map_to_Z(comp
->edges
[1], Z
);
398 v
= isl_map_plain_get_val_if_fixed(rel
, isl_dim_out
, 0);
399 if (isl_val_is_int(v
)) {
400 map
= isl_map_floordiv_val(
401 isl_map_copy(comp
->edges
[0]->relation
), v
);
408 /* Construct a map for CLooG's ceild operation.
410 * We simply apply the identity
412 * ceil(a/b) = - floor(-a/b)
414 static isl_map
*affine_expression_ceildiv(computation
*comp
, computation
*Z
)
420 map2
= map_to_Z(comp
->edges
[1], Z
);
421 v
= isl_map_plain_get_val_if_fixed(map2
, isl_dim_out
, 0);
422 if (isl_val_is_int(v
)) {
423 map
= isl_map_neg(map_to_Z(comp
->edges
[0], Z
));
424 map
= isl_map_neg(isl_map_floordiv_val(map
, v
));
432 /* Construct a mapping for the maximum of two mappings.
434 * In particular, given two mappings in the arguments of "comp"
441 * [input] -> [a] : a >= b; [input] -> [b] : a < b
445 * [input] -> [[a] -> [b]]
447 * intersect the range with [a] -> [b] : a >= b and [a] -> [b] : a < b
448 * and then map the range to its domain and range in the respective results.
450 static isl_map
*affine_expression_max(computation
*comp
, computation
*Z
)
453 isl_map
*map
, *ge
, *lt
;
454 isl_map
*map1
, *map2
;
456 map1
= map_to_Z(comp
->edges
[0], Z
);
457 map2
= map_to_Z(comp
->edges
[1], Z
);
459 dim
= isl_map_get_space(map1
);
460 dim
= isl_space_range(dim
);
462 map
= isl_map_range_product(map1
, map2
);
464 ge
= isl_map_lex_ge(isl_space_copy(dim
));
465 lt
= isl_map_lex_lt(isl_space_copy(dim
));
467 ge
= isl_map_intersect_range(isl_map_copy(map
), isl_map_wrap(ge
));
468 lt
= isl_map_intersect_range(map
, isl_map_wrap(lt
));
470 dim
= isl_space_map_from_set(dim
);
471 map
= isl_map_universe(isl_space_copy(dim
));
472 map
= isl_map_domain_map(map
);
473 ge
= isl_map_apply_range(ge
, map
);
475 map
= isl_map_universe(dim
);
476 map
= isl_map_range_map(map
);
477 lt
= isl_map_apply_range(lt
, map
);
479 map
= isl_map_union(ge
, lt
);
484 /* Given two maps A -> f(A) and B -> g(B), construct a map
485 * A -> f(A) * g(B) or B -> f(A) * g(B), if at least one of
486 * "rel0" or "rel1" maps to a constant value.
487 * Otherwise, return NULL.
489 static __isl_give isl_map
*map_mul(__isl_take isl_map
*rel0
,
490 __isl_take isl_map
*rel1
)
494 v
= isl_map_plain_get_val_if_fixed(rel0
, isl_dim_out
, 0);
495 if (isl_val_is_int(v
)) {
497 return scale(rel1
, v
);
501 v
= isl_map_plain_get_val_if_fixed(rel1
, isl_dim_out
, 0);
502 if (isl_val_is_int(v
)) {
504 return scale(rel0
, v
);
514 /* If comp represents an operation on affine expressions that can
515 * be represented by a single affine expression, then return a mapping
516 * encoding this single affine expression. Otherwise return NULL.
518 static isl_map
*affine_expression(computation
*comp
, computation
*Z
)
520 if (comp
->arity
!= comp
->edges
.size())
522 for (int i
= 0; i
< comp
->edges
.size(); ++i
) {
523 computation
*source
= comp
->edges
[i
]->source
;
526 if (source
&& source
->is_copy() && source
->edges
.size() == 1 &&
527 source
->edges
[0]->source
== Z
)
531 if (!strcmp(comp
->operation
, "+") && comp
->arity
== 2)
532 return isl_map_sum(map_to_Z(comp
->edges
[0], Z
),
533 map_to_Z(comp
->edges
[1], Z
));
534 if (!strcmp(comp
->operation
, "-") && comp
->arity
== 2)
536 map_to_Z(comp
->edges
[0], Z
),
537 isl_map_neg(map_to_Z(comp
->edges
[1], Z
)));
538 if (!strcmp(comp
->operation
, "-") && comp
->arity
== 1)
539 return isl_map_neg(map_to_Z(comp
->edges
[0], Z
));
540 if (!strcmp(comp
->operation
, "/") && comp
->arity
== 2)
541 return affine_expression_div(comp
, Z
);
542 if (!strcmp(comp
->operation
, "floord") && comp
->arity
== 2)
543 return affine_expression_div(comp
, Z
);
544 if (!strcmp(comp
->operation
, "ceild") && comp
->arity
== 2)
545 return affine_expression_ceildiv(comp
, Z
);
546 if (!strcmp(comp
->operation
, "max") && comp
->arity
== 2)
547 return affine_expression_max(comp
, Z
);
548 if (!strcmp(comp
->operation
, "*") && comp
->arity
== 2) {
549 isl_map
*rel0
= map_to_Z(comp
->edges
[0], Z
);
550 isl_map
*rel1
= map_to_Z(comp
->edges
[1], Z
);
551 return map_mul(rel0
, rel1
);
556 static void add_computation_edge(dependence_graph
*dg
, pdg::PDG
*pdg
,
557 std::map
<pdg::access
*, computation
*> &s2c
,
558 std::map
<pdg::array
*, computation
*> &a2c
,
559 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
560 pdg::function_call
*call
, computation
*comp
, int pos
)
564 cst
= strtol(call
->name
->s
.c_str(), &endp
, 0);
565 if (*call
->name
->s
.c_str() && !*endp
) {
566 add_constant_edge(pdg
, a2c
, pos
, cst
, comp
);
570 computation
*child
= new computation();
571 child
->domain
= isl_set_copy(comp
->domain
);
572 child
->operation
= strdup(call
->name
->s
.c_str());
573 child
->arity
= call
->arguments
.size();
574 child
->location
= comp
->location
;
578 comp
->edges
.push_back(e
);
580 add_edges(dg
, pdg
, s2c
, a2c
, missing
, call
, child
);
582 isl_map
*expr
= affine_expression(child
, a2c
[NULL
]);
585 e
->source
= a2c
[NULL
];
588 dg
->vertices
.push_back(child
);
590 isl_space
*dim
= isl_set_get_space(comp
->domain
);
591 e
->relation
= isl_map_identity(isl_space_map_from_set(dim
));
595 /* Add and edge from computation "at" to the integer computation "Z".
596 * The edge maps the domain of "at" to the value of its dimension "index_dim".
598 static void add_index_edge(dependence_graph
*dg
,
599 computation
*at
, computation
*Z
, int pos
, unsigned index_dim
)
601 unsigned total_out
= isl_set_dim(at
->domain
, isl_dim_out
);
602 isl_space
*dim
= isl_set_get_space(at
->domain
);
603 isl_map
*id
= isl_map_identity(isl_space_map_from_set(dim
));
604 id
= isl_map_remove_dims(id
, isl_dim_out
, index_dim
+ 1,
605 total_out
- index_dim
- 1);
606 id
= isl_map_remove_dims(id
, isl_dim_out
, 0, index_dim
);
607 add_split_edge(at
, Z
, pos
, id
, NULL
);
610 /* Given a write access "a" inside the arguments of a function call
611 * that was translated into computation "comp", make "a" point to
612 * "comp" as its computation and fill in locations collected in
613 * "missing" that should refer to the computation of "a".
615 * If the write access has an extension, i.e., if a whole row or
616 * an even larger chunk of an array is written, then we introduce
617 * a new node that corresponds to selecting the individual elements.
618 * Dependences that flow from the write are then turned into edges that
619 * point to the new computation.
620 * If the write access extends the dimension by s, i.e., if the
621 * chunk written is of dimension s, then the new computation with
622 * operation "#at" has 1 + s arguments, one for the chunk itself
623 * and one for each index in the chunk.
624 * There is exactly one edge for each arguments position, each
625 * selecting the appropriate dimensions from the domain.
626 * The first argument ensures that the values of the chunks
627 * are the same in both programs, while the remaining arguments
628 * ensure that the same element from this chunk is selected.
630 static void set_write_access(dependence_graph
*dg
, pdg::PDG
*pdg
,
631 std::map
<pdg::access
*, computation
*> &s2c
,
632 std::map
<pdg::array
*, computation
*> &a2c
,
633 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
634 pdg::access
*a
, computation
*comp
)
637 isl_ctx
*ctx
= pdg
->get_isl_ctx();
638 isl_map
*ext
= a
->extension
->get_isl_map(ctx
);
639 unsigned dim
= isl_map_dim(ext
, isl_dim_in
);
640 unsigned e_dim
= isl_map_dim(ext
, isl_dim_out
);
642 computation
*at
= new computation();
643 at
->domain
= isl_set_apply(isl_set_copy(comp
->domain
),
645 at
->operation
= strdup("#at");
646 at
->arity
= 1 + (e_dim
- dim
);
647 at
->location
= comp
->location
;
648 dg
->vertices
.push_back(at
);
650 ext
= isl_map_reverse(ext
);
651 add_split_edge(at
, comp
, 0, ext
, NULL
);
652 for (int i
= 0; i
< e_dim
- dim
; ++i
)
653 add_index_edge(dg
, at
, a2c
[NULL
], 1 + i
, dim
+ i
);
657 for (int k
= 0; k
< missing
[a
].size(); ++k
)
658 *missing
[a
][k
] = comp
;
661 /* For a given call or access "coa" which appears as argument "pos" of
662 * a function call, add one or more edges to computation comp.
663 * In case the argument is an access, the edges corresponding to
664 * the dependences are added in add_dep_edges.
665 * If it is itself a function call, then a single edge is added
666 * to a new child and add_edges is called recursively on the
667 * nested function call.
668 * Return 1 if any edges were added for this position and 0 if not.
669 * In particular, a write access should not be considered as an
670 * argument and is therefore skipped.
672 static int add_edges_at_pos(dependence_graph
*dg
, pdg::PDG
*pdg
,
673 std::map
<pdg::access
*, computation
*> &s2c
,
674 std::map
<pdg::array
*, computation
*> &a2c
,
675 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
676 int pos
, pdg::call_or_access
*coa
, computation
*comp
)
678 if (coa
->type
== pdg::call_or_access::t_access
) {
679 pdg::access
*a
= coa
->access
;
680 if (a
->type
== pdg::access::write
) {
681 set_write_access(dg
, pdg
, s2c
, a2c
, missing
, a
, comp
);
685 int nested_dim
= a
->map
->input() -
686 isl_set_dim(comp
->domain
, isl_dim_set
);
688 add_nested_access_edge(dg
, pdg
, s2c
, a2c
, missing
,
689 pos
, coa
->access
, comp
);
691 add_access_edges(dg
, pdg
, s2c
, a2c
, missing
,
692 pos
, coa
->access
, comp
);
694 add_computation_edge(dg
, pdg
, s2c
, a2c
, missing
,
695 coa
->call
, comp
, pos
);
700 /* For each argument of function call call, we add one or more edges to
703 static void add_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
704 std::map
<pdg::access
*, computation
*> &s2c
,
705 std::map
<pdg::array
*, computation
*> &a2c
,
706 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
707 pdg::function_call
*call
, computation
*comp
)
710 for (int i
= 0; i
< call
->arguments
.size(); ++i
) {
711 pdg::call_or_access
*coa
= call
->arguments
[i
];
713 if (add_edges_at_pos(dg
, pdg
, s2c
, a2c
,
714 missing
, pos
, coa
, comp
))
721 /* Add an edge from the output computation (out) to a computation computing
722 * (part of) an output array. node is pdg node corresponding to this
723 * computation. It is assumed to have a single write access, corresponding
724 * to the output array. exposed is that part of the node domain that
725 * is not the source of an output dependence, i.e., those for which
726 * the computed array elements are not overwritten.
727 * Let f(i) be the index expression of the write access and let
728 * pos be the position of the output array in the sorted list of
729 * output arrays, then an edge is added to out with relation
731 * { [ f(i), 0, pos ] -> [i] | i \in exposed }
733 * where the total dimension of the domain is out_dim, and this domain
734 * is added to the domain of the output computation.
736 static void add_output_edge(isl_ctx
*ctx
, computation
*out
, pdg::node
*node
,
737 std::map
<pdg::array
*, int> &array2pos
,
739 isl_set
*exposed
, unsigned nparam
, unsigned out_dim
)
741 pdg::statement
*s
= node
->statement
;
743 for (j
= 0; j
< s
->accesses
.size(); ++j
)
744 if (s
->accesses
[j
]->type
== pdg::access::write
&&
745 s
->accesses
[j
]->array
->type
== pdg::array::output
)
747 pdg::array
*array
= s
->accesses
[j
]->array
;
748 int pos
= array2pos
[array
];
750 if (!s
->accesses
[j
]->extended_map
)
751 rel
= isl_map_reverse(s
->accesses
[j
]->map
->get_isl_map(ctx
));
753 rel
= isl_map_reverse(s
->accesses
[j
]->extended_map
->get_isl_map(ctx
));
754 rel
= isl_map_add_dims(rel
, isl_dim_in
,
755 out_dim
- isl_map_dim(rel
, isl_dim_in
));
756 rel
= isl_map_intersect_range(rel
, exposed
);
757 rel
= isl_map_fix_input_si(rel
, out_dim
-1, pos
);
758 for (int k
= array
->dims
.size(); k
< out_dim
-1; ++k
)
759 rel
= isl_map_fix_input_si(rel
, k
, 0);
761 out
->domain
= isl_set_union_disjoint(out
->domain
,
762 isl_map_domain(isl_map_copy(rel
)));
763 add_split_edge(out
, last
, 0, rel
, NULL
);
766 static void connect_top_level_outputs(computation
*comp
,
767 std::map
<pdg::access
*, computation
*> &s2c
,
768 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
771 pdg::statement
*s
= node
->statement
;
773 for (int j
= 0; j
< s
->top_outputs
.size(); ++j
) {
774 pdg::access
*a
= s
->top_outputs
[j
];
776 for (int k
= 0; k
< missing
[a
].size(); ++k
)
777 *missing
[a
][k
] = comp
;
781 /* Add some computations to model a "#test" operation generated
782 * by pers for data dependent assignments.
783 * The test operation has three arguments.
784 * The first is an "iteration" access with nested accesses corresponding
785 * to the data dependent constructs in the conditions of the assignment.
786 * The access relation maps iteration in the extended domain that
787 * satisfy the conditions to 1, and those that do not satisfy the
789 * The other two arguments are the operations or accesses performed
790 * when the conditions hold (second argument) or do not hold (third
793 * We first create a copy computation and put it inside a #NEST
794 * computation that add the required dimensions.
795 * Then we split the extended iteration domain according to
796 * the data dependent conditions using two child copy computations
797 * with the appropriate dependence relations.
798 * The second and third arguments of the "#test" operation are
799 * then attached to these two child copy computations.
801 static void add_test_computation(dependence_graph
*dg
, pdg::PDG
*pdg
,
802 std::map
<pdg::access
*, computation
*> &s2c
,
803 std::map
<pdg::array
*, computation
*> &a2c
,
804 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
807 isl_ctx
*ctx
= pdg
->get_isl_ctx();
808 pdg::statement
*s
= node
->statement
;
811 copy
= add_copy_computation(dg
, node
->source
->get_isl_set(ctx
),
814 assert(s
->top_function
->arguments
.size() == 3);
815 pdg::call_or_access
*coa
= s
->top_function
->arguments
[0];
816 assert(coa
->type
== pdg::call_or_access::t_access
);
817 pdg::access
*a
= coa
->access
;
820 nest
= add_nest_computation(dg
, pdg
, s2c
, a2c
, missing
, a
, copy
);
822 isl_map
*map_true
= a
->map
->get_isl_map(ctx
);
823 isl_map
*map_false
= isl_map_copy(map_true
);
825 map_true
= isl_map_fix_si(map_true
, isl_dim_out
, 0, 1);
826 map_false
= isl_map_fix_si(map_false
, isl_dim_out
, 0, 0);
828 isl_space
*dim
= isl_space_domain(isl_map_get_space(map_true
));
829 dim
= isl_space_map_from_set(dim
);
830 map_true
= isl_map_intersect_domain(isl_map_identity(isl_space_copy(dim
)),
831 isl_map_domain(map_true
));
832 map_false
= isl_map_intersect_domain(isl_map_identity(dim
),
833 isl_map_domain(map_false
));
835 computation
*copy_true
;
836 copy_true
= add_copy_computation(dg
, isl_set_copy(copy
->domain
),
838 computation
*copy_false
;
839 copy_false
= add_copy_computation(dg
, isl_set_copy(copy
->domain
),
842 add_split_edge(copy
, copy_true
, 0, map_true
, NULL
);
843 add_split_edge(copy
, copy_false
, 0, map_false
, NULL
);
845 add_edges_at_pos(dg
, pdg
, s2c
, a2c
, missing
, 0,
846 s
->top_function
->arguments
[1], copy_true
);
847 add_edges_at_pos(dg
, pdg
, s2c
, a2c
, missing
, 0,
848 s
->top_function
->arguments
[2], copy_false
);
850 connect_top_level_outputs(nest
, s2c
, missing
, node
);
853 static void add_top_level_computation(dependence_graph
*dg
, pdg::PDG
*pdg
,
854 std::map
<pdg::access
*, computation
*> &s2c
,
855 std::map
<pdg::array
*, computation
*> &a2c
,
856 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
857 pdg::node
*node
, __isl_keep isl_set
*context
)
859 isl_ctx
*ctx
= pdg
->get_isl_ctx();
860 pdg::statement
*s
= node
->statement
;
862 if (!strcmp(s
->top_function
->name
->s
.c_str(), "#test")) {
863 add_test_computation(dg
, pdg
, s2c
, a2c
, missing
, node
);
867 computation
*comp
= new computation();
868 comp
->domain
= node
->source
->get_isl_set(ctx
);
869 comp
->domain
= isl_set_intersect_params(comp
->domain
,
870 isl_set_copy(context
));
871 comp
->location
= s
->line
;
875 cst
= strtol(s
->top_function
->name
->s
.c_str(), &endp
, 0);
876 if (*s
->top_function
->name
->s
.c_str() && !*endp
) {
878 comp
->operation
= strdup("");
879 add_constant_edge(pdg
, a2c
, 0, cst
, comp
);
881 comp
->arity
= s
->top_function
->arguments
.size();
882 comp
->operation
= strdup(s
->top_function
->name
->s
.c_str());
883 add_edges(dg
, pdg
, s2c
, a2c
, missing
, s
->top_function
, comp
);
884 isl_map
*expr
= affine_expression(comp
, a2c
[NULL
]);
887 comp
= new computation();
888 comp
->domain
= node
->source
->get_isl_set(ctx
);
889 comp
->domain
= isl_set_intersect_params(comp
->domain
,
890 isl_set_copy(context
));
891 comp
->location
= s
->line
;
893 comp
->operation
= strdup("");
897 e
->source
= a2c
[NULL
];
899 comp
->edges
.push_back(e
);
903 connect_top_level_outputs(comp
, s2c
, missing
, node
);
905 dg
->vertices
.push_back(comp
);
908 static void ensure_output_deps(pdg::PDG
*pdg
, pdg::array
*array
)
910 for (int i
= 0; i
< array
->analysis_performed
.size(); ++i
)
911 if (array
->analysis_performed
[i
]->type
==
912 pdg::dependence::output
)
914 fprintf(stderr
, "no output dependence analysis performed on array %s\n",
915 array
->name
->s
.c_str());
919 struct less_name
: public std::binary_function
<pdg::array
*, pdg::array
*, bool> {
920 bool operator()(pdg::array
* x
, pdg::array
* y
) {
921 return strcmp(x
->name
->s
.c_str(), y
->name
->s
.c_str()) < 0;
925 /* Convert a pdg to a dependence_graph containing all the information
926 * needed for equivalence checking.
928 * We first compute the total dimension of the output computation
929 * and create input computations for each input array.
930 * Then we create computations for each node in the pdg and
931 * a special output computation.
932 * For each node, we subsequently compute all the edges emanating
933 * from the corresponding computation.
934 * Finally, for each node writing to an output array, we compute
935 * that part of the domain that computes part of the final
936 * contents of the output array and add corresponding edges
937 * to the output computation.
939 dependence_graph
*pdg_to_dg(pdg::PDG
*pdg
, unsigned out_dim
,
940 __isl_take isl_set
*context
)
943 isl_ctx
*ctx
= pdg
->get_isl_ctx();
944 unsigned nparam
= pdg
->params
.size();
945 dependence_graph
*dg
= new dependence_graph
;
946 std::map
<pdg::access
*, computation
*> s2c
;
947 std::map
<pdg::array
*, computation
*> a2c
;
948 std::map
<pdg::access
*, std::vector
<computation
**> > missing
;
950 std::vector
<pdg::array
*> output_arrays
;
951 std::map
<pdg::array
*, int> array2pos
;
952 std::map
<pdg::access
*, isl_set
*> exposed
;
955 context
= isl_set_intersect(context
, pdg
->get_context_isl_set());
957 context
= pdg
->get_context_isl_set();
959 /* add a special "input computation"; see add_iterator_edges */
961 dims
= isl_space_set_alloc(ctx
, nparam
, 1);
962 isl_dim_set_parameter_names(dims
, pdg
->params
);
963 comp
= new input_array("", dims
);
965 dg
->vertices
.push_back(comp
);
967 for (int i
= 0; i
< pdg
->arrays
.size(); ++i
) {
968 pdg::array
*array
= pdg
->arrays
[i
];
969 if (array
->type
== pdg::array::output
) {
970 output_arrays
.push_back(array
);
971 ensure_output_deps(pdg
, array
);
974 if (array
->type
!= pdg::array::input
)
978 dims
= isl_space_set_alloc(ctx
, nparam
, array
->dims
.size());
979 isl_dim_set_parameter_names(dims
, pdg
->params
);
980 comp
= new input_array(array
->name
->s
.c_str(), dims
);
982 dg
->vertices
.push_back(comp
);
983 dg
->input_computations
.push_back(comp
);
986 fprintf(stderr
, "no output arrays\n");
989 std::sort(output_arrays
.begin(), output_arrays
.end(), less_name());
990 for (int i
= 0; i
< output_arrays
.size(); ++i
) {
991 dg
->output_arrays
.push_back(strdup(output_arrays
[i
]->name
->s
.c_str()));
992 dg
->output_array_dims
.push_back(output_arrays
[i
]->dims
.size());
993 array2pos
[output_arrays
[i
]] = i
;
995 dg
->out
= new computation();
996 dims
= isl_space_set_alloc(ctx
, nparam
, out_dim
);
997 isl_dim_set_parameter_names(dims
, pdg
->params
);
998 dg
->out
->domain
= isl_set_empty(dims
);
999 dg
->out
->operation
= strdup("#Out");
1001 dg
->out
->location
= -1;
1002 for (int i
= 0; i
< pdg
->nodes
.size(); ++i
) {
1003 pdg::node
*node
= pdg
->nodes
[i
];
1004 pdg::statement
*s
= node
->statement
;
1006 add_top_level_computation(dg
, pdg
, s2c
, a2c
, missing
, node
,
1009 for (int j
= 0; j
< s
->accesses
.size(); ++j
) {
1010 if (s
->accesses
[j
]->type
!= pdg::access::write
)
1012 pdg::access
*a
= s
->accesses
[j
];
1014 if (s
->accesses
[j
]->array
->type
!= pdg::array::output
)
1017 exposed
[a
] = node
->source
->get_isl_set(ctx
);
1019 isl_map
*ext
= a
->extension
->get_isl_map(ctx
);
1020 exposed
[a
] = isl_set_apply(exposed
[a
], ext
);
1022 exposed
[a
] = isl_set_intersect_params(exposed
[a
],
1023 isl_set_copy(context
));
1026 for (int i
= 0; i
< pdg
->dependences
.size(); ++i
) {
1027 pdg::dependence
*dep
= pdg
->dependences
[i
];
1028 if (dep
->type
!= pdg::dependence::output
)
1030 pdg::access
*a
= dep
->from_access
;
1031 if (!exposed
[a
] || isl_set_is_empty(exposed
[a
]))
1034 if (!dep
->extended_relation
)
1035 rel
= dep
->relation
->get_isl_map(ctx
);
1037 rel
= dep
->extended_relation
->get_isl_map(ctx
);
1038 exposed
[a
] = isl_set_subtract(exposed
[a
], isl_map_domain(rel
));
1040 for (int i
= 0; i
< pdg
->nodes
.size(); ++i
) {
1041 pdg::node
*node
= pdg
->nodes
[i
];
1042 pdg::statement
*s
= node
->statement
;
1043 for (int j
= 0; j
< s
->accesses
.size(); ++j
) {
1044 pdg::access
*a
= s
->accesses
[j
];
1045 if (!exposed
[a
] || isl_set_is_empty(exposed
[a
])) {
1046 isl_set_free(exposed
[a
]);
1049 add_output_edge(ctx
, dg
->out
, node
, array2pos
,
1050 s2c
[a
], exposed
[a
], nparam
, out_dim
);
1053 dg
->context
= context
;