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 int check_transition(__isl_take isl_basic_map
*firing
, void *user
)
360 transition_info
*ti
= (transition_info
*)user
;
362 if (!is_translation(firing
)) {
363 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
364 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
365 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
366 unsigned total
= n_in
+ n_out
+ n_div
;
368 if (total
> ti
->nvar
)
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 int 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 int print_action_constraint(__isl_take isl_constraint
*c
, void *user
)
553 transition_info
*ti
= (transition_info
*)user
;
561 if (!isl_constraint_is_equality(c
)) {
562 isl_constraint_free(c
);
566 n_in
= isl_constraint_dim(c
, isl_dim_in
);
567 n_out
= isl_constraint_dim(c
, isl_dim_out
);
569 for (out
= 0; out
< n_out
; ++out
) {
570 if (isl_constraint_involves_dims(c
, isl_dim_out
, out
, 1))
575 isl_constraint_free(c
);
579 if (out
> ti
->last_out
)
582 if (isl_constraint_is_lower_bound(c
, isl_dim_out
, out
))
587 ctx
= isl_constraint_get_ctx(c
);
588 p
= isl_printer_to_file(ctx
, ti
->out
);
590 p
= isl_printer_print_str(p
, ", ");
592 p
= isl_printer_print_str(p
, "x");
593 p
= isl_printer_print_int(p
, out
);
594 p
= isl_printer_print_str(p
, "' = ");
595 p
= print_linear(p
, c
, s
, 1);
599 isl_constraint_free(c
);
603 /* A firing relation that is not a translation is encoded using
604 * several transitions. We first allow an unconditional transition
605 * to an extra state. In this state, we allow arbitrary increments
606 * on the extra variables that will correspond to the output variables
607 * and the existentially quantified variables of the relation. This allows
608 * those variables to attain any value, since they are initially set
609 * to zero and FAST and Lever only support non-negative variables.
610 * Finally, we add a transition from the extra
611 * state to the original target state with a guard that corresponds
612 * to the firing relation and an action that assigns the output
613 * variables to the main variables.
615 static int print_non_translation(__isl_take isl_basic_map
*firing
,
618 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
619 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
620 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
622 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
624 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
625 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
626 fprintf(ti
->out
, "\tguard := true;\n");
627 fprintf(ti
->out
, "\taction := x0' = x0");
629 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->from
);
630 fprintf(ti
->out
, "\taction := state' = %d", ti
->state
);
632 for (int i
= 0; i
< n_out
+ n_div
; ++i
)
633 fprintf(ti
->out
, ", x%d' = 0", n_in
+ i
);
634 fprintf(ti
->out
, ";\n");
635 fprintf(ti
->out
, "};\n");
639 for (int i
= 0; i
< n_out
+ n_div
; ++i
) {
640 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
642 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
643 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
644 fprintf(ti
->out
, "\tguard := true;\n");
646 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->state
);
647 fprintf(ti
->out
, "\taction := x%d' = x%d + 1;\n",
649 fprintf(ti
->out
, "};\n");
653 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
655 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
656 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
658 fprintf(ti
->out
, "\tguard := ");
662 fprintf(ti
->out
, "state = %d", ti
->state
);
665 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
667 fprintf(ti
->out
, "true");
668 fprintf(ti
->out
, ";\n");
669 fprintf(ti
->out
, "\taction := ");
670 for (int i
= 0; i
< n_out
; ++i
)
671 fprintf(ti
->out
, "%sx%d' = x%d", i
? ", " : "", i
, n_in
+ i
);
672 for (int i
= n_out
; i
< ti
->nvar
; ++i
)
673 fprintf(ti
->out
, ", x%d' = 0", i
);
675 fprintf(ti
->out
, ", state' = %d", ti
->to
);
676 fprintf(ti
->out
, ";\n");
677 fprintf(ti
->out
, "};\n");
683 isl_basic_map_free(firing
);
688 /* A firing relation that is a translation can be encoded directly
689 * into a transition. We simply put the translation in the action
690 * and the remaining constraints in the guard.
692 static int print_translation(__isl_take isl_basic_map
*firing
,
695 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
697 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
698 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
701 fprintf(ti
->out
, "\tguard := ");
705 fprintf(ti
->out
, "state = %d", ti
->from
);
708 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
710 fprintf(ti
->out
, "true");
711 fprintf(ti
->out
, ";\n");
713 fprintf(ti
->out
, "\taction := ");
717 fprintf(ti
->out
, "state' = %d", ti
->to
);
721 isl_basic_map_foreach_constraint(firing
, print_action_constraint
, ti
);
722 for (int i
= ti
->last_out
+ 1; i
< ti
->nvar
; ++i
)
723 fprintf(ti
->out
, "%sx%d' = 0",
724 (i
|| !ti
->first
) ? ", " : "", i
);
725 fprintf(ti
->out
, ";\n");
727 fprintf(ti
->out
, "};\n");
731 isl_basic_map_free(firing
);
736 /* Print one or more transitions based on the firing relation.
737 * The way a firing relation is encoded depends on whether it is
738 * a translation or not.
740 static int print_transition(__isl_take isl_basic_map
*firing
, void *user
)
742 transition_info
*ti
= (transition_info
*)user
;
744 ti
->translation
= is_translation(firing
);
745 if (!ti
->translation
)
746 return print_non_translation(firing
, ti
);
747 return print_translation(firing
, ti
);
757 static int print_region_constraint(__isl_take isl_constraint
*c
, void *user
)
759 region_info
*ri
= (region_info
*)user
;
763 ctx
= isl_constraint_get_ctx(c
);
764 p
= isl_printer_to_file(ctx
, ri
->out
);
765 p
= isl_printer_print_str(p
, " && ");
767 p
= print_linear(p
, c
, 1, 0);
768 if (isl_constraint_is_equality(c
))
769 p
= isl_printer_print_str(p
, " = 0");
771 p
= isl_printer_print_str(p
, " >= 0");
774 isl_constraint_free(c
);
778 /* Print a convex part of a region.
779 * In FAST, states are designated by labels, while in Lever, they
780 * are encoded as integer, so we have to drop the first letter ("s")
783 static int print_region_basic_set(__isl_take isl_basic_set
*bset
, void *user
)
785 region_info
*ri
= (region_info
*)user
;
788 fprintf(ri
->out
, " || ");
790 fprintf(ri
->out
, "(");
791 fprintf(ri
->out
, "state = %s",
792 isl_basic_set_get_tuple_name(bset
) + ri
->name_offset
);
794 isl_basic_set_foreach_constraint(bset
, &print_region_constraint
, user
);
796 fprintf(ri
->out
, ")");
800 isl_basic_set_free(bset
);
804 static int print_region_set(__isl_take isl_set
*set
, void *user
)
806 isl_set_foreach_basic_set(set
, &print_region_basic_set
, user
);
812 static void print_region(FILE *out
, const char *name
,
813 __isl_keep isl_union_set
*region
)
821 fprintf(out
, "Region %s := { ", name
);
822 isl_union_set_foreach_set(region
, &print_region_set
, &ri
);
823 fprintf(out
, " };\n");
826 /* Construct a FAST model that tests whether all reachable
827 * final states are a subset of the good final states.
829 static int check_equivalence_fast(isl_ctx
*ctx
,
830 dependence_graph
*dg1
, dependence_graph
*dg2
)
839 isl_union_set
*final
;
842 if (check_input_arrays(dg1
, dg2
))
845 root
= msa
.add(dg1
->out
, dg2
->out
);
848 ti
.state
= msa
.state
.size();
851 compute_max_var(msa
, &ti
);
852 init
= compute_equal_output(dg1
, dg2
);
853 final
= compute_final_states(ctx
, msa
);
854 good
= compute_good_states(ctx
, msa
);
855 dim
= isl_union_set_get_space(init
);
856 nparam
= isl_space_dim(dim
, isl_dim_param
);
858 fprintf(out
, "model m1 {\n");
861 for (int i
= 0; i
< ti
.nvar
; ++i
)
862 fprintf(out
, "%s x%d", i
? "," : "", i
);
863 for (int i
= 0; i
< nparam
; ++i
)
864 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
867 fprintf(out
, "states");
868 for (int i
= 0; i
< ti
.state
; ++i
)
869 fprintf(out
, "%s s%d", i
? "," : "", i
);
874 ti
.state
= msa
.state
.size();
875 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
876 int f
= msa
.transition
[i
].second
;
877 ti
.from
= msa
.transition
[i
].first
.first
;
878 ti
.to
= msa
.transition
[i
].first
.second
;
879 isl_map_foreach_basic_map(msa
.firing
[f
],
880 &print_transition
, &ti
);
885 fprintf(out
, "strategy s1 {\n");
886 print_region(out
, "init", init
);
887 print_region(out
, "final", final
);
888 print_region(out
, "good", good
);
889 fprintf(out
, "Transitions t := {");
890 for (int i
= 0; i
< ti
.transition
; ++i
)
891 fprintf(out
, "%s t%d", i
? "," : "", i
);
892 fprintf(out
, " };\n");
893 fprintf(out
, "Region reach := post*(init, t);\n");
894 fprintf(out
, "if (subSet(reach && final, good))\n");
895 fprintf(out
, "\tthen print(\"OK\\n\");\n");
896 fprintf(out
, "\telse print(\"NOT OK\\n\");\n");
897 fprintf(out
, "endif\n");
901 isl_union_set_free(init
);
902 isl_union_set_free(final
);
903 isl_union_set_free(good
);
908 static void print_label(FILE *out
, const char *name
,
909 __isl_keep isl_union_set
*region
)
917 fprintf(out
, "label %s := { ", name
);
918 isl_union_set_foreach_set(region
, &print_region_set
, &ri
);
919 fprintf(out
, " };\n");
922 /* Construct a Lever model that tests whether the bad final states
925 static int check_equivalence_lever(isl_ctx
*ctx
,
926 dependence_graph
*dg1
, dependence_graph
*dg2
)
937 if (check_input_arrays(dg1
, dg2
))
940 root
= msa
.add(dg1
->out
, dg2
->out
);
943 ti
.state
= msa
.state
.size();
946 compute_max_var(msa
, &ti
);
947 init
= compute_equal_output(dg1
, dg2
);
948 bad
= isl_union_set_subtract(compute_final_states(ctx
, msa
),
949 compute_good_states(ctx
, msa
));
950 dim
= isl_union_set_get_space(init
);
951 nparam
= isl_space_dim(dim
, isl_dim_param
);
953 fprintf(out
, "var state");
954 for (int i
= 0; i
< ti
.nvar
; ++i
)
955 fprintf(out
, ", x%d", i
);
956 for (int i
= 0; i
< nparam
; ++i
)
957 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
960 fprintf(out
, "ctl_spec {\n");
961 print_label(out
, "bad", bad
);
964 fprintf(out
, "system {\n");
965 print_label(out
, "init", init
);
969 ti
.state
= msa
.state
.size();
970 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
971 int f
= msa
.transition
[i
].second
;
972 ti
.from
= msa
.transition
[i
].first
.first
;
973 ti
.to
= msa
.transition
[i
].first
.second
;
974 isl_map_foreach_basic_map(msa
.firing
[f
],
975 &print_transition
, &ti
);
981 isl_union_set_free(init
);
982 isl_union_set_free(bad
);
987 /* A firing relation that is not a translation is encoded in Aspic using
988 * using two transitions passing through an extra state.
989 * The first transition assigns arbitrary values to the extra variables
990 * that will correspond to the output variables and the existentially
991 * quantified variables of the relation.
992 * The second transition, from the extra
993 * state to the original target state, has a guard that corresponds
994 * to the firing relation and an action that assigns the output
995 * variables to the main variables.
997 static int print_aspic_non_translation(__isl_take isl_basic_map
*firing
,
1000 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
1001 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
1002 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
1004 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
1005 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
1006 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
1007 fprintf(ti
->out
, "\tguard := true;\n");
1008 fprintf(ti
->out
, "\taction := ");
1009 for (int i
= 0; i
< n_out
+ n_div
; ++i
)
1010 fprintf(ti
->out
, "%sx%d' = ?", i
? ", " : "", n_in
+ i
);
1011 fprintf(ti
->out
, ";\n");
1013 fprintf(ti
->out
, "};\n");
1017 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
1019 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
1020 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
1022 fprintf(ti
->out
, "\tguard := ");
1026 fprintf(ti
->out
, "state = %d", ti
->state
);
1029 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
1031 fprintf(ti
->out
, "true");
1032 fprintf(ti
->out
, ";\n");
1033 fprintf(ti
->out
, "\taction := ");
1034 for (int i
= 0; i
< n_out
; ++i
)
1035 fprintf(ti
->out
, "%sx%d' = x%d", i
? ", " : "", i
, n_in
+ i
);
1036 for (int i
= n_out
; i
< ti
->nvar
; ++i
)
1037 fprintf(ti
->out
, ", x%d' = 0", i
);
1039 fprintf(ti
->out
, ", state' = %d", ti
->to
);
1040 fprintf(ti
->out
, ";\n");
1041 fprintf(ti
->out
, "};\n");
1047 isl_basic_map_free(firing
);
1052 /* Print one or more transitions based on the firing relation.
1053 * The way a firing relation is encoded depends on whether it is
1054 * a translation or not.
1056 static int print_aspic_transition(__isl_take isl_basic_map
*firing
, void *user
)
1058 transition_info
*ti
= (transition_info
*)user
;
1060 ti
->translation
= is_translation(firing
);
1061 if (!ti
->translation
)
1062 return print_aspic_non_translation(firing
, ti
);
1063 return print_translation(firing
, ti
);
1066 /* Print a transition from a bad final state to the canonical bad state.
1068 static int print_bad_aspic_transition(__isl_take isl_map
*firing
, void *user
)
1070 transition_info
*ti
= (transition_info
*)user
;
1073 name
= isl_map_get_tuple_name(firing
, isl_dim_in
);
1074 ti
->from
= atoi(name
+ 1);
1076 isl_map_foreach_basic_map(firing
, &print_aspic_transition
, user
);
1078 isl_map_free(firing
);
1083 /* Construct an Aspic model that tests whether the bad final states
1084 * are unreachable. Since Aspic only supports convex "bad" regions,
1085 * we add an extra state with transitions from all instances of states
1086 * that are bad. The bad region is then simply any instance of that
1089 static int check_equivalence_aspic(isl_ctx
*ctx
,
1090 dependence_graph
*dg1
, dependence_graph
*dg2
)
1098 isl_union_set
*init
;
1100 isl_union_map
*bad_map
;
1102 if (check_input_arrays(dg1
, dg2
))
1105 root
= msa
.add(dg1
->out
, dg2
->out
);
1108 ti
.state
= msa
.state
.size();
1111 compute_max_var(msa
, &ti
);
1112 init
= compute_equal_output(dg1
, dg2
);
1113 bad
= isl_union_set_subtract(compute_final_states(ctx
, msa
),
1114 compute_good_states(ctx
, msa
));
1115 dim
= isl_union_set_get_space(init
);
1116 nparam
= isl_space_dim(dim
, isl_dim_param
);
1118 fprintf(out
, "model m1 {\n");
1120 fprintf(out
, "var");
1121 for (int i
= 0; i
< ti
.nvar
; ++i
)
1122 fprintf(out
, "%s x%d", i
? "," : "", i
);
1123 for (int i
= 0; i
< nparam
; ++i
)
1124 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
1125 fprintf(out
, ";\n");
1127 fprintf(out
, "states");
1128 for (int i
= 0; i
< ti
.state
+ 1; ++i
)
1129 fprintf(out
, "%s s%d", i
? "," : "", i
);
1130 fprintf(out
, ";\n");
1134 ti
.state
= msa
.state
.size();
1135 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
1136 int f
= msa
.transition
[i
].second
;
1137 ti
.from
= msa
.transition
[i
].first
.first
;
1138 ti
.to
= msa
.transition
[i
].first
.second
;
1139 isl_map_foreach_basic_map(msa
.firing
[f
],
1140 &print_aspic_transition
, &ti
);
1143 bad_map
= isl_union_map_from_domain_and_range(bad
,
1144 isl_union_set_from_set(isl_set_universe(dim
)));
1146 isl_union_map_foreach_map(bad_map
, &print_bad_aspic_transition
, &ti
);
1148 fprintf(out
, "}\n");
1150 fprintf(out
, "strategy s1 {\n");
1151 print_region(out
, "init", init
);
1152 fprintf(out
, "Region bad := { state = s%d };\n", ti
.state
);
1153 fprintf(out
, "}\n");
1155 isl_union_set_free(init
);
1156 isl_union_map_free(bad_map
);
1161 int main(int argc
, char *argv
[])
1164 struct options
*options
= options_new_with_defaults();
1165 isl_set
*context
= NULL
;
1166 dependence_graph
*dg
[2];
1169 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
1171 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
1173 fprintf(stderr
, "Unable to allocate ctx\n");
1177 if (options
->context
)
1178 context
= isl_set_read_from_str(ctx
, options
->context
);
1181 unsigned out_dim
= 0;
1182 for (int i
= 0; i
< 2; ++i
) {
1184 in
= fopen(options
->program
[i
], "r");
1186 fprintf(stderr
, "Unable to open %s\n", options
->program
[i
]);
1189 pdg
[i
] = yaml::Load
<pdg::PDG
>(in
, ctx
);
1192 fprintf(stderr
, "Unable to read %s\n", options
->program
[i
]);
1195 out_dim
= update_out_dim(pdg
[i
], out_dim
);
1198 pdg
[i
]->params
.size() != isl_set_dim(context
, isl_dim_param
)) {
1200 "Parameter dimension mismatch; context ignored\n");
1201 isl_set_free(context
);
1206 for (int i
= 0; i
< 2; ++i
) {
1207 dg
[i
] = pdg_to_dg(pdg
[i
], out_dim
, isl_set_copy(context
));
1212 if (options
->reachability
== REACH_TC
)
1213 res
= check_equivalence_tc(ctx
, dg
[0], dg
[1]);
1214 else if (options
->reachability
== REACH_FAST
)
1215 res
= check_equivalence_fast(ctx
, dg
[0], dg
[1]);
1216 else if (options
->reachability
== REACH_LEVER
)
1217 res
= check_equivalence_lever(ctx
, dg
[0], dg
[1]);
1218 else if (options
->reachability
== REACH_ASPIC
)
1219 res
= check_equivalence_aspic(ctx
, dg
[0], dg
[1]);
1221 for (int i
= 0; i
< 2; ++i
)
1223 isl_set_free(context
);