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 static void add_iterator_edges(pdg::PDG
*pdg
,
172 std::map
<pdg::array
*, computation
*> &a2c
,
174 pdg::access
*a
, computation
*comp
)
176 isl_ctx
*ctx
= pdg
->get_isl_ctx();
177 computation
*Z
= a2c
[NULL
];
178 isl_map
*map
= a
->map
->get_isl_map(ctx
);
179 map
= isl_map_intersect_domain(map
, isl_set_copy(comp
->domain
));
180 add_split_edge(comp
, Z
, pos
, map
, NULL
);
183 static void add_access_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
184 std::map
<pdg::access
*, computation
*> &s2c
,
185 std::map
<pdg::array
*, computation
*> &a2c
,
186 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
187 int pos
, pdg::access
*a
, computation
*comp
)
190 add_dep_edges(dg
, pdg
, s2c
, a2c
, missing
, pos
, a
, comp
);
192 add_iterator_edges(pdg
, a2c
, pos
, a
, comp
);
195 static int add_edges_at_pos(dependence_graph
*dg
, pdg::PDG
*pdg
,
196 std::map
<pdg::access
*, computation
*> &s2c
,
197 std::map
<pdg::array
*, computation
*> &a2c
,
198 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
199 int pos
, pdg::call_or_access
*coa
, computation
*comp
);
201 /* Add a new copy computation to dependence graph dg with given
202 * domain and location and return a pointer to the copy computation.
204 static computation
*add_copy_computation(dependence_graph
*dg
,
205 isl_set
*domain
, int location
)
208 copy
= new computation();
209 copy
->domain
= domain
;
210 copy
->operation
= strdup("");
212 copy
->location
= location
;
213 dg
->vertices
.push_back(copy
);
217 /* Create a "#NEST computation, with n + 1 arguments, where n is the
218 * number of nested accesses.
219 * The first n arguments correspond to the nested accesses and
220 * edges for these arguments are added as usual.
221 * Note that if the access "a" appears in a "#test" function,
222 * then some of the nested accesses will already have been
223 * added by an outer #NEST computation and should therefore be skipped.
224 * The final argument corresponds to the expansion and for this position
225 * a single expansion edge is added to the given copy computation.
226 * The mapping on the expansion edge is the identity
227 * mapping on the domain, with the range extended by one dimension for
228 * each of nested accesses. The range of values in each of these dimensions
229 * is given by the value_bounds on the corresponding array.
230 * The iteration domain of the given copy computation is adjusted accordingly.
232 static computation
*add_nest_computation(dependence_graph
*dg
, pdg::PDG
*pdg
,
233 std::map
<pdg::access
*, computation
*> &s2c
,
234 std::map
<pdg::array
*, computation
*> &a2c
,
235 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
236 pdg::access
*a
, computation
*copy
)
238 isl_ctx
*ctx
= pdg
->get_isl_ctx();
239 computation
*nest
= new computation();
240 nest
->domain
= isl_set_copy(copy
->domain
);
241 nest
->operation
= strdup("#NEST");
242 nest
->arity
= a
->nested
.size() + 1;
243 nest
->location
= copy
->location
;
244 dg
->vertices
.push_back(nest
);
246 int nested_dim
= a
->map
->input() - isl_set_dim(copy
->domain
, isl_dim_set
);
247 assert(nested_dim
> 0 && nested_dim
<= a
->nested
.size());
248 int skip_dim
= a
->nested
.size() - nested_dim
;
255 dim
= isl_set_get_space(copy
->domain
);
256 dim
= isl_space_drop_dims(dim
, isl_dim_set
,
257 0, isl_space_dim(dim
, isl_dim_set
));
258 bounds
= isl_set_universe(dim
);
260 for (int i
= skip_dim
; i
< a
->nested
.size(); ++i
) {
261 pdg::call_or_access
*coa
= a
->nested
[i
];
263 if (add_edges_at_pos(dg
, pdg
, s2c
, a2c
,
264 missing
, pos
, coa
, nest
))
267 assert(coa
->type
== pdg::call_or_access::t_access
);
268 pdg::access
*nested
= coa
->access
;
269 assert(nested
->array
->value_bounds
);
271 bounds_i
= nested
->array
->value_bounds
->get_isl_set(ctx
);
273 bounds
= isl_set_flat_product(bounds
, bounds_i
);
276 dim
= isl_set_get_space(copy
->domain
);
277 copy
->domain
= isl_set_product(copy
->domain
, isl_set_copy(bounds
));
278 bounds
= isl_set_product(isl_set_universe(dim
), bounds
);
279 ext
= isl_map_reverse(isl_map_domain_map(isl_set_unwrap(bounds
)));
281 add_split_edge(nest
, copy
, pos
, ext
, NULL
, edge::expansion
);
282 nest
->is_expanded
= 1;
287 /* Add an edge to "comp" at position "pos" according to access "a",
288 * where "a" is has nested accesses.
289 * We first create a copy computation where the actual access
290 * will be attached. Then we create a "#NEST" computation that
291 * sits between "comp" and the copy computation.
293 static void add_nested_access_edge(dependence_graph
*dg
, pdg::PDG
*pdg
,
294 std::map
<pdg::access
*, computation
*> &s2c
,
295 std::map
<pdg::array
*, computation
*> &a2c
,
296 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
297 int pos
, pdg::access
*a
, computation
*comp
)
300 copy
= add_copy_computation(dg
, isl_set_copy(comp
->domain
),
304 nest
= add_nest_computation(dg
, pdg
, s2c
, a2c
, missing
, a
, copy
);
309 isl_space
*dim
= isl_space_map_from_set(isl_set_get_space(comp
->domain
));
310 e
->relation
= isl_map_identity(dim
);
311 comp
->edges
.push_back(e
);
313 add_access_edges(dg
, pdg
, s2c
, a2c
, missing
, 0, a
, copy
);
316 /* "Invocations" of integer constants are replaced by accesses
317 * to the "input computation", so we can treat them like and combine them
318 * with iterator accesses.
320 static void add_constant_edge(pdg::PDG
*pdg
,
321 std::map
<pdg::array
*, computation
*> &a2c
,
322 int pos
, long cst
, computation
*comp
)
324 isl_ctx
*ctx
= pdg
->get_isl_ctx();
325 computation
*Z
= a2c
[NULL
];
327 isl_map
*map
= isl_map_from_range(isl_set_copy(comp
->domain
));
328 map
= isl_map_reverse(map
);
329 map
= isl_map_extend(map
, isl_map_n_param(map
),
330 isl_map_n_in(map
), 1);
331 map
= isl_map_fix_si(map
, isl_dim_out
, 0, cst
);
332 add_split_edge(comp
, Z
, pos
, map
, NULL
);
335 static void add_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
336 std::map
<pdg::access
*, computation
*> &s2c
,
337 std::map
<pdg::array
*, computation
*> &a2c
,
338 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
339 pdg::function_call
*call
, computation
*comp
);
341 /* Scale the single output dimension of bmap by a factor of f */
342 static isl_map
*scale(isl_map
*map
, isl_int f
)
344 struct isl_constraint
*c
;
345 isl_basic_map
*scaling
;
348 dim
= isl_map_get_space(map
);
349 dim
= isl_space_range(dim
);
350 dim
= isl_space_map_from_set(dim
);
351 scaling
= isl_basic_map_universe(dim
);
352 c
= isl_equality_alloc(isl_basic_map_get_local_space(scaling
));
353 isl_constraint_set_coefficient(c
, isl_dim_in
, 0, f
);
354 isl_int_set_si(f
, -1);
355 isl_constraint_set_coefficient(c
, isl_dim_out
, 0, f
);
356 scaling
= isl_basic_map_add_constraint(scaling
, c
);
358 map
= isl_map_apply_range(map
, isl_map_from_basic_map(scaling
));
363 /* Compute the mapping along the given edge to the Z computation,
364 * where the Z computation may be either the source of the edge
365 * or it may be the source of the single edge leaving the copy
366 * computation that is the source of the given edge.
368 static isl_map
*map_to_Z(edge
*edge
, computation
*Z
)
370 if (edge
->source
== Z
)
371 return isl_map_copy(edge
->relation
);
372 assert(edge
->source
->is_copy());
373 assert(edge
->source
->edges
.size() == 1);
374 assert(edge
->source
->edges
[0]->source
== Z
);
375 return isl_map_apply_range(
376 isl_map_copy(edge
->relation
),
377 isl_map_copy(edge
->source
->edges
[0]->relation
));
380 static isl_map
*affine_expression_div(computation
*comp
, computation
*Z
)
385 isl_map
*rel
= map_to_Z(comp
->edges
[1], Z
);
386 if (isl_map_fast_is_fixed(rel
, isl_dim_out
, 0, &v
)) {
387 map
= isl_map_floordiv(
388 isl_map_copy(comp
->edges
[0]->relation
), v
);
395 /* Construct a map for CLooG's ceild operation.
397 * We simply apply the identity
399 * ceil(a/b) = - floor(-a/b)
401 static isl_map
*affine_expression_ceildiv(computation
*comp
, computation
*Z
)
409 map2
= map_to_Z(comp
->edges
[1], Z
);
410 if (isl_map_fast_is_fixed(map2
, isl_dim_out
, 0, &v
)) {
411 map
= isl_map_neg(map_to_Z(comp
->edges
[0], Z
));
412 map
= isl_map_neg(isl_map_floordiv(map
, v
));
420 /* Construct a mapping for the maximum of two mappings.
422 * In particular, given two mappings in the arguments of "comp"
429 * [input] -> [a] : a >= b; [input] -> [b] : a < b
433 * [input] -> [[a] -> [b]]
435 * intersect the range with [a] -> [b] : a >= b and [a] -> [b] : a < b
436 * and then map the range to its domain and range in the respective results.
438 static isl_map
*affine_expression_max(computation
*comp
, computation
*Z
)
441 isl_map
*map
, *ge
, *lt
;
442 isl_map
*map1
, *map2
;
444 map1
= map_to_Z(comp
->edges
[0], Z
);
445 map2
= map_to_Z(comp
->edges
[1], Z
);
447 dim
= isl_map_get_space(map1
);
448 dim
= isl_space_range(dim
);
450 map
= isl_map_range_product(map1
, map2
);
452 ge
= isl_map_lex_ge(isl_space_copy(dim
));
453 lt
= isl_map_lex_lt(isl_space_copy(dim
));
455 ge
= isl_map_intersect_range(isl_map_copy(map
), isl_map_wrap(ge
));
456 lt
= isl_map_intersect_range(map
, isl_map_wrap(lt
));
458 dim
= isl_space_map_from_set(dim
);
459 map
= isl_map_universe(isl_space_copy(dim
));
460 map
= isl_map_domain_map(map
);
461 ge
= isl_map_apply_range(ge
, map
);
463 map
= isl_map_universe(dim
);
464 map
= isl_map_range_map(map
);
465 lt
= isl_map_apply_range(lt
, map
);
467 map
= isl_map_union(ge
, lt
);
472 /* If comp represents an operation on affine expressions that can
473 * be represented by a single affine expression, then return a mapping
474 * encoding this single affine expression. Otherwise return NULL.
476 static isl_map
*affine_expression(computation
*comp
, computation
*Z
)
478 if (comp
->arity
!= comp
->edges
.size())
480 for (int i
= 0; i
< comp
->edges
.size(); ++i
) {
481 computation
*source
= comp
->edges
[i
]->source
;
484 if (source
&& source
->is_copy() && source
->edges
.size() == 1 &&
485 source
->edges
[0]->source
== Z
)
489 if (!strcmp(comp
->operation
, "+") && comp
->arity
== 2)
490 return isl_map_sum(map_to_Z(comp
->edges
[0], Z
),
491 map_to_Z(comp
->edges
[1], Z
));
492 if (!strcmp(comp
->operation
, "-") && comp
->arity
== 2)
494 map_to_Z(comp
->edges
[0], Z
),
495 isl_map_neg(map_to_Z(comp
->edges
[1], Z
)));
496 if (!strcmp(comp
->operation
, "-") && comp
->arity
== 1)
497 return isl_map_neg(map_to_Z(comp
->edges
[0], Z
));
498 if (!strcmp(comp
->operation
, "/") && comp
->arity
== 2)
499 return affine_expression_div(comp
, Z
);
500 if (!strcmp(comp
->operation
, "floord") && comp
->arity
== 2)
501 return affine_expression_div(comp
, Z
);
502 if (!strcmp(comp
->operation
, "ceild") && comp
->arity
== 2)
503 return affine_expression_ceildiv(comp
, Z
);
504 if (!strcmp(comp
->operation
, "max") && comp
->arity
== 2)
505 return affine_expression_max(comp
, Z
);
506 if (!strcmp(comp
->operation
, "*") && comp
->arity
== 2) {
510 isl_map
*rel0
= map_to_Z(comp
->edges
[0], Z
);
511 isl_map
*rel1
= map_to_Z(comp
->edges
[1], Z
);
512 if (isl_map_fast_is_fixed(rel0
, isl_dim_out
, 0, &v
))
513 map
= scale(isl_map_copy(rel1
), v
);
514 else if (isl_map_fast_is_fixed(rel1
, isl_dim_out
, 0, &v
))
515 map
= scale(isl_map_copy(rel0
), v
);
524 static void add_computation_edge(dependence_graph
*dg
, pdg::PDG
*pdg
,
525 std::map
<pdg::access
*, computation
*> &s2c
,
526 std::map
<pdg::array
*, computation
*> &a2c
,
527 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
528 pdg::function_call
*call
, computation
*comp
, int pos
)
532 cst
= strtol(call
->name
->s
.c_str(), &endp
, 0);
533 if (*call
->name
->s
.c_str() && !*endp
) {
534 add_constant_edge(pdg
, a2c
, pos
, cst
, comp
);
538 computation
*child
= new computation();
539 child
->domain
= isl_set_copy(comp
->domain
);
540 child
->operation
= strdup(call
->name
->s
.c_str());
541 child
->arity
= call
->arguments
.size();
542 child
->location
= comp
->location
;
546 comp
->edges
.push_back(e
);
548 add_edges(dg
, pdg
, s2c
, a2c
, missing
, call
, child
);
550 isl_map
*expr
= affine_expression(child
, a2c
[NULL
]);
553 e
->source
= a2c
[NULL
];
556 dg
->vertices
.push_back(child
);
558 isl_space
*dim
= isl_set_get_space(comp
->domain
);
559 e
->relation
= isl_map_identity(isl_space_map_from_set(dim
));
563 /* Add and edge from computation "at" to the integer computation "Z".
564 * The edge maps the domain of "at" to the value of its dimension "index_dim".
566 static void add_index_edge(dependence_graph
*dg
,
567 computation
*at
, computation
*Z
, int pos
, unsigned index_dim
)
569 unsigned total_out
= isl_set_dim(at
->domain
, isl_dim_out
);
570 isl_space
*dim
= isl_set_get_space(at
->domain
);
571 isl_map
*id
= isl_map_identity(isl_space_map_from_set(dim
));
572 id
= isl_map_remove_dims(id
, isl_dim_out
, index_dim
+ 1,
573 total_out
- index_dim
- 1);
574 id
= isl_map_remove_dims(id
, isl_dim_out
, 0, index_dim
);
575 add_split_edge(at
, Z
, pos
, id
, NULL
);
578 /* Given a write access "a" inside the arguments of a function call
579 * that was translated into computation "comp", make "a" point to
580 * "comp" as its computation and fill in locations collected in
581 * "missing" that should refer to the computation of "a".
583 * If the write access has an extension, i.e., if a whole row or
584 * an even larger chunk of an array is written, then we introduce
585 * a new node that corresponds to selecting the individual elements.
586 * Dependences that flow from the write are then turned into edges that
587 * point to the new computation.
588 * If the write access extends the dimension by s, i.e., if the
589 * chunk written is of dimension s, then the new computation with
590 * operation "#at" has 1 + s arguments, one for the chunk itself
591 * and one for each index in the chunk.
592 * There is exactly one edge for each arguments position, each
593 * selecting the appropriate dimensions from the domain.
594 * The first argument ensures that the values of the chunks
595 * are the same in both programs, while the remaining arguments
596 * ensure that the same element from this chunk is selected.
598 static void set_write_access(dependence_graph
*dg
, pdg::PDG
*pdg
,
599 std::map
<pdg::access
*, computation
*> &s2c
,
600 std::map
<pdg::array
*, computation
*> &a2c
,
601 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
602 pdg::access
*a
, computation
*comp
)
605 isl_ctx
*ctx
= pdg
->get_isl_ctx();
606 isl_map
*ext
= a
->extension
->get_isl_map(ctx
);
607 unsigned dim
= isl_map_dim(ext
, isl_dim_in
);
608 unsigned e_dim
= isl_map_dim(ext
, isl_dim_out
);
610 computation
*at
= new computation();
611 at
->domain
= isl_set_apply(isl_set_copy(comp
->domain
),
613 at
->operation
= strdup("#at");
614 at
->arity
= 1 + (e_dim
- dim
);
615 at
->location
= comp
->location
;
616 dg
->vertices
.push_back(at
);
618 ext
= isl_map_reverse(ext
);
619 add_split_edge(at
, comp
, 0, ext
, NULL
);
620 for (int i
= 0; i
< e_dim
- dim
; ++i
)
621 add_index_edge(dg
, at
, a2c
[NULL
], 1 + i
, dim
+ i
);
625 for (int k
= 0; k
< missing
[a
].size(); ++k
)
626 *missing
[a
][k
] = comp
;
629 /* For a given call or access "coa" which appears as argument "pos" of
630 * a function call, add one or more edges to computation comp.
631 * In case the argument is an access, the edges corresponding to
632 * the dependences are added in add_dep_edges.
633 * If it is itself a function call, then a single edge is added
634 * to a new child and add_edges is called recursively on the
635 * nested function call.
636 * Return 1 if any edges were added for this position and 0 if not.
637 * In particular, a write access should not be considered as an
638 * argument and is therefore skipped.
640 static int add_edges_at_pos(dependence_graph
*dg
, pdg::PDG
*pdg
,
641 std::map
<pdg::access
*, computation
*> &s2c
,
642 std::map
<pdg::array
*, computation
*> &a2c
,
643 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
644 int pos
, pdg::call_or_access
*coa
, computation
*comp
)
646 if (coa
->type
== pdg::call_or_access::t_access
) {
647 pdg::access
*a
= coa
->access
;
648 if (a
->type
== pdg::access::write
) {
649 set_write_access(dg
, pdg
, s2c
, a2c
, missing
, a
, comp
);
653 int nested_dim
= a
->map
->input() -
654 isl_set_dim(comp
->domain
, isl_dim_set
);
656 add_nested_access_edge(dg
, pdg
, s2c
, a2c
, missing
,
657 pos
, coa
->access
, comp
);
659 add_access_edges(dg
, pdg
, s2c
, a2c
, missing
,
660 pos
, coa
->access
, comp
);
662 add_computation_edge(dg
, pdg
, s2c
, a2c
, missing
,
663 coa
->call
, comp
, pos
);
668 /* For each argument of function call call, we add one or more edges to
671 static void add_edges(dependence_graph
*dg
, pdg::PDG
*pdg
,
672 std::map
<pdg::access
*, computation
*> &s2c
,
673 std::map
<pdg::array
*, computation
*> &a2c
,
674 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
675 pdg::function_call
*call
, computation
*comp
)
678 for (int i
= 0; i
< call
->arguments
.size(); ++i
) {
679 pdg::call_or_access
*coa
= call
->arguments
[i
];
681 if (add_edges_at_pos(dg
, pdg
, s2c
, a2c
,
682 missing
, pos
, coa
, comp
))
689 /* Add an edge from the output computation (out) to a computation computing
690 * (part of) an output array. node is pdg node corresponding to this
691 * computation. It is assumed to have a single write access, corresponding
692 * to the output array. exposed is that part of the node domain that
693 * is not the source of an output dependence, i.e., those for which
694 * the computed array elements are not overwritten.
695 * Let f(i) be the index expression of the write access and let
696 * pos be the position of the output array in the sorted list of
697 * output arrays, then an edge is added to out with relation
699 * { [ f(i), 0, pos ] -> [i] | i \in exposed }
701 * where the total dimension of the domain is out_dim, and this domain
702 * is added to the domain of the output computation.
704 static void add_output_edge(isl_ctx
*ctx
, computation
*out
, pdg::node
*node
,
705 std::map
<pdg::array
*, int> &array2pos
,
707 isl_set
*exposed
, unsigned nparam
, unsigned out_dim
)
709 pdg::statement
*s
= node
->statement
;
711 for (j
= 0; j
< s
->accesses
.size(); ++j
)
712 if (s
->accesses
[j
]->type
== pdg::access::write
&&
713 s
->accesses
[j
]->array
->type
== pdg::array::output
)
715 pdg::array
*array
= s
->accesses
[j
]->array
;
716 int pos
= array2pos
[array
];
718 if (!s
->accesses
[j
]->extended_map
)
719 rel
= isl_map_reverse(s
->accesses
[j
]->map
->get_isl_map(ctx
));
721 rel
= isl_map_reverse(s
->accesses
[j
]->extended_map
->get_isl_map(ctx
));
722 rel
= isl_map_add_dims(rel
, isl_dim_in
,
723 out_dim
- isl_map_dim(rel
, isl_dim_in
));
724 rel
= isl_map_intersect_range(rel
, exposed
);
725 rel
= isl_map_fix_input_si(rel
, out_dim
-1, pos
);
726 for (int k
= array
->dims
.size(); k
< out_dim
-1; ++k
)
727 rel
= isl_map_fix_input_si(rel
, k
, 0);
729 out
->domain
= isl_set_union_disjoint(out
->domain
,
730 isl_map_domain(isl_map_copy(rel
)));
731 add_split_edge(out
, last
, 0, rel
, NULL
);
734 static void connect_top_level_outputs(computation
*comp
,
735 std::map
<pdg::access
*, computation
*> &s2c
,
736 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
739 pdg::statement
*s
= node
->statement
;
741 for (int j
= 0; j
< s
->top_outputs
.size(); ++j
) {
742 pdg::access
*a
= s
->top_outputs
[j
];
744 for (int k
= 0; k
< missing
[a
].size(); ++k
)
745 *missing
[a
][k
] = comp
;
749 /* Add some computations to model a "#test" operation generated
750 * by pers for data dependent assignments.
751 * The test operation has three arguments.
752 * The first is an "iteration" access with nested accesses corresponding
753 * to the data dependent constructs in the conditions of the assignment.
754 * The access relation maps iteration in the extended domain that
755 * satisfy the conditions to 1, and those that do not satisfy the
757 * The other two arguments are the operations or accesses performed
758 * when the conditions hold (second argument) or do not hold (third
761 * We first create a copy computation and put it inside a #NEST
762 * computation that add the required dimensions.
763 * Then we split the extended iteration domain according to
764 * the data dependent conditions using two child copy computations
765 * with the appropriate dependence relations.
766 * The second and third arguments of the "#test" operation are
767 * then attached to these two child copy computations.
769 static void add_test_computation(dependence_graph
*dg
, pdg::PDG
*pdg
,
770 std::map
<pdg::access
*, computation
*> &s2c
,
771 std::map
<pdg::array
*, computation
*> &a2c
,
772 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
775 isl_ctx
*ctx
= pdg
->get_isl_ctx();
776 pdg::statement
*s
= node
->statement
;
779 copy
= add_copy_computation(dg
, node
->source
->get_isl_set(ctx
),
782 assert(s
->top_function
->arguments
.size() == 3);
783 pdg::call_or_access
*coa
= s
->top_function
->arguments
[0];
784 assert(coa
->type
== pdg::call_or_access::t_access
);
785 pdg::access
*a
= coa
->access
;
788 nest
= add_nest_computation(dg
, pdg
, s2c
, a2c
, missing
, a
, copy
);
790 isl_map
*map_true
= a
->map
->get_isl_map(ctx
);
791 isl_map
*map_false
= isl_map_copy(map_true
);
793 map_true
= isl_map_fix_si(map_true
, isl_dim_out
, 0, 1);
794 map_false
= isl_map_fix_si(map_false
, isl_dim_out
, 0, 0);
796 isl_space
*dim
= isl_space_domain(isl_map_get_space(map_true
));
797 dim
= isl_space_map_from_set(dim
);
798 map_true
= isl_map_intersect_domain(isl_map_identity(isl_space_copy(dim
)),
799 isl_map_domain(map_true
));
800 map_false
= isl_map_intersect_domain(isl_map_identity(dim
),
801 isl_map_domain(map_false
));
803 computation
*copy_true
;
804 copy_true
= add_copy_computation(dg
, isl_set_copy(copy
->domain
),
806 computation
*copy_false
;
807 copy_false
= add_copy_computation(dg
, isl_set_copy(copy
->domain
),
810 add_split_edge(copy
, copy_true
, 0, map_true
, NULL
);
811 add_split_edge(copy
, copy_false
, 0, map_false
, NULL
);
813 add_edges_at_pos(dg
, pdg
, s2c
, a2c
, missing
, 0,
814 s
->top_function
->arguments
[1], copy_true
);
815 add_edges_at_pos(dg
, pdg
, s2c
, a2c
, missing
, 0,
816 s
->top_function
->arguments
[2], copy_false
);
818 connect_top_level_outputs(nest
, s2c
, missing
, node
);
821 static void add_top_level_computation(dependence_graph
*dg
, pdg::PDG
*pdg
,
822 std::map
<pdg::access
*, computation
*> &s2c
,
823 std::map
<pdg::array
*, computation
*> &a2c
,
824 std::map
<pdg::access
*, std::vector
<computation
**> > &missing
,
825 pdg::node
*node
, __isl_keep isl_set
*context
)
827 isl_ctx
*ctx
= pdg
->get_isl_ctx();
828 pdg::statement
*s
= node
->statement
;
830 if (!strcmp(s
->top_function
->name
->s
.c_str(), "#test")) {
831 add_test_computation(dg
, pdg
, s2c
, a2c
, missing
, node
);
835 computation
*comp
= new computation();
836 comp
->domain
= node
->source
->get_isl_set(ctx
);
837 comp
->domain
= isl_set_intersect_params(comp
->domain
,
838 isl_set_copy(context
));
839 comp
->location
= s
->line
;
843 cst
= strtol(s
->top_function
->name
->s
.c_str(), &endp
, 0);
844 if (*s
->top_function
->name
->s
.c_str() && !*endp
) {
846 comp
->operation
= strdup("");
847 add_constant_edge(pdg
, a2c
, 0, cst
, comp
);
849 comp
->arity
= s
->top_function
->arguments
.size();
850 comp
->operation
= strdup(s
->top_function
->name
->s
.c_str());
851 add_edges(dg
, pdg
, s2c
, a2c
, missing
, s
->top_function
, comp
);
852 isl_map
*expr
= affine_expression(comp
, a2c
[NULL
]);
855 comp
= new computation();
856 comp
->domain
= node
->source
->get_isl_set(ctx
);
857 comp
->domain
= isl_set_intersect_params(comp
->domain
,
858 isl_set_copy(context
));
859 comp
->location
= s
->line
;
861 comp
->operation
= strdup("");
865 e
->source
= a2c
[NULL
];
867 comp
->edges
.push_back(e
);
871 connect_top_level_outputs(comp
, s2c
, missing
, node
);
873 dg
->vertices
.push_back(comp
);
876 static void ensure_output_deps(pdg::PDG
*pdg
, pdg::array
*array
)
878 for (int i
= 0; i
< array
->analysis_performed
.size(); ++i
)
879 if (array
->analysis_performed
[i
]->type
==
880 pdg::dependence::output
)
882 fprintf(stderr
, "no output dependence analysis performed on array %s\n",
883 array
->name
->s
.c_str());
887 struct less_name
: public std::binary_function
<pdg::array
*, pdg::array
*, bool> {
888 bool operator()(pdg::array
* x
, pdg::array
* y
) {
889 return strcmp(x
->name
->s
.c_str(), y
->name
->s
.c_str()) < 0;
893 /* Convert a pdg to a dependence_graph containing all the information
894 * needed for equivalence checking.
896 * We first compute the total dimension of the output computation
897 * and create input computations for each input array.
898 * Then we create computations for each node in the pdg and
899 * a special output computation.
900 * For each node, we subsequently compute all the edges emanating
901 * from the corresponding computation.
902 * Finally, for each node writing to an output array, we compute
903 * that part of the domain that computes part of the final
904 * contents of the output array and add corresponding edges
905 * to the output computation.
907 dependence_graph
*pdg_to_dg(pdg::PDG
*pdg
, unsigned out_dim
,
908 __isl_take isl_set
*context
)
911 isl_ctx
*ctx
= pdg
->get_isl_ctx();
912 unsigned nparam
= pdg
->params
.size();
913 dependence_graph
*dg
= new dependence_graph
;
914 std::map
<pdg::access
*, computation
*> s2c
;
915 std::map
<pdg::array
*, computation
*> a2c
;
916 std::map
<pdg::access
*, std::vector
<computation
**> > missing
;
918 std::vector
<pdg::array
*> output_arrays
;
919 std::map
<pdg::array
*, int> array2pos
;
920 std::map
<pdg::access
*, isl_set
*> exposed
;
923 context
= isl_set_intersect(context
, pdg
->get_context_isl_set());
925 context
= pdg
->get_context_isl_set();
927 /* add a special "input computation"; see add_iterator_edges */
929 dims
= isl_space_set_alloc(ctx
, nparam
, 1);
930 isl_dim_set_parameter_names(dims
, pdg
->params
);
931 comp
= new input_array("", dims
);
933 dg
->vertices
.push_back(comp
);
935 for (int i
= 0; i
< pdg
->arrays
.size(); ++i
) {
936 pdg::array
*array
= pdg
->arrays
[i
];
937 if (array
->type
== pdg::array::output
) {
938 output_arrays
.push_back(array
);
939 ensure_output_deps(pdg
, array
);
942 if (array
->type
!= pdg::array::input
)
946 dims
= isl_space_set_alloc(ctx
, nparam
, array
->dims
.size());
947 isl_dim_set_parameter_names(dims
, pdg
->params
);
948 comp
= new input_array(array
->name
->s
.c_str(), dims
);
950 dg
->vertices
.push_back(comp
);
951 dg
->input_computations
.push_back(comp
);
954 fprintf(stderr
, "no output arrays\n");
957 std::sort(output_arrays
.begin(), output_arrays
.end(), less_name());
958 for (int i
= 0; i
< output_arrays
.size(); ++i
) {
959 dg
->output_arrays
.push_back(strdup(output_arrays
[i
]->name
->s
.c_str()));
960 dg
->output_array_dims
.push_back(output_arrays
[i
]->dims
.size());
961 array2pos
[output_arrays
[i
]] = i
;
963 dg
->out
= new computation();
964 dims
= isl_space_set_alloc(ctx
, nparam
, out_dim
);
965 isl_dim_set_parameter_names(dims
, pdg
->params
);
966 dg
->out
->domain
= isl_set_empty(dims
);
967 dg
->out
->operation
= strdup("#Out");
969 dg
->out
->location
= -1;
970 for (int i
= 0; i
< pdg
->nodes
.size(); ++i
) {
971 pdg::node
*node
= pdg
->nodes
[i
];
972 pdg::statement
*s
= node
->statement
;
974 add_top_level_computation(dg
, pdg
, s2c
, a2c
, missing
, node
,
977 for (int j
= 0; j
< s
->accesses
.size(); ++j
) {
978 if (s
->accesses
[j
]->type
!= pdg::access::write
)
980 pdg::access
*a
= s
->accesses
[j
];
982 if (s
->accesses
[j
]->array
->type
!= pdg::array::output
)
985 exposed
[a
] = node
->source
->get_isl_set(ctx
);
987 isl_map
*ext
= a
->extension
->get_isl_map(ctx
);
988 exposed
[a
] = isl_set_apply(exposed
[a
], ext
);
990 exposed
[a
] = isl_set_intersect_params(exposed
[a
],
991 isl_set_copy(context
));
994 for (int i
= 0; i
< pdg
->dependences
.size(); ++i
) {
995 pdg::dependence
*dep
= pdg
->dependences
[i
];
996 if (dep
->type
!= pdg::dependence::output
)
998 pdg::access
*a
= dep
->from_access
;
999 if (!exposed
[a
] || isl_set_is_empty(exposed
[a
]))
1002 if (!dep
->extended_relation
)
1003 rel
= dep
->relation
->get_isl_map(ctx
);
1005 rel
= dep
->extended_relation
->get_isl_map(ctx
);
1006 exposed
[a
] = isl_set_subtract(exposed
[a
], isl_map_domain(rel
));
1008 for (int i
= 0; i
< pdg
->nodes
.size(); ++i
) {
1009 pdg::node
*node
= pdg
->nodes
[i
];
1010 pdg::statement
*s
= node
->statement
;
1011 for (int j
= 0; j
< s
->accesses
.size(); ++j
) {
1012 pdg::access
*a
= s
->accesses
[j
];
1013 if (!exposed
[a
] || isl_set_is_empty(exposed
[a
])) {
1014 isl_set_free(exposed
[a
]);
1017 add_output_edge(ctx
, dg
->out
, node
, array2pos
,
1018 s2c
[a
], exposed
[a
], nparam
, out_dim
);
1021 dg
->context
= context
;