da.cc: convert_access: avoid use of isl_map_n_out
[ppn.git] / dependence_graph.cc
blobd3f44cccf5c51dab31e9c1a6f0bb1da08050e9e0
1 #include <isl/val.h>
2 #include <isl/space.h>
3 #include <isl/aff.h>
4 #include <isl/set.h>
5 #include <isl/map.h>
6 #include <isl/printer.h>
7 #include <isa/pdg.h>
8 #include "dependence_graph.h"
10 void edge::dump(void)
12 isl_ctx *ctx;
13 isl_printer *prn;
14 const char *s;
15 switch (type) {
16 case normal: s = ""; break;
17 case expansion: s = "expansion "; break;
19 fprintf(stderr, "\tpos: %d, source: %d,%s %s[%p]\n",
20 pos, source->location, source->operation, s, source);
21 ctx = isl_map_get_ctx(relation);
22 prn = isl_printer_to_file(ctx, stderr);
23 prn = isl_printer_set_indent(prn, 8);
24 prn = isl_printer_print_map(prn, relation);
25 prn = isl_printer_end_line(prn);
26 isl_printer_free(prn);
29 void computation::dump(void)
31 isl_ctx *ctx;
32 isl_printer *prn;
33 const char *s;
34 if (input)
35 s = "input ";
36 else if (is_expanded)
37 s = "is_expanded ";
38 else
39 s = "";
40 fprintf(stderr, "op: %d,%s/%d %s[%p]\n",
41 location, operation, arity, s, this);
42 ctx = isl_set_get_ctx(domain);
43 prn = isl_printer_to_file(ctx, stderr);
44 prn = isl_printer_print_set(prn, domain);
45 prn = isl_printer_end_line(prn);
46 isl_printer_free(prn);
48 for (int i = 0; i < edges.size(); ++i) {
49 fprintf(stderr, "edge %d:\n", i);
50 edges[i]->dump();
54 void dependence_graph::dump(void)
56 for (int i = 0; i < vertices.size(); ++i)
57 vertices[i]->dump();
58 out->dump();
61 static void add_input_edges(isl_ctx *ctx, int pos, pdg::dependence *dep,
62 computation *comp, computation *source)
64 pdg::access *a = dep->to_access;
65 assert(source);
66 isl_map *map = a->map->get_isl_map(ctx);
67 isl_map *relation = dep->relation->get_isl_map(ctx);
68 isl_set *domain = isl_map_range(relation);
69 map = isl_map_intersect_domain(map, domain);
71 edge *e = new edge;
72 e->source = source;
73 e->pos = pos;
74 e->relation = map;
75 comp->edges.push_back(e);
78 /* For each basic map in relation, add an edge to comp with
79 * given source and pos.
81 static void add_split_edge(computation *comp, computation *source,
82 int pos, isl_map *relation, std::vector<computation **> *missing,
83 enum edge::type type = edge::normal)
85 edge *e = new edge;
86 e->source = source;
87 if (!source) {
88 assert(missing);
89 missing->push_back(&e->source);
91 e->pos = pos;
92 e->relation = relation;
93 e->type = type;
94 comp->edges.push_back(e);
97 /* For each (flow) dependence with write access a, and an
98 * edge to computation comp.
99 * Note that in the pdg, the dependences have as "input" the source (read)
100 * and as "output" the sink (write), while in the dependence_graph,
101 * we want edges that point from sink to source.
103 * If the (read) access has an extension, i.e., if the access
104 * reads a row or an even larger chunk of an array, then we introduces
105 * an extra "expansion" edge to an extra node that corresponds to the
106 * reading of the whole array. The iteration domain of this extra node
107 * has extra dimensions, as many as were missing from the access.
108 * The extra edge maps each iteration of the statement in which
109 * the read appears to all iterations in the extended iteration domain
110 * with the same values for the first iterators.
111 * The remaining iterators range over the elements of the array chunk.
112 * All dependences in which the given access is involved are attached
113 * to the new node, where we need to be careful to use the extended_relation
114 * instead of the projected "standard" relations.
115 * The operation associated with the new node is the
116 * empty string since the new node can be treated as a copy operation.
117 * The edge connecting the original computation and the new node
118 * is marked as an "expansion" edge because it needs to be treated
119 * in a special way.
121 static void add_dep_edges(dependence_graph *dg, pdg::PDG *pdg,
122 std::map<pdg::access *, computation *> &s2c,
123 std::map<pdg::array *, computation *> &a2c,
124 std::map<pdg::access *, std::vector<computation **> > &missing,
125 int pos, pdg::access *a, computation *comp)
127 isl_ctx *ctx = pdg->get_isl_ctx();
128 if (a->extension) {
129 isl_map *ext = a->extension->get_isl_map(ctx);
131 computation *child = new computation();
132 child->domain = isl_set_apply(isl_set_copy(comp->domain),
133 isl_map_copy(ext));
134 child->operation = strdup("");
135 child->arity = 1;
136 child->location = comp->location;
137 dg->vertices.push_back(child);
139 add_split_edge(comp, child, pos, ext, NULL, edge::expansion);
140 comp->is_expanded = 1;
141 comp = child;
142 pos = 0;
144 for (int i = 0; i < pdg->dependences.size(); ++i) {
145 pdg::dependence *dep = pdg->dependences[i];
146 if (dep->to_access != a)
147 continue;
149 if (dep->type == pdg::dependence::uninitialized) {
150 add_input_edges(ctx, pos, dep, comp, a2c[dep->array]);
151 continue;
153 assert(dep->type == pdg::dependence::flow);
155 isl_map *relation;
156 if (!dep->extended_relation)
157 relation = dep->relation->get_isl_map(ctx);
158 else
159 relation = dep->extended_relation->get_isl_map(ctx);
160 relation = isl_map_reverse(relation);
161 add_split_edge(comp, s2c[dep->from_access], pos, relation,
162 &missing[dep->from_access]);
166 /* Each access without an associated array corresponds to a read
167 * of an (affine combination of) iterator value(s).
168 * The expression in terms of the iterators is given by the access map.
169 * For each of these accesses, we add an edge to a special
170 * "input computation" that is not associated to any particular
171 * (input) array. The map of the edge is the access map,
172 * with the domain intersected with the domain of the current computation.
173 * This ensures that corresponding reads are considered equal iff the
174 * value they read is the same for both programs.
176 * In the special case where the affine expression is a mere integer constant
177 * that appears as an argument to a #test operation, the domain of "comp"
178 * may reference an argument that may not be present in "a" if it was
179 * created by c2pdg from an integer expression.
180 * In this case, we not only need to intersect the domain of
181 * the access relation, but also introduce a reference to the argument.
183 static void add_iterator_edges(pdg::PDG *pdg,
184 std::map<pdg::array *, computation *> &a2c,
185 int pos,
186 pdg::access *a, computation *comp)
188 isl_ctx *ctx = pdg->get_isl_ctx();
189 computation *Z = a2c[NULL];
190 isl_map *map = a->map->get_isl_map(ctx);
191 isl_set *domain = isl_set_copy(comp->domain);
192 if (isl_set_is_wrapping(domain) && !isl_map_domain_is_wrapping(map)) {
193 isl_map *domain_map;
194 domain_map = isl_map_domain_map(isl_set_unwrap(domain));
195 map = isl_map_apply_range(domain_map, map);
196 } else
197 map = isl_map_intersect_domain(map, domain);
198 add_split_edge(comp, Z, pos, map, NULL);
201 static void add_access_edges(dependence_graph *dg, pdg::PDG *pdg,
202 std::map<pdg::access *, computation *> &s2c,
203 std::map<pdg::array *, computation *> &a2c,
204 std::map<pdg::access *, std::vector<computation **> > &missing,
205 int pos, pdg::access *a, computation *comp)
207 if (a->array)
208 add_dep_edges(dg, pdg, s2c, a2c, missing, pos, a, comp);
209 else
210 add_iterator_edges(pdg, a2c, pos, a, comp);
213 static int add_edges_at_pos(dependence_graph *dg, pdg::PDG *pdg,
214 std::map<pdg::access *, computation *> &s2c,
215 std::map<pdg::array *, computation *> &a2c,
216 std::map<pdg::access *, std::vector<computation **> > &missing,
217 int pos, pdg::call_or_access *coa, computation *comp);
219 /* Add a new copy computation to dependence graph dg with given
220 * domain and location and return a pointer to the copy computation.
222 static computation *add_copy_computation(dependence_graph *dg,
223 isl_set *domain, int location)
225 computation *copy;
226 copy = new computation();
227 copy->domain = domain;
228 copy->operation = strdup("");
229 copy->arity = 1;
230 copy->location = location;
231 dg->vertices.push_back(copy);
232 return copy;
235 /* Create a "#NEST computation, with n + 1 arguments, where n is the
236 * number of nested accesses.
237 * The first n arguments correspond to the nested accesses and
238 * edges for these arguments are added as usual.
239 * Note that if the access "a" appears in a "#test" function,
240 * then some of the nested accesses will already have been
241 * added by an outer #NEST computation and should therefore be skipped.
242 * The final argument corresponds to the expansion and for this position
243 * a single expansion edge is added to the given copy computation.
244 * The mapping on the expansion edge is the identity
245 * mapping on the domain, with the range extended by one dimension for
246 * each of nested accesses. The range of values in each of these dimensions
247 * is given by the value_bounds on the corresponding array.
248 * The iteration domain of the given copy computation is adjusted accordingly.
250 static computation *add_nest_computation(dependence_graph *dg, pdg::PDG *pdg,
251 std::map<pdg::access *, computation *> &s2c,
252 std::map<pdg::array *, computation *> &a2c,
253 std::map<pdg::access *, std::vector<computation **> > &missing,
254 pdg::access *a, computation *copy)
256 isl_ctx *ctx = pdg->get_isl_ctx();
257 computation *nest = new computation();
258 nest->domain = isl_set_copy(copy->domain);
259 nest->operation = strdup("#NEST");
260 nest->arity = a->nested.size() + 1;
261 nest->location = copy->location;
262 dg->vertices.push_back(nest);
264 int nested_dim = a->map->input() - isl_set_dim(copy->domain, isl_dim_set);
265 assert(nested_dim > 0 && nested_dim <= a->nested.size());
266 int skip_dim = a->nested.size() - nested_dim;
268 isl_space *dim;
269 isl_set *bounds;
270 isl_map *ext;
271 int pos = 0;
273 dim = isl_set_get_space(copy->domain);
274 dim = isl_space_drop_dims(dim, isl_dim_set,
275 0, isl_space_dim(dim, isl_dim_set));
276 bounds = isl_set_universe(dim);
278 for (int i = skip_dim; i < a->nested.size(); ++i) {
279 pdg::call_or_access *coa = a->nested[i];
281 if (add_edges_at_pos(dg, pdg, s2c, a2c,
282 missing, pos, coa, nest))
283 ++pos;
285 assert(coa->type == pdg::call_or_access::t_access);
286 pdg::access *nested = coa->access;
287 assert(nested->array->value_bounds);
288 isl_set *bounds_i;
289 bounds_i = nested->array->value_bounds->get_isl_set(ctx);
291 bounds = isl_set_flat_product(bounds, bounds_i);
294 dim = isl_set_get_space(copy->domain);
295 copy->domain = isl_set_product(copy->domain, isl_set_copy(bounds));
296 bounds = isl_set_product(isl_set_universe(dim), bounds);
297 ext = isl_map_reverse(isl_map_domain_map(isl_set_unwrap(bounds)));
299 add_split_edge(nest, copy, pos, ext, NULL, edge::expansion);
300 nest->is_expanded = 1;
302 return nest;
305 /* Add an edge to "comp" at position "pos" according to access "a",
306 * where "a" is has nested accesses.
307 * We first create a copy computation where the actual access
308 * will be attached. Then we create a "#NEST" computation that
309 * sits between "comp" and the copy computation.
311 static void add_nested_access_edge(dependence_graph *dg, pdg::PDG *pdg,
312 std::map<pdg::access *, computation *> &s2c,
313 std::map<pdg::array *, computation *> &a2c,
314 std::map<pdg::access *, std::vector<computation **> > &missing,
315 int pos, pdg::access *a, computation *comp)
317 computation *copy;
318 copy = add_copy_computation(dg, isl_set_copy(comp->domain),
319 comp->location);
321 computation *nest;
322 nest = add_nest_computation(dg, pdg, s2c, a2c, missing, a, copy);
324 edge *e = new edge;
325 e->pos = pos;
326 e->source = nest;
327 isl_space *dim = isl_space_map_from_set(isl_set_get_space(comp->domain));
328 e->relation = isl_map_identity(dim);
329 comp->edges.push_back(e);
331 add_access_edges(dg, pdg, s2c, a2c, missing, 0, a, copy);
334 /* "Invocations" of integer constants are replaced by accesses
335 * to the "input computation", so we can treat them like and combine them
336 * with iterator accesses.
338 static void add_constant_edge(pdg::PDG *pdg,
339 std::map<pdg::array *, computation *> &a2c,
340 int pos, long cst, computation *comp)
342 isl_ctx *ctx = pdg->get_isl_ctx();
343 computation *Z = a2c[NULL];
345 isl_map *map = isl_map_from_range(isl_set_copy(comp->domain));
346 map = isl_map_reverse(map);
347 map = isl_map_add_dims(map, isl_dim_out, 1);
348 map = isl_map_fix_si(map, isl_dim_out, 0, cst);
349 add_split_edge(comp, Z, pos, map, NULL);
352 static void add_edges(dependence_graph *dg, pdg::PDG *pdg,
353 std::map<pdg::access *, computation *> &s2c,
354 std::map<pdg::array *, computation *> &a2c,
355 std::map<pdg::access *, std::vector<computation **> > &missing,
356 pdg::function_call *call, computation *comp);
358 /* Scale the single output dimension of bmap by a factor of f */
359 static __isl_give isl_map *scale(__isl_take isl_map *map, __isl_take isl_val *f)
361 isl_multi_aff *scaling;
362 isl_space *space;
364 space = isl_map_get_space(map);
365 space = isl_space_range(space);
366 space = isl_space_map_from_set(space);
367 scaling = isl_multi_aff_identity(space);
368 scaling = isl_multi_aff_scale_val(scaling, f);
370 map = isl_map_apply_range(map, isl_map_from_multi_aff(scaling));
372 return map;
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)
394 isl_map *map = NULL;
395 isl_val *v;
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);
402 } else
403 isl_val_free(v);
404 isl_map_free(rel);
405 return map;
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)
416 isl_map *map = NULL;
417 isl_map *map2;
418 isl_val *v;
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));
425 } else
426 isl_val_free(v);
428 isl_map_free(map2);
429 return map;
432 /* Construct a mapping for the maximum of two mappings.
434 * In particular, given two mappings in the arguments of "comp"
436 * [input] -> [a]
437 * [input] -> [b]
439 * construct
441 * [input] -> [a] : a >= b; [input] -> [b] : a < b
443 * We first construct
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)
452 isl_space *dim;
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);
481 return map;
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)
492 isl_val *v;
494 v = isl_map_plain_get_val_if_fixed(rel0, isl_dim_out, 0);
495 if (isl_val_is_int(v)) {
496 isl_map_free(rel0);
497 return scale(rel1, v);
499 isl_val_free(v);
501 v = isl_map_plain_get_val_if_fixed(rel1, isl_dim_out, 0);
502 if (isl_val_is_int(v)) {
503 isl_map_free(rel1);
504 return scale(rel0, v);
506 isl_val_free(v);
508 isl_map_free(rel0);
509 isl_map_free(rel1);
511 return NULL;
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())
521 return NULL;
522 for (int i = 0; i < comp->edges.size(); ++i) {
523 computation *source = comp->edges[i]->source;
524 if (source == Z)
525 continue;
526 if (source && source->is_copy() && source->edges.size() == 1 &&
527 source->edges[0]->source == Z)
528 continue;
529 return NULL;
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)
535 return isl_map_sum(
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);
553 return NULL;
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)
562 char *endp;
563 long int cst;
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);
567 return;
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;
576 edge *e = new edge;
577 e->pos = pos;
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]);
583 if (expr) {
584 delete child;
585 e->source = a2c[NULL];
586 e->relation = expr;
587 } else {
588 dg->vertices.push_back(child);
589 e->source = 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)
636 if (a->extension) {
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),
644 isl_map_copy(ext));
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);
654 comp = at;
656 s2c[a] = comp;
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);
682 comp->arity--;
683 return 0;
685 int nested_dim = a->map->input() -
686 isl_set_dim(comp->domain, isl_dim_set);
687 if (nested_dim > 0)
688 add_nested_access_edge(dg, pdg, s2c, a2c, missing,
689 pos, coa->access, comp);
690 else
691 add_access_edges(dg, pdg, s2c, a2c, missing,
692 pos, coa->access, comp);
693 } else {
694 add_computation_edge(dg, pdg, s2c, a2c, missing,
695 coa->call, comp, pos);
697 return 1;
700 /* For each argument of function call call, we add one or more edges to
701 * computation comp.
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)
709 int pos = 0;
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))
715 ++pos;
718 return;
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,
738 computation *last,
739 isl_set *exposed, unsigned nparam, unsigned out_dim)
741 pdg::statement *s = node->statement;
742 int j;
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)
746 break;
747 pdg::array *array = s->accesses[j]->array;
748 int pos = array2pos[array];
749 isl_map *rel;
750 if (!s->accesses[j]->extended_map)
751 rel = isl_map_reverse(s->accesses[j]->map->get_isl_map(ctx));
752 else
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,
769 pdg::node *node)
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];
775 s2c[a] = comp;
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
788 * conditions to 0.
789 * The other two arguments are the operations or accesses performed
790 * when the conditions hold (second argument) or do not hold (third
791 * argument).
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,
805 pdg::node *node)
807 isl_ctx *ctx = pdg->get_isl_ctx();
808 pdg::statement *s = node->statement;
810 computation *copy;
811 copy = add_copy_computation(dg, node->source->get_isl_set(ctx),
812 s->line);
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;
819 computation *nest;
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),
837 copy->location);
838 computation *copy_false;
839 copy_false = add_copy_computation(dg, isl_set_copy(copy->domain),
840 copy->location);
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);
864 return;
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;
873 char *endp;
874 long int cst;
875 cst = strtol(s->top_function->name->s.c_str(), &endp, 0);
876 if (*s->top_function->name->s.c_str() && !*endp) {
877 comp->arity = 1;
878 comp->operation = strdup("");
879 add_constant_edge(pdg, a2c, 0, cst, comp);
880 } else {
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]);
885 if (expr) {
886 delete comp;
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;
892 comp->arity = 1;
893 comp->operation = strdup("");
895 edge *e = new edge;
896 e->pos = 0;
897 e->source = a2c[NULL];
898 e->relation = expr;
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)
913 return;
914 fprintf(stderr, "no output dependence analysis performed on array %s\n",
915 array->name->s.c_str());
916 exit(-1);
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)
942 isl_space *dims;
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;
954 if (context)
955 context = isl_set_intersect(context, pdg->get_context_isl_set());
956 else
957 context = pdg->get_context_isl_set();
959 /* add a special "input computation"; see add_iterator_edges */
960 computation *comp;
961 dims = isl_space_set_alloc(ctx, nparam, 1);
962 isl_dim_set_parameter_names(dims, pdg->params);
963 comp = new input_array("", dims);
964 a2c[NULL] = comp;
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)
975 continue;
977 computation *comp;
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);
981 a2c[array] = comp;
982 dg->vertices.push_back(comp);
983 dg->input_computations.push_back(comp);
985 if (!out_dim) {
986 fprintf(stderr, "no output arrays\n");
987 exit(-1);
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");
1000 dg->out->arity = 1;
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,
1007 context);
1009 for (int j = 0; j < s->accesses.size(); ++j) {
1010 if (s->accesses[j]->type != pdg::access::write)
1011 continue;
1012 pdg::access *a = s->accesses[j];
1014 if (s->accesses[j]->array->type != pdg::array::output)
1015 continue;
1017 exposed[a] = node->source->get_isl_set(ctx);
1018 if (a->extension) {
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)
1029 continue;
1030 pdg::access *a = dep->from_access;
1031 if (!exposed[a] || isl_set_is_empty(exposed[a]))
1032 continue;
1033 isl_map *rel;
1034 if (!dep->extended_relation)
1035 rel = dep->relation->get_isl_map(ctx);
1036 else
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]);
1047 continue;
1049 add_output_edge(ctx, dg->out, node, array2pos,
1050 s2c[a], exposed[a], nparam, out_dim);
1053 dg->context = context;
1054 return dg;