remove cloog_util
[ppn.git] / dependence_graph.cc
blobb6e9a984d5aa7a6b832c281b5ab89222060b52c7
1 #include <isl/constraint.h>
2 #include <isa/pdg.h>
3 #include "dependence_graph.h"
5 void edge::dump(void)
7 isl_ctx *ctx;
8 isl_printer *prn;
9 const char *s;
10 switch (type) {
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)
26 isl_ctx *ctx;
27 isl_printer *prn;
28 const char *s;
29 if (input)
30 s = "input ";
31 else if (is_expanded)
32 s = "is_expanded ";
33 else
34 s = "";
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);
45 edges[i]->dump();
49 void dependence_graph::dump(void)
51 for (int i = 0; i < vertices.size(); ++i)
52 vertices[i]->dump();
53 out->dump();
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;
60 assert(source);
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);
66 edge *e = new edge;
67 e->source = source;
68 e->pos = pos;
69 e->relation = map;
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)
80 edge *e = new edge;
81 e->source = source;
82 if (!source) {
83 assert(missing);
84 missing->push_back(&e->source);
86 e->pos = pos;
87 e->relation = relation;
88 e->type = type;
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
114 * in a special way.
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();
123 if (a->extension) {
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),
128 isl_map_copy(ext));
129 child->operation = strdup("");
130 child->arity = 1;
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;
136 comp = child;
137 pos = 0;
139 for (int i = 0; i < pdg->dependences.size(); ++i) {
140 pdg::dependence *dep = pdg->dependences[i];
141 if (dep->to_access != a)
142 continue;
144 if (dep->type == pdg::dependence::uninitialized) {
145 add_input_edges(ctx, pos, dep, comp, a2c[dep->array]);
146 continue;
148 assert(dep->type == pdg::dependence::flow);
150 isl_map *relation;
151 if (!dep->extended_relation)
152 relation = dep->relation->get_isl_map(ctx);
153 else
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,
173 int pos,
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)
189 if (a->array)
190 add_dep_edges(dg, pdg, s2c, a2c, missing, pos, a, comp);
191 else
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)
207 computation *copy;
208 copy = new computation();
209 copy->domain = domain;
210 copy->operation = strdup("");
211 copy->arity = 1;
212 copy->location = location;
213 dg->vertices.push_back(copy);
214 return 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;
250 isl_space *dim;
251 isl_set *bounds;
252 isl_map *ext;
253 int pos = 0;
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))
265 ++pos;
267 assert(coa->type == pdg::call_or_access::t_access);
268 pdg::access *nested = coa->access;
269 assert(nested->array->value_bounds);
270 isl_set *bounds_i;
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;
284 return nest;
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)
299 computation *copy;
300 copy = add_copy_computation(dg, isl_set_copy(comp->domain),
301 comp->location);
303 computation *nest;
304 nest = add_nest_computation(dg, pdg, s2c, a2c, missing, a, copy);
306 edge *e = new edge;
307 e->pos = pos;
308 e->source = nest;
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;
346 isl_space *dim;
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));
360 return map;
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)
382 isl_map *map = NULL;
383 isl_int v;
384 isl_int_init(v);
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);
390 isl_map_free(rel);
391 isl_int_clear(v);
392 return map;
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)
403 isl_map *map = NULL;
404 isl_map *map2;
405 isl_int v;
407 isl_int_init(v);
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));
415 isl_map_free(map2);
416 isl_int_clear(v);
417 return map;
420 /* Construct a mapping for the maximum of two mappings.
422 * In particular, given two mappings in the arguments of "comp"
424 * [input] -> [a]
425 * [input] -> [b]
427 * construct
429 * [input] -> [a] : a >= b; [input] -> [b] : a < b
431 * We first construct
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)
440 isl_space *dim;
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);
469 return map;
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())
479 return NULL;
480 for (int i = 0; i < comp->edges.size(); ++i) {
481 computation *source = comp->edges[i]->source;
482 if (source == Z)
483 continue;
484 if (source && source->is_copy() && source->edges.size() == 1 &&
485 source->edges[0]->source == Z)
486 continue;
487 return NULL;
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)
493 return isl_map_sum(
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) {
507 isl_map *map = NULL;
508 isl_int v;
509 isl_int_init(v);
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);
516 isl_map_free(rel0);
517 isl_map_free(rel1);
518 isl_int_clear(v);
519 return map;
521 return NULL;
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)
530 char *endp;
531 long int cst;
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);
535 return;
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;
544 edge *e = new edge;
545 e->pos = pos;
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]);
551 if (expr) {
552 delete child;
553 e->source = a2c[NULL];
554 e->relation = expr;
555 } else {
556 dg->vertices.push_back(child);
557 e->source = 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)
604 if (a->extension) {
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),
612 isl_map_copy(ext));
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);
622 comp = at;
624 s2c[a] = comp;
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);
650 comp->arity--;
651 return 0;
653 int nested_dim = a->map->input() -
654 isl_set_dim(comp->domain, isl_dim_set);
655 if (nested_dim > 0)
656 add_nested_access_edge(dg, pdg, s2c, a2c, missing,
657 pos, coa->access, comp);
658 else
659 add_access_edges(dg, pdg, s2c, a2c, missing,
660 pos, coa->access, comp);
661 } else {
662 add_computation_edge(dg, pdg, s2c, a2c, missing,
663 coa->call, comp, pos);
665 return 1;
668 /* For each argument of function call call, we add one or more edges to
669 * computation comp.
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)
677 int pos = 0;
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))
683 ++pos;
686 return;
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,
706 computation *last,
707 isl_set *exposed, unsigned nparam, unsigned out_dim)
709 pdg::statement *s = node->statement;
710 int j;
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)
714 break;
715 pdg::array *array = s->accesses[j]->array;
716 int pos = array2pos[array];
717 isl_map *rel;
718 if (!s->accesses[j]->extended_map)
719 rel = isl_map_reverse(s->accesses[j]->map->get_isl_map(ctx));
720 else
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,
737 pdg::node *node)
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];
743 s2c[a] = comp;
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
756 * conditions to 0.
757 * The other two arguments are the operations or accesses performed
758 * when the conditions hold (second argument) or do not hold (third
759 * argument).
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,
773 pdg::node *node)
775 isl_ctx *ctx = pdg->get_isl_ctx();
776 pdg::statement *s = node->statement;
778 computation *copy;
779 copy = add_copy_computation(dg, node->source->get_isl_set(ctx),
780 s->line);
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;
787 computation *nest;
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),
805 copy->location);
806 computation *copy_false;
807 copy_false = add_copy_computation(dg, isl_set_copy(copy->domain),
808 copy->location);
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);
832 return;
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;
841 char *endp;
842 long int cst;
843 cst = strtol(s->top_function->name->s.c_str(), &endp, 0);
844 if (*s->top_function->name->s.c_str() && !*endp) {
845 comp->arity = 1;
846 comp->operation = strdup("");
847 add_constant_edge(pdg, a2c, 0, cst, comp);
848 } else {
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]);
853 if (expr) {
854 delete comp;
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;
860 comp->arity = 1;
861 comp->operation = strdup("");
863 edge *e = new edge;
864 e->pos = 0;
865 e->source = a2c[NULL];
866 e->relation = expr;
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)
881 return;
882 fprintf(stderr, "no output dependence analysis performed on array %s\n",
883 array->name->s.c_str());
884 exit(-1);
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)
910 isl_space *dims;
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;
922 if (context)
923 context = isl_set_intersect(context, pdg->get_context_isl_set());
924 else
925 context = pdg->get_context_isl_set();
927 /* add a special "input computation"; see add_iterator_edges */
928 computation *comp;
929 dims = isl_space_set_alloc(ctx, nparam, 1);
930 isl_dim_set_parameter_names(dims, pdg->params);
931 comp = new input_array("", dims);
932 a2c[NULL] = comp;
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)
943 continue;
945 computation *comp;
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);
949 a2c[array] = comp;
950 dg->vertices.push_back(comp);
951 dg->input_computations.push_back(comp);
953 if (!out_dim) {
954 fprintf(stderr, "no output arrays\n");
955 exit(-1);
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");
968 dg->out->arity = 1;
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,
975 context);
977 for (int j = 0; j < s->accesses.size(); ++j) {
978 if (s->accesses[j]->type != pdg::access::write)
979 continue;
980 pdg::access *a = s->accesses[j];
982 if (s->accesses[j]->array->type != pdg::array::output)
983 continue;
985 exposed[a] = node->source->get_isl_set(ctx);
986 if (a->extension) {
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)
997 continue;
998 pdg::access *a = dep->from_access;
999 if (!exposed[a] || isl_set_is_empty(exposed[a]))
1000 continue;
1001 isl_map *rel;
1002 if (!dep->extended_relation)
1003 rel = dep->relation->get_isl_map(ctx);
1004 else
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]);
1015 continue;
1017 add_output_edge(ctx, dg->out, node, array2pos,
1018 s2c[a], exposed[a], nparam, out_dim);
1021 dg->context = context;
1022 return dg;