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 void print_linear(FILE *out
, __isl_keep isl_constraint
*c
, int s
,
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
);
424 isl_constraint_get_constant(c
, &v
);
427 if (isl_int_is_neg(v
))
429 isl_int_print(out
, v
, 0);
430 for (int i
= 0; i
< nparam
; ++i
) {
432 isl_constraint_get_coefficient(c
, isl_dim_param
, i
, &v
);
433 if (isl_int_is_zero(v
))
437 if (isl_int_is_pos(v
))
439 isl_int_print(out
, v
, 0);
440 name
= isl_constraint_get_dim_name(c
, isl_dim_param
, i
);
441 fprintf(out
, "*%s", name
);
443 for (int i
= 0; i
< n_in
; ++i
) {
444 isl_constraint_get_coefficient(c
, isl_dim_in
, i
, &v
);
445 if (isl_int_is_zero(v
))
449 if (isl_int_is_pos(v
))
451 isl_int_print(out
, v
, 0);
452 fprintf(out
, "*x%d", i
);
455 for (int i
= 0; i
< n_out
; ++i
) {
456 isl_constraint_get_coefficient(c
, isl_dim_out
, i
, &v
);
457 if (isl_int_is_zero(v
))
461 if (isl_int_is_pos(v
))
463 isl_int_print(out
, v
, 0);
464 fprintf(out
, "*x%d", n_in
+ i
);
466 for (int i
= 0; i
< n_div
; ++i
) {
467 isl_constraint_get_coefficient(c
, isl_dim_div
, i
, &v
);
468 if (isl_int_is_zero(v
))
472 if (isl_int_is_pos(v
))
474 isl_int_print(out
, v
, 0);
475 fprintf(out
, "*x%d", n_in
+ n_out
+ i
);
482 /* Print the constraints that go into the guard.
483 * If we are dealing with a translation, then we shouldn't print any
484 * constraints involving the output variables.
485 * Those constraint should be equalities defining the translation
486 * and will be printed in the action.
488 static int print_guard_constraint(__isl_take isl_constraint
*c
, void *user
)
490 transition_info
*ti
= (transition_info
*)user
;
495 n_out
= isl_constraint_dim(c
, isl_dim_out
);
499 if (ti
->translation
) {
500 for (int i
= 0; i
< n_out
; ++i
) {
501 isl_constraint_get_coefficient(c
, isl_dim_out
, i
, &v
);
502 if (!isl_int_is_zero(v
)) {
503 assert(isl_constraint_is_equality(c
));
504 isl_constraint_free(c
);
512 fprintf(ti
->out
, " && ");
513 print_linear(ti
->out
, c
, 1, 0);
514 if (isl_constraint_is_equality(c
))
515 fprintf(ti
->out
, " = 0");
517 fprintf(ti
->out
, " >= 0");
521 isl_constraint_free(c
);
527 /* Print the constraints that go into the action.
528 * That is, equality constraints that define one of the output variables.
530 static int print_action_constraint(__isl_take isl_constraint
*c
, void *user
)
532 transition_info
*ti
= (transition_info
*)user
;
539 if (!isl_constraint_is_equality(c
)) {
540 isl_constraint_free(c
);
544 n_in
= isl_constraint_dim(c
, isl_dim_in
);
545 n_out
= isl_constraint_dim(c
, isl_dim_out
);
549 for (out
= 0; out
< n_out
; ++out
) {
550 isl_constraint_get_coefficient(c
, isl_dim_out
, out
, &v
);
551 if (!isl_int_is_zero(v
))
556 isl_constraint_free(c
);
560 if (out
> ti
->last_out
)
566 fprintf(ti
->out
, ", ");
568 fprintf(ti
->out
, "x%d' = ", out
);
569 print_linear(ti
->out
, c
, s
, 1);
574 isl_constraint_free(c
);
578 /* A firing relation that is not a translation is encoded using
579 * several transitions. We first allow an unconditional transition
580 * to an extra state. In this state, we allow arbitrary increments
581 * on the extra variables that will correspond to the output variables
582 * and the existentially quantified variables of the relation. This allows
583 * those variables to attain any value, since they are initially set
584 * to zero and FAST and Lever only support non-negative variables.
585 * Finally, we add a transition from the extra
586 * state to the original target state with a guard that corresponds
587 * to the firing relation and an action that assigns the output
588 * variables to the main variables.
590 static int print_non_translation(__isl_take isl_basic_map
*firing
,
593 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
594 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
595 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
597 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
599 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
600 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
601 fprintf(ti
->out
, "\tguard := true;\n");
602 fprintf(ti
->out
, "\taction := x0' = x0");
604 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->from
);
605 fprintf(ti
->out
, "\taction := state' = %d", ti
->state
);
607 for (int i
= 0; i
< n_out
+ n_div
; ++i
)
608 fprintf(ti
->out
, ", x%d' = 0", n_in
+ i
);
609 fprintf(ti
->out
, ";\n");
610 fprintf(ti
->out
, "};\n");
614 for (int i
= 0; i
< n_out
+ n_div
; ++i
) {
615 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
617 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
618 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
619 fprintf(ti
->out
, "\tguard := true;\n");
621 fprintf(ti
->out
, "\tguard := state = %d;\n", ti
->state
);
622 fprintf(ti
->out
, "\taction := x%d' = x%d + 1;\n",
624 fprintf(ti
->out
, "};\n");
628 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
630 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
631 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
633 fprintf(ti
->out
, "\tguard := ");
637 fprintf(ti
->out
, "state = %d", ti
->state
);
640 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
642 fprintf(ti
->out
, "true");
643 fprintf(ti
->out
, ";\n");
644 fprintf(ti
->out
, "\taction := ");
645 for (int i
= 0; i
< n_out
; ++i
)
646 fprintf(ti
->out
, "%sx%d' = x%d", i
? ", " : "", i
, n_in
+ i
);
647 for (int i
= n_out
; i
< ti
->nvar
; ++i
)
648 fprintf(ti
->out
, ", x%d' = 0", i
);
650 fprintf(ti
->out
, ", state' = %d", ti
->to
);
651 fprintf(ti
->out
, ";\n");
652 fprintf(ti
->out
, "};\n");
658 isl_basic_map_free(firing
);
663 /* A firing relation that is a translation can be encoded directly
664 * into a transition. We simply put the translation in the action
665 * and the remaining constraints in the guard.
667 static int print_translation(__isl_take isl_basic_map
*firing
,
670 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
672 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
673 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
676 fprintf(ti
->out
, "\tguard := ");
680 fprintf(ti
->out
, "state = %d", ti
->from
);
683 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
685 fprintf(ti
->out
, "true");
686 fprintf(ti
->out
, ";\n");
688 fprintf(ti
->out
, "\taction := ");
692 fprintf(ti
->out
, "state' = %d", ti
->to
);
696 isl_basic_map_foreach_constraint(firing
, print_action_constraint
, ti
);
697 for (int i
= ti
->last_out
+ 1; i
< ti
->nvar
; ++i
)
698 fprintf(ti
->out
, "%sx%d' = 0",
699 (i
|| !ti
->first
) ? ", " : "", i
);
700 fprintf(ti
->out
, ";\n");
702 fprintf(ti
->out
, "};\n");
706 isl_basic_map_free(firing
);
711 /* Print one or more transitions based on the firing relation.
712 * The way a firing relation is encoded depends on whether it is
713 * a translation or not.
715 static int print_transition(__isl_take isl_basic_map
*firing
, void *user
)
717 transition_info
*ti
= (transition_info
*)user
;
719 ti
->translation
= is_translation(firing
);
720 if (!ti
->translation
)
721 return print_non_translation(firing
, ti
);
722 return print_translation(firing
, ti
);
732 static int print_region_constraint(__isl_take isl_constraint
*c
, void *user
)
734 region_info
*ri
= (region_info
*)user
;
736 fprintf(ri
->out
, " && ");
738 print_linear(ri
->out
, c
, 1, 0);
739 if (isl_constraint_is_equality(c
))
740 fprintf(ri
->out
, " = 0");
742 fprintf(ri
->out
, " >= 0");
744 isl_constraint_free(c
);
748 /* Print a convex part of a region.
749 * In FAST, states are designated by labels, while in Lever, they
750 * are encoded as integer, so we have to drop the first letter ("s")
753 static int print_region_basic_set(__isl_take isl_basic_set
*bset
, void *user
)
755 region_info
*ri
= (region_info
*)user
;
758 fprintf(ri
->out
, " || ");
760 fprintf(ri
->out
, "(");
761 fprintf(ri
->out
, "state = %s",
762 isl_basic_set_get_tuple_name(bset
) + ri
->name_offset
);
764 isl_basic_set_foreach_constraint(bset
, &print_region_constraint
, user
);
766 fprintf(ri
->out
, ")");
770 isl_basic_set_free(bset
);
774 static int print_region_set(__isl_take isl_set
*set
, void *user
)
776 isl_set_foreach_basic_set(set
, &print_region_basic_set
, user
);
782 static void print_region(FILE *out
, const char *name
,
783 __isl_keep isl_union_set
*region
)
791 fprintf(out
, "Region %s := { ", name
);
792 isl_union_set_foreach_set(region
, &print_region_set
, &ri
);
793 fprintf(out
, " };\n");
796 /* Construct a FAST model that tests whether all reachable
797 * final states are a subset of the good final states.
799 static int check_equivalence_fast(isl_ctx
*ctx
,
800 dependence_graph
*dg1
, dependence_graph
*dg2
)
809 isl_union_set
*final
;
812 if (check_input_arrays(dg1
, dg2
))
815 root
= msa
.add(dg1
->out
, dg2
->out
);
818 ti
.state
= msa
.state
.size();
821 compute_max_var(msa
, &ti
);
822 init
= compute_equal_output(dg1
, dg2
);
823 final
= compute_final_states(ctx
, msa
);
824 good
= compute_good_states(ctx
, msa
);
825 dim
= isl_union_set_get_space(init
);
826 nparam
= isl_space_dim(dim
, isl_dim_param
);
828 fprintf(out
, "model m1 {\n");
831 for (int i
= 0; i
< ti
.nvar
; ++i
)
832 fprintf(out
, "%s x%d", i
? "," : "", i
);
833 for (int i
= 0; i
< nparam
; ++i
)
834 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
837 fprintf(out
, "states");
838 for (int i
= 0; i
< ti
.state
; ++i
)
839 fprintf(out
, "%s s%d", i
? "," : "", i
);
844 ti
.state
= msa
.state
.size();
845 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
846 int f
= msa
.transition
[i
].second
;
847 ti
.from
= msa
.transition
[i
].first
.first
;
848 ti
.to
= msa
.transition
[i
].first
.second
;
849 isl_map_foreach_basic_map(msa
.firing
[f
],
850 &print_transition
, &ti
);
855 fprintf(out
, "strategy s1 {\n");
856 print_region(out
, "init", init
);
857 print_region(out
, "final", final
);
858 print_region(out
, "good", good
);
859 fprintf(out
, "Transitions t := {");
860 for (int i
= 0; i
< ti
.transition
; ++i
)
861 fprintf(out
, "%s t%d", i
? "," : "", i
);
862 fprintf(out
, " };\n");
863 fprintf(out
, "Region reach := post*(init, t);\n");
864 fprintf(out
, "if (subSet(reach && final, good))\n");
865 fprintf(out
, "\tthen print(\"OK\\n\");\n");
866 fprintf(out
, "\telse print(\"NOT OK\\n\");\n");
867 fprintf(out
, "endif\n");
871 isl_union_set_free(init
);
872 isl_union_set_free(final
);
873 isl_union_set_free(good
);
878 static void print_label(FILE *out
, const char *name
,
879 __isl_keep isl_union_set
*region
)
887 fprintf(out
, "label %s := { ", name
);
888 isl_union_set_foreach_set(region
, &print_region_set
, &ri
);
889 fprintf(out
, " };\n");
892 /* Construct a Lever model that tests whether the bad final states
895 static int check_equivalence_lever(isl_ctx
*ctx
,
896 dependence_graph
*dg1
, dependence_graph
*dg2
)
907 if (check_input_arrays(dg1
, dg2
))
910 root
= msa
.add(dg1
->out
, dg2
->out
);
913 ti
.state
= msa
.state
.size();
916 compute_max_var(msa
, &ti
);
917 init
= compute_equal_output(dg1
, dg2
);
918 bad
= isl_union_set_subtract(compute_final_states(ctx
, msa
),
919 compute_good_states(ctx
, msa
));
920 dim
= isl_union_set_get_space(init
);
921 nparam
= isl_space_dim(dim
, isl_dim_param
);
923 fprintf(out
, "var state");
924 for (int i
= 0; i
< ti
.nvar
; ++i
)
925 fprintf(out
, ", x%d", i
);
926 for (int i
= 0; i
< nparam
; ++i
)
927 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
930 fprintf(out
, "ctl_spec {\n");
931 print_label(out
, "bad", bad
);
934 fprintf(out
, "system {\n");
935 print_label(out
, "init", init
);
939 ti
.state
= msa
.state
.size();
940 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
941 int f
= msa
.transition
[i
].second
;
942 ti
.from
= msa
.transition
[i
].first
.first
;
943 ti
.to
= msa
.transition
[i
].first
.second
;
944 isl_map_foreach_basic_map(msa
.firing
[f
],
945 &print_transition
, &ti
);
951 isl_union_set_free(init
);
952 isl_union_set_free(bad
);
957 /* A firing relation that is not a translation is encoded in Aspic using
958 * using two transitions passing through an extra state.
959 * The first transition assigns arbitrary values to the extra variables
960 * that will correspond to the output variables and the existentially
961 * quantified variables of the relation.
962 * The second transition, from the extra
963 * state to the original target state, has a guard that corresponds
964 * to the firing relation and an action that assigns the output
965 * variables to the main variables.
967 static int print_aspic_non_translation(__isl_take isl_basic_map
*firing
,
970 unsigned n_in
= isl_basic_map_dim(firing
, isl_dim_in
);
971 unsigned n_out
= isl_basic_map_dim(firing
, isl_dim_out
);
972 unsigned n_div
= isl_basic_map_dim(firing
, isl_dim_div
);
974 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
975 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->from
);
976 fprintf(ti
->out
, "\tto := s%d;\n", ti
->state
);
977 fprintf(ti
->out
, "\tguard := true;\n");
978 fprintf(ti
->out
, "\taction := ");
979 for (int i
= 0; i
< n_out
+ n_div
; ++i
)
980 fprintf(ti
->out
, "%sx%d' = ?", i
? ", " : "", n_in
+ i
);
981 fprintf(ti
->out
, ";\n");
983 fprintf(ti
->out
, "};\n");
987 fprintf(ti
->out
, "transition t%d := {\n", ti
->transition
);
989 fprintf(ti
->out
, "\tfrom := s%d;\n", ti
->state
);
990 fprintf(ti
->out
, "\tto := s%d;\n", ti
->to
);
992 fprintf(ti
->out
, "\tguard := ");
996 fprintf(ti
->out
, "state = %d", ti
->state
);
999 isl_basic_map_foreach_constraint(firing
, print_guard_constraint
, ti
);
1001 fprintf(ti
->out
, "true");
1002 fprintf(ti
->out
, ";\n");
1003 fprintf(ti
->out
, "\taction := ");
1004 for (int i
= 0; i
< n_out
; ++i
)
1005 fprintf(ti
->out
, "%sx%d' = x%d", i
? ", " : "", i
, n_in
+ i
);
1006 for (int i
= n_out
; i
< ti
->nvar
; ++i
)
1007 fprintf(ti
->out
, ", x%d' = 0", i
);
1009 fprintf(ti
->out
, ", state' = %d", ti
->to
);
1010 fprintf(ti
->out
, ";\n");
1011 fprintf(ti
->out
, "};\n");
1017 isl_basic_map_free(firing
);
1022 /* Print one or more transitions based on the firing relation.
1023 * The way a firing relation is encoded depends on whether it is
1024 * a translation or not.
1026 static int print_aspic_transition(__isl_take isl_basic_map
*firing
, void *user
)
1028 transition_info
*ti
= (transition_info
*)user
;
1030 ti
->translation
= is_translation(firing
);
1031 if (!ti
->translation
)
1032 return print_aspic_non_translation(firing
, ti
);
1033 return print_translation(firing
, ti
);
1036 /* Print a transition from a bad final state to the canonical bad state.
1038 static int print_bad_aspic_transition(__isl_take isl_map
*firing
, void *user
)
1040 transition_info
*ti
= (transition_info
*)user
;
1043 name
= isl_map_get_tuple_name(firing
, isl_dim_in
);
1044 ti
->from
= atoi(name
+ 1);
1046 isl_map_foreach_basic_map(firing
, &print_aspic_transition
, user
);
1048 isl_map_free(firing
);
1053 /* Construct an Aspic model that tests whether the bad final states
1054 * are unreachable. Since Aspic only supports convex "bad" regions,
1055 * we add an extra state with transitions from all instances of states
1056 * that are bad. The bad region is then simply any instance of that
1059 static int check_equivalence_aspic(isl_ctx
*ctx
,
1060 dependence_graph
*dg1
, dependence_graph
*dg2
)
1068 isl_union_set
*init
;
1070 isl_union_map
*bad_map
;
1072 if (check_input_arrays(dg1
, dg2
))
1075 root
= msa
.add(dg1
->out
, dg2
->out
);
1078 ti
.state
= msa
.state
.size();
1081 compute_max_var(msa
, &ti
);
1082 init
= compute_equal_output(dg1
, dg2
);
1083 bad
= isl_union_set_subtract(compute_final_states(ctx
, msa
),
1084 compute_good_states(ctx
, msa
));
1085 dim
= isl_union_set_get_space(init
);
1086 nparam
= isl_space_dim(dim
, isl_dim_param
);
1088 fprintf(out
, "model m1 {\n");
1090 fprintf(out
, "var");
1091 for (int i
= 0; i
< ti
.nvar
; ++i
)
1092 fprintf(out
, "%s x%d", i
? "," : "", i
);
1093 for (int i
= 0; i
< nparam
; ++i
)
1094 fprintf(out
, ", %s", isl_space_get_dim_name(dim
, isl_dim_param
, i
));
1095 fprintf(out
, ";\n");
1097 fprintf(out
, "states");
1098 for (int i
= 0; i
< ti
.state
+ 1; ++i
)
1099 fprintf(out
, "%s s%d", i
? "," : "", i
);
1100 fprintf(out
, ";\n");
1104 ti
.state
= msa
.state
.size();
1105 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
1106 int f
= msa
.transition
[i
].second
;
1107 ti
.from
= msa
.transition
[i
].first
.first
;
1108 ti
.to
= msa
.transition
[i
].first
.second
;
1109 isl_map_foreach_basic_map(msa
.firing
[f
],
1110 &print_aspic_transition
, &ti
);
1113 bad_map
= isl_union_map_from_domain_and_range(bad
,
1114 isl_union_set_from_set(isl_set_universe(dim
)));
1116 isl_union_map_foreach_map(bad_map
, &print_bad_aspic_transition
, &ti
);
1118 fprintf(out
, "}\n");
1120 fprintf(out
, "strategy s1 {\n");
1121 print_region(out
, "init", init
);
1122 fprintf(out
, "Region bad := { state = s%d };\n", ti
.state
);
1123 fprintf(out
, "}\n");
1125 isl_union_set_free(init
);
1126 isl_union_map_free(bad_map
);
1131 int main(int argc
, char *argv
[])
1134 struct options
*options
= options_new_with_defaults();
1135 isl_set
*context
= NULL
;
1136 dependence_graph
*dg
[2];
1139 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
1141 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
1143 fprintf(stderr
, "Unable to allocate ctx\n");
1147 if (options
->context
)
1148 context
= isl_set_read_from_str(ctx
, options
->context
);
1151 unsigned out_dim
= 0;
1152 for (int i
= 0; i
< 2; ++i
) {
1154 in
= fopen(options
->program
[i
], "r");
1156 fprintf(stderr
, "Unable to open %s\n", options
->program
[i
]);
1159 pdg
[i
] = yaml::Load
<pdg::PDG
>(in
, ctx
);
1162 fprintf(stderr
, "Unable to read %s\n", options
->program
[i
]);
1165 out_dim
= update_out_dim(pdg
[i
], out_dim
);
1168 pdg
[i
]->params
.size() != isl_set_dim(context
, isl_dim_param
)) {
1170 "Parameter dimension mismatch; context ignored\n");
1171 isl_set_free(context
);
1176 for (int i
= 0; i
< 2; ++i
) {
1177 dg
[i
] = pdg_to_dg(pdg
[i
], out_dim
, isl_set_copy(context
));
1182 if (options
->reachability
== REACH_TC
)
1183 res
= check_equivalence_tc(ctx
, dg
[0], dg
[1]);
1184 else if (options
->reachability
== REACH_FAST
)
1185 res
= check_equivalence_fast(ctx
, dg
[0], dg
[1]);
1186 else if (options
->reachability
== REACH_LEVER
)
1187 res
= check_equivalence_lever(ctx
, dg
[0], dg
[1]);
1188 else if (options
->reachability
== REACH_ASPIC
)
1189 res
= check_equivalence_aspic(ctx
, dg
[0], dg
[1]);
1191 for (int i
= 0; i
< 2; ++i
)
1193 isl_set_free(context
);