4 #include <isl/constraint.h>
7 #include <isl/union_map.h>
8 #include <isl/union_set.h>
9 #include <isl/printer.h>
11 #include "dependence_graph.h"
12 #include "eqv3_options.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
)
31 if (array
->dims
.size() + 1 > out_dim
)
32 out_dim
= array
->dims
.size() + 1;
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
))
49 if (dg1
->input_computations
[i
]->dim
==
50 dg2
->input_computations
[j
]->dim
)
53 "input arrays \"%s\" do not have the same dimension\n",
54 dg1
->input_computations
[i
]->operation
);
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
)
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
);
85 fprintf(stderr
, "different output domains for array %s\n",
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
)
118 isl_set
*equal_output
;
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()) {
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
]);
133 "Array \"%s\" only in program 1; not checked\n",
134 dg1
->output_arrays
[i1
]);
136 } else if (cmp
> 0) {
138 "Array \"%s\" only in program 2; not checked\n",
139 dg2
->output_arrays
[i2
]);
143 eq_array
= compute_equal_array(dg1
, dg2
, i1
, i2
);
144 equal_output
= isl_set_union(equal_output
, eq_array
);
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
)
161 isl_space
*dim1
, *dim2
;
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
)
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
)
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
));
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
)
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
));
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
)
261 isl_union_map
*transition
;
262 isl_union_set
*equal_output
;
264 isl_union_set
*final
;
269 if (check_input_arrays(dg1
, dg2
))
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
)) {
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
);
308 isl_union_set_free(equal_output
);
309 isl_union_map_free(transition
);
312 isl_union_set_free(test
);
315 isl_printer_print_str(p
, "NOT ");
316 isl_printer_print_str(p
, "OK");
317 isl_printer_end_line(p
);
324 struct transition_info
{
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
);
347 for (int i
= 0; i
< n_out
; ++i
) {
348 isl_constraint
*c
= NULL
;
349 if (!isl_basic_map_has_defining_equality(bmap
,
352 isl_constraint_free(c
);
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
)
381 isl_basic_map_free(firing
);
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
)
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
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
)
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
);
433 if (isl_val_is_neg(v
))
434 p
= isl_printer_print_str(p
, "0");
435 p
= isl_printer_print_val(p
, v
);
437 for (int i
= 0; i
< nparam
; ++i
) {
439 v
= isl_constraint_get_coefficient_val(c
, isl_dim_param
, i
);
440 if (isl_val_is_zero(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
);
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
)) {
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
);
470 for (int i
= 0; i
< n_out
; ++i
) {
471 v
= isl_constraint_get_coefficient_val(c
,
473 if (isl_val_is_zero(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
);
486 for (int i
= 0; i
< n_div
; ++i
) {
487 v
= isl_constraint_get_coefficient_val(c
,
489 if (isl_val_is_zero(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
);
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
;
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
,
528 if (!isl_val_is_zero(v
)) {
529 assert(isl_constraint_is_equality(c
));
530 isl_constraint_free(c
);
537 ctx
= isl_constraint_get_ctx(c
);
538 p
= isl_printer_to_file(ctx
, ti
->out
);
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");
545 p
= isl_printer_print_str(p
, " >= 0");
550 isl_constraint_free(c
);
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
,
562 transition_info
*ti
= (transition_info
*)user
;
570 if (!isl_constraint_is_equality(c
)) {
571 isl_constraint_free(c
);
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))
584 isl_constraint_free(c
);
588 if (out
> ti
->last_out
)
591 if (isl_constraint_is_lower_bound(c
, isl_dim_out
, out
))
596 ctx
= isl_constraint_get_ctx(c
);
597 p
= isl_printer_to_file(ctx
, ti
->out
);
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);
608 isl_constraint_free(c
);
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
,
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
);
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");
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");
648 for (int i
= 0; i
< n_out
+ n_div
; ++i
) {
649 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
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");
655 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->state
);
656 fprintf(ti
->out
, "\taction := x%d' = x%d + 1;\n",
658 fprintf(ti
->out
, "};\n");
662 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
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 := ");
671 fprintf(ti
->out
, "state = %d", ti
->state
);
674 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
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
);
684 fprintf(ti
->out
, ", state' = %d", ti
->to
);
685 fprintf(ti
->out
, ";\n");
686 fprintf(ti
->out
, "};\n");
692 isl_basic_map_free(firing
);
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
,
704 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
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 := ");
714 fprintf(ti
->out
, "state = %d", ti
->from
);
717 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
719 fprintf(ti
->out
, "true");
720 fprintf(ti
->out
, ";\n");
722 fprintf(ti
->out
, "\taction := ");
726 fprintf(ti
->out
, "state' = %d", ti
->to
);
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");
740 isl_basic_map_free(firing
);
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
);
766 static isl_stat
print_region_constraint(__isl_take isl_constraint
*c
,
769 region_info
*ri
= (region_info
*)user
;
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");
781 p
= isl_printer_print_str(p
, " >= 0");
784 isl_constraint_free(c
);
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")
793 static isl_stat
print_region_basic_set(__isl_take isl_basic_set
*bset
,
796 region_info
*ri
= (region_info
*)user
;
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
, ")");
811 isl_basic_set_free(bset
);
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
);
823 static void print_region(FILE *out
, const char *name
,
824 __isl_keep isl_union_set
*region
)
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
)
850 isl_union_set
*final
;
853 if (check_input_arrays(dg1
, dg2
))
856 root
= msa
.add(dg1
->out
, dg2
->out
);
859 ti
.state
= msa
.state
.size();
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");
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
));
878 fprintf(out
, "states");
879 for (int i
= 0; i
< ti
.state
; ++i
)
880 fprintf(out
, "%s s%d", i
? "," : "", i
);
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
);
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");
912 isl_union_set_free(init
);
913 isl_union_set_free(final
);
914 isl_union_set_free(good
);
919 static void print_label(FILE *out
, const char *name
,
920 __isl_keep isl_union_set
*region
)
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
936 static int check_equivalence_lever(isl_ctx
*ctx
,
937 dependence_graph
*dg1
, dependence_graph
*dg2
)
948 if (check_input_arrays(dg1
, dg2
))
951 root
= msa
.add(dg1
->out
, dg2
->out
);
954 ti
.state
= msa
.state
.size();
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
));
971 fprintf(out
, "ctl_spec {\n");
972 print_label(out
, "bad", bad
);
975 fprintf(out
, "system {\n");
976 print_label(out
, "init", init
);
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
);
992 isl_union_set_free(init
);
993 isl_union_set_free(bad
);
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");
1028 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
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 := ");
1037 fprintf(ti
->out
, "state = %d", ti
->state
);
1040 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
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
);
1050 fprintf(ti
->out
, ", state' = %d", ti
->to
);
1051 fprintf(ti
->out
, ";\n");
1052 fprintf(ti
->out
, "};\n");
1058 isl_basic_map_free(firing
);
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
,
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
,
1083 transition_info
*ti
= (transition_info
*)user
;
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
);
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
1102 static int check_equivalence_aspic(isl_ctx
*ctx
,
1103 dependence_graph
*dg1
, dependence_graph
*dg2
)
1111 isl_union_set
*init
;
1113 isl_union_map
*bad_map
;
1115 if (check_input_arrays(dg1
, dg2
))
1118 root
= msa
.add(dg1
->out
, dg2
->out
);
1121 ti
.state
= msa
.state
.size();
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");
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
)));
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
);
1174 int main(int argc
, char *argv
[])
1177 struct options
*options
= options_new_with_defaults();
1178 isl_set
*context
= NULL
;
1179 dependence_graph
*dg
[2];
1182 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
1184 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
1186 fprintf(stderr
, "Unable to allocate ctx\n");
1190 if (options
->context
)
1191 context
= isl_set_read_from_str(ctx
, options
->context
);
1194 unsigned out_dim
= 0;
1195 for (int i
= 0; i
< 2; ++i
) {
1197 in
= fopen(options
->program
[i
], "r");
1199 fprintf(stderr
, "Unable to open %s\n", options
->program
[i
]);
1202 pdg
[i
] = yaml::Load
<pdg::PDG
>(in
, ctx
);
1205 fprintf(stderr
, "Unable to read %s\n", options
->program
[i
]);
1208 out_dim
= update_out_dim(pdg
[i
], out_dim
);
1211 pdg
[i
]->params
.size() != isl_set_dim(context
, isl_dim_param
)) {
1213 "Parameter dimension mismatch; context ignored\n");
1214 isl_set_free(context
);
1219 for (int i
= 0; i
< 2; ++i
) {
1220 dg
[i
] = pdg_to_dg(pdg
[i
], out_dim
, isl_set_copy(context
));
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
)
1236 isl_set_free(context
);