update isl to version 0.12
[ppn.git] / eqv3.cc
blob1c3ae4207ae807083cb0b502c27d58c6ffdbbe87
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 int 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 0;
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 int 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 0;
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 0;
548 /* Print the constraints that go into the action.
549 * That is, equality constraints that define one of the output variables.
551 static int print_action_constraint(__isl_take isl_constraint *c, void *user)
553 transition_info *ti = (transition_info *)user;
554 int n_in;
555 int n_out;
556 isl_ctx *ctx;
557 isl_printer *p;
558 int out;
559 int s;
561 if (!isl_constraint_is_equality(c)) {
562 isl_constraint_free(c);
563 return 0;
566 n_in = isl_constraint_dim(c, isl_dim_in);
567 n_out = isl_constraint_dim(c, isl_dim_out);
569 for (out = 0; out < n_out; ++out) {
570 if (isl_constraint_involves_dims(c, isl_dim_out, out, 1))
571 break;
574 if (out >= n_out) {
575 isl_constraint_free(c);
576 return 0;
579 if (out > ti->last_out)
580 ti->last_out = out;
582 if (isl_constraint_is_lower_bound(c, isl_dim_out, out))
583 s = -1;
584 else
585 s = 1;
587 ctx = isl_constraint_get_ctx(c);
588 p = isl_printer_to_file(ctx, ti->out);
589 if (!ti->first)
590 p = isl_printer_print_str(p, ", ");
592 p = isl_printer_print_str(p, "x");
593 p = isl_printer_print_int(p, out);
594 p = isl_printer_print_str(p, "' = ");
595 p = print_linear(p, c, s, 1);
597 ti->first = 0;
599 isl_constraint_free(c);
600 return 0;
603 /* A firing relation that is not a translation is encoded using
604 * several transitions. We first allow an unconditional transition
605 * to an extra state. In this state, we allow arbitrary increments
606 * on the extra variables that will correspond to the output variables
607 * and the existentially quantified variables of the relation. This allows
608 * those variables to attain any value, since they are initially set
609 * to zero and FAST and Lever only support non-negative variables.
610 * Finally, we add a transition from the extra
611 * state to the original target state with a guard that corresponds
612 * to the firing relation and an action that assigns the output
613 * variables to the main variables.
615 static int print_non_translation(__isl_take isl_basic_map *firing,
616 transition_info *ti)
618 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
619 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
620 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
622 fprintf(ti->out, "transition t%d := {\n", ti->transition);
623 if (!ti->lever) {
624 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
625 fprintf(ti->out, "\tto := s%d;\n", ti->state);
626 fprintf(ti->out, "\tguard := true;\n");
627 fprintf(ti->out, "\taction := x0' = x0");
628 } else {
629 fprintf(ti->out, "\tguard := state = %d;\n", ti->from);
630 fprintf(ti->out, "\taction := state' = %d", ti->state);
632 for (int i = 0; i < n_out + n_div; ++i)
633 fprintf(ti->out, ", x%d' = 0", n_in + i);
634 fprintf(ti->out, ";\n");
635 fprintf(ti->out, "};\n");
637 ti->transition++;
639 for (int i = 0; i < n_out + n_div; ++i) {
640 fprintf(ti->out, "transition t%d := {\n", ti->transition);
641 if (!ti->lever) {
642 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
643 fprintf(ti->out, "\tto := s%d;\n", ti->state);
644 fprintf(ti->out, "\tguard := true;\n");
645 } else
646 fprintf(ti->out, "\tguard := state = %d;\n", ti->state);
647 fprintf(ti->out, "\taction := x%d' = x%d + 1;\n",
648 n_in + i, n_in + i);
649 fprintf(ti->out, "};\n");
650 ti->transition++;
653 fprintf(ti->out, "transition t%d := {\n", ti->transition);
654 if (!ti->lever) {
655 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
656 fprintf(ti->out, "\tto := s%d;\n", ti->to);
658 fprintf(ti->out, "\tguard := ");
659 if (!ti->lever)
660 ti->first = 1;
661 else {
662 fprintf(ti->out, "state = %d", ti->state);
663 ti->first = 0;
665 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
666 if (ti->first)
667 fprintf(ti->out, "true");
668 fprintf(ti->out, ";\n");
669 fprintf(ti->out, "\taction := ");
670 for (int i = 0; i < n_out; ++i)
671 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
672 for (int i = n_out; i < ti->nvar; ++i)
673 fprintf(ti->out, ", x%d' = 0", i);
674 if (ti->lever)
675 fprintf(ti->out, ", state' = %d", ti->to);
676 fprintf(ti->out, ";\n");
677 fprintf(ti->out, "};\n");
679 ti->transition++;
681 ti->state++;
683 isl_basic_map_free(firing);
685 return 0;
688 /* A firing relation that is a translation can be encoded directly
689 * into a transition. We simply put the translation in the action
690 * and the remaining constraints in the guard.
692 static int print_translation(__isl_take isl_basic_map *firing,
693 transition_info *ti)
695 fprintf(ti->out, "transition t%d := {\n", ti->transition);
696 if (!ti->lever) {
697 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
698 fprintf(ti->out, "\tto := s%d;\n", ti->to);
701 fprintf(ti->out, "\tguard := ");
702 if (!ti->lever)
703 ti->first = 1;
704 else {
705 fprintf(ti->out, "state = %d", ti->from);
706 ti->first = 0;
708 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
709 if (ti->first)
710 fprintf(ti->out, "true");
711 fprintf(ti->out, ";\n");
713 fprintf(ti->out, "\taction := ");
714 if (!ti->lever)
715 ti->first = 1;
716 else {
717 fprintf(ti->out, "state' = %d", ti->to);
718 ti->first = 0;
720 ti->last_out = -1;
721 isl_basic_map_foreach_constraint(firing, print_action_constraint, ti);
722 for (int i = ti->last_out + 1; i < ti->nvar; ++i)
723 fprintf(ti->out, "%sx%d' = 0",
724 (i || !ti->first) ? ", " : "", i);
725 fprintf(ti->out, ";\n");
727 fprintf(ti->out, "};\n");
729 ti->transition++;
731 isl_basic_map_free(firing);
733 return 0;
736 /* Print one or more transitions based on the firing relation.
737 * The way a firing relation is encoded depends on whether it is
738 * a translation or not.
740 static int print_transition(__isl_take isl_basic_map *firing, void *user)
742 transition_info *ti = (transition_info *)user;
744 ti->translation = is_translation(firing);
745 if (!ti->translation)
746 return print_non_translation(firing, ti);
747 return print_translation(firing, ti);
750 struct region_info {
751 FILE *out;
752 int name_offset;
754 int first;
757 static int print_region_constraint(__isl_take isl_constraint *c, void *user)
759 region_info *ri = (region_info *)user;
760 isl_ctx *ctx;
761 isl_printer *p;
763 ctx = isl_constraint_get_ctx(c);
764 p = isl_printer_to_file(ctx, ri->out);
765 p = isl_printer_print_str(p, " && ");
767 p = print_linear(p, c, 1, 0);
768 if (isl_constraint_is_equality(c))
769 p = isl_printer_print_str(p, " = 0");
770 else
771 p = isl_printer_print_str(p, " >= 0");
773 isl_printer_free(p);
774 isl_constraint_free(c);
775 return 0;
778 /* Print a convex part of a region.
779 * In FAST, states are designated by labels, while in Lever, they
780 * are encoded as integer, so we have to drop the first letter ("s")
781 * of the state name.
783 static int print_region_basic_set(__isl_take isl_basic_set *bset, void *user)
785 region_info *ri = (region_info *)user;
787 if (!ri->first)
788 fprintf(ri->out, " || ");
790 fprintf(ri->out, "(");
791 fprintf(ri->out, "state = %s",
792 isl_basic_set_get_tuple_name(bset) + ri->name_offset);
794 isl_basic_set_foreach_constraint(bset, &print_region_constraint, user);
796 fprintf(ri->out, ")");
798 ri->first = 0;
800 isl_basic_set_free(bset);
801 return 0;
804 static int print_region_set(__isl_take isl_set *set, void *user)
806 isl_set_foreach_basic_set(set, &print_region_basic_set, user);
808 isl_set_free(set);
809 return 0;
812 static void print_region(FILE *out, const char *name,
813 __isl_keep isl_union_set *region)
815 region_info ri;
817 ri.out = out;
818 ri.first = 1;
819 ri.name_offset = 0;
821 fprintf(out, "Region %s := { ", name);
822 isl_union_set_foreach_set(region, &print_region_set, &ri);
823 fprintf(out, " };\n");
826 /* Construct a FAST model that tests whether all reachable
827 * final states are a subset of the good final states.
829 static int check_equivalence_fast(isl_ctx *ctx,
830 dependence_graph *dg1, dependence_graph *dg2)
832 int root;
833 MSA msa;
834 FILE *out = stdout;
835 unsigned nparam;
836 transition_info ti;
837 isl_space *dim;
838 isl_union_set *init;
839 isl_union_set *final;
840 isl_union_set *good;
842 if (check_input_arrays(dg1, dg2))
843 return -1;
845 root = msa.add(dg1->out, dg2->out);
847 ti.nvar = 0;
848 ti.state = msa.state.size();
849 ti.lever = 0;
851 compute_max_var(msa, &ti);
852 init = compute_equal_output(dg1, dg2);
853 final = compute_final_states(ctx, msa);
854 good = compute_good_states(ctx, msa);
855 dim = isl_union_set_get_space(init);
856 nparam = isl_space_dim(dim, isl_dim_param);
858 fprintf(out, "model m1 {\n");
860 fprintf(out, "var");
861 for (int i = 0; i < ti.nvar; ++i)
862 fprintf(out, "%s x%d", i ? "," : "", i);
863 for (int i = 0; i < nparam; ++i)
864 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
865 fprintf(out, ";\n");
867 fprintf(out, "states");
868 for (int i = 0; i < ti.state; ++i)
869 fprintf(out, "%s s%d", i ? "," : "", i);
870 fprintf(out, ";\n");
872 ti.out = out;
873 ti.transition = 0;
874 ti.state = msa.state.size();
875 for (int i = 0; i < msa.transition.size(); ++i) {
876 int f = msa.transition[i].second;
877 ti.from = msa.transition[i].first.first;
878 ti.to = msa.transition[i].first.second;
879 isl_map_foreach_basic_map(msa.firing[f],
880 &print_transition, &ti);
883 fprintf(out, "}\n");
885 fprintf(out, "strategy s1 {\n");
886 print_region(out, "init", init);
887 print_region(out, "final", final);
888 print_region(out, "good", good);
889 fprintf(out, "Transitions t := {");
890 for (int i = 0; i < ti.transition; ++i)
891 fprintf(out, "%s t%d", i ? "," : "", i);
892 fprintf(out, " };\n");
893 fprintf(out, "Region reach := post*(init, t);\n");
894 fprintf(out, "if (subSet(reach && final, good))\n");
895 fprintf(out, "\tthen print(\"OK\\n\");\n");
896 fprintf(out, "\telse print(\"NOT OK\\n\");\n");
897 fprintf(out, "endif\n");
898 fprintf(out, "}\n");
900 isl_space_free(dim);
901 isl_union_set_free(init);
902 isl_union_set_free(final);
903 isl_union_set_free(good);
905 return 0;
908 static void print_label(FILE *out, const char *name,
909 __isl_keep isl_union_set *region)
911 region_info ri;
913 ri.out = out;
914 ri.first = 1;
915 ri.name_offset = 1;
917 fprintf(out, "label %s := { ", name);
918 isl_union_set_foreach_set(region, &print_region_set, &ri);
919 fprintf(out, " };\n");
922 /* Construct a Lever model that tests whether the bad final states
923 * are unreachable.
925 static int check_equivalence_lever(isl_ctx *ctx,
926 dependence_graph *dg1, dependence_graph *dg2)
928 int root;
929 MSA msa;
930 FILE *out = stdout;
931 unsigned nparam;
932 transition_info ti;
933 isl_space *dim;
934 isl_union_set *init;
935 isl_union_set *bad;
937 if (check_input_arrays(dg1, dg2))
938 return -1;
940 root = msa.add(dg1->out, dg2->out);
942 ti.nvar = 0;
943 ti.state = msa.state.size();
944 ti.lever = 1;
946 compute_max_var(msa, &ti);
947 init = compute_equal_output(dg1, dg2);
948 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
949 compute_good_states(ctx, msa));
950 dim = isl_union_set_get_space(init);
951 nparam = isl_space_dim(dim, isl_dim_param);
953 fprintf(out, "var state");
954 for (int i = 0; i < ti.nvar; ++i)
955 fprintf(out, ", x%d", i);
956 for (int i = 0; i < nparam; ++i)
957 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
958 fprintf(out, ";\n");
960 fprintf(out, "ctl_spec {\n");
961 print_label(out, "bad", bad);
962 fprintf(out, "}\n");
964 fprintf(out, "system {\n");
965 print_label(out, "init", init);
967 ti.out = out;
968 ti.transition = 0;
969 ti.state = msa.state.size();
970 for (int i = 0; i < msa.transition.size(); ++i) {
971 int f = msa.transition[i].second;
972 ti.from = msa.transition[i].first.first;
973 ti.to = msa.transition[i].first.second;
974 isl_map_foreach_basic_map(msa.firing[f],
975 &print_transition, &ti);
978 fprintf(out, "}\n");
980 isl_space_free(dim);
981 isl_union_set_free(init);
982 isl_union_set_free(bad);
984 return 0;
987 /* A firing relation that is not a translation is encoded in Aspic using
988 * using two transitions passing through an extra state.
989 * The first transition assigns arbitrary values to the extra variables
990 * that will correspond to the output variables and the existentially
991 * quantified variables of the relation.
992 * The second transition, from the extra
993 * state to the original target state, has a guard that corresponds
994 * to the firing relation and an action that assigns the output
995 * variables to the main variables.
997 static int print_aspic_non_translation(__isl_take isl_basic_map *firing,
998 transition_info *ti)
1000 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
1001 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
1002 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
1004 fprintf(ti->out, "transition t%d := {\n", ti->transition);
1005 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
1006 fprintf(ti->out, "\tto := s%d;\n", ti->state);
1007 fprintf(ti->out, "\tguard := true;\n");
1008 fprintf(ti->out, "\taction := ");
1009 for (int i = 0; i < n_out + n_div; ++i)
1010 fprintf(ti->out, "%sx%d' = ?", i ? ", " : "", n_in + i);
1011 fprintf(ti->out, ";\n");
1013 fprintf(ti->out, "};\n");
1015 ti->transition++;
1017 fprintf(ti->out, "transition t%d := {\n", ti->transition);
1018 if (!ti->lever) {
1019 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
1020 fprintf(ti->out, "\tto := s%d;\n", ti->to);
1022 fprintf(ti->out, "\tguard := ");
1023 if (!ti->lever)
1024 ti->first = 1;
1025 else {
1026 fprintf(ti->out, "state = %d", ti->state);
1027 ti->first = 0;
1029 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
1030 if (ti->first)
1031 fprintf(ti->out, "true");
1032 fprintf(ti->out, ";\n");
1033 fprintf(ti->out, "\taction := ");
1034 for (int i = 0; i < n_out; ++i)
1035 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
1036 for (int i = n_out; i < ti->nvar; ++i)
1037 fprintf(ti->out, ", x%d' = 0", i);
1038 if (ti->lever)
1039 fprintf(ti->out, ", state' = %d", ti->to);
1040 fprintf(ti->out, ";\n");
1041 fprintf(ti->out, "};\n");
1043 ti->transition++;
1045 ti->state++;
1047 isl_basic_map_free(firing);
1049 return 0;
1052 /* Print one or more transitions based on the firing relation.
1053 * The way a firing relation is encoded depends on whether it is
1054 * a translation or not.
1056 static int print_aspic_transition(__isl_take isl_basic_map *firing, void *user)
1058 transition_info *ti = (transition_info *)user;
1060 ti->translation = is_translation(firing);
1061 if (!ti->translation)
1062 return print_aspic_non_translation(firing, ti);
1063 return print_translation(firing, ti);
1066 /* Print a transition from a bad final state to the canonical bad state.
1068 static int print_bad_aspic_transition(__isl_take isl_map *firing, void *user)
1070 transition_info *ti = (transition_info *)user;
1071 const char *name;
1073 name = isl_map_get_tuple_name(firing, isl_dim_in);
1074 ti->from = atoi(name + 1);
1076 isl_map_foreach_basic_map(firing, &print_aspic_transition, user);
1078 isl_map_free(firing);
1080 return 0;
1083 /* Construct an Aspic model that tests whether the bad final states
1084 * are unreachable. Since Aspic only supports convex "bad" regions,
1085 * we add an extra state with transitions from all instances of states
1086 * that are bad. The bad region is then simply any instance of that
1087 * extra state.
1089 static int check_equivalence_aspic(isl_ctx *ctx,
1090 dependence_graph *dg1, dependence_graph *dg2)
1092 int root;
1093 MSA msa;
1094 FILE *out = stdout;
1095 unsigned nparam;
1096 transition_info ti;
1097 isl_space *dim;
1098 isl_union_set *init;
1099 isl_union_set *bad;
1100 isl_union_map *bad_map;
1102 if (check_input_arrays(dg1, dg2))
1103 return -1;
1105 root = msa.add(dg1->out, dg2->out);
1107 ti.nvar = 0;
1108 ti.state = msa.state.size();
1109 ti.lever = 0;
1111 compute_max_var(msa, &ti);
1112 init = compute_equal_output(dg1, dg2);
1113 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
1114 compute_good_states(ctx, msa));
1115 dim = isl_union_set_get_space(init);
1116 nparam = isl_space_dim(dim, isl_dim_param);
1118 fprintf(out, "model m1 {\n");
1120 fprintf(out, "var");
1121 for (int i = 0; i < ti.nvar; ++i)
1122 fprintf(out, "%s x%d", i ? "," : "", i);
1123 for (int i = 0; i < nparam; ++i)
1124 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
1125 fprintf(out, ";\n");
1127 fprintf(out, "states");
1128 for (int i = 0; i < ti.state + 1; ++i)
1129 fprintf(out, "%s s%d", i ? "," : "", i);
1130 fprintf(out, ";\n");
1132 ti.out = out;
1133 ti.transition = 0;
1134 ti.state = msa.state.size();
1135 for (int i = 0; i < msa.transition.size(); ++i) {
1136 int f = msa.transition[i].second;
1137 ti.from = msa.transition[i].first.first;
1138 ti.to = msa.transition[i].first.second;
1139 isl_map_foreach_basic_map(msa.firing[f],
1140 &print_aspic_transition, &ti);
1143 bad_map = isl_union_map_from_domain_and_range(bad,
1144 isl_union_set_from_set(isl_set_universe(dim)));
1145 ti.to = ti.state;
1146 isl_union_map_foreach_map(bad_map, &print_bad_aspic_transition, &ti);
1148 fprintf(out, "}\n");
1150 fprintf(out, "strategy s1 {\n");
1151 print_region(out, "init", init);
1152 fprintf(out, "Region bad := { state = s%d };\n", ti.state);
1153 fprintf(out, "}\n");
1155 isl_union_set_free(init);
1156 isl_union_map_free(bad_map);
1158 return 0;
1161 int main(int argc, char *argv[])
1163 isl_ctx *ctx;
1164 struct options *options = options_new_with_defaults();
1165 isl_set *context = NULL;
1166 dependence_graph *dg[2];
1167 int res = 1;
1169 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
1171 ctx = isl_ctx_alloc_with_options(&options_args, options);
1172 if (!ctx) {
1173 fprintf(stderr, "Unable to allocate ctx\n");
1174 return -1;
1177 if (options->context)
1178 context = isl_set_read_from_str(ctx, options->context);
1180 pdg::PDG *pdg[2];
1181 unsigned out_dim = 0;
1182 for (int i = 0; i < 2; ++i) {
1183 FILE *in;
1184 in = fopen(options->program[i], "r");
1185 if (!in) {
1186 fprintf(stderr, "Unable to open %s\n", options->program[i]);
1187 return -1;
1189 pdg[i] = yaml::Load<pdg::PDG>(in, ctx);
1190 fclose(in);
1191 if (!pdg[i]) {
1192 fprintf(stderr, "Unable to read %s\n", options->program[i]);
1193 return -1;
1195 out_dim = update_out_dim(pdg[i], out_dim);
1197 if (context &&
1198 pdg[i]->params.size() != isl_set_dim(context, isl_dim_param)) {
1199 fprintf(stderr,
1200 "Parameter dimension mismatch; context ignored\n");
1201 isl_set_free(context);
1202 context = NULL;
1206 for (int i = 0; i < 2; ++i) {
1207 dg[i] = pdg_to_dg(pdg[i], out_dim, isl_set_copy(context));
1208 pdg[i]->free();
1209 delete pdg[i];
1212 if (options->reachability == REACH_TC)
1213 res = check_equivalence_tc(ctx, dg[0], dg[1]);
1214 else if (options->reachability == REACH_FAST)
1215 res = check_equivalence_fast(ctx, dg[0], dg[1]);
1216 else if (options->reachability == REACH_LEVER)
1217 res = check_equivalence_lever(ctx, dg[0], dg[1]);
1218 else if (options->reachability == REACH_ASPIC)
1219 res = check_equivalence_aspic(ctx, dg[0], dg[1]);
1221 for (int i = 0; i < 2; ++i)
1222 delete dg[i];
1223 isl_set_free(context);
1224 isl_ctx_free(ctx);
1226 return res;