update isl to version 0.14
[ppn.git] / dependence_graph.cc
blob9d7eb5efa00fe94631ac641411fe69620c28adc1
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 * In the special case where the affine expression is a mere integer constant
172 * that appears as an argument to a #test operation, the domain of "comp"
173 * may reference an argument that may not be present in "a" if it was
174 * created by c2pdg from an integer expression.
175 * In this case, we not only need to intersect the domain of
176 * the access relation, but also introduce a reference to the argument.
178 static void add_iterator_edges(pdg::PDG *pdg,
179 std::map<pdg::array *, computation *> &a2c,
180 int pos,
181 pdg::access *a, computation *comp)
183 isl_ctx *ctx = pdg->get_isl_ctx();
184 computation *Z = a2c[NULL];
185 isl_map *map = a->map->get_isl_map(ctx);
186 isl_set *domain = isl_set_copy(comp->domain);
187 if (isl_set_is_wrapping(domain) && !isl_map_domain_is_wrapping(map)) {
188 isl_map *domain_map;
189 domain_map = isl_map_domain_map(isl_set_unwrap(domain));
190 map = isl_map_apply_range(domain_map, map);
191 } else
192 map = isl_map_intersect_domain(map, domain);
193 add_split_edge(comp, Z, pos, map, NULL);
196 static void add_access_edges(dependence_graph *dg, pdg::PDG *pdg,
197 std::map<pdg::access *, computation *> &s2c,
198 std::map<pdg::array *, computation *> &a2c,
199 std::map<pdg::access *, std::vector<computation **> > &missing,
200 int pos, pdg::access *a, computation *comp)
202 if (a->array)
203 add_dep_edges(dg, pdg, s2c, a2c, missing, pos, a, comp);
204 else
205 add_iterator_edges(pdg, a2c, pos, a, comp);
208 static int add_edges_at_pos(dependence_graph *dg, pdg::PDG *pdg,
209 std::map<pdg::access *, computation *> &s2c,
210 std::map<pdg::array *, computation *> &a2c,
211 std::map<pdg::access *, std::vector<computation **> > &missing,
212 int pos, pdg::call_or_access *coa, computation *comp);
214 /* Add a new copy computation to dependence graph dg with given
215 * domain and location and return a pointer to the copy computation.
217 static computation *add_copy_computation(dependence_graph *dg,
218 isl_set *domain, int location)
220 computation *copy;
221 copy = new computation();
222 copy->domain = domain;
223 copy->operation = strdup("");
224 copy->arity = 1;
225 copy->location = location;
226 dg->vertices.push_back(copy);
227 return copy;
230 /* Create a "#NEST computation, with n + 1 arguments, where n is the
231 * number of nested accesses.
232 * The first n arguments correspond to the nested accesses and
233 * edges for these arguments are added as usual.
234 * Note that if the access "a" appears in a "#test" function,
235 * then some of the nested accesses will already have been
236 * added by an outer #NEST computation and should therefore be skipped.
237 * The final argument corresponds to the expansion and for this position
238 * a single expansion edge is added to the given copy computation.
239 * The mapping on the expansion edge is the identity
240 * mapping on the domain, with the range extended by one dimension for
241 * each of nested accesses. The range of values in each of these dimensions
242 * is given by the value_bounds on the corresponding array.
243 * The iteration domain of the given copy computation is adjusted accordingly.
245 static computation *add_nest_computation(dependence_graph *dg, pdg::PDG *pdg,
246 std::map<pdg::access *, computation *> &s2c,
247 std::map<pdg::array *, computation *> &a2c,
248 std::map<pdg::access *, std::vector<computation **> > &missing,
249 pdg::access *a, computation *copy)
251 isl_ctx *ctx = pdg->get_isl_ctx();
252 computation *nest = new computation();
253 nest->domain = isl_set_copy(copy->domain);
254 nest->operation = strdup("#NEST");
255 nest->arity = a->nested.size() + 1;
256 nest->location = copy->location;
257 dg->vertices.push_back(nest);
259 int nested_dim = a->map->input() - isl_set_dim(copy->domain, isl_dim_set);
260 assert(nested_dim > 0 && nested_dim <= a->nested.size());
261 int skip_dim = a->nested.size() - nested_dim;
263 isl_space *dim;
264 isl_set *bounds;
265 isl_map *ext;
266 int pos = 0;
268 dim = isl_set_get_space(copy->domain);
269 dim = isl_space_drop_dims(dim, isl_dim_set,
270 0, isl_space_dim(dim, isl_dim_set));
271 bounds = isl_set_universe(dim);
273 for (int i = skip_dim; i < a->nested.size(); ++i) {
274 pdg::call_or_access *coa = a->nested[i];
276 if (add_edges_at_pos(dg, pdg, s2c, a2c,
277 missing, pos, coa, nest))
278 ++pos;
280 assert(coa->type == pdg::call_or_access::t_access);
281 pdg::access *nested = coa->access;
282 assert(nested->array->value_bounds);
283 isl_set *bounds_i;
284 bounds_i = nested->array->value_bounds->get_isl_set(ctx);
286 bounds = isl_set_flat_product(bounds, bounds_i);
289 dim = isl_set_get_space(copy->domain);
290 copy->domain = isl_set_product(copy->domain, isl_set_copy(bounds));
291 bounds = isl_set_product(isl_set_universe(dim), bounds);
292 ext = isl_map_reverse(isl_map_domain_map(isl_set_unwrap(bounds)));
294 add_split_edge(nest, copy, pos, ext, NULL, edge::expansion);
295 nest->is_expanded = 1;
297 return nest;
300 /* Add an edge to "comp" at position "pos" according to access "a",
301 * where "a" is has nested accesses.
302 * We first create a copy computation where the actual access
303 * will be attached. Then we create a "#NEST" computation that
304 * sits between "comp" and the copy computation.
306 static void add_nested_access_edge(dependence_graph *dg, pdg::PDG *pdg,
307 std::map<pdg::access *, computation *> &s2c,
308 std::map<pdg::array *, computation *> &a2c,
309 std::map<pdg::access *, std::vector<computation **> > &missing,
310 int pos, pdg::access *a, computation *comp)
312 computation *copy;
313 copy = add_copy_computation(dg, isl_set_copy(comp->domain),
314 comp->location);
316 computation *nest;
317 nest = add_nest_computation(dg, pdg, s2c, a2c, missing, a, copy);
319 edge *e = new edge;
320 e->pos = pos;
321 e->source = nest;
322 isl_space *dim = isl_space_map_from_set(isl_set_get_space(comp->domain));
323 e->relation = isl_map_identity(dim);
324 comp->edges.push_back(e);
326 add_access_edges(dg, pdg, s2c, a2c, missing, 0, a, copy);
329 /* "Invocations" of integer constants are replaced by accesses
330 * to the "input computation", so we can treat them like and combine them
331 * with iterator accesses.
333 static void add_constant_edge(pdg::PDG *pdg,
334 std::map<pdg::array *, computation *> &a2c,
335 int pos, long cst, computation *comp)
337 isl_ctx *ctx = pdg->get_isl_ctx();
338 computation *Z = a2c[NULL];
340 isl_map *map = isl_map_from_range(isl_set_copy(comp->domain));
341 map = isl_map_reverse(map);
342 map = isl_map_extend(map, isl_map_n_param(map),
343 isl_map_n_in(map), 1);
344 map = isl_map_fix_si(map, isl_dim_out, 0, cst);
345 add_split_edge(comp, Z, pos, map, NULL);
348 static void add_edges(dependence_graph *dg, pdg::PDG *pdg,
349 std::map<pdg::access *, computation *> &s2c,
350 std::map<pdg::array *, computation *> &a2c,
351 std::map<pdg::access *, std::vector<computation **> > &missing,
352 pdg::function_call *call, computation *comp);
354 /* Scale the single output dimension of bmap by a factor of f */
355 static __isl_give isl_map *scale(__isl_take isl_map *map, __isl_take isl_val *f)
357 struct isl_constraint *c;
358 isl_basic_map *scaling;
359 isl_space *dim;
361 dim = isl_map_get_space(map);
362 dim = isl_space_range(dim);
363 dim = isl_space_map_from_set(dim);
364 scaling = isl_basic_map_universe(dim);
365 c = isl_equality_alloc(isl_basic_map_get_local_space(scaling));
366 c = isl_constraint_set_coefficient_val(c, isl_dim_in, 0, f);
367 c = isl_constraint_set_coefficient_si(c, isl_dim_out, 0, -1);
368 scaling = isl_basic_map_add_constraint(scaling, c);
370 map = isl_map_apply_range(map, isl_map_from_basic_map(scaling));
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;