1 #include <isl/constraint.h>
2 #include <isl/union_map.h>
3 #include <isl/union_set.h>
5 #include "dependence_graph.h"
6 #include "eqv3_options.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
)
25 if (array
->dims
.size() + 1 > out_dim
)
26 out_dim
= array
->dims
.size() + 1;
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
))
43 if (dg1
->input_computations
[i
]->dim
==
44 dg2
->input_computations
[j
]->dim
)
47 "input arrays \"%s\" do not have the same dimension\n",
48 dg1
->input_computations
[i
]->operation
);
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
);
78 fprintf(stderr
, "different output domains for array %s\n",
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
)
110 isl_set
*equal_output
;
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()) {
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
]);
125 "Array \"%s\" only in program 1; not checked\n",
126 dg1
->output_arrays
[i1
]);
128 } else if (cmp
> 0) {
130 "Array \"%s\" only in program 2; not checked\n",
131 dg2
->output_arrays
[i2
]);
135 eq_array
= compute_equal_array(dg1
, dg2
, i1
, i2
);
136 equal_output
= isl_set_union(equal_output
, eq_array
);
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
)
153 isl_space
*dim1
, *dim2
;
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
)
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
)
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
));
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
)
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
));
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
)
253 isl_union_map
*transition
;
254 isl_union_set
*equal_output
;
256 isl_union_set
*final
;
261 if (check_input_arrays(dg1
, dg2
))
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
)) {
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
);
300 isl_union_set_free(equal_output
);
301 isl_union_map_free(transition
);
304 isl_union_set_free(test
);
307 isl_printer_print_str(p
, "NOT ");
308 isl_printer_print_str(p
, "OK");
309 isl_printer_end_line(p
);
316 struct transition_info
{
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
);
339 for (int i
= 0; i
< n_out
; ++i
) {
340 isl_constraint
*c
= NULL
;
341 if (!isl_basic_map_has_defining_equality(bmap
,
344 isl_constraint_free(c
);
350 /* Update the maximal number of variables and the total number of states
351 * based on the given firing relation.
352 * If the firing relation is a translation, then we don't need anything extra.
353 * Otherwise, we need an extra state and
354 * to encode the firing relation we need at most as many variables
355 * as are needed to describe the relation. Note that this does
356 * not include the parameters as they are treated separately.
358 static isl_stat
check_transition(__isl_take isl_basic_map
*firing
, void *user
)
360 transition_info
*ti
= (transition_info
*)user
;
362 if (!is_translation(firing
)) {
363 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
364 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
365 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
366 unsigned total
= n_in
+ n_out
+ n_div
;
368 if (total
> ti
->nvar
)
373 isl_basic_map_free(firing
);
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
)
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
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
)
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
);
425 if (isl_val_is_neg(v
))
426 p
= isl_printer_print_str(p
, "0");
427 p
= isl_printer_print_val(p
, v
);
429 for (int i
= 0; i
< nparam
; ++i
) {
431 v
= isl_constraint_get_coefficient_val(c
, isl_dim_param
, i
);
432 if (isl_val_is_zero(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
);
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
)) {
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
);
462 for (int i
= 0; i
< n_out
; ++i
) {
463 v
= isl_constraint_get_coefficient_val(c
,
465 if (isl_val_is_zero(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
);
478 for (int i
= 0; i
< n_div
; ++i
) {
479 v
= isl_constraint_get_coefficient_val(c
,
481 if (isl_val_is_zero(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
);
499 /* Print the constraints that go into the guard.
500 * If we are dealing with a translation, then we shouldn't print any
501 * constraints involving the output variables.
502 * Those constraint should be equalities defining the translation
503 * and will be printed in the action.
505 static isl_stat
print_guard_constraint(__isl_take isl_constraint
*c
, void *user
)
507 transition_info
*ti
= (transition_info
*)user
;
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
,
520 if (!isl_val_is_zero(v
)) {
521 assert(isl_constraint_is_equality(c
));
522 isl_constraint_free(c
);
529 ctx
= isl_constraint_get_ctx(c
);
530 p
= isl_printer_to_file(ctx
, ti
->out
);
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");
537 p
= isl_printer_print_str(p
, " >= 0");
542 isl_constraint_free(c
);
548 /* Print the constraints that go into the action.
549 * That is, equality constraints that define one of the output variables.
551 static isl_stat
print_action_constraint(__isl_take isl_constraint
*c
,
554 transition_info
*ti
= (transition_info
*)user
;
562 if (!isl_constraint_is_equality(c
)) {
563 isl_constraint_free(c
);
567 n_in
= isl_constraint_dim(c
, isl_dim_in
);
568 n_out
= isl_constraint_dim(c
, isl_dim_out
);
570 for (out
= 0; out
< n_out
; ++out
) {
571 if (isl_constraint_involves_dims(c
, isl_dim_out
, out
, 1))
576 isl_constraint_free(c
);
580 if (out
> ti
->last_out
)
583 if (isl_constraint_is_lower_bound(c
, isl_dim_out
, out
))
588 ctx
= isl_constraint_get_ctx(c
);
589 p
= isl_printer_to_file(ctx
, ti
->out
);
591 p
= isl_printer_print_str(p
, ", ");
593 p
= isl_printer_print_str(p
, "x");
594 p
= isl_printer_print_int(p
, out
);
595 p
= isl_printer_print_str(p
, "' = ");
596 p
= print_linear(p
, c
, s
, 1);
600 isl_constraint_free(c
);
604 /* A firing relation that is not a translation is encoded using
605 * several transitions. We first allow an unconditional transition
606 * to an extra state. In this state, we allow arbitrary increments
607 * on the extra variables that will correspond to the output variables
608 * and the existentially quantified variables of the relation. This allows
609 * those variables to attain any value, since they are initially set
610 * to zero and FAST and Lever only support non-negative variables.
611 * Finally, we add a transition from the extra
612 * state to the original target state with a guard that corresponds
613 * to the firing relation and an action that assigns the output
614 * variables to the main variables.
616 static isl_stat
print_non_translation(__isl_take isl_basic_map
*firing
,
619 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
620 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
621 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
623 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
625 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
626 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
627 fprintf(ti
->out
, "\tguard := true;\n");
628 fprintf(ti
->out
, "\taction := x0' = x0");
630 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->from
);
631 fprintf(ti
->out
, "\taction := state' = %d", ti
->state
);
633 for (int i
= 0; i
< n_out
+ n_div
; ++i
)
634 fprintf(ti
->out
, ", x%d' = 0", n_in
+ i
);
635 fprintf(ti
->out
, ";\n");
636 fprintf(ti
->out
, "};\n");
640 for (int i
= 0; i
< n_out
+ n_div
; ++i
) {
641 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
643 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
644 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
645 fprintf(ti
->out
, "\tguard := true;\n");
647 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->state
);
648 fprintf(ti
->out
, "\taction := x%d' = x%d + 1;\n",
650 fprintf(ti
->out
, "};\n");
654 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
656 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
657 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
659 fprintf(ti
->out
, "\tguard := ");
663 fprintf(ti
->out
, "state = %d", ti
->state
);
666 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
668 fprintf(ti
->out
, "true");
669 fprintf(ti
->out
, ";\n");
670 fprintf(ti
->out
, "\taction := ");
671 for (int i
= 0; i
< n_out
; ++i
)
672 fprintf(ti
->out
, "%sx%d' = x%d", i
? ", " : "", i
, n_in
+ i
);
673 for (int i
= n_out
; i
< ti
->nvar
; ++i
)
674 fprintf(ti
->out
, ", x%d' = 0", i
);
676 fprintf(ti
->out
, ", state' = %d", ti
->to
);
677 fprintf(ti
->out
, ";\n");
678 fprintf(ti
->out
, "};\n");
684 isl_basic_map_free(firing
);
689 /* A firing relation that is a translation can be encoded directly
690 * into a transition. We simply put the translation in the action
691 * and the remaining constraints in the guard.
693 static isl_stat
print_translation(__isl_take isl_basic_map
*firing
,
696 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
698 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
699 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
702 fprintf(ti
->out
, "\tguard := ");
706 fprintf(ti
->out
, "state = %d", ti
->from
);
709 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
711 fprintf(ti
->out
, "true");
712 fprintf(ti
->out
, ";\n");
714 fprintf(ti
->out
, "\taction := ");
718 fprintf(ti
->out
, "state' = %d", ti
->to
);
722 isl_basic_map_foreach_constraint(firing
, print_action_constraint
, ti
);
723 for (int i
= ti
->last_out
+ 1; i
< ti
->nvar
; ++i
)
724 fprintf(ti
->out
, "%sx%d' = 0",
725 (i
|| !ti
->first
) ? ", " : "", i
);
726 fprintf(ti
->out
, ";\n");
728 fprintf(ti
->out
, "};\n");
732 isl_basic_map_free(firing
);
737 /* Print one or more transitions based on the firing relation.
738 * The way a firing relation is encoded depends on whether it is
739 * a translation or not.
741 static isl_stat
print_transition(__isl_take isl_basic_map
*firing
, void *user
)
743 transition_info
*ti
= (transition_info
*)user
;
745 ti
->translation
= is_translation(firing
);
746 if (!ti
->translation
)
747 return print_non_translation(firing
, ti
);
748 return print_translation(firing
, ti
);
758 static isl_stat
print_region_constraint(__isl_take isl_constraint
*c
,
761 region_info
*ri
= (region_info
*)user
;
765 ctx
= isl_constraint_get_ctx(c
);
766 p
= isl_printer_to_file(ctx
, ri
->out
);
767 p
= isl_printer_print_str(p
, " && ");
769 p
= print_linear(p
, c
, 1, 0);
770 if (isl_constraint_is_equality(c
))
771 p
= isl_printer_print_str(p
, " = 0");
773 p
= isl_printer_print_str(p
, " >= 0");
776 isl_constraint_free(c
);
780 /* Print a convex part of a region.
781 * In FAST, states are designated by labels, while in Lever, they
782 * are encoded as integer, so we have to drop the first letter ("s")
785 static isl_stat
print_region_basic_set(__isl_take isl_basic_set
*bset
,
788 region_info
*ri
= (region_info
*)user
;
791 fprintf(ri
->out
, " || ");
793 fprintf(ri
->out
, "(");
794 fprintf(ri
->out
, "state = %s",
795 isl_basic_set_get_tuple_name(bset
) + ri
->name_offset
);
797 isl_basic_set_foreach_constraint(bset
, &print_region_constraint
, user
);
799 fprintf(ri
->out
, ")");
803 isl_basic_set_free(bset
);
807 static isl_stat
print_region_set(__isl_take isl_set
*set
, void *user
)
809 isl_set_foreach_basic_set(set
, &print_region_basic_set
, user
);
815 static void print_region(FILE *out
, const char *name
,
816 __isl_keep isl_union_set
*region
)
824 fprintf(out
, "Region %s := { ", name
);
825 isl_union_set_foreach_set(region
, &print_region_set
, &ri
);
826 fprintf(out
, " };\n");
829 /* Construct a FAST model that tests whether all reachable
830 * final states are a subset of the good final states.
832 static int check_equivalence_fast(isl_ctx
*ctx
,
833 dependence_graph
*dg1
, dependence_graph
*dg2
)
842 isl_union_set
*final
;
845 if (check_input_arrays(dg1
, dg2
))
848 root
= msa
.add(dg1
->out
, dg2
->out
);
851 ti
.state
= msa
.state
.size();
854 compute_max_var(msa
, &ti
);
855 init
= compute_equal_output(dg1
, dg2
);
856 final
= compute_final_states(ctx
, msa
);
857 good
= compute_good_states(ctx
, msa
);
858 dim
= isl_union_set_get_space(init
);
859 nparam
= isl_space_dim(dim
, isl_dim_param
);
861 fprintf(out
, "model m1 {\n");
864 for (int i
= 0; i
< ti
.nvar
; ++i
)
865 fprintf(out
, "%s x%d", i
? "," : "", i
);
866 for (int i
= 0; i
< nparam
; ++i
)
867 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
870 fprintf(out
, "states");
871 for (int i
= 0; i
< ti
.state
; ++i
)
872 fprintf(out
, "%s s%d", i
? "," : "", i
);
877 ti
.state
= msa
.state
.size();
878 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
879 int f
= msa
.transition
[i
].second
;
880 ti
.from
= msa
.transition
[i
].first
.first
;
881 ti
.to
= msa
.transition
[i
].first
.second
;
882 isl_map_foreach_basic_map(msa
.firing
[f
],
883 &print_transition
, &ti
);
888 fprintf(out
, "strategy s1 {\n");
889 print_region(out
, "init", init
);
890 print_region(out
, "final", final
);
891 print_region(out
, "good", good
);
892 fprintf(out
, "Transitions t := {");
893 for (int i
= 0; i
< ti
.transition
; ++i
)
894 fprintf(out
, "%s t%d", i
? "," : "", i
);
895 fprintf(out
, " };\n");
896 fprintf(out
, "Region reach := post*(init, t);\n");
897 fprintf(out
, "if (subSet(reach && final, good))\n");
898 fprintf(out
, "\tthen print(\"OK\\n\");\n");
899 fprintf(out
, "\telse print(\"NOT OK\\n\");\n");
900 fprintf(out
, "endif\n");
904 isl_union_set_free(init
);
905 isl_union_set_free(final
);
906 isl_union_set_free(good
);
911 static void print_label(FILE *out
, const char *name
,
912 __isl_keep isl_union_set
*region
)
920 fprintf(out
, "label %s := { ", name
);
921 isl_union_set_foreach_set(region
, &print_region_set
, &ri
);
922 fprintf(out
, " };\n");
925 /* Construct a Lever model that tests whether the bad final states
928 static int check_equivalence_lever(isl_ctx
*ctx
,
929 dependence_graph
*dg1
, dependence_graph
*dg2
)
940 if (check_input_arrays(dg1
, dg2
))
943 root
= msa
.add(dg1
->out
, dg2
->out
);
946 ti
.state
= msa
.state
.size();
949 compute_max_var(msa
, &ti
);
950 init
= compute_equal_output(dg1
, dg2
);
951 bad
= isl_union_set_subtract(compute_final_states(ctx
, msa
),
952 compute_good_states(ctx
, msa
));
953 dim
= isl_union_set_get_space(init
);
954 nparam
= isl_space_dim(dim
, isl_dim_param
);
956 fprintf(out
, "var state");
957 for (int i
= 0; i
< ti
.nvar
; ++i
)
958 fprintf(out
, ", x%d", i
);
959 for (int i
= 0; i
< nparam
; ++i
)
960 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
963 fprintf(out
, "ctl_spec {\n");
964 print_label(out
, "bad", bad
);
967 fprintf(out
, "system {\n");
968 print_label(out
, "init", init
);
972 ti
.state
= msa
.state
.size();
973 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
974 int f
= msa
.transition
[i
].second
;
975 ti
.from
= msa
.transition
[i
].first
.first
;
976 ti
.to
= msa
.transition
[i
].first
.second
;
977 isl_map_foreach_basic_map(msa
.firing
[f
],
978 &print_transition
, &ti
);
984 isl_union_set_free(init
);
985 isl_union_set_free(bad
);
990 /* A firing relation that is not a translation is encoded in Aspic using
991 * using two transitions passing through an extra state.
992 * The first transition assigns arbitrary values to the extra variables
993 * that will correspond to the output variables and the existentially
994 * quantified variables of the relation.
995 * The second transition, from the extra
996 * state to the original target state, has a guard that corresponds
997 * to the firing relation and an action that assigns the output
998 * variables to the main variables.
1000 static isl_stat
print_aspic_non_translation(__isl_take isl_basic_map
*firing
,
1001 transition_info
*ti
)
1003 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
1004 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
1005 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
1007 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
1008 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
1009 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
1010 fprintf(ti
->out
, "\tguard := true;\n");
1011 fprintf(ti
->out
, "\taction := ");
1012 for (int i
= 0; i
< n_out
+ n_div
; ++i
)
1013 fprintf(ti
->out
, "%sx%d' = ?", i
? ", " : "", n_in
+ i
);
1014 fprintf(ti
->out
, ";\n");
1016 fprintf(ti
->out
, "};\n");
1020 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
1022 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
1023 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
1025 fprintf(ti
->out
, "\tguard := ");
1029 fprintf(ti
->out
, "state = %d", ti
->state
);
1032 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
1034 fprintf(ti
->out
, "true");
1035 fprintf(ti
->out
, ";\n");
1036 fprintf(ti
->out
, "\taction := ");
1037 for (int i
= 0; i
< n_out
; ++i
)
1038 fprintf(ti
->out
, "%sx%d' = x%d", i
? ", " : "", i
, n_in
+ i
);
1039 for (int i
= n_out
; i
< ti
->nvar
; ++i
)
1040 fprintf(ti
->out
, ", x%d' = 0", i
);
1042 fprintf(ti
->out
, ", state' = %d", ti
->to
);
1043 fprintf(ti
->out
, ";\n");
1044 fprintf(ti
->out
, "};\n");
1050 isl_basic_map_free(firing
);
1055 /* Print one or more transitions based on the firing relation.
1056 * The way a firing relation is encoded depends on whether it is
1057 * a translation or not.
1059 static isl_stat
print_aspic_transition(__isl_take isl_basic_map
*firing
,
1062 transition_info
*ti
= (transition_info
*)user
;
1064 ti
->translation
= is_translation(firing
);
1065 if (!ti
->translation
)
1066 return print_aspic_non_translation(firing
, ti
);
1067 return print_translation(firing
, ti
);
1070 /* Print a transition from a bad final state to the canonical bad state.
1072 static isl_stat
print_bad_aspic_transition(__isl_take isl_map
*firing
,
1075 transition_info
*ti
= (transition_info
*)user
;
1078 name
= isl_map_get_tuple_name(firing
, isl_dim_in
);
1079 ti
->from
= atoi(name
+ 1);
1081 isl_map_foreach_basic_map(firing
, &print_aspic_transition
, user
);
1083 isl_map_free(firing
);
1088 /* Construct an Aspic model that tests whether the bad final states
1089 * are unreachable. Since Aspic only supports convex "bad" regions,
1090 * we add an extra state with transitions from all instances of states
1091 * that are bad. The bad region is then simply any instance of that
1094 static int check_equivalence_aspic(isl_ctx
*ctx
,
1095 dependence_graph
*dg1
, dependence_graph
*dg2
)
1103 isl_union_set
*init
;
1105 isl_union_map
*bad_map
;
1107 if (check_input_arrays(dg1
, dg2
))
1110 root
= msa
.add(dg1
->out
, dg2
->out
);
1113 ti
.state
= msa
.state
.size();
1116 compute_max_var(msa
, &ti
);
1117 init
= compute_equal_output(dg1
, dg2
);
1118 bad
= isl_union_set_subtract(compute_final_states(ctx
, msa
),
1119 compute_good_states(ctx
, msa
));
1120 dim
= isl_union_set_get_space(init
);
1121 nparam
= isl_space_dim(dim
, isl_dim_param
);
1123 fprintf(out
, "model m1 {\n");
1125 fprintf(out
, "var");
1126 for (int i
= 0; i
< ti
.nvar
; ++i
)
1127 fprintf(out
, "%s x%d", i
? "," : "", i
);
1128 for (int i
= 0; i
< nparam
; ++i
)
1129 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
1130 fprintf(out
, ";\n");
1132 fprintf(out
, "states");
1133 for (int i
= 0; i
< ti
.state
+ 1; ++i
)
1134 fprintf(out
, "%s s%d", i
? "," : "", i
);
1135 fprintf(out
, ";\n");
1139 ti
.state
= msa
.state
.size();
1140 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
1141 int f
= msa
.transition
[i
].second
;
1142 ti
.from
= msa
.transition
[i
].first
.first
;
1143 ti
.to
= msa
.transition
[i
].first
.second
;
1144 isl_map_foreach_basic_map(msa
.firing
[f
],
1145 &print_aspic_transition
, &ti
);
1148 bad_map
= isl_union_map_from_domain_and_range(bad
,
1149 isl_union_set_from_set(isl_set_universe(dim
)));
1151 isl_union_map_foreach_map(bad_map
, &print_bad_aspic_transition
, &ti
);
1153 fprintf(out
, "}\n");
1155 fprintf(out
, "strategy s1 {\n");
1156 print_region(out
, "init", init
);
1157 fprintf(out
, "Region bad := { state = s%d };\n", ti
.state
);
1158 fprintf(out
, "}\n");
1160 isl_union_set_free(init
);
1161 isl_union_map_free(bad_map
);
1166 int main(int argc
, char *argv
[])
1169 struct options
*options
= options_new_with_defaults();
1170 isl_set
*context
= NULL
;
1171 dependence_graph
*dg
[2];
1174 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
1176 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
1178 fprintf(stderr
, "Unable to allocate ctx\n");
1182 if (options
->context
)
1183 context
= isl_set_read_from_str(ctx
, options
->context
);
1186 unsigned out_dim
= 0;
1187 for (int i
= 0; i
< 2; ++i
) {
1189 in
= fopen(options
->program
[i
], "r");
1191 fprintf(stderr
, "Unable to open %s\n", options
->program
[i
]);
1194 pdg
[i
] = yaml::Load
<pdg::PDG
>(in
, ctx
);
1197 fprintf(stderr
, "Unable to read %s\n", options
->program
[i
]);
1200 out_dim
= update_out_dim(pdg
[i
], out_dim
);
1203 pdg
[i
]->params
.size() != isl_set_dim(context
, isl_dim_param
)) {
1205 "Parameter dimension mismatch; context ignored\n");
1206 isl_set_free(context
);
1211 for (int i
= 0; i
< 2; ++i
) {
1212 dg
[i
] = pdg_to_dg(pdg
[i
], out_dim
, isl_set_copy(context
));
1217 if (options
->reachability
== REACH_TC
)
1218 res
= check_equivalence_tc(ctx
, dg
[0], dg
[1]);
1219 else if (options
->reachability
== REACH_FAST
)
1220 res
= check_equivalence_fast(ctx
, dg
[0], dg
[1]);
1221 else if (options
->reachability
== REACH_LEVER
)
1222 res
= check_equivalence_lever(ctx
, dg
[0], dg
[1]);
1223 else if (options
->reachability
== REACH_ASPIC
)
1224 res
= check_equivalence_aspic(ctx
, dg
[0], dg
[1]);
1226 for (int i
= 0; i
< 2; ++i
)
1228 isl_set_free(context
);