update isl for change in lexicographic optimization
[isa.git] / eqv3.cc
blobf48352fac5eab770eeb65a8a3fcddbc1d6b59745
1 #include <isl/ctx.h>
2 #include <isl/val.h>
3 #include <isl/space.h>
4 #include <isl/constraint.h>
5 #include <isl/map.h>
6 #include <isl/set.h>
7 #include <isl/union_map.h>
8 #include <isl/union_set.h>
9 #include <isl/printer.h>
10 #include <isa/pdg.h>
11 #include "dependence_graph.h"
12 #include "eqv3_options.h"
13 #include "msa.h"
15 /* Check the equivalence of two programs, given as dependence graphs,
16 * by computing what is essentially the cross product of the two
17 * dependence graph and checking that we can only reach states corresponding
18 * to equal inputs from the state corresponding to equal output.
19 * We consider various ways of performing the reachability analysis:
20 * applying the transitive closure to the initial state, FAST, Lever and Aspic.
21 * In all but the first case, we simply print a model that can be used
22 * as input to the corresponding tool.
25 static unsigned update_out_dim(pdg::PDG *pdg, unsigned out_dim)
27 for (int i = 0; i < pdg->arrays.size(); ++i) {
28 pdg::array *array = pdg->arrays[i];
29 if (array->type != pdg::array::output)
30 continue;
31 if (array->dims.size() + 1 > out_dim)
32 out_dim = array->dims.size() + 1;
35 return out_dim;
38 /* The input arrays of the two programs are supposed to be the same,
39 * so they should at least have the same dimension. Make sure
40 * this is true, because we depend on it later on.
42 static int check_input_arrays(dependence_graph *dg1, dependence_graph *dg2)
44 for (int i = 0; i < dg1->input_computations.size(); ++i)
45 for (int j = 0; j < dg2->input_computations.size(); ++j) {
46 if (strcmp(dg1->input_computations[i]->operation,
47 dg2->input_computations[j]->operation))
48 continue;
49 if (dg1->input_computations[i]->dim ==
50 dg2->input_computations[j]->dim)
51 continue;
52 fprintf(stderr,
53 "input arrays \"%s\" do not have the same dimension\n",
54 dg1->input_computations[i]->operation);
55 return -1;
58 return 0;
61 /* Compute a map between the domains of the output nodes of
62 * the dependence graphs that expresses that array1 in dg1
63 * and array2 in dg2 are equal.
64 * This map is then returned as a set.
66 static __isl_give isl_set *compute_equal_array(
67 dependence_graph *dg1, dependence_graph *dg2, int array1, int array2)
69 unsigned n_in;
70 const char *array_name = dg1->output_arrays[array1];
71 isl_set *out1 = isl_set_copy(dg1->out->domain);
72 isl_set *out2 = isl_set_copy(dg2->out->domain);
73 unsigned dim1 = isl_set_n_dim(out1);
74 unsigned dim2 = isl_set_n_dim(out2);
76 out1 = isl_set_fix_dim_si(out1, dim1 - 1, array1);
77 out2 = isl_set_fix_dim_si(out2, dim2 - 1, array2);
78 out1 = isl_set_remove_dims(out1, isl_dim_set, dim1 - 1, 1);
79 out2 = isl_set_remove_dims(out2, isl_dim_set, dim2 - 1, 1);
80 int equal = isl_set_is_equal(out1, out2);
81 isl_set_free(out1);
82 isl_set_free(out2);
83 assert(equal >= 0);
84 if (!equal) {
85 fprintf(stderr, "different output domains for array %s\n",
86 array_name);
87 return NULL;
90 out1 = isl_set_copy(dg1->out->domain);
91 out2 = isl_set_copy(dg2->out->domain);
92 out1 = isl_set_fix_dim_si(out1, dim1 - 1, array1);
93 out2 = isl_set_fix_dim_si(out2, dim2 - 1, array2);
94 out1 = isl_set_coalesce(out1);
95 out2 = isl_set_coalesce(out2);
96 isl_space *dim = isl_space_map_from_set(isl_set_get_space(out2));
97 isl_map *id = isl_map_identity(dim);
98 n_in = isl_map_dim(id, isl_dim_in);
99 id = isl_map_remove_dims(id, isl_dim_in, n_in - 1, 1);
100 id = isl_map_apply_domain(id, isl_map_copy(id));
101 id = isl_map_intersect_domain(id, out1);
102 id = isl_map_intersect_range(id, out2);
104 return isl_map_wrap(id);
107 /* Compute a map between the domains of the output nodes of
108 * the dependence graphs that expresses that arrays with
109 * the same names in both graphs are equal.
110 * The map is returned as a set.
112 static __isl_give isl_union_set *compute_equal_output(
113 dependence_graph *dg1, dependence_graph *dg2)
115 int i1 = 0, i2 = 0;
116 isl_space *dim;
117 isl_map *map;
118 isl_set *equal_output;
119 unsigned d;
121 dim = isl_set_get_space(dg1->out->domain);
122 d = isl_space_dim(dim, isl_dim_set);
123 map = isl_map_empty(isl_space_map_from_set(dim));
124 equal_output = isl_map_wrap(map);
126 while (i1 < dg1->output_arrays.size() || i2 < dg2->output_arrays.size()) {
127 int cmp;
128 cmp = i1 == dg1->output_arrays.size() ? 1 :
129 i2 == dg2->output_arrays.size() ? -1 :
130 strcmp(dg1->output_arrays[i1], dg2->output_arrays[i2]);
131 if (cmp < 0) {
132 fprintf(stderr,
133 "Array \"%s\" only in program 1; not checked\n",
134 dg1->output_arrays[i1]);
135 ++i1;
136 } else if (cmp > 0) {
137 fprintf(stderr,
138 "Array \"%s\" only in program 2; not checked\n",
139 dg2->output_arrays[i2]);
140 ++i2;
141 } else {
142 isl_set *eq_array;
143 eq_array = compute_equal_array(dg1, dg2, i1, i2);
144 equal_output = isl_set_union(equal_output, eq_array);
145 ++i1;
146 ++i2;
150 equal_output = isl_set_set_tuple_name(equal_output, "s0");
152 return isl_union_set_from_set(equal_output);
155 /* For a given state "s" of the MSA, return a set describing all
156 * instances of that state.
158 static __isl_give isl_union_set *state_domain(MSA &msa, int s)
160 char name[50];
161 isl_space *dim1, *dim2;
162 isl_set *set;
164 snprintf(name, sizeof(name), "s%d", s);
165 dim1 = isl_set_get_space(msa.state[s].first->domain);
166 dim2 = isl_set_get_space(msa.state[s].second->domain);
167 dim1 = isl_space_from_domain(dim1);
168 dim2 = isl_space_from_range(dim2);
169 dim1 = isl_space_join(dim1, dim2);
170 dim1 = isl_space_wrap(dim1);
171 dim1 = isl_space_set_tuple_name(dim1, isl_dim_set, name);
172 set = isl_set_universe(dim1);
174 return isl_union_set_from_set(set);
177 /* For a given state corresponding to a pair of equal input arrays,
178 * return a set describing pairs of equal instances of those arrays.
180 static __isl_give isl_union_set *equal_input_domain(MSA &msa, int s)
182 char name[50];
183 isl_space *dim;
184 isl_set *set;
186 snprintf(name, sizeof(name), "s%d", s);
187 dim = isl_set_get_space(msa.state[s].first->domain);
188 set = isl_map_wrap(isl_map_identity(isl_space_map_from_set(dim)));
189 set = isl_set_set_tuple_name(set, name);
191 return isl_union_set_from_set(set);
194 /* Return all instances of final states, i.e., pairs of equal input
195 * arrays, pairs of equal constant functions and all bad combinations.
197 static __isl_give isl_union_set *compute_final_states(isl_ctx *ctx, MSA &msa)
199 isl_space *dim;
200 isl_union_set *final;
202 dim = isl_space_set_alloc(ctx, 0, 0);
203 final = isl_union_set_empty(dim);
205 for (int i = 0; i < msa.input_state.size(); ++i) {
206 int s = msa.input_state[i];
207 final = isl_union_set_union(final, state_domain(msa, s));
210 for (int i = 0; i < msa.fail_state.size(); ++i) {
211 int s = msa.fail_state[i];
212 final = isl_union_set_union(final, state_domain(msa, s));
215 for (int i = 0; i < msa.cst_state.size(); ++i) {
216 int s = msa.cst_state[i];
217 final = isl_union_set_union(final, state_domain(msa, s));
220 return final;
223 /* Return all instances of good final states, i.e., equal instances
224 * of equal input arrays and pairs of equal constant functions.
226 static __isl_give isl_union_set *compute_good_states(isl_ctx *ctx, MSA &msa)
228 isl_space *dim;
229 isl_union_set *final;
231 dim = isl_space_set_alloc(ctx, 0, 0);
232 final = isl_union_set_empty(dim);
234 for (int i = 0; i < msa.input_state.size(); ++i) {
235 int s = msa.input_state[i];
236 final = isl_union_set_union(final, equal_input_domain(msa, s));
239 for (int i = 0; i < msa.cst_state.size(); ++i) {
240 int s = msa.cst_state[i];
241 final = isl_union_set_union(final, state_domain(msa, s));
244 return final;
247 /* Perform the reachability analysis by first computing the
248 * transitive closure and applying it to a set representing
249 * equal output. We then check if all reachable final states
250 * are good final states.
251 * If we find a bad state that may be reachable, we also
252 * apply the inverse of the transitive closure to that state
253 * to find out where it may have come from.
255 static int check_equivalence_tc(isl_ctx *ctx,
256 dependence_graph *dg1, dependence_graph *dg2)
258 int root;
259 MSA msa;
260 isl_space *dim;
261 isl_union_map *transition;
262 isl_union_set *equal_output;
263 isl_union_set *test;
264 isl_union_set *final;
265 isl_union_set *good;
266 isl_printer *p;
267 int ok = 1;
269 if (check_input_arrays(dg1, dg2))
270 return -1;
272 p = isl_printer_to_file(ctx, stdout);
274 root = msa.add(dg1->out, dg2->out);
276 dim = isl_space_alloc(ctx, 0, 0, 0);
277 transition = isl_union_map_empty(dim);
278 for (int i = 0; i < msa.firing.size(); ++i) {
279 isl_map *map = isl_map_copy(msa.firing[i]);
280 transition = isl_union_map_union(transition,
281 isl_union_map_from_map(map));
284 transition = isl_union_map_transitive_closure(transition, NULL);
286 equal_output = compute_equal_output(dg1, dg2);
287 test = isl_union_set_apply(isl_union_set_copy(equal_output),
288 isl_union_map_copy(transition));
290 final = compute_final_states(ctx, msa);
291 test = isl_union_set_intersect(test, final);
293 good = compute_good_states(ctx, msa);
294 test = isl_union_set_subtract(test, good);
296 if (!isl_union_set_is_empty(test)) {
297 ok = 0;
298 transition = isl_union_map_reverse(transition);
299 test = isl_union_set_apply(test, transition);
300 test = isl_union_set_coalesce(test);
301 test = isl_union_set_intersect(test, equal_output);
303 isl_printer_print_str(p, "NOT equivalent");
304 isl_printer_end_line(p);
305 isl_printer_print_union_set(p, test);
306 isl_printer_end_line(p);
307 } else {
308 isl_union_set_free(equal_output);
309 isl_union_map_free(transition);
312 isl_union_set_free(test);
314 if (!ok)
315 isl_printer_print_str(p, "NOT ");
316 isl_printer_print_str(p, "OK");
317 isl_printer_end_line(p);
319 isl_printer_free(p);
321 return 0;
324 struct transition_info {
325 FILE *out;
326 int nvar; /* Total number of variables */
327 int state; /* Next extra state/total number of states */
328 int transition; /* Current transition */
329 int from; /* From state of current firing relation */
330 int to; /* To state of current firing relation */
332 int first; /* Are we printing the first expression? */
333 int last_out; /* Last variable that was explicitly assigned */
334 int translation; /* Current firing relation is a translation */
336 int lever; /* Are we printing Lever output? */
339 static int is_translation(__isl_keep isl_basic_map *bmap)
341 unsigned n_out = isl_basic_map_dim(bmap, isl_dim_out);
342 unsigned n_div = isl_basic_map_dim(bmap, isl_dim_div);
344 if (n_div > 0)
345 return 0;
347 for (int i = 0; i < n_out; ++i) {
348 isl_constraint *c = NULL;
349 if (!isl_basic_map_has_defining_equality(bmap,
350 isl_dim_out, i, &c))
351 return 0;
352 isl_constraint_free(c);
355 return 1;
358 /* Update the maximal number of variables and the total number of states
359 * based on the given firing relation.
360 * If the firing relation is a translation, then we don't need anything extra.
361 * Otherwise, we need an extra state and
362 * to encode the firing relation we need at most as many variables
363 * as are needed to describe the relation. Note that this does
364 * not include the parameters as they are treated separately.
366 static isl_stat check_transition(__isl_take isl_basic_map *firing, void *user)
368 transition_info *ti = (transition_info *)user;
370 if (!is_translation(firing)) {
371 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
372 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
373 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
374 unsigned total = n_in + n_out + n_div;
376 if (total > ti->nvar)
377 ti->nvar = total;
378 ti->state++;
381 isl_basic_map_free(firing);
382 return isl_stat_ok;
385 /* Compute the maximal number of variables needed as well as the
386 * total number of states.
387 * The number of variables needs to be at least the maximal
388 * number of iterators needed to describe a state in the MSA.
389 * For transitions that are not translations, we may need more.
391 static void compute_max_var(MSA &msa, struct transition_info *ti)
393 for (int i = 0; i < msa.state.size(); ++i) {
394 computation *c1 = msa.state[i].first;
395 computation *c2 = msa.state[i].second;
396 unsigned d1 = isl_set_dim(c1->domain, isl_dim_set);
397 unsigned d2 = isl_set_dim(c2->domain, isl_dim_set);
399 if (d1 + d2 > ti->nvar)
400 ti->nvar = d1 + d2;
402 for (int i = 0; i < msa.transition.size(); ++i) {
403 int f = msa.transition[i].second;
404 isl_map_foreach_basic_map(msa.firing[f],
405 &check_transition, ti);
409 /* Print the constraint as a linear combination.
410 * Because Lever doesn't like linear expressions that start with
411 * "-", we always start off with a non-negative constant (possibly
412 * zero).
413 * "s" is the sign with which to multiply the linear combination.
414 * For constraints that end up in the action, we shouldn't print
415 * the coefficient of the output variable being defined. In this
416 * case "no_output" is set to 1.
418 static __isl_give isl_printer *print_linear(__isl_take isl_printer *p,
419 __isl_keep isl_constraint *c, int s, int no_output)
421 isl_val *v;
422 int nparam;
423 int n_in, n_out, n_div;
425 nparam = isl_constraint_dim(c, isl_dim_param);
426 n_in = isl_constraint_dim(c, isl_dim_in);
427 n_out = isl_constraint_dim(c, isl_dim_out);
428 n_div = isl_constraint_dim(c, isl_dim_div);
430 v = isl_constraint_get_constant_val(c);
431 if (s < 0)
432 v = isl_val_neg(v);
433 if (isl_val_is_neg(v))
434 p = isl_printer_print_str(p, "0");
435 p = isl_printer_print_val(p, v);
436 isl_val_free(v);
437 for (int i = 0; i < nparam; ++i) {
438 const char *name;
439 v = isl_constraint_get_coefficient_val(c, isl_dim_param, i);
440 if (isl_val_is_zero(v)) {
441 isl_val_free(v);
442 continue;
444 if (s < 0)
445 v = isl_val_neg(v);
446 if (isl_val_is_pos(v))
447 p = isl_printer_print_str(p, "+");
448 p = isl_printer_print_val(p, v);
449 name = isl_constraint_get_dim_name(c, isl_dim_param, i);
450 p = isl_printer_print_str(p, "*");
451 p = isl_printer_print_str(p, name);
452 isl_val_free(v);
454 for (int i = 0; i < n_in; ++i) {
455 v = isl_constraint_get_coefficient_val(c, isl_dim_in, i);
456 if (isl_val_is_zero(v)) {
457 isl_val_free(v);
458 continue;
460 if (s < 0)
461 v = isl_val_neg(v);
462 if (isl_val_is_pos(v))
463 p = isl_printer_print_str(p, "+");
464 p = isl_printer_print_val(p, v);
465 p = isl_printer_print_str(p, "*x");
466 p = isl_printer_print_int(p, i);
467 isl_val_free(v);
469 if (!no_output) {
470 for (int i = 0; i < n_out; ++i) {
471 v = isl_constraint_get_coefficient_val(c,
472 isl_dim_out, i);
473 if (isl_val_is_zero(v)) {
474 isl_val_free(v);
475 continue;
477 if (s < 0)
478 v = isl_val_neg(v);
479 if (isl_val_is_pos(v))
480 p = isl_printer_print_str(p, "+");
481 p = isl_printer_print_val(p, v);
482 p = isl_printer_print_str(p, "*x");
483 p = isl_printer_print_int(p, n_in + i);
484 isl_val_free(v);
486 for (int i = 0; i < n_div; ++i) {
487 v = isl_constraint_get_coefficient_val(c,
488 isl_dim_div, i);
489 if (isl_val_is_zero(v)) {
490 isl_val_free(v);
491 continue;
493 if (s < 0)
494 v = isl_val_neg(v);
495 if (isl_val_is_pos(v))
496 p = isl_printer_print_str(p, "+");
497 p = isl_printer_print_val(p, v);
498 p = isl_printer_print_str(p, "*x");
499 p = isl_printer_print_int(p, n_in + n_out + i);
500 isl_val_free(v);
504 return p;
507 /* Print the constraints that go into the guard.
508 * If we are dealing with a translation, then we shouldn't print any
509 * constraints involving the output variables.
510 * Those constraint should be equalities defining the translation
511 * and will be printed in the action.
513 static isl_stat print_guard_constraint(__isl_take isl_constraint *c, void *user)
515 transition_info *ti = (transition_info *)user;
516 int n_out;
517 int n_in;
518 isl_ctx *ctx;
519 isl_val *v;
520 isl_printer *p;
522 n_out = isl_constraint_dim(c, isl_dim_out);
524 if (ti->translation) {
525 for (int i = 0; i < n_out; ++i) {
526 v = isl_constraint_get_coefficient_val(c,
527 isl_dim_out, i);
528 if (!isl_val_is_zero(v)) {
529 assert(isl_constraint_is_equality(c));
530 isl_constraint_free(c);
531 isl_val_free(v);
532 return isl_stat_ok;
537 ctx = isl_constraint_get_ctx(c);
538 p = isl_printer_to_file(ctx, ti->out);
539 if (!ti->first)
540 p = isl_printer_print_str(p, " && ");
541 p = print_linear(p, c, 1, 0);
542 if (isl_constraint_is_equality(c))
543 p = isl_printer_print_str(p, " = 0");
544 else
545 p = isl_printer_print_str(p, " >= 0");
547 ti->first = 0;
549 isl_printer_free(p);
550 isl_constraint_free(c);
551 isl_val_free(v);
553 return isl_stat_ok;
556 /* Print the constraints that go into the action.
557 * That is, equality constraints that define one of the output variables.
559 static isl_stat print_action_constraint(__isl_take isl_constraint *c,
560 void *user)
562 transition_info *ti = (transition_info *)user;
563 int n_in;
564 int n_out;
565 isl_ctx *ctx;
566 isl_printer *p;
567 int out;
568 int s;
570 if (!isl_constraint_is_equality(c)) {
571 isl_constraint_free(c);
572 return isl_stat_ok;
575 n_in = isl_constraint_dim(c, isl_dim_in);
576 n_out = isl_constraint_dim(c, isl_dim_out);
578 for (out = 0; out < n_out; ++out) {
579 if (isl_constraint_involves_dims(c, isl_dim_out, out, 1))
580 break;
583 if (out >= n_out) {
584 isl_constraint_free(c);
585 return isl_stat_ok;
588 if (out > ti->last_out)
589 ti->last_out = out;
591 if (isl_constraint_is_lower_bound(c, isl_dim_out, out))
592 s = -1;
593 else
594 s = 1;
596 ctx = isl_constraint_get_ctx(c);
597 p = isl_printer_to_file(ctx, ti->out);
598 if (!ti->first)
599 p = isl_printer_print_str(p, ", ");
601 p = isl_printer_print_str(p, "x");
602 p = isl_printer_print_int(p, out);
603 p = isl_printer_print_str(p, "' = ");
604 p = print_linear(p, c, s, 1);
606 ti->first = 0;
608 isl_constraint_free(c);
609 return isl_stat_ok;
612 /* A firing relation that is not a translation is encoded using
613 * several transitions. We first allow an unconditional transition
614 * to an extra state. In this state, we allow arbitrary increments
615 * on the extra variables that will correspond to the output variables
616 * and the existentially quantified variables of the relation. This allows
617 * those variables to attain any value, since they are initially set
618 * to zero and FAST and Lever only support non-negative variables.
619 * Finally, we add a transition from the extra
620 * state to the original target state with a guard that corresponds
621 * to the firing relation and an action that assigns the output
622 * variables to the main variables.
624 static isl_stat print_non_translation(__isl_take isl_basic_map *firing,
625 transition_info *ti)
627 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
628 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
629 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
631 fprintf(ti->out, "transition t%d := {\n", ti->transition);
632 if (!ti->lever) {
633 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
634 fprintf(ti->out, "\tto := s%d;\n", ti->state);
635 fprintf(ti->out, "\tguard := true;\n");
636 fprintf(ti->out, "\taction := x0' = x0");
637 } else {
638 fprintf(ti->out, "\tguard := state = %d;\n", ti->from);
639 fprintf(ti->out, "\taction := state' = %d", ti->state);
641 for (int i = 0; i < n_out + n_div; ++i)
642 fprintf(ti->out, ", x%d' = 0", n_in + i);
643 fprintf(ti->out, ";\n");
644 fprintf(ti->out, "};\n");
646 ti->transition++;
648 for (int i = 0; i < n_out + n_div; ++i) {
649 fprintf(ti->out, "transition t%d := {\n", ti->transition);
650 if (!ti->lever) {
651 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
652 fprintf(ti->out, "\tto := s%d;\n", ti->state);
653 fprintf(ti->out, "\tguard := true;\n");
654 } else
655 fprintf(ti->out, "\tguard := state = %d;\n", ti->state);
656 fprintf(ti->out, "\taction := x%d' = x%d + 1;\n",
657 n_in + i, n_in + i);
658 fprintf(ti->out, "};\n");
659 ti->transition++;
662 fprintf(ti->out, "transition t%d := {\n", ti->transition);
663 if (!ti->lever) {
664 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
665 fprintf(ti->out, "\tto := s%d;\n", ti->to);
667 fprintf(ti->out, "\tguard := ");
668 if (!ti->lever)
669 ti->first = 1;
670 else {
671 fprintf(ti->out, "state = %d", ti->state);
672 ti->first = 0;
674 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
675 if (ti->first)
676 fprintf(ti->out, "true");
677 fprintf(ti->out, ";\n");
678 fprintf(ti->out, "\taction := ");
679 for (int i = 0; i < n_out; ++i)
680 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
681 for (int i = n_out; i < ti->nvar; ++i)
682 fprintf(ti->out, ", x%d' = 0", i);
683 if (ti->lever)
684 fprintf(ti->out, ", state' = %d", ti->to);
685 fprintf(ti->out, ";\n");
686 fprintf(ti->out, "};\n");
688 ti->transition++;
690 ti->state++;
692 isl_basic_map_free(firing);
694 return isl_stat_ok;
697 /* A firing relation that is a translation can be encoded directly
698 * into a transition. We simply put the translation in the action
699 * and the remaining constraints in the guard.
701 static isl_stat print_translation(__isl_take isl_basic_map *firing,
702 transition_info *ti)
704 fprintf(ti->out, "transition t%d := {\n", ti->transition);
705 if (!ti->lever) {
706 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
707 fprintf(ti->out, "\tto := s%d;\n", ti->to);
710 fprintf(ti->out, "\tguard := ");
711 if (!ti->lever)
712 ti->first = 1;
713 else {
714 fprintf(ti->out, "state = %d", ti->from);
715 ti->first = 0;
717 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
718 if (ti->first)
719 fprintf(ti->out, "true");
720 fprintf(ti->out, ";\n");
722 fprintf(ti->out, "\taction := ");
723 if (!ti->lever)
724 ti->first = 1;
725 else {
726 fprintf(ti->out, "state' = %d", ti->to);
727 ti->first = 0;
729 ti->last_out = -1;
730 isl_basic_map_foreach_constraint(firing, print_action_constraint, ti);
731 for (int i = ti->last_out + 1; i < ti->nvar; ++i)
732 fprintf(ti->out, "%sx%d' = 0",
733 (i || !ti->first) ? ", " : "", i);
734 fprintf(ti->out, ";\n");
736 fprintf(ti->out, "};\n");
738 ti->transition++;
740 isl_basic_map_free(firing);
742 return isl_stat_ok;
745 /* Print one or more transitions based on the firing relation.
746 * The way a firing relation is encoded depends on whether it is
747 * a translation or not.
749 static isl_stat print_transition(__isl_take isl_basic_map *firing, void *user)
751 transition_info *ti = (transition_info *)user;
753 ti->translation = is_translation(firing);
754 if (!ti->translation)
755 return print_non_translation(firing, ti);
756 return print_translation(firing, ti);
759 struct region_info {
760 FILE *out;
761 int name_offset;
763 int first;
766 static isl_stat print_region_constraint(__isl_take isl_constraint *c,
767 void *user)
769 region_info *ri = (region_info *)user;
770 isl_ctx *ctx;
771 isl_printer *p;
773 ctx = isl_constraint_get_ctx(c);
774 p = isl_printer_to_file(ctx, ri->out);
775 p = isl_printer_print_str(p, " && ");
777 p = print_linear(p, c, 1, 0);
778 if (isl_constraint_is_equality(c))
779 p = isl_printer_print_str(p, " = 0");
780 else
781 p = isl_printer_print_str(p, " >= 0");
783 isl_printer_free(p);
784 isl_constraint_free(c);
785 return isl_stat_ok;
788 /* Print a convex part of a region.
789 * In FAST, states are designated by labels, while in Lever, they
790 * are encoded as integer, so we have to drop the first letter ("s")
791 * of the state name.
793 static isl_stat print_region_basic_set(__isl_take isl_basic_set *bset,
794 void *user)
796 region_info *ri = (region_info *)user;
798 if (!ri->first)
799 fprintf(ri->out, " || ");
801 fprintf(ri->out, "(");
802 fprintf(ri->out, "state = %s",
803 isl_basic_set_get_tuple_name(bset) + ri->name_offset);
805 isl_basic_set_foreach_constraint(bset, &print_region_constraint, user);
807 fprintf(ri->out, ")");
809 ri->first = 0;
811 isl_basic_set_free(bset);
812 return isl_stat_ok;
815 static isl_stat print_region_set(__isl_take isl_set *set, void *user)
817 isl_set_foreach_basic_set(set, &print_region_basic_set, user);
819 isl_set_free(set);
820 return isl_stat_ok;
823 static void print_region(FILE *out, const char *name,
824 __isl_keep isl_union_set *region)
826 region_info ri;
828 ri.out = out;
829 ri.first = 1;
830 ri.name_offset = 0;
832 fprintf(out, "Region %s := { ", name);
833 isl_union_set_foreach_set(region, &print_region_set, &ri);
834 fprintf(out, " };\n");
837 /* Construct a FAST model that tests whether all reachable
838 * final states are a subset of the good final states.
840 static int check_equivalence_fast(isl_ctx *ctx,
841 dependence_graph *dg1, dependence_graph *dg2)
843 int root;
844 MSA msa;
845 FILE *out = stdout;
846 unsigned nparam;
847 transition_info ti;
848 isl_space *dim;
849 isl_union_set *init;
850 isl_union_set *final;
851 isl_union_set *good;
853 if (check_input_arrays(dg1, dg2))
854 return -1;
856 root = msa.add(dg1->out, dg2->out);
858 ti.nvar = 0;
859 ti.state = msa.state.size();
860 ti.lever = 0;
862 compute_max_var(msa, &ti);
863 init = compute_equal_output(dg1, dg2);
864 final = compute_final_states(ctx, msa);
865 good = compute_good_states(ctx, msa);
866 dim = isl_union_set_get_space(init);
867 nparam = isl_space_dim(dim, isl_dim_param);
869 fprintf(out, "model m1 {\n");
871 fprintf(out, "var");
872 for (int i = 0; i < ti.nvar; ++i)
873 fprintf(out, "%s x%d", i ? "," : "", i);
874 for (int i = 0; i < nparam; ++i)
875 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
876 fprintf(out, ";\n");
878 fprintf(out, "states");
879 for (int i = 0; i < ti.state; ++i)
880 fprintf(out, "%s s%d", i ? "," : "", i);
881 fprintf(out, ";\n");
883 ti.out = out;
884 ti.transition = 0;
885 ti.state = msa.state.size();
886 for (int i = 0; i < msa.transition.size(); ++i) {
887 int f = msa.transition[i].second;
888 ti.from = msa.transition[i].first.first;
889 ti.to = msa.transition[i].first.second;
890 isl_map_foreach_basic_map(msa.firing[f],
891 &print_transition, &ti);
894 fprintf(out, "}\n");
896 fprintf(out, "strategy s1 {\n");
897 print_region(out, "init", init);
898 print_region(out, "final", final);
899 print_region(out, "good", good);
900 fprintf(out, "Transitions t := {");
901 for (int i = 0; i < ti.transition; ++i)
902 fprintf(out, "%s t%d", i ? "," : "", i);
903 fprintf(out, " };\n");
904 fprintf(out, "Region reach := post*(init, t);\n");
905 fprintf(out, "if (subSet(reach && final, good))\n");
906 fprintf(out, "\tthen print(\"OK\\n\");\n");
907 fprintf(out, "\telse print(\"NOT OK\\n\");\n");
908 fprintf(out, "endif\n");
909 fprintf(out, "}\n");
911 isl_space_free(dim);
912 isl_union_set_free(init);
913 isl_union_set_free(final);
914 isl_union_set_free(good);
916 return 0;
919 static void print_label(FILE *out, const char *name,
920 __isl_keep isl_union_set *region)
922 region_info ri;
924 ri.out = out;
925 ri.first = 1;
926 ri.name_offset = 1;
928 fprintf(out, "label %s := { ", name);
929 isl_union_set_foreach_set(region, &print_region_set, &ri);
930 fprintf(out, " };\n");
933 /* Construct a Lever model that tests whether the bad final states
934 * are unreachable.
936 static int check_equivalence_lever(isl_ctx *ctx,
937 dependence_graph *dg1, dependence_graph *dg2)
939 int root;
940 MSA msa;
941 FILE *out = stdout;
942 unsigned nparam;
943 transition_info ti;
944 isl_space *dim;
945 isl_union_set *init;
946 isl_union_set *bad;
948 if (check_input_arrays(dg1, dg2))
949 return -1;
951 root = msa.add(dg1->out, dg2->out);
953 ti.nvar = 0;
954 ti.state = msa.state.size();
955 ti.lever = 1;
957 compute_max_var(msa, &ti);
958 init = compute_equal_output(dg1, dg2);
959 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
960 compute_good_states(ctx, msa));
961 dim = isl_union_set_get_space(init);
962 nparam = isl_space_dim(dim, isl_dim_param);
964 fprintf(out, "var state");
965 for (int i = 0; i < ti.nvar; ++i)
966 fprintf(out, ", x%d", i);
967 for (int i = 0; i < nparam; ++i)
968 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
969 fprintf(out, ";\n");
971 fprintf(out, "ctl_spec {\n");
972 print_label(out, "bad", bad);
973 fprintf(out, "}\n");
975 fprintf(out, "system {\n");
976 print_label(out, "init", init);
978 ti.out = out;
979 ti.transition = 0;
980 ti.state = msa.state.size();
981 for (int i = 0; i < msa.transition.size(); ++i) {
982 int f = msa.transition[i].second;
983 ti.from = msa.transition[i].first.first;
984 ti.to = msa.transition[i].first.second;
985 isl_map_foreach_basic_map(msa.firing[f],
986 &print_transition, &ti);
989 fprintf(out, "}\n");
991 isl_space_free(dim);
992 isl_union_set_free(init);
993 isl_union_set_free(bad);
995 return 0;
998 /* A firing relation that is not a translation is encoded in Aspic using
999 * using two transitions passing through an extra state.
1000 * The first transition assigns arbitrary values to the extra variables
1001 * that will correspond to the output variables and the existentially
1002 * quantified variables of the relation.
1003 * The second transition, from the extra
1004 * state to the original target state, has a guard that corresponds
1005 * to the firing relation and an action that assigns the output
1006 * variables to the main variables.
1008 static isl_stat print_aspic_non_translation(__isl_take isl_basic_map *firing,
1009 transition_info *ti)
1011 unsigned n_in = isl_basic_map_dim(firing, isl_dim_in);
1012 unsigned n_out = isl_basic_map_dim(firing, isl_dim_out);
1013 unsigned n_div = isl_basic_map_dim(firing, isl_dim_div);
1015 fprintf(ti->out, "transition t%d := {\n", ti->transition);
1016 fprintf(ti->out, "\tfrom := s%d;\n", ti->from);
1017 fprintf(ti->out, "\tto := s%d;\n", ti->state);
1018 fprintf(ti->out, "\tguard := true;\n");
1019 fprintf(ti->out, "\taction := ");
1020 for (int i = 0; i < n_out + n_div; ++i)
1021 fprintf(ti->out, "%sx%d' = ?", i ? ", " : "", n_in + i);
1022 fprintf(ti->out, ";\n");
1024 fprintf(ti->out, "};\n");
1026 ti->transition++;
1028 fprintf(ti->out, "transition t%d := {\n", ti->transition);
1029 if (!ti->lever) {
1030 fprintf(ti->out, "\tfrom := s%d;\n", ti->state);
1031 fprintf(ti->out, "\tto := s%d;\n", ti->to);
1033 fprintf(ti->out, "\tguard := ");
1034 if (!ti->lever)
1035 ti->first = 1;
1036 else {
1037 fprintf(ti->out, "state = %d", ti->state);
1038 ti->first = 0;
1040 isl_basic_map_foreach_constraint(firing, print_guard_constraint, ti);
1041 if (ti->first)
1042 fprintf(ti->out, "true");
1043 fprintf(ti->out, ";\n");
1044 fprintf(ti->out, "\taction := ");
1045 for (int i = 0; i < n_out; ++i)
1046 fprintf(ti->out, "%sx%d' = x%d", i ? ", " : "", i, n_in + i);
1047 for (int i = n_out; i < ti->nvar; ++i)
1048 fprintf(ti->out, ", x%d' = 0", i);
1049 if (ti->lever)
1050 fprintf(ti->out, ", state' = %d", ti->to);
1051 fprintf(ti->out, ";\n");
1052 fprintf(ti->out, "};\n");
1054 ti->transition++;
1056 ti->state++;
1058 isl_basic_map_free(firing);
1060 return isl_stat_ok;
1063 /* Print one or more transitions based on the firing relation.
1064 * The way a firing relation is encoded depends on whether it is
1065 * a translation or not.
1067 static isl_stat print_aspic_transition(__isl_take isl_basic_map *firing,
1068 void *user)
1070 transition_info *ti = (transition_info *)user;
1072 ti->translation = is_translation(firing);
1073 if (!ti->translation)
1074 return print_aspic_non_translation(firing, ti);
1075 return print_translation(firing, ti);
1078 /* Print a transition from a bad final state to the canonical bad state.
1080 static isl_stat print_bad_aspic_transition(__isl_take isl_map *firing,
1081 void *user)
1083 transition_info *ti = (transition_info *)user;
1084 const char *name;
1086 name = isl_map_get_tuple_name(firing, isl_dim_in);
1087 ti->from = atoi(name + 1);
1089 isl_map_foreach_basic_map(firing, &print_aspic_transition, user);
1091 isl_map_free(firing);
1093 return isl_stat_ok;
1096 /* Construct an Aspic model that tests whether the bad final states
1097 * are unreachable. Since Aspic only supports convex "bad" regions,
1098 * we add an extra state with transitions from all instances of states
1099 * that are bad. The bad region is then simply any instance of that
1100 * extra state.
1102 static int check_equivalence_aspic(isl_ctx *ctx,
1103 dependence_graph *dg1, dependence_graph *dg2)
1105 int root;
1106 MSA msa;
1107 FILE *out = stdout;
1108 unsigned nparam;
1109 transition_info ti;
1110 isl_space *dim;
1111 isl_union_set *init;
1112 isl_union_set *bad;
1113 isl_union_map *bad_map;
1115 if (check_input_arrays(dg1, dg2))
1116 return -1;
1118 root = msa.add(dg1->out, dg2->out);
1120 ti.nvar = 0;
1121 ti.state = msa.state.size();
1122 ti.lever = 0;
1124 compute_max_var(msa, &ti);
1125 init = compute_equal_output(dg1, dg2);
1126 bad = isl_union_set_subtract(compute_final_states(ctx, msa),
1127 compute_good_states(ctx, msa));
1128 dim = isl_union_set_get_space(init);
1129 nparam = isl_space_dim(dim, isl_dim_param);
1131 fprintf(out, "model m1 {\n");
1133 fprintf(out, "var");
1134 for (int i = 0; i < ti.nvar; ++i)
1135 fprintf(out, "%s x%d", i ? "," : "", i);
1136 for (int i = 0; i < nparam; ++i)
1137 fprintf(out, ", %s", isl_space_get_dim_name(dim, isl_dim_param, i));
1138 fprintf(out, ";\n");
1140 fprintf(out, "states");
1141 for (int i = 0; i < ti.state + 1; ++i)
1142 fprintf(out, "%s s%d", i ? "," : "", i);
1143 fprintf(out, ";\n");
1145 ti.out = out;
1146 ti.transition = 0;
1147 ti.state = msa.state.size();
1148 for (int i = 0; i < msa.transition.size(); ++i) {
1149 int f = msa.transition[i].second;
1150 ti.from = msa.transition[i].first.first;
1151 ti.to = msa.transition[i].first.second;
1152 isl_map_foreach_basic_map(msa.firing[f],
1153 &print_aspic_transition, &ti);
1156 bad_map = isl_union_map_from_domain_and_range(bad,
1157 isl_union_set_from_set(isl_set_universe(dim)));
1158 ti.to = ti.state;
1159 isl_union_map_foreach_map(bad_map, &print_bad_aspic_transition, &ti);
1161 fprintf(out, "}\n");
1163 fprintf(out, "strategy s1 {\n");
1164 print_region(out, "init", init);
1165 fprintf(out, "Region bad := { state = s%d };\n", ti.state);
1166 fprintf(out, "}\n");
1168 isl_union_set_free(init);
1169 isl_union_map_free(bad_map);
1171 return 0;
1174 int main(int argc, char *argv[])
1176 isl_ctx *ctx;
1177 struct options *options = options_new_with_defaults();
1178 isl_set *context = NULL;
1179 dependence_graph *dg[2];
1180 int res = 1;
1182 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
1184 ctx = isl_ctx_alloc_with_options(&options_args, options);
1185 if (!ctx) {
1186 fprintf(stderr, "Unable to allocate ctx\n");
1187 return -1;
1190 if (options->context)
1191 context = isl_set_read_from_str(ctx, options->context);
1193 pdg::PDG *pdg[2];
1194 unsigned out_dim = 0;
1195 for (int i = 0; i < 2; ++i) {
1196 FILE *in;
1197 in = fopen(options->program[i], "r");
1198 if (!in) {
1199 fprintf(stderr, "Unable to open %s\n", options->program[i]);
1200 return -1;
1202 pdg[i] = yaml::Load<pdg::PDG>(in, ctx);
1203 fclose(in);
1204 if (!pdg[i]) {
1205 fprintf(stderr, "Unable to read %s\n", options->program[i]);
1206 return -1;
1208 out_dim = update_out_dim(pdg[i], out_dim);
1210 if (context &&
1211 pdg[i]->params.size() != isl_set_dim(context, isl_dim_param)) {
1212 fprintf(stderr,
1213 "Parameter dimension mismatch; context ignored\n");
1214 isl_set_free(context);
1215 context = NULL;
1219 for (int i = 0; i < 2; ++i) {
1220 dg[i] = pdg_to_dg(pdg[i], out_dim, isl_set_copy(context));
1221 pdg[i]->free();
1222 delete pdg[i];
1225 if (options->reachability == REACH_TC)
1226 res = check_equivalence_tc(ctx, dg[0], dg[1]);
1227 else if (options->reachability == REACH_FAST)
1228 res = check_equivalence_fast(ctx, dg[0], dg[1]);
1229 else if (options->reachability == REACH_LEVER)
1230 res = check_equivalence_lever(ctx, dg[0], dg[1]);
1231 else if (options->reachability == REACH_ASPIC)
1232 res = check_equivalence_aspic(ctx, dg[0], dg[1]);
1234 for (int i = 0; i < 2; ++i)
1235 delete dg[i];
1236 isl_set_free(context);
1237 isl_ctx_free(ctx);
1239 return res;