update isl for fix in isl_map_plain_is_disjoint
[ppn.git] / eqv3.cc
blob34cc27dc75983386ae21259dd11b185450831878
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 void print_linear(FILE *out, __isl_keep isl_constraint *c, int s,
411 int no_output)
413 isl_int 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 isl_int_init(v);
424 isl_constraint_get_constant(c, &v);
425 if (s < 0)
426 isl_int_neg(v, v);
427 if (isl_int_is_neg(v))
428 fprintf(out, "0");
429 isl_int_print(out, v, 0);
430 for (int i = 0; i < nparam; ++i) {
431 const char *name;
432 isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
433 if (isl_int_is_zero(v))
434 continue;
435 if (s < 0)
436 isl_int_neg(v, v);
437 if (isl_int_is_pos(v))
438 fprintf(out, "+");
439 isl_int_print(out, v, 0);
440 name = isl_constraint_get_dim_name(c, isl_dim_param, i);
441 fprintf(out, "*%s", name);
443 for (int i = 0; i < n_in; ++i) {
444 isl_constraint_get_coefficient(c, isl_dim_in, i, &v);
445 if (isl_int_is_zero(v))
446 continue;
447 if (s < 0)
448 isl_int_neg(v, v);
449 if (isl_int_is_pos(v))
450 fprintf(out, "+");
451 isl_int_print(out, v, 0);
452 fprintf(out, "*x%d", i);
454 if (!no_output) {
455 for (int i = 0; i < n_out; ++i) {
456 isl_constraint_get_coefficient(c, isl_dim_out, i, &v);
457 if (isl_int_is_zero(v))
458 continue;
459 if (s < 0)
460 isl_int_neg(v, v);
461 if (isl_int_is_pos(v))
462 fprintf(out, "+");
463 isl_int_print(out, v, 0);
464 fprintf(out, "*x%d", n_in + i);
466 for (int i = 0; i < n_div; ++i) {
467 isl_constraint_get_coefficient(c, isl_dim_div, i, &v);
468 if (isl_int_is_zero(v))
469 continue;
470 if (s < 0)
471 isl_int_neg(v, v);
472 if (isl_int_is_pos(v))
473 fprintf(out, "+");
474 isl_int_print(out, v, 0);
475 fprintf(out, "*x%d", n_in + n_out + i);
479 isl_int_clear(v);
482 /* Print the constraints that go into the guard.
483 * If we are dealing with a translation, then we shouldn't print any
484 * constraints involving the output variables.
485 * Those constraint should be equalities defining the translation
486 * and will be printed in the action.
488 static int print_guard_constraint(__isl_take isl_constraint *c, void *user)
490 transition_info *ti = (transition_info *)user;
491 int n_out;
492 int n_in;
493 isl_int v;
495 n_out = isl_constraint_dim(c, isl_dim_out);
497 isl_int_init(v);
499 if (ti->translation) {
500 for (int i = 0; i < n_out; ++i) {
501 isl_constraint_get_coefficient(c, isl_dim_out, i, &v);
502 if (!isl_int_is_zero(v)) {
503 assert(isl_constraint_is_equality(c));
504 isl_constraint_free(c);
505 isl_int_clear(v);
506 return 0;
511 if (!ti->first)
512 fprintf(ti->out, " && ");
513 print_linear(ti->out, c, 1, 0);
514 if (isl_constraint_is_equality(c))
515 fprintf(ti->out, " = 0");
516 else
517 fprintf(ti->out, " >= 0");
519 ti->first = 0;
521 isl_constraint_free(c);
522 isl_int_clear(v);
524 return 0;
527 /* Print the constraints that go into the action.
528 * That is, equality constraints that define one of the output variables.
530 static int print_action_constraint(__isl_take isl_constraint *c, void *user)
532 transition_info *ti = (transition_info *)user;
533 int n_in;
534 int n_out;
535 isl_int v;
536 int out;
537 int s;
539 if (!isl_constraint_is_equality(c)) {
540 isl_constraint_free(c);
541 return 0;
544 n_in = isl_constraint_dim(c, isl_dim_in);
545 n_out = isl_constraint_dim(c, isl_dim_out);
547 isl_int_init(v);
549 for (out = 0; out < n_out; ++out) {
550 isl_constraint_get_coefficient(c, isl_dim_out, out, &v);
551 if (!isl_int_is_zero(v))
552 break;
555 if (out >= n_out) {
556 isl_constraint_free(c);
557 return 0;
560 if (out > ti->last_out)
561 ti->last_out = out;
563 s = -isl_int_sgn(v);
565 if (!ti->first)
566 fprintf(ti->out, ", ");
568 fprintf(ti->out, "x%d' = ", out);
569 print_linear(ti->out, c, s, 1);
571 ti->first = 0;
573 isl_int_clear(v);
574 isl_constraint_free(c);
575 return 0;
578 /* A firing relation that is not a translation is encoded using
579 * several transitions. We first allow an unconditional transition
580 * to an extra state. In this state, we allow arbitrary increments
581 * on the extra variables that will correspond to the output variables
582 * and the existentially quantified variables of the relation. This allows
583 * those variables to attain any value, since they are initially set
584 * to zero and FAST and Lever only support non-negative variables.
585 * Finally, we add a transition from the extra
586 * state to the original target state with a guard that corresponds
587 * to the firing relation and an action that assigns the output
588 * variables to the main variables.
590 static int print_non_translation(__isl_take isl_basic_map *firing,
591 transition_info *ti)
593 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
594 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
595 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
597 fprintf(ti->out, "transition t%d := {\n", ti->transition);
598 if (!ti->lever) {
599 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
600 fprintf(ti->out, "\tto := s%d;\n", ti->state);
601 fprintf(ti->out, "\tguard := true;\n");
602 fprintf(ti->out, "\taction := x0' = x0");
603 } else {
604 fprintf(ti->out, "\tguard := state = %d;\n", ti->from);
605 fprintf(ti->out, "\taction := state' = %d", ti->state);
607 for (int i = 0; i < n_out + n_div; ++i)
608 fprintf(ti->out, ", x%d' = 0", n_in + i);
609 fprintf(ti->out, ";\n");
610 fprintf(ti->out, "};\n");
612 ti->transition++;
614 for (int i = 0; i < n_out + n_div; ++i) {
615 fprintf(ti->out, "transition t%d := {\n", ti->transition);
616 if (!ti->lever) {
617 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
618 fprintf(ti->out, "\tto := s%d;\n", ti->state);
619 fprintf(ti->out, "\tguard := true;\n");
620 } else
621 fprintf(ti->out, "\tguard := state = %d;\n", ti->state);
622 fprintf(ti->out, "\taction := x%d' = x%d + 1;\n",
623 n_in + i, n_in + i);
624 fprintf(ti->out, "};\n");
625 ti->transition++;
628 fprintf(ti->out, "transition t%d := {\n", ti->transition);
629 if (!ti->lever) {
630 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
631 fprintf(ti->out, "\tto := s%d;\n", ti->to);
633 fprintf(ti->out, "\tguard := ");
634 if (!ti->lever)
635 ti->first = 1;
636 else {
637 fprintf(ti->out, "state = %d", ti->state);
638 ti->first = 0;
640 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
641 if (ti->first)
642 fprintf(ti->out, "true");
643 fprintf(ti->out, ";\n");
644 fprintf(ti->out, "\taction := ");
645 for (int i = 0; i < n_out; ++i)
646 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
647 for (int i = n_out; i < ti->nvar; ++i)
648 fprintf(ti->out, ", x%d' = 0", i);
649 if (ti->lever)
650 fprintf(ti->out, ", state' = %d", ti->to);
651 fprintf(ti->out, ";\n");
652 fprintf(ti->out, "};\n");
654 ti->transition++;
656 ti->state++;
658 isl_basic_map_free(firing);
660 return 0;
663 /* A firing relation that is a translation can be encoded directly
664 * into a transition. We simply put the translation in the action
665 * and the remaining constraints in the guard.
667 static int print_translation(__isl_take isl_basic_map *firing,
668 transition_info *ti)
670 fprintf(ti->out, "transition t%d := {\n", ti->transition);
671 if (!ti->lever) {
672 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
673 fprintf(ti->out, "\tto := s%d;\n", ti->to);
676 fprintf(ti->out, "\tguard := ");
677 if (!ti->lever)
678 ti->first = 1;
679 else {
680 fprintf(ti->out, "state = %d", ti->from);
681 ti->first = 0;
683 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
684 if (ti->first)
685 fprintf(ti->out, "true");
686 fprintf(ti->out, ";\n");
688 fprintf(ti->out, "\taction := ");
689 if (!ti->lever)
690 ti->first = 1;
691 else {
692 fprintf(ti->out, "state' = %d", ti->to);
693 ti->first = 0;
695 ti->last_out = -1;
696 isl_basic_map_foreach_constraint(firing, print_action_constraint, ti);
697 for (int i = ti->last_out + 1; i < ti->nvar; ++i)
698 fprintf(ti->out, "%sx%d' = 0",
699 (i || !ti->first) ? ", " : "", i);
700 fprintf(ti->out, ";\n");
702 fprintf(ti->out, "};\n");
704 ti->transition++;
706 isl_basic_map_free(firing);
708 return 0;
711 /* Print one or more transitions based on the firing relation.
712 * The way a firing relation is encoded depends on whether it is
713 * a translation or not.
715 static int print_transition(__isl_take isl_basic_map *firing, void *user)
717 transition_info *ti = (transition_info *)user;
719 ti->translation = is_translation(firing);
720 if (!ti->translation)
721 return print_non_translation(firing, ti);
722 return print_translation(firing, ti);
725 struct region_info {
726 FILE *out;
727 int name_offset;
729 int first;
732 static int print_region_constraint(__isl_take isl_constraint *c, void *user)
734 region_info *ri = (region_info *)user;
736 fprintf(ri->out, " && ");
738 print_linear(ri->out, c, 1, 0);
739 if (isl_constraint_is_equality(c))
740 fprintf(ri->out, " = 0");
741 else
742 fprintf(ri->out, " >= 0");
744 isl_constraint_free(c);
745 return 0;
748 /* Print a convex part of a region.
749 * In FAST, states are designated by labels, while in Lever, they
750 * are encoded as integer, so we have to drop the first letter ("s")
751 * of the state name.
753 static int print_region_basic_set(__isl_take isl_basic_set *bset, void *user)
755 region_info *ri = (region_info *)user;
757 if (!ri->first)
758 fprintf(ri->out, " || ");
760 fprintf(ri->out, "(");
761 fprintf(ri->out, "state = %s",
762 isl_basic_set_get_tuple_name(bset) + ri->name_offset);
764 isl_basic_set_foreach_constraint(bset, &print_region_constraint, user);
766 fprintf(ri->out, ")");
768 ri->first = 0;
770 isl_basic_set_free(bset);
771 return 0;
774 static int print_region_set(__isl_take isl_set *set, void *user)
776 isl_set_foreach_basic_set(set, &print_region_basic_set, user);
778 isl_set_free(set);
779 return 0;
782 static void print_region(FILE *out, const char *name,
783 __isl_keep isl_union_set *region)
785 region_info ri;
787 ri.out = out;
788 ri.first = 1;
789 ri.name_offset = 0;
791 fprintf(out, "Region %s := { ", name);
792 isl_union_set_foreach_set(region, &print_region_set, &ri);
793 fprintf(out, " };\n");
796 /* Construct a FAST model that tests whether all reachable
797 * final states are a subset of the good final states.
799 static int check_equivalence_fast(isl_ctx *ctx,
800 dependence_graph *dg1, dependence_graph *dg2)
802 int root;
803 MSA msa;
804 FILE *out = stdout;
805 unsigned nparam;
806 transition_info ti;
807 isl_space *dim;
808 isl_union_set *init;
809 isl_union_set *final;
810 isl_union_set *good;
812 if (check_input_arrays(dg1, dg2))
813 return -1;
815 root = msa.add(dg1->out, dg2->out);
817 ti.nvar = 0;
818 ti.state = msa.state.size();
819 ti.lever = 0;
821 compute_max_var(msa, &ti);
822 init = compute_equal_output(dg1, dg2);
823 final = compute_final_states(ctx, msa);
824 good = compute_good_states(ctx, msa);
825 dim = isl_union_set_get_space(init);
826 nparam = isl_space_dim(dim, isl_dim_param);
828 fprintf(out, "model m1 {\n");
830 fprintf(out, "var");
831 for (int i = 0; i < ti.nvar; ++i)
832 fprintf(out, "%s x%d", i ? "," : "", i);
833 for (int i = 0; i < nparam; ++i)
834 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
835 fprintf(out, ";\n");
837 fprintf(out, "states");
838 for (int i = 0; i < ti.state; ++i)
839 fprintf(out, "%s s%d", i ? "," : "", i);
840 fprintf(out, ";\n");
842 ti.out = out;
843 ti.transition = 0;
844 ti.state = msa.state.size();
845 for (int i = 0; i < msa.transition.size(); ++i) {
846 int f = msa.transition[i].second;
847 ti.from = msa.transition[i].first.first;
848 ti.to = msa.transition[i].first.second;
849 isl_map_foreach_basic_map(msa.firing[f],
850 &print_transition, &ti);
853 fprintf(out, "}\n");
855 fprintf(out, "strategy s1 {\n");
856 print_region(out, "init", init);
857 print_region(out, "final", final);
858 print_region(out, "good", good);
859 fprintf(out, "Transitions t := {");
860 for (int i = 0; i < ti.transition; ++i)
861 fprintf(out, "%s t%d", i ? "," : "", i);
862 fprintf(out, " };\n");
863 fprintf(out, "Region reach := post*(init, t);\n");
864 fprintf(out, "if (subSet(reach && final, good))\n");
865 fprintf(out, "\tthen print(\"OK\\n\");\n");
866 fprintf(out, "\telse print(\"NOT OK\\n\");\n");
867 fprintf(out, "endif\n");
868 fprintf(out, "}\n");
870 isl_space_free(dim);
871 isl_union_set_free(init);
872 isl_union_set_free(final);
873 isl_union_set_free(good);
875 return 0;
878 static void print_label(FILE *out, const char *name,
879 __isl_keep isl_union_set *region)
881 region_info ri;
883 ri.out = out;
884 ri.first = 1;
885 ri.name_offset = 1;
887 fprintf(out, "label %s := { ", name);
888 isl_union_set_foreach_set(region, &print_region_set, &ri);
889 fprintf(out, " };\n");
892 /* Construct a Lever model that tests whether the bad final states
893 * are unreachable.
895 static int check_equivalence_lever(isl_ctx *ctx,
896 dependence_graph *dg1, dependence_graph *dg2)
898 int root;
899 MSA msa;
900 FILE *out = stdout;
901 unsigned nparam;
902 transition_info ti;
903 isl_space *dim;
904 isl_union_set *init;
905 isl_union_set *bad;
907 if (check_input_arrays(dg1, dg2))
908 return -1;
910 root = msa.add(dg1->out, dg2->out);
912 ti.nvar = 0;
913 ti.state = msa.state.size();
914 ti.lever = 1;
916 compute_max_var(msa, &ti);
917 init = compute_equal_output(dg1, dg2);
918 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
919 compute_good_states(ctx, msa));
920 dim = isl_union_set_get_space(init);
921 nparam = isl_space_dim(dim, isl_dim_param);
923 fprintf(out, "var state");
924 for (int i = 0; i < ti.nvar; ++i)
925 fprintf(out, ", x%d", i);
926 for (int i = 0; i < nparam; ++i)
927 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
928 fprintf(out, ";\n");
930 fprintf(out, "ctl_spec {\n");
931 print_label(out, "bad", bad);
932 fprintf(out, "}\n");
934 fprintf(out, "system {\n");
935 print_label(out, "init", init);
937 ti.out = out;
938 ti.transition = 0;
939 ti.state = msa.state.size();
940 for (int i = 0; i < msa.transition.size(); ++i) {
941 int f = msa.transition[i].second;
942 ti.from = msa.transition[i].first.first;
943 ti.to = msa.transition[i].first.second;
944 isl_map_foreach_basic_map(msa.firing[f],
945 &print_transition, &ti);
948 fprintf(out, "}\n");
950 isl_space_free(dim);
951 isl_union_set_free(init);
952 isl_union_set_free(bad);
954 return 0;
957 /* A firing relation that is not a translation is encoded in Aspic using
958 * using two transitions passing through an extra state.
959 * The first transition assigns arbitrary values to the extra variables
960 * that will correspond to the output variables and the existentially
961 * quantified variables of the relation.
962 * The second transition, from the extra
963 * state to the original target state, has a guard that corresponds
964 * to the firing relation and an action that assigns the output
965 * variables to the main variables.
967 static int print_aspic_non_translation(__isl_take isl_basic_map *firing,
968 transition_info *ti)
970 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
971 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
972 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
974 fprintf(ti->out, "transition t%d := {\n", ti->transition);
975 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
976 fprintf(ti->out, "\tto := s%d;\n", ti->state);
977 fprintf(ti->out, "\tguard := true;\n");
978 fprintf(ti->out, "\taction := ");
979 for (int i = 0; i < n_out + n_div; ++i)
980 fprintf(ti->out, "%sx%d' = ?", i ? ", " : "", n_in + i);
981 fprintf(ti->out, ";\n");
983 fprintf(ti->out, "};\n");
985 ti->transition++;
987 fprintf(ti->out, "transition t%d := {\n", ti->transition);
988 if (!ti->lever) {
989 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
990 fprintf(ti->out, "\tto := s%d;\n", ti->to);
992 fprintf(ti->out, "\tguard := ");
993 if (!ti->lever)
994 ti->first = 1;
995 else {
996 fprintf(ti->out, "state = %d", ti->state);
997 ti->first = 0;
999 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
1000 if (ti->first)
1001 fprintf(ti->out, "true");
1002 fprintf(ti->out, ";\n");
1003 fprintf(ti->out, "\taction := ");
1004 for (int i = 0; i < n_out; ++i)
1005 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
1006 for (int i = n_out; i < ti->nvar; ++i)
1007 fprintf(ti->out, ", x%d' = 0", i);
1008 if (ti->lever)
1009 fprintf(ti->out, ", state' = %d", ti->to);
1010 fprintf(ti->out, ";\n");
1011 fprintf(ti->out, "};\n");
1013 ti->transition++;
1015 ti->state++;
1017 isl_basic_map_free(firing);
1019 return 0;
1022 /* Print one or more transitions based on the firing relation.
1023 * The way a firing relation is encoded depends on whether it is
1024 * a translation or not.
1026 static int print_aspic_transition(__isl_take isl_basic_map *firing, void *user)
1028 transition_info *ti = (transition_info *)user;
1030 ti->translation = is_translation(firing);
1031 if (!ti->translation)
1032 return print_aspic_non_translation(firing, ti);
1033 return print_translation(firing, ti);
1036 /* Print a transition from a bad final state to the canonical bad state.
1038 static int print_bad_aspic_transition(__isl_take isl_map *firing, void *user)
1040 transition_info *ti = (transition_info *)user;
1041 const char *name;
1043 name = isl_map_get_tuple_name(firing, isl_dim_in);
1044 ti->from = atoi(name + 1);
1046 isl_map_foreach_basic_map(firing, &print_aspic_transition, user);
1048 isl_map_free(firing);
1050 return 0;
1053 /* Construct an Aspic model that tests whether the bad final states
1054 * are unreachable. Since Aspic only supports convex "bad" regions,
1055 * we add an extra state with transitions from all instances of states
1056 * that are bad. The bad region is then simply any instance of that
1057 * extra state.
1059 static int check_equivalence_aspic(isl_ctx *ctx,
1060 dependence_graph *dg1, dependence_graph *dg2)
1062 int root;
1063 MSA msa;
1064 FILE *out = stdout;
1065 unsigned nparam;
1066 transition_info ti;
1067 isl_space *dim;
1068 isl_union_set *init;
1069 isl_union_set *bad;
1070 isl_union_map *bad_map;
1072 if (check_input_arrays(dg1, dg2))
1073 return -1;
1075 root = msa.add(dg1->out, dg2->out);
1077 ti.nvar = 0;
1078 ti.state = msa.state.size();
1079 ti.lever = 0;
1081 compute_max_var(msa, &ti);
1082 init = compute_equal_output(dg1, dg2);
1083 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
1084 compute_good_states(ctx, msa));
1085 dim = isl_union_set_get_space(init);
1086 nparam = isl_space_dim(dim, isl_dim_param);
1088 fprintf(out, "model m1 {\n");
1090 fprintf(out, "var");
1091 for (int i = 0; i < ti.nvar; ++i)
1092 fprintf(out, "%s x%d", i ? "," : "", i);
1093 for (int i = 0; i < nparam; ++i)
1094 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
1095 fprintf(out, ";\n");
1097 fprintf(out, "states");
1098 for (int i = 0; i < ti.state + 1; ++i)
1099 fprintf(out, "%s s%d", i ? "," : "", i);
1100 fprintf(out, ";\n");
1102 ti.out = out;
1103 ti.transition = 0;
1104 ti.state = msa.state.size();
1105 for (int i = 0; i < msa.transition.size(); ++i) {
1106 int f = msa.transition[i].second;
1107 ti.from = msa.transition[i].first.first;
1108 ti.to = msa.transition[i].first.second;
1109 isl_map_foreach_basic_map(msa.firing[f],
1110 &print_aspic_transition, &ti);
1113 bad_map = isl_union_map_from_domain_and_range(bad,
1114 isl_union_set_from_set(isl_set_universe(dim)));
1115 ti.to = ti.state;
1116 isl_union_map_foreach_map(bad_map, &print_bad_aspic_transition, &ti);
1118 fprintf(out, "}\n");
1120 fprintf(out, "strategy s1 {\n");
1121 print_region(out, "init", init);
1122 fprintf(out, "Region bad := { state = s%d };\n", ti.state);
1123 fprintf(out, "}\n");
1125 isl_union_set_free(init);
1126 isl_union_map_free(bad_map);
1128 return 0;
1131 int main(int argc, char *argv[])
1133 isl_ctx *ctx;
1134 struct options *options = options_new_with_defaults();
1135 isl_set *context = NULL;
1136 dependence_graph *dg[2];
1137 int res = 1;
1139 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
1141 ctx = isl_ctx_alloc_with_options(&options_args, options);
1142 if (!ctx) {
1143 fprintf(stderr, "Unable to allocate ctx\n");
1144 return -1;
1147 if (options->context)
1148 context = isl_set_read_from_str(ctx, options->context);
1150 pdg::PDG *pdg[2];
1151 unsigned out_dim = 0;
1152 for (int i = 0; i < 2; ++i) {
1153 FILE *in;
1154 in = fopen(options->program[i], "r");
1155 if (!in) {
1156 fprintf(stderr, "Unable to open %s\n", options->program[i]);
1157 return -1;
1159 pdg[i] = yaml::Load<pdg::PDG>(in, ctx);
1160 fclose(in);
1161 if (!pdg[i]) {
1162 fprintf(stderr, "Unable to read %s\n", options->program[i]);
1163 return -1;
1165 out_dim = update_out_dim(pdg[i], out_dim);
1167 if (context &&
1168 pdg[i]->params.size() != isl_set_dim(context, isl_dim_param)) {
1169 fprintf(stderr,
1170 "Parameter dimension mismatch; context ignored\n");
1171 isl_set_free(context);
1172 context = NULL;
1176 for (int i = 0; i < 2; ++i) {
1177 dg[i] = pdg_to_dg(pdg[i], out_dim, isl_set_copy(context));
1178 pdg[i]->free();
1179 delete pdg[i];
1182 if (options->reachability == REACH_TC)
1183 res = check_equivalence_tc(ctx, dg[0], dg[1]);
1184 else if (options->reachability == REACH_FAST)
1185 res = check_equivalence_fast(ctx, dg[0], dg[1]);
1186 else if (options->reachability == REACH_LEVER)
1187 res = check_equivalence_lever(ctx, dg[0], dg[1]);
1188 else if (options->reachability == REACH_ASPIC)
1189 res = check_equivalence_aspic(ctx, dg[0], dg[1]);
1191 for (int i = 0; i < 2; ++i)
1192 delete dg[i];
1193 isl_set_free(context);
1194 isl_ctx_free(ctx);
1196 return res;