get_submodules.sh: take into account imath submodule of isl
[ppn.git] / eqv3.cc
blob8560d1812253a02fe4d505c746518be7fafccefd
1 #include <isl/constraint.h>
2 #include <isl/union_map.h>
3 #include <isl/union_set.h>
4 #include <isa/pdg.h>
5 #include "dependence_graph.h"
6 #include "eqv3_options.h"
7 #include "msa.h"
9 /* Check the equivalence of two programs, given as dependence graphs,
10 * by computing what is essentially the cross product of the two
11 * dependence graph and checking that we can only reach states corresponding
12 * to equal inputs from the state corresponding to equal output.
13 * We consider various ways of performing the reachability analysis:
14 * applying the transitive closure to the initial state, FAST, Lever and Aspic.
15 * In all but the first case, we simply print a model that can be used
16 * as input to the corresponding tool.
19 static unsigned update_out_dim(pdg::PDG *pdg, unsigned out_dim)
21 for (int i = 0; i < pdg->arrays.size(); ++i) {
22 pdg::array *array = pdg->arrays[i];
23 if (array->type != pdg::array::output)
24 continue;
25 if (array->dims.size() + 1 > out_dim)
26 out_dim = array->dims.size() + 1;
29 return out_dim;
32 /* The input arrays of the two programs are supposed to be the same,
33 * so they should at least have the same dimension. Make sure
34 * this is true, because we depend on it later on.
36 static int check_input_arrays(dependence_graph *dg1, dependence_graph *dg2)
38 for (int i = 0; i < dg1->input_computations.size(); ++i)
39 for (int j = 0; j < dg2->input_computations.size(); ++j) {
40 if (strcmp(dg1->input_computations[i]->operation,
41 dg2->input_computations[j]->operation))
42 continue;
43 if (dg1->input_computations[i]->dim ==
44 dg2->input_computations[j]->dim)
45 continue;
46 fprintf(stderr,
47 "input arrays \"%s\" do not have the same dimension\n",
48 dg1->input_computations[i]->operation);
49 return -1;
52 return 0;
55 /* Compute a map between the domains of the output nodes of
56 * the dependence graphs that expresses that array1 in dg1
57 * and array2 in dg2 are equal.
58 * This map is then returned as a set.
60 static __isl_give isl_set *compute_equal_array(
61 dependence_graph *dg1, dependence_graph *dg2, int array1, int array2)
63 const char *array_name = dg1->output_arrays[array1];
64 isl_set *out1 = isl_set_copy(dg1->out->domain);
65 isl_set *out2 = isl_set_copy(dg2->out->domain);
66 unsigned dim1 = isl_set_n_dim(out1);
67 unsigned dim2 = isl_set_n_dim(out2);
69 out1 = isl_set_fix_dim_si(out1, dim1 - 1, array1);
70 out2 = isl_set_fix_dim_si(out2, dim2 - 1, array2);
71 out1 = isl_set_remove_dims(out1, isl_dim_set, dim1 - 1, 1);
72 out2 = isl_set_remove_dims(out2, isl_dim_set, dim2 - 1, 1);
73 int equal = isl_set_is_equal(out1, out2);
74 isl_set_free(out1);
75 isl_set_free(out2);
76 assert(equal >= 0);
77 if (!equal) {
78 fprintf(stderr, "different output domains for array %s\n",
79 array_name);
80 return NULL;
83 out1 = isl_set_copy(dg1->out->domain);
84 out2 = isl_set_copy(dg2->out->domain);
85 out1 = isl_set_fix_dim_si(out1, dim1 - 1, array1);
86 out2 = isl_set_fix_dim_si(out2, dim2 - 1, array2);
87 out1 = isl_set_coalesce(out1);
88 out2 = isl_set_coalesce(out2);
89 isl_space *dim = isl_space_map_from_set(isl_set_get_space(out2));
90 isl_map *id = isl_map_identity(dim);
91 id = isl_map_remove_dims(id, isl_dim_in, isl_map_n_in(id) - 1, 1);
92 id = isl_map_apply_domain(id, isl_map_copy(id));
93 id = isl_map_intersect_domain(id, out1);
94 id = isl_map_intersect_range(id, out2);
96 return isl_map_wrap(id);
99 /* Compute a map between the domains of the output nodes of
100 * the dependence graphs that expresses that arrays with
101 * the same names in both graphs are equal.
102 * The map is returned as a set.
104 static __isl_give isl_union_set *compute_equal_output(
105 dependence_graph *dg1, dependence_graph *dg2)
107 int i1 = 0, i2 = 0;
108 isl_space *dim;
109 isl_map *map;
110 isl_set *equal_output;
111 unsigned d;
113 dim = isl_set_get_space(dg1->out->domain);
114 d = isl_space_dim(dim, isl_dim_set);
115 map = isl_map_empty(isl_space_map_from_set(dim));
116 equal_output = isl_map_wrap(map);
118 while (i1 < dg1->output_arrays.size() || i2 < dg2->output_arrays.size()) {
119 int cmp;
120 cmp = i1 == dg1->output_arrays.size() ? 1 :
121 i2 == dg2->output_arrays.size() ? -1 :
122 strcmp(dg1->output_arrays[i1], dg2->output_arrays[i2]);
123 if (cmp < 0) {
124 fprintf(stderr,
125 "Array \"%s\" only in program 1; not checked\n",
126 dg1->output_arrays[i1]);
127 ++i1;
128 } else if (cmp > 0) {
129 fprintf(stderr,
130 "Array \"%s\" only in program 2; not checked\n",
131 dg2->output_arrays[i2]);
132 ++i2;
133 } else {
134 isl_set *eq_array;
135 eq_array = compute_equal_array(dg1, dg2, i1, i2);
136 equal_output = isl_set_union(equal_output, eq_array);
137 ++i1;
138 ++i2;
142 equal_output = isl_set_set_tuple_name(equal_output, "s0");
144 return isl_union_set_from_set(equal_output);
147 /* For a given state "s" of the MSA, return a set describing all
148 * instances of that state.
150 static __isl_give isl_union_set *state_domain(MSA &msa, int s)
152 char name[50];
153 isl_space *dim1, *dim2;
154 isl_set *set;
156 snprintf(name, sizeof(name), "s%d", s);
157 dim1 = isl_set_get_space(msa.state[s].first->domain);
158 dim2 = isl_set_get_space(msa.state[s].second->domain);
159 dim1 = isl_space_from_domain(dim1);
160 dim2 = isl_space_from_range(dim2);
161 dim1 = isl_space_join(dim1, dim2);
162 dim1 = isl_space_wrap(dim1);
163 dim1 = isl_space_set_tuple_name(dim1, isl_dim_set, name);
164 set = isl_set_universe(dim1);
166 return isl_union_set_from_set(set);
169 /* For a given state corresponding to a pair of equal input arrays,
170 * return a set describing pairs of equal instances of those arrays.
172 static __isl_give isl_union_set *equal_input_domain(MSA &msa, int s)
174 char name[50];
175 isl_space *dim;
176 isl_set *set;
178 snprintf(name, sizeof(name), "s%d", s);
179 dim = isl_set_get_space(msa.state[s].first->domain);
180 set = isl_map_wrap(isl_map_identity(isl_space_map_from_set(dim)));
181 set = isl_set_set_tuple_name(set, name);
183 return isl_union_set_from_set(set);
186 /* Return all instances of final states, i.e., pairs of equal input
187 * arrays, pairs of equal constant functions and all bad combinations.
189 static __isl_give isl_union_set *compute_final_states(isl_ctx *ctx, MSA &msa)
191 isl_space *dim;
192 isl_union_set *final;
194 dim = isl_space_set_alloc(ctx, 0, 0);
195 final = isl_union_set_empty(dim);
197 for (int i = 0; i < msa.input_state.size(); ++i) {
198 int s = msa.input_state[i];
199 final = isl_union_set_union(final, state_domain(msa, s));
202 for (int i = 0; i < msa.fail_state.size(); ++i) {
203 int s = msa.fail_state[i];
204 final = isl_union_set_union(final, state_domain(msa, s));
207 for (int i = 0; i < msa.cst_state.size(); ++i) {
208 int s = msa.cst_state[i];
209 final = isl_union_set_union(final, state_domain(msa, s));
212 return final;
215 /* Return all instances of good final states, i.e., equal instances
216 * of equal input arrays and pairs of equal constant functions.
218 static __isl_give isl_union_set *compute_good_states(isl_ctx *ctx, MSA &msa)
220 isl_space *dim;
221 isl_union_set *final;
223 dim = isl_space_set_alloc(ctx, 0, 0);
224 final = isl_union_set_empty(dim);
226 for (int i = 0; i < msa.input_state.size(); ++i) {
227 int s = msa.input_state[i];
228 final = isl_union_set_union(final, equal_input_domain(msa, s));
231 for (int i = 0; i < msa.cst_state.size(); ++i) {
232 int s = msa.cst_state[i];
233 final = isl_union_set_union(final, state_domain(msa, s));
236 return final;
239 /* Perform the reachability analysis by first computing the
240 * transitive closure and applying it to a set representing
241 * equal output. We then check if all reachable final states
242 * are good final states.
243 * If we find a bad state that may be reachable, we also
244 * apply the inverse of the transitive closure to that state
245 * to find out where it may have come from.
247 static int check_equivalence_tc(isl_ctx *ctx,
248 dependence_graph *dg1, dependence_graph *dg2)
250 int root;
251 MSA msa;
252 isl_space *dim;
253 isl_union_map *transition;
254 isl_union_set *equal_output;
255 isl_union_set *test;
256 isl_union_set *final;
257 isl_union_set *good;
258 isl_printer *p;
259 int ok = 1;
261 if (check_input_arrays(dg1, dg2))
262 return -1;
264 p = isl_printer_to_file(ctx, stdout);
266 root = msa.add(dg1->out, dg2->out);
268 dim = isl_space_alloc(ctx, 0, 0, 0);
269 transition = isl_union_map_empty(dim);
270 for (int i = 0; i < msa.firing.size(); ++i) {
271 isl_map *map = isl_map_copy(msa.firing[i]);
272 transition = isl_union_map_union(transition,
273 isl_union_map_from_map(map));
276 transition = isl_union_map_transitive_closure(transition, NULL);
278 equal_output = compute_equal_output(dg1, dg2);
279 test = isl_union_set_apply(isl_union_set_copy(equal_output),
280 isl_union_map_copy(transition));
282 final = compute_final_states(ctx, msa);
283 test = isl_union_set_intersect(test, final);
285 good = compute_good_states(ctx, msa);
286 test = isl_union_set_subtract(test, good);
288 if (!isl_union_set_is_empty(test)) {
289 ok = 0;
290 transition = isl_union_map_reverse(transition);
291 test = isl_union_set_apply(test, transition);
292 test = isl_union_set_coalesce(test);
293 test = isl_union_set_intersect(test, equal_output);
295 isl_printer_print_str(p, "NOT equivalent");
296 isl_printer_end_line(p);
297 isl_printer_print_union_set(p, test);
298 isl_printer_end_line(p);
299 } else {
300 isl_union_set_free(equal_output);
301 isl_union_map_free(transition);
304 isl_union_set_free(test);
306 if (!ok)
307 isl_printer_print_str(p, "NOT ");
308 isl_printer_print_str(p, "OK");
309 isl_printer_end_line(p);
311 isl_printer_free(p);
313 return 0;
316 struct transition_info {
317 FILE *out;
318 int nvar; /* Total number of variables */
319 int state; /* Next extra state/total number of states */
320 int transition; /* Current transition */
321 int from; /* From state of current firing relation */
322 int to; /* To state of current firing relation */
324 int first; /* Are we printing the first expression? */
325 int last_out; /* Last variable that was explicitly assigned */
326 int translation; /* Current firing relation is a translation */
328 int lever; /* Are we printing Lever output? */
331 static int is_translation(__isl_keep isl_basic_map *bmap)
333 unsigned n_out = isl_basic_map_dim(bmap, isl_dim_out);
334 unsigned n_div = isl_basic_map_dim(bmap, isl_dim_div);
336 if (n_div > 0)
337 return 0;
339 for (int i = 0; i < n_out; ++i) {
340 isl_constraint *c = NULL;
341 if (!isl_basic_map_has_defining_equality(bmap,
342 isl_dim_out, i, &c))
343 return 0;
344 isl_constraint_free(c);
347 return 1;
350 /* Update the maximal number of variables and the total number of states
351 * based on the given firing relation.
352 * If the firing relation is a translation, then we don't need anything extra.
353 * Otherwise, we need an extra state and
354 * to encode the firing relation we need at most as many variables
355 * as are needed to describe the relation. Note that this does
356 * not include the parameters as they are treated separately.
358 static isl_stat check_transition(__isl_take isl_basic_map *firing, void *user)
360 transition_info *ti = (transition_info *)user;
362 if (!is_translation(firing)) {
363 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
364 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
365 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
366 unsigned total = n_in + n_out + n_div;
368 if (total > ti->nvar)
369 ti->nvar = total;
370 ti->state++;
373 isl_basic_map_free(firing);
374 return isl_stat_ok;
377 /* Compute the maximal number of variables needed as well as the
378 * total number of states.
379 * The number of variables needs to be at least the maximal
380 * number of iterators needed to describe a state in the MSA.
381 * For transitions that are not translations, we may need more.
383 static void compute_max_var(MSA &msa, struct transition_info *ti)
385 for (int i = 0; i < msa.state.size(); ++i) {
386 computation *c1 = msa.state[i].first;
387 computation *c2 = msa.state[i].second;
388 unsigned d1 = isl_set_dim(c1->domain, isl_dim_set);
389 unsigned d2 = isl_set_dim(c2->domain, isl_dim_set);
391 if (d1 + d2 > ti->nvar)
392 ti->nvar = d1 + d2;
394 for (int i = 0; i < msa.transition.size(); ++i) {
395 int f = msa.transition[i].second;
396 isl_map_foreach_basic_map(msa.firing[f],
397 &check_transition, ti);
401 /* Print the constraint as a linear combination.
402 * Because Lever doesn't like linear expressions that start with
403 * "-", we always start off with a non-negative constant (possibly
404 * zero).
405 * "s" is the sign with which to multiply the linear combination.
406 * For constraints that end up in the action, we shouldn't print
407 * the coefficient of the output variable being defined. In this
408 * case "no_output" is set to 1.
410 static __isl_give isl_printer *print_linear(__isl_take isl_printer *p,
411 __isl_keep isl_constraint *c, int s, int no_output)
413 isl_val *v;
414 int nparam;
415 int n_in, n_out, n_div;
417 nparam = isl_constraint_dim(c, isl_dim_param);
418 n_in = isl_constraint_dim(c, isl_dim_in);
419 n_out = isl_constraint_dim(c, isl_dim_out);
420 n_div = isl_constraint_dim(c, isl_dim_div);
422 v = isl_constraint_get_constant_val(c);
423 if (s < 0)
424 v = isl_val_neg(v);
425 if (isl_val_is_neg(v))
426 p = isl_printer_print_str(p, "0");
427 p = isl_printer_print_val(p, v);
428 isl_val_free(v);
429 for (int i = 0; i < nparam; ++i) {
430 const char *name;
431 v = isl_constraint_get_coefficient_val(c, isl_dim_param, i);
432 if (isl_val_is_zero(v)) {
433 isl_val_free(v);
434 continue;
436 if (s < 0)
437 v = isl_val_neg(v);
438 if (isl_val_is_pos(v))
439 p = isl_printer_print_str(p, "+");
440 p = isl_printer_print_val(p, v);
441 name = isl_constraint_get_dim_name(c, isl_dim_param, i);
442 p = isl_printer_print_str(p, "*");
443 p = isl_printer_print_str(p, name);
444 isl_val_free(v);
446 for (int i = 0; i < n_in; ++i) {
447 v = isl_constraint_get_coefficient_val(c, isl_dim_in, i);
448 if (isl_val_is_zero(v)) {
449 isl_val_free(v);
450 continue;
452 if (s < 0)
453 v = isl_val_neg(v);
454 if (isl_val_is_pos(v))
455 p = isl_printer_print_str(p, "+");
456 p = isl_printer_print_val(p, v);
457 p = isl_printer_print_str(p, "*x");
458 p = isl_printer_print_int(p, i);
459 isl_val_free(v);
461 if (!no_output) {
462 for (int i = 0; i < n_out; ++i) {
463 v = isl_constraint_get_coefficient_val(c,
464 isl_dim_out, i);
465 if (isl_val_is_zero(v)) {
466 isl_val_free(v);
467 continue;
469 if (s < 0)
470 v = isl_val_neg(v);
471 if (isl_val_is_pos(v))
472 p = isl_printer_print_str(p, "+");
473 p = isl_printer_print_val(p, v);
474 p = isl_printer_print_str(p, "*x");
475 p = isl_printer_print_int(p, n_in + i);
476 isl_val_free(v);
478 for (int i = 0; i < n_div; ++i) {
479 v = isl_constraint_get_coefficient_val(c,
480 isl_dim_div, i);
481 if (isl_val_is_zero(v)) {
482 isl_val_free(v);
483 continue;
485 if (s < 0)
486 v = isl_val_neg(v);
487 if (isl_val_is_pos(v))
488 p = isl_printer_print_str(p, "+");
489 p = isl_printer_print_val(p, v);
490 p = isl_printer_print_str(p, "*x");
491 p = isl_printer_print_int(p, n_in + n_out + i);
492 isl_val_free(v);
496 return p;
499 /* Print the constraints that go into the guard.
500 * If we are dealing with a translation, then we shouldn't print any
501 * constraints involving the output variables.
502 * Those constraint should be equalities defining the translation
503 * and will be printed in the action.
505 static isl_stat print_guard_constraint(__isl_take isl_constraint *c, void *user)
507 transition_info *ti = (transition_info *)user;
508 int n_out;
509 int n_in;
510 isl_ctx *ctx;
511 isl_val *v;
512 isl_printer *p;
514 n_out = isl_constraint_dim(c, isl_dim_out);
516 if (ti->translation) {
517 for (int i = 0; i < n_out; ++i) {
518 v = isl_constraint_get_coefficient_val(c,
519 isl_dim_out, i);
520 if (!isl_val_is_zero(v)) {
521 assert(isl_constraint_is_equality(c));
522 isl_constraint_free(c);
523 isl_val_free(v);
524 return isl_stat_ok;
529 ctx = isl_constraint_get_ctx(c);
530 p = isl_printer_to_file(ctx, ti->out);
531 if (!ti->first)
532 p = isl_printer_print_str(p, " && ");
533 p = print_linear(p, c, 1, 0);
534 if (isl_constraint_is_equality(c))
535 p = isl_printer_print_str(p, " = 0");
536 else
537 p = isl_printer_print_str(p, " >= 0");
539 ti->first = 0;
541 isl_printer_free(p);
542 isl_constraint_free(c);
543 isl_val_free(v);
545 return isl_stat_ok;
548 /* Print the constraints that go into the action.
549 * That is, equality constraints that define one of the output variables.
551 static isl_stat print_action_constraint(__isl_take isl_constraint *c,
552 void *user)
554 transition_info *ti = (transition_info *)user;
555 int n_in;
556 int n_out;
557 isl_ctx *ctx;
558 isl_printer *p;
559 int out;
560 int s;
562 if (!isl_constraint_is_equality(c)) {
563 isl_constraint_free(c);
564 return isl_stat_ok;
567 n_in = isl_constraint_dim(c, isl_dim_in);
568 n_out = isl_constraint_dim(c, isl_dim_out);
570 for (out = 0; out < n_out; ++out) {
571 if (isl_constraint_involves_dims(c, isl_dim_out, out, 1))
572 break;
575 if (out >= n_out) {
576 isl_constraint_free(c);
577 return isl_stat_ok;
580 if (out > ti->last_out)
581 ti->last_out = out;
583 if (isl_constraint_is_lower_bound(c, isl_dim_out, out))
584 s = -1;
585 else
586 s = 1;
588 ctx = isl_constraint_get_ctx(c);
589 p = isl_printer_to_file(ctx, ti->out);
590 if (!ti->first)
591 p = isl_printer_print_str(p, ", ");
593 p = isl_printer_print_str(p, "x");
594 p = isl_printer_print_int(p, out);
595 p = isl_printer_print_str(p, "' = ");
596 p = print_linear(p, c, s, 1);
598 ti->first = 0;
600 isl_constraint_free(c);
601 return isl_stat_ok;
604 /* A firing relation that is not a translation is encoded using
605 * several transitions. We first allow an unconditional transition
606 * to an extra state. In this state, we allow arbitrary increments
607 * on the extra variables that will correspond to the output variables
608 * and the existentially quantified variables of the relation. This allows
609 * those variables to attain any value, since they are initially set
610 * to zero and FAST and Lever only support non-negative variables.
611 * Finally, we add a transition from the extra
612 * state to the original target state with a guard that corresponds
613 * to the firing relation and an action that assigns the output
614 * variables to the main variables.
616 static isl_stat print_non_translation(__isl_take isl_basic_map *firing,
617 transition_info *ti)
619 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
620 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
621 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
623 fprintf(ti->out, "transition t%d := {\n", ti->transition);
624 if (!ti->lever) {
625 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
626 fprintf(ti->out, "\tto := s%d;\n", ti->state);
627 fprintf(ti->out, "\tguard := true;\n");
628 fprintf(ti->out, "\taction := x0' = x0");
629 } else {
630 fprintf(ti->out, "\tguard := state = %d;\n", ti->from);
631 fprintf(ti->out, "\taction := state' = %d", ti->state);
633 for (int i = 0; i < n_out + n_div; ++i)
634 fprintf(ti->out, ", x%d' = 0", n_in + i);
635 fprintf(ti->out, ";\n");
636 fprintf(ti->out, "};\n");
638 ti->transition++;
640 for (int i = 0; i < n_out + n_div; ++i) {
641 fprintf(ti->out, "transition t%d := {\n", ti->transition);
642 if (!ti->lever) {
643 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
644 fprintf(ti->out, "\tto := s%d;\n", ti->state);
645 fprintf(ti->out, "\tguard := true;\n");
646 } else
647 fprintf(ti->out, "\tguard := state = %d;\n", ti->state);
648 fprintf(ti->out, "\taction := x%d' = x%d + 1;\n",
649 n_in + i, n_in + i);
650 fprintf(ti->out, "};\n");
651 ti->transition++;
654 fprintf(ti->out, "transition t%d := {\n", ti->transition);
655 if (!ti->lever) {
656 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
657 fprintf(ti->out, "\tto := s%d;\n", ti->to);
659 fprintf(ti->out, "\tguard := ");
660 if (!ti->lever)
661 ti->first = 1;
662 else {
663 fprintf(ti->out, "state = %d", ti->state);
664 ti->first = 0;
666 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
667 if (ti->first)
668 fprintf(ti->out, "true");
669 fprintf(ti->out, ";\n");
670 fprintf(ti->out, "\taction := ");
671 for (int i = 0; i < n_out; ++i)
672 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
673 for (int i = n_out; i < ti->nvar; ++i)
674 fprintf(ti->out, ", x%d' = 0", i);
675 if (ti->lever)
676 fprintf(ti->out, ", state' = %d", ti->to);
677 fprintf(ti->out, ";\n");
678 fprintf(ti->out, "};\n");
680 ti->transition++;
682 ti->state++;
684 isl_basic_map_free(firing);
686 return isl_stat_ok;
689 /* A firing relation that is a translation can be encoded directly
690 * into a transition. We simply put the translation in the action
691 * and the remaining constraints in the guard.
693 static isl_stat print_translation(__isl_take isl_basic_map *firing,
694 transition_info *ti)
696 fprintf(ti->out, "transition t%d := {\n", ti->transition);
697 if (!ti->lever) {
698 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
699 fprintf(ti->out, "\tto := s%d;\n", ti->to);
702 fprintf(ti->out, "\tguard := ");
703 if (!ti->lever)
704 ti->first = 1;
705 else {
706 fprintf(ti->out, "state = %d", ti->from);
707 ti->first = 0;
709 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
710 if (ti->first)
711 fprintf(ti->out, "true");
712 fprintf(ti->out, ";\n");
714 fprintf(ti->out, "\taction := ");
715 if (!ti->lever)
716 ti->first = 1;
717 else {
718 fprintf(ti->out, "state' = %d", ti->to);
719 ti->first = 0;
721 ti->last_out = -1;
722 isl_basic_map_foreach_constraint(firing, print_action_constraint, ti);
723 for (int i = ti->last_out + 1; i < ti->nvar; ++i)
724 fprintf(ti->out, "%sx%d' = 0",
725 (i || !ti->first) ? ", " : "", i);
726 fprintf(ti->out, ";\n");
728 fprintf(ti->out, "};\n");
730 ti->transition++;
732 isl_basic_map_free(firing);
734 return isl_stat_ok;
737 /* Print one or more transitions based on the firing relation.
738 * The way a firing relation is encoded depends on whether it is
739 * a translation or not.
741 static isl_stat print_transition(__isl_take isl_basic_map *firing, void *user)
743 transition_info *ti = (transition_info *)user;
745 ti->translation = is_translation(firing);
746 if (!ti->translation)
747 return print_non_translation(firing, ti);
748 return print_translation(firing, ti);
751 struct region_info {
752 FILE *out;
753 int name_offset;
755 int first;
758 static isl_stat print_region_constraint(__isl_take isl_constraint *c,
759 void *user)
761 region_info *ri = (region_info *)user;
762 isl_ctx *ctx;
763 isl_printer *p;
765 ctx = isl_constraint_get_ctx(c);
766 p = isl_printer_to_file(ctx, ri->out);
767 p = isl_printer_print_str(p, " && ");
769 p = print_linear(p, c, 1, 0);
770 if (isl_constraint_is_equality(c))
771 p = isl_printer_print_str(p, " = 0");
772 else
773 p = isl_printer_print_str(p, " >= 0");
775 isl_printer_free(p);
776 isl_constraint_free(c);
777 return isl_stat_ok;
780 /* Print a convex part of a region.
781 * In FAST, states are designated by labels, while in Lever, they
782 * are encoded as integer, so we have to drop the first letter ("s")
783 * of the state name.
785 static isl_stat print_region_basic_set(__isl_take isl_basic_set *bset,
786 void *user)
788 region_info *ri = (region_info *)user;
790 if (!ri->first)
791 fprintf(ri->out, " || ");
793 fprintf(ri->out, "(");
794 fprintf(ri->out, "state = %s",
795 isl_basic_set_get_tuple_name(bset) + ri->name_offset);
797 isl_basic_set_foreach_constraint(bset, &print_region_constraint, user);
799 fprintf(ri->out, ")");
801 ri->first = 0;
803 isl_basic_set_free(bset);
804 return isl_stat_ok;
807 static isl_stat print_region_set(__isl_take isl_set *set, void *user)
809 isl_set_foreach_basic_set(set, &print_region_basic_set, user);
811 isl_set_free(set);
812 return isl_stat_ok;
815 static void print_region(FILE *out, const char *name,
816 __isl_keep isl_union_set *region)
818 region_info ri;
820 ri.out = out;
821 ri.first = 1;
822 ri.name_offset = 0;
824 fprintf(out, "Region %s := { ", name);
825 isl_union_set_foreach_set(region, &print_region_set, &ri);
826 fprintf(out, " };\n");
829 /* Construct a FAST model that tests whether all reachable
830 * final states are a subset of the good final states.
832 static int check_equivalence_fast(isl_ctx *ctx,
833 dependence_graph *dg1, dependence_graph *dg2)
835 int root;
836 MSA msa;
837 FILE *out = stdout;
838 unsigned nparam;
839 transition_info ti;
840 isl_space *dim;
841 isl_union_set *init;
842 isl_union_set *final;
843 isl_union_set *good;
845 if (check_input_arrays(dg1, dg2))
846 return -1;
848 root = msa.add(dg1->out, dg2->out);
850 ti.nvar = 0;
851 ti.state = msa.state.size();
852 ti.lever = 0;
854 compute_max_var(msa, &ti);
855 init = compute_equal_output(dg1, dg2);
856 final = compute_final_states(ctx, msa);
857 good = compute_good_states(ctx, msa);
858 dim = isl_union_set_get_space(init);
859 nparam = isl_space_dim(dim, isl_dim_param);
861 fprintf(out, "model m1 {\n");
863 fprintf(out, "var");
864 for (int i = 0; i < ti.nvar; ++i)
865 fprintf(out, "%s x%d", i ? "," : "", i);
866 for (int i = 0; i < nparam; ++i)
867 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
868 fprintf(out, ";\n");
870 fprintf(out, "states");
871 for (int i = 0; i < ti.state; ++i)
872 fprintf(out, "%s s%d", i ? "," : "", i);
873 fprintf(out, ";\n");
875 ti.out = out;
876 ti.transition = 0;
877 ti.state = msa.state.size();
878 for (int i = 0; i < msa.transition.size(); ++i) {
879 int f = msa.transition[i].second;
880 ti.from = msa.transition[i].first.first;
881 ti.to = msa.transition[i].first.second;
882 isl_map_foreach_basic_map(msa.firing[f],
883 &print_transition, &ti);
886 fprintf(out, "}\n");
888 fprintf(out, "strategy s1 {\n");
889 print_region(out, "init", init);
890 print_region(out, "final", final);
891 print_region(out, "good", good);
892 fprintf(out, "Transitions t := {");
893 for (int i = 0; i < ti.transition; ++i)
894 fprintf(out, "%s t%d", i ? "," : "", i);
895 fprintf(out, " };\n");
896 fprintf(out, "Region reach := post*(init, t);\n");
897 fprintf(out, "if (subSet(reach && final, good))\n");
898 fprintf(out, "\tthen print(\"OK\\n\");\n");
899 fprintf(out, "\telse print(\"NOT OK\\n\");\n");
900 fprintf(out, "endif\n");
901 fprintf(out, "}\n");
903 isl_space_free(dim);
904 isl_union_set_free(init);
905 isl_union_set_free(final);
906 isl_union_set_free(good);
908 return 0;
911 static void print_label(FILE *out, const char *name,
912 __isl_keep isl_union_set *region)
914 region_info ri;
916 ri.out = out;
917 ri.first = 1;
918 ri.name_offset = 1;
920 fprintf(out, "label %s := { ", name);
921 isl_union_set_foreach_set(region, &print_region_set, &ri);
922 fprintf(out, " };\n");
925 /* Construct a Lever model that tests whether the bad final states
926 * are unreachable.
928 static int check_equivalence_lever(isl_ctx *ctx,
929 dependence_graph *dg1, dependence_graph *dg2)
931 int root;
932 MSA msa;
933 FILE *out = stdout;
934 unsigned nparam;
935 transition_info ti;
936 isl_space *dim;
937 isl_union_set *init;
938 isl_union_set *bad;
940 if (check_input_arrays(dg1, dg2))
941 return -1;
943 root = msa.add(dg1->out, dg2->out);
945 ti.nvar = 0;
946 ti.state = msa.state.size();
947 ti.lever = 1;
949 compute_max_var(msa, &ti);
950 init = compute_equal_output(dg1, dg2);
951 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
952 compute_good_states(ctx, msa));
953 dim = isl_union_set_get_space(init);
954 nparam = isl_space_dim(dim, isl_dim_param);
956 fprintf(out, "var state");
957 for (int i = 0; i < ti.nvar; ++i)
958 fprintf(out, ", x%d", i);
959 for (int i = 0; i < nparam; ++i)
960 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
961 fprintf(out, ";\n");
963 fprintf(out, "ctl_spec {\n");
964 print_label(out, "bad", bad);
965 fprintf(out, "}\n");
967 fprintf(out, "system {\n");
968 print_label(out, "init", init);
970 ti.out = out;
971 ti.transition = 0;
972 ti.state = msa.state.size();
973 for (int i = 0; i < msa.transition.size(); ++i) {
974 int f = msa.transition[i].second;
975 ti.from = msa.transition[i].first.first;
976 ti.to = msa.transition[i].first.second;
977 isl_map_foreach_basic_map(msa.firing[f],
978 &print_transition, &ti);
981 fprintf(out, "}\n");
983 isl_space_free(dim);
984 isl_union_set_free(init);
985 isl_union_set_free(bad);
987 return 0;
990 /* A firing relation that is not a translation is encoded in Aspic using
991 * using two transitions passing through an extra state.
992 * The first transition assigns arbitrary values to the extra variables
993 * that will correspond to the output variables and the existentially
994 * quantified variables of the relation.
995 * The second transition, from the extra
996 * state to the original target state, has a guard that corresponds
997 * to the firing relation and an action that assigns the output
998 * variables to the main variables.
1000 static isl_stat print_aspic_non_translation(__isl_take isl_basic_map *firing,
1001 transition_info *ti)
1003 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
1004 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
1005 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
1007 fprintf(ti->out, "transition t%d := {\n", ti->transition);
1008 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
1009 fprintf(ti->out, "\tto := s%d;\n", ti->state);
1010 fprintf(ti->out, "\tguard := true;\n");
1011 fprintf(ti->out, "\taction := ");
1012 for (int i = 0; i < n_out + n_div; ++i)
1013 fprintf(ti->out, "%sx%d' = ?", i ? ", " : "", n_in + i);
1014 fprintf(ti->out, ";\n");
1016 fprintf(ti->out, "};\n");
1018 ti->transition++;
1020 fprintf(ti->out, "transition t%d := {\n", ti->transition);
1021 if (!ti->lever) {
1022 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
1023 fprintf(ti->out, "\tto := s%d;\n", ti->to);
1025 fprintf(ti->out, "\tguard := ");
1026 if (!ti->lever)
1027 ti->first = 1;
1028 else {
1029 fprintf(ti->out, "state = %d", ti->state);
1030 ti->first = 0;
1032 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
1033 if (ti->first)
1034 fprintf(ti->out, "true");
1035 fprintf(ti->out, ";\n");
1036 fprintf(ti->out, "\taction := ");
1037 for (int i = 0; i < n_out; ++i)
1038 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
1039 for (int i = n_out; i < ti->nvar; ++i)
1040 fprintf(ti->out, ", x%d' = 0", i);
1041 if (ti->lever)
1042 fprintf(ti->out, ", state' = %d", ti->to);
1043 fprintf(ti->out, ";\n");
1044 fprintf(ti->out, "};\n");
1046 ti->transition++;
1048 ti->state++;
1050 isl_basic_map_free(firing);
1052 return isl_stat_ok;
1055 /* Print one or more transitions based on the firing relation.
1056 * The way a firing relation is encoded depends on whether it is
1057 * a translation or not.
1059 static isl_stat print_aspic_transition(__isl_take isl_basic_map *firing,
1060 void *user)
1062 transition_info *ti = (transition_info *)user;
1064 ti->translation = is_translation(firing);
1065 if (!ti->translation)
1066 return print_aspic_non_translation(firing, ti);
1067 return print_translation(firing, ti);
1070 /* Print a transition from a bad final state to the canonical bad state.
1072 static isl_stat print_bad_aspic_transition(__isl_take isl_map *firing,
1073 void *user)
1075 transition_info *ti = (transition_info *)user;
1076 const char *name;
1078 name = isl_map_get_tuple_name(firing, isl_dim_in);
1079 ti->from = atoi(name + 1);
1081 isl_map_foreach_basic_map(firing, &print_aspic_transition, user);
1083 isl_map_free(firing);
1085 return isl_stat_ok;
1088 /* Construct an Aspic model that tests whether the bad final states
1089 * are unreachable. Since Aspic only supports convex "bad" regions,
1090 * we add an extra state with transitions from all instances of states
1091 * that are bad. The bad region is then simply any instance of that
1092 * extra state.
1094 static int check_equivalence_aspic(isl_ctx *ctx,
1095 dependence_graph *dg1, dependence_graph *dg2)
1097 int root;
1098 MSA msa;
1099 FILE *out = stdout;
1100 unsigned nparam;
1101 transition_info ti;
1102 isl_space *dim;
1103 isl_union_set *init;
1104 isl_union_set *bad;
1105 isl_union_map *bad_map;
1107 if (check_input_arrays(dg1, dg2))
1108 return -1;
1110 root = msa.add(dg1->out, dg2->out);
1112 ti.nvar = 0;
1113 ti.state = msa.state.size();
1114 ti.lever = 0;
1116 compute_max_var(msa, &ti);
1117 init = compute_equal_output(dg1, dg2);
1118 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
1119 compute_good_states(ctx, msa));
1120 dim = isl_union_set_get_space(init);
1121 nparam = isl_space_dim(dim, isl_dim_param);
1123 fprintf(out, "model m1 {\n");
1125 fprintf(out, "var");
1126 for (int i = 0; i < ti.nvar; ++i)
1127 fprintf(out, "%s x%d", i ? "," : "", i);
1128 for (int i = 0; i < nparam; ++i)
1129 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
1130 fprintf(out, ";\n");
1132 fprintf(out, "states");
1133 for (int i = 0; i < ti.state + 1; ++i)
1134 fprintf(out, "%s s%d", i ? "," : "", i);
1135 fprintf(out, ";\n");
1137 ti.out = out;
1138 ti.transition = 0;
1139 ti.state = msa.state.size();
1140 for (int i = 0; i < msa.transition.size(); ++i) {
1141 int f = msa.transition[i].second;
1142 ti.from = msa.transition[i].first.first;
1143 ti.to = msa.transition[i].first.second;
1144 isl_map_foreach_basic_map(msa.firing[f],
1145 &print_aspic_transition, &ti);
1148 bad_map = isl_union_map_from_domain_and_range(bad,
1149 isl_union_set_from_set(isl_set_universe(dim)));
1150 ti.to = ti.state;
1151 isl_union_map_foreach_map(bad_map, &print_bad_aspic_transition, &ti);
1153 fprintf(out, "}\n");
1155 fprintf(out, "strategy s1 {\n");
1156 print_region(out, "init", init);
1157 fprintf(out, "Region bad := { state = s%d };\n", ti.state);
1158 fprintf(out, "}\n");
1160 isl_union_set_free(init);
1161 isl_union_map_free(bad_map);
1163 return 0;
1166 int main(int argc, char *argv[])
1168 isl_ctx *ctx;
1169 struct options *options = options_new_with_defaults();
1170 isl_set *context = NULL;
1171 dependence_graph *dg[2];
1172 int res = 1;
1174 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
1176 ctx = isl_ctx_alloc_with_options(&options_args, options);
1177 if (!ctx) {
1178 fprintf(stderr, "Unable to allocate ctx\n");
1179 return -1;
1182 if (options->context)
1183 context = isl_set_read_from_str(ctx, options->context);
1185 pdg::PDG *pdg[2];
1186 unsigned out_dim = 0;
1187 for (int i = 0; i < 2; ++i) {
1188 FILE *in;
1189 in = fopen(options->program[i], "r");
1190 if (!in) {
1191 fprintf(stderr, "Unable to open %s\n", options->program[i]);
1192 return -1;
1194 pdg[i] = yaml::Load<pdg::PDG>(in, ctx);
1195 fclose(in);
1196 if (!pdg[i]) {
1197 fprintf(stderr, "Unable to read %s\n", options->program[i]);
1198 return -1;
1200 out_dim = update_out_dim(pdg[i], out_dim);
1202 if (context &&
1203 pdg[i]->params.size() != isl_set_dim(context, isl_dim_param)) {
1204 fprintf(stderr,
1205 "Parameter dimension mismatch; context ignored\n");
1206 isl_set_free(context);
1207 context = NULL;
1211 for (int i = 0; i < 2; ++i) {
1212 dg[i] = pdg_to_dg(pdg[i], out_dim, isl_set_copy(context));
1213 pdg[i]->free();
1214 delete pdg[i];
1217 if (options->reachability == REACH_TC)
1218 res = check_equivalence_tc(ctx, dg[0], dg[1]);
1219 else if (options->reachability == REACH_FAST)
1220 res = check_equivalence_fast(ctx, dg[0], dg[1]);
1221 else if (options->reachability == REACH_LEVER)
1222 res = check_equivalence_lever(ctx, dg[0], dg[1]);
1223 else if (options->reachability == REACH_ASPIC)
1224 res = check_equivalence_aspic(ctx, dg[0], dg[1]);
1226 for (int i = 0; i < 2; ++i)
1227 delete dg[i];
1228 isl_set_free(context);
1229 isl_ctx_free(ctx);
1231 return res;