18 #include <isl/constraint.h>
19 #include <isl/union_set.h>
20 #include "eqv_options.h"
21 #include "dependence_graph.h"
24 std::vector
<const char *> associative
;
25 std::vector
<const char *> commutative
;
27 int ops_init(void *user
)
29 struct ops
**ops
= (struct ops
**)user
;
30 *ops
= new struct ops
;
33 void ops_clear(void *user
)
35 struct ops
**ops
= (struct ops
**)user
;
39 /* For each basic map in relation, add an edge to comp with
40 * given source and pos.
42 static void add_split_edge(computation
*comp
, computation
*source
,
43 int pos
, isl_map
*relation
, std::vector
<computation
**> *missing
,
44 enum edge::type type
= edge::normal
)
50 missing
->push_back(&e
->source
);
53 e
->relation
= relation
;
55 comp
->edges
.push_back(e
);
58 static unsigned update_out_dim(pdg::PDG
*pdg
, unsigned out_dim
)
60 for (int i
= 0; i
< pdg
->arrays
.size(); ++i
) {
61 pdg::array
*array
= pdg
->arrays
[i
];
62 if (array
->type
!= pdg::array::output
)
64 if (array
->dims
.size() + 1 > out_dim
)
65 out_dim
= array
->dims
.size() + 1;
71 static bool is_associative(const std::vector
<const char *> &associative
,
74 std::vector
<const char *>::const_iterator iter
;
76 for (iter
= associative
.begin(); iter
!= associative
.end(); ++iter
)
77 if (!strcmp(*iter
, op
))
82 /* Return a computation that has an associative operation
83 * that takes a different computation with the same operation
84 * as one of its arguments. Also return the edge from the
85 * first to the second computation in *e.
87 * If no such computation exists, then return NULL.
89 computation
*dependence_graph::associative_node(edge
**e
,
90 const std::vector
<const char *> &associative
)
92 for (int i
= 0; i
< vertices
.size(); ++i
) {
93 computation
*comp
= vertices
[i
];
94 if (!is_associative(associative
, comp
->operation
))
96 for (int j
= 0; j
< comp
->edges
.size(); ++j
) {
97 computation
*other
= comp
->edges
[j
]->source
;
98 if (comp
->has_same_source(other
))
100 if (strcmp(comp
->operation
, other
->operation
))
109 /* Splice the source of edge edge into the position of edge e,
110 * removing all other edges with the same positions and bumping
111 * up edges with a greater position.
113 void dependence_graph::splice(computation
*comp
, edge
*e
)
115 computation
*source
= e
->source
;
117 isl_map
*map
= isl_map_copy(e
->relation
);
118 std::vector
<struct edge
*> new_edges
;
120 for (int i
= 0; i
< comp
->edges
.size(); ++i
) {
121 if (comp
->edges
[i
]->pos
== pos
) {
122 delete comp
->edges
[i
];
125 if (comp
->edges
[i
]->pos
> pos
)
126 comp
->edges
[i
]->pos
+= source
->arity
- 1;
127 new_edges
.push_back(comp
->edges
[i
]);
129 for (int i
= 0; i
< source
->edges
.size(); ++i
) {
130 edge
*old_e
= source
->edges
[i
];
132 e
->source
= old_e
->source
;
133 e
->pos
= old_e
->pos
+ pos
;
134 e
->relation
= isl_map_apply_range(
136 isl_map_copy(old_e
->relation
));
137 new_edges
.push_back(e
);
140 comp
->edges
= new_edges
;
141 comp
->arity
+= source
->arity
- 1;
144 /* Split computation comp into a part that always takes the edge e
145 * and one that never takes that edge.
146 * The second is returned and the initial computation is modified
147 * to match the first.
149 * We need to be careful not to destroy e, as it is still used
150 * by the calling method.
152 computation
*dependence_graph::split_comp(computation
*comp
, edge
*e
)
154 computation
*dup
= new computation
;
155 dup
->original
= comp
->original
? comp
->original
: comp
;
156 dup
->operation
= strdup(comp
->operation
);
157 dup
->arity
= comp
->arity
;
158 dup
->location
= comp
->location
;
160 isl_map
*map
= isl_map_copy(e
->relation
);
161 map
= isl_map_reverse(map
);
162 isl_set
*dom
= isl_set_apply(isl_set_copy(e
->source
->domain
), map
);
163 dup
->domain
= isl_set_subtract(isl_set_copy(comp
->domain
),
165 comp
->domain
= isl_set_intersect(comp
->domain
, dom
);
167 std::vector
<struct edge
*> old_edges
= comp
->edges
;
170 for (int i
= 0; i
< old_edges
.size(); ++i
) {
171 if (old_edges
[i
] == e
) {
172 comp
->edges
.push_back(e
);
176 edge
*e
= old_edges
[i
];
177 isl_map
*map
, *map_dup
;
178 map
= isl_map_copy(e
->relation
);
179 map_dup
= isl_map_copy(map
);
181 map
= isl_map_intersect_domain(map
, isl_set_copy(comp
->domain
));
182 map_dup
= isl_map_intersect_domain(map_dup
,
183 isl_set_copy(dup
->domain
));
184 add_split_edge(comp
, e
->source
, e
->pos
, map
, NULL
);
185 add_split_edge(dup
, e
->source
, e
->pos
, map_dup
, NULL
);
189 vertices
.push_back(dup
);
193 /* If any edge from comp points to comp_orig, then split it
194 * into two edges, one still pointing to comp_orig and the
195 * other pointing to comp_dup.
196 * comp_orig and comp_dup are assumed to have disjoint domains
197 * and the edge relations are adjusted according to these domains.
199 void dependence_graph::split_edges(computation
*comp
,
200 computation
*comp_orig
, computation
*comp_dup
)
202 std::vector
<struct edge
*> old_edges
= comp
->edges
;
205 for (int i
= 0; i
< old_edges
.size(); ++i
) {
206 edge
*e
= old_edges
[i
];
208 if (e
->source
!= comp_orig
) {
209 comp
->edges
.push_back(e
);
213 isl_map
*map_orig
, *map_dup
;
214 map_orig
= isl_map_copy(e
->relation
);
215 map_dup
= isl_map_copy(map_orig
);
216 map_orig
= isl_map_intersect_range(map_orig
,
217 isl_set_copy(comp_orig
->domain
));
218 map_dup
= isl_map_intersect_range(map_dup
,
219 isl_set_copy(comp_dup
->domain
));
220 add_split_edge(comp
, comp_orig
, e
->pos
, map_orig
, NULL
);
221 add_split_edge(comp
, comp_dup
, e
->pos
, map_dup
, NULL
);
226 void dependence_graph::split_edges(computation
*comp_orig
,
227 computation
*comp_dup
)
229 split_edges(out
, comp_orig
, comp_dup
);
230 for (int i
= 0; i
< vertices
.size(); ++i
)
231 split_edges(vertices
[i
], comp_orig
, comp_dup
);
234 /* Replace all nested calls of an associative operator,
235 * by a call with the nested call spliced into the first call.
237 * For each nested call we find, we first check if there are
238 * any other edges with the same position. If not, then
239 * the nested call is performed for each iteration of the computation
240 * and we can simplify splice the nested call.
242 * Otherwise, we first create a duplicate computation for the iterations
243 * that do not take the found edge and adjust the edges of both computations
244 * to their domains. This meand that the edge corresponding to the
245 * nested call will no longer appear in the duplicated computation
246 * and the other edges with the same position will no longer appear
247 * in the original computation.
248 * Then we splice the nested call in the original computation.
249 * Finally, we split all edges that pointed to the original computation
250 * into two edges, one going to the original computation and one
251 * going to the duplicated computation.
253 void dependence_graph::flatten_associative_operators(
254 const std::vector
<const char *> &associative
)
259 while ((comp
= associative_node(&e
, associative
)) != NULL
) {
260 computation
*comp_dup
= NULL
;
262 for (j
= 0; j
< comp
->edges
.size(); ++j
) {
263 if (comp
->edges
[j
] == e
)
265 if (comp
->edges
[j
]->pos
== e
->pos
)
268 if (j
!= comp
->edges
.size())
269 comp_dup
= split_comp(comp
, e
);
272 split_edges(comp
, comp_dup
);
281 computation
*comp
[2];
284 std::set
<eq_node
*> assumed
;
286 unsigned narrowing
: 1;
287 unsigned widening
: 1;
288 unsigned invalidated
: 1;
291 isl_map
*lost_sample
;
293 eq_node(computation
*c1
, computation
*c2
,
294 isl_map
*w
) : want(w
), closed(0),
295 widening(0), narrowing(0), invalidated(0), reset(0),
296 need(NULL
), got(NULL
), lost(NULL
), lost_sample(NULL
) {
300 bool is_still_valid();
301 void collect_open_assumed(std::set
<eq_node
*> &c
);
307 isl_map_free(lost_sample
);
311 got
= isl_map_copy(want
);
312 got
= isl_map_subtract(got
, isl_map_copy(lost
));
317 return isl_map_copy(got
);
319 isl_map
*peek_got() {
324 void compute_lost() {
326 lost
= isl_map_copy(want
);
327 lost
= isl_map_subtract(lost
, isl_map_copy(got
));
329 isl_map
*get_lost() {
332 return isl_map_copy(lost
);
334 isl_map
*peek_lost() {
339 void set_got(isl_map
*got
) {
340 isl_map_free(this->lost
);
341 isl_map_free(this->got
);
345 void set_lost(isl_map
*lost
) {
346 isl_map_free(this->lost
);
347 isl_map_free(this->got
);
351 std::string
to_string();
354 std::string
eq_node::to_string()
356 std::ostringstream strm
;
357 strm
<< comp
[0]->location
<< "," << comp
[0]->operation
358 << "/" << comp
[0]->arity
;
360 strm
<< comp
[1]->location
<< "," << comp
[1]->operation
361 << "/" << comp
[1]->arity
;
366 bool eq_node::is_still_valid()
371 std::set
<eq_node
*>::iterator i
;
372 for (i
= assumed
.begin(); i
!= assumed
.end(); ++i
) {
375 ((*i
)->closed
&& !(*i
)->is_still_valid())) {
383 void eq_node::collect_open_assumed(std::set
<eq_node
*> &c
)
385 std::set
<eq_node
*>::iterator i
;
386 for (i
= assumed
.begin(); i
!= assumed
.end(); ++i
) {
388 (*i
)->collect_open_assumed(c
);
394 /* A comp_pair contains all the edges that have the same pair
398 computation
*comp
[2];
400 std::vector
<eq_node
*> nodes
;
402 eq_node
*tabled(eq_node
*node
);
403 eq_node
*last_ancestor(eq_node
*node
);
407 comp_pair::~comp_pair()
409 std::vector
<eq_node
*>::iterator i
;
410 for (i
= nodes
.begin(); i
!= nodes
.end(); ++i
)
414 eq_node
*comp_pair::tabled(eq_node
*node
)
416 std::vector
<eq_node
*>::iterator i
;
418 for (i
= nodes
.begin(); i
!= nodes
.end(); ++i
) {
423 if (!(*i
)->is_still_valid())
426 is_subset
= isl_map_is_subset(node
->want
, (*i
)->want
);
427 assert(is_subset
>= 0);
435 eq_node
*comp_pair::last_ancestor(eq_node
*node
)
437 std::vector
<eq_node
*>::reverse_iterator i
;
438 for (i
= nodes
.rbegin(); i
!= nodes
.rend(); ++i
) {
448 typedef std::pair
<computation
*, computation
*> computation_pair
;
449 typedef std::map
<computation_pair
, comp_pair
*> c2p_t
;
451 struct equivalence_checker
{
453 struct options
*options
;
457 equivalence_checker(struct options
*o
) :
458 options(o
), widenings(0), narrowings(0) {}
459 comp_pair
*get_comp_pair(eq_node
*node
);
460 void handle(eq_node
*node
);
461 void dismiss(eq_node
*node
);
462 void handle_propagation(eq_node
*node
);
463 void handle_copy(eq_node
*node
, int i
);
464 void handle_widening(eq_node
*node
, eq_node
*a
);
465 void handle_with_ancestor(eq_node
*node
, eq_node
*a
);
466 isl_map
*lost_at_pos(eq_node
*node
, int pos1
, int pos2
);
467 void handle_propagation_same(eq_node
*node
);
468 void handle_propagation_comm(eq_node
*node
);
469 void handle_narrowing(eq_node
*node
);
471 isl_map
*lost_from_propagation(eq_node
*parent
,
472 eq_node
*node
, edge
*e1
, edge
*e2
);
474 void init_trace(eq_node
*node
);
475 void extend_trace_propagation(eq_node
*node
, eq_node
*child
,
477 void extend_trace_widening(eq_node
*node
, eq_node
*child
);
479 bool is_commutative(const char *op
);
481 ~equivalence_checker();
484 equivalence_checker::~equivalence_checker()
488 for (i
= c2p
.begin(); i
!= c2p
.end(); ++i
)
492 bool equivalence_checker::is_commutative(const char *op
)
494 std::vector
<const char *>::iterator iter
;
496 for (iter
= options
->ops
->commutative
.begin();
497 iter
!= options
->ops
->commutative
.end(); ++iter
)
498 if (!strcmp(*iter
, op
))
503 comp_pair
*equivalence_checker::get_comp_pair(eq_node
*node
)
508 i
= c2p
.find(computation_pair(node
->comp
[0], node
->comp
[1]));
509 if (i
== c2p
.end()) {
511 c2p
[computation_pair(node
->comp
[0], node
->comp
[1])] = cp
;
517 void equivalence_checker::dismiss(eq_node
*node
)
519 /* unclosed nodes weren't added to c2p->nodes */
520 if (node
&& !node
->closed
)
524 void equivalence_checker::init_trace(eq_node
*node
)
529 if (!options
->trace_error
)
531 if (isl_map_is_empty(node
->peek_lost()))
533 node
->trace
= node
->to_string();
534 std::cerr
<< node
->trace
;
535 isl_map_free(node
->lost_sample
);
536 node
->lost_sample
= isl_map_from_basic_map(
537 isl_map_sample(node
->get_lost()));
538 ctx
= isl_map_get_ctx(node
->lost_sample
);
539 prn
= isl_printer_to_file(ctx
, stderr
);
540 prn
= isl_printer_print_map(prn
, node
->lost_sample
);
541 prn
= isl_printer_end_line(prn
);
542 isl_printer_free(prn
);
545 void equivalence_checker::extend_trace_propagation(eq_node
*node
,
546 eq_node
*child
, edge
*e1
, edge
*e2
)
551 if (!options
->trace_error
)
553 if (!child
->lost_sample
)
555 if (node
->lost_sample
)
557 node
->trace
= child
->trace
+ node
->to_string();
558 std::cerr
<< node
->trace
;
559 node
->lost_sample
= isl_map_copy(child
->lost_sample
);
561 node
->lost_sample
= isl_map_apply_domain(node
->lost_sample
,
563 isl_map_copy(e1
->relation
)));
565 node
->lost_sample
= isl_map_apply_range(node
->lost_sample
,
567 isl_map_copy(e2
->relation
)));
568 node
->lost_sample
= isl_map_intersect(node
->lost_sample
,
569 isl_map_copy(node
->want
));
570 node
->lost_sample
= isl_map_from_basic_map(
571 isl_map_sample(node
->lost_sample
));
572 ctx
= isl_map_get_ctx(node
->lost_sample
);
573 prn
= isl_printer_to_file(ctx
, stderr
);
574 prn
= isl_printer_print_map(prn
, node
->lost_sample
);
575 prn
= isl_printer_end_line(prn
);
576 isl_printer_free(prn
);
579 void equivalence_checker::extend_trace_widening(eq_node
*node
, eq_node
*child
)
584 if (!options
->trace_error
)
586 if (!child
->lost_sample
)
588 if (isl_map_is_empty(node
->peek_lost()))
590 node
->trace
= child
->trace
+ node
->to_string();
591 std::cerr
<< node
->trace
;
592 node
->lost_sample
= isl_map_from_basic_map(
593 isl_map_sample(node
->get_lost()));
594 ctx
= isl_map_get_ctx(node
->lost_sample
);
595 prn
= isl_printer_to_file(ctx
, stderr
);
596 prn
= isl_printer_print_map(prn
, node
->lost_sample
);
597 prn
= isl_printer_end_line(prn
);
598 isl_printer_free(prn
);
601 /* Check for which subset (got) of the want relation equivelance
602 * holds for the pair of computations in the equivalence node.
604 * We first handle the easy cases: empty want, input computations
607 * If the current node is not a narrowing or a widening node
608 * and we can find an ancestor with the same pair of computations,
609 * then we will try to apply induction or widening in handle_with_ancestor.
610 * However, if the requested relation (want) of the ancestor is a strict
611 * subset of that of the current node, then we have already applied
612 * widening on an intermediate node (with a different pair of computations)
613 * so we shouldn't apply widening again (and we can't apply induction
614 * because the relation of the ancestor is a strict subset).
616 * In all other cases we try to apply propagation in handle_propagation.
618 void equivalence_checker::handle(eq_node
*node
)
620 computation
*comp1
= node
->comp
[0];
621 computation
*comp2
= node
->comp
[1];
623 if (!comp1
->is_copy() && !comp2
->is_copy() &&
624 (strcmp(comp1
->operation
, comp2
->operation
) ||
625 comp1
->arity
!= comp2
->arity
)) {
626 node
->set_lost(isl_map_copy(node
->want
));
634 isl_map
*want
= node
->want
;
635 node
->need
= isl_map_empty(isl_map_get_space(want
));
636 int empty
= isl_map_is_empty(want
);
639 node
->set_lost(isl_map_empty(isl_map_get_space(want
)));
642 if (node
->comp
[0]->is_input() && node
->comp
[1]->is_input()) {
643 if (strcmp(node
->comp
[0]->operation
, node
->comp
[1]->operation
)) {
644 node
->set_lost(isl_map_copy(want
));
647 node
->set_lost(isl_map_subtract(
648 isl_map_copy(node
->want
),
649 isl_map_identity(isl_map_get_space(want
))));
653 comp_pair
*cp
= get_comp_pair(node
);
654 if ((s
= cp
->tabled(node
)) != NULL
) {
655 node
->set_lost(isl_map_intersect(s
->get_lost(),
656 isl_map_copy(node
->want
)));
657 s
->collect_open_assumed(node
->assumed
);
661 cp
->nodes
.push_back(node
);
663 if (!node
->narrowing
&& !node
->widening
&&
664 (a
= cp
->last_ancestor(node
)) != NULL
) {
666 is_subset
= isl_map_is_strict_subset(a
->want
, node
->want
);
667 assert(is_subset
>= 0);
669 handle_propagation(node
);
671 handle_with_ancestor(node
, a
);
673 handle_propagation(node
);
677 /* Check if we can apply propagation to prove equivalence of the given node.
678 * First check if we need to apply copy propagation and if not
679 * check if the operations are the same and apply "regular" propagation.
681 * After we get back, we need to check that any induction hypotheses
682 * we have used in the process of proving the node hold.
683 * If not, we replace the obtained relation (got) by that
684 * of a narrowing node in handle_narrowin.
686 void equivalence_checker::handle_propagation(eq_node
*node
)
688 if (node
->comp
[0]->is_copy())
689 handle_copy(node
, 0);
690 else if (node
->comp
[1]->is_copy())
691 handle_copy(node
, 1);
693 handle_propagation_same(node
);
694 node
->assumed
.erase(node
);
695 isl_map
*lost
= node
->get_lost();
696 lost
= isl_map_intersect(lost
, isl_map_copy(node
->need
));
697 int is_empty
= isl_map_is_empty(lost
);
699 assert(is_empty
>= 0);
701 handle_narrowing(node
);
704 /* When applying the mappings on expansion edges to both sides of
705 * an equivalence relation, each element in the original equivalence
706 * relation is mapped to many elements on both sides. For example,
707 * if the original equivalence relation has i R i' for i = i', then the new
708 * equivalence relation may have (i,j) R (i',j') for i = i' and
709 * 0 <= j,j' <= 10, expressing that all elements of the row read by
710 * iteration i should be equal to all elements of the row read by i'.
711 * Instead, we want to express that each individual element read by i'
712 * should be equal to the corresponding element read by i'.
713 * In the example, we need to introduce the equality j = j'.
715 * We first check that the number of dimensions added by the expansions
716 * is the same in both programs. Then we construct an identity relation
717 * between this number of dimensions, lift it to the space of the
718 * new equivalence relation and take the intersection.
720 * We have to be careful, though, that we don't loose any array elements
721 * by taking the intersection. In particular, the expansion maps
722 * a given read operation to iterations of an extended domain that
723 * reads all the individual array elements. The read operation is
724 * only equivalent to some other read operation if all the reads
725 * of the individual array elements are equivalent.
726 * We therefore need to make sure that adding the equalities does
727 * not remove any of the reads in the extended domain.
728 * We do this by projecting out the extra dimensions on one side
729 * from both "want" and "want \cap eq". The resulting maps should
730 * be the same. We do this check for both sides separately.
732 * If either of the above tests fails, then we simply return
733 * the original over-ambitious want.
735 static isl_map
*expansion_want(isl_map
*want
, edge
*e1
, edge
*e2
)
737 unsigned n_in
, n_out
;
738 unsigned s_dim_1
= isl_map_dim(e1
->relation
, isl_dim_out
) -
739 isl_map_dim(e1
->relation
, isl_dim_in
);
740 unsigned s_dim_2
= isl_map_dim(e2
->relation
, isl_dim_out
) -
741 isl_map_dim(e2
->relation
, isl_dim_in
);
742 if (s_dim_1
!= s_dim_2
)
745 isl_space
*dim
= isl_map_get_space(e1
->relation
);
746 dim
= isl_space_drop_dims(dim
, isl_dim_in
,
747 0, isl_space_dim(dim
, isl_dim_in
));
748 dim
= isl_space_drop_dims(dim
, isl_dim_out
,
749 0, isl_space_dim(dim
, isl_dim_out
));
750 dim
= isl_space_add_dims(dim
, isl_dim_in
, s_dim_1
);
751 dim
= isl_space_add_dims(dim
, isl_dim_out
, s_dim_1
);
752 isl_basic_map
*id
= isl_basic_map_identity(dim
);
754 dim
= isl_space_range(isl_map_get_space(e1
->relation
));
755 isl_basic_map
*s_1
= isl_basic_map_identity(isl_space_map_from_set(dim
));
756 s_1
= isl_basic_map_remove_dims(s_1
, isl_dim_in
, 0,
757 isl_basic_map_dim(s_1
, isl_dim_in
) - s_dim_1
);
758 id
= isl_basic_map_apply_domain(id
, s_1
);
760 dim
= isl_space_range(isl_map_get_space(e2
->relation
));
761 isl_basic_map
*s_2
= isl_basic_map_identity(isl_space_map_from_set(dim
));
762 s_2
= isl_basic_map_remove_dims(s_2
, isl_dim_in
, 0,
763 isl_basic_map_dim(s_2
, isl_dim_in
) - s_dim_2
);
764 id
= isl_basic_map_apply_range(id
, s_2
);
766 bool unmatched
= false;
767 isl_map
*matched_want
;
768 isl_map
*proj_want
, *proj_matched
;
769 matched_want
= isl_map_intersect(isl_map_copy(want
),
770 isl_map_from_basic_map(id
));
772 n_in
= isl_map_dim(want
, isl_dim_in
);
773 proj_want
= isl_map_remove_dims(isl_map_copy(want
),
774 isl_dim_in
, n_in
- s_dim_1
, s_dim_1
);
775 proj_matched
= isl_map_remove_dims(isl_map_copy(matched_want
),
776 isl_dim_in
, n_in
- s_dim_1
, s_dim_1
);
777 if (!isl_map_is_equal(proj_want
, proj_matched
))
779 isl_map_free(proj_want
);
780 isl_map_free(proj_matched
);
782 n_out
= isl_map_dim(want
, isl_dim_out
);
783 proj_want
= isl_map_remove_dims(isl_map_copy(want
),
784 isl_dim_out
, n_out
- s_dim_2
, s_dim_2
);
785 proj_matched
= isl_map_remove_dims(isl_map_copy(matched_want
),
786 isl_dim_out
, n_out
- s_dim_2
, s_dim_2
);
787 if (!isl_map_is_equal(proj_want
, proj_matched
))
789 isl_map_free(proj_want
);
790 isl_map_free(proj_matched
);
793 isl_map_free(matched_want
);
800 static isl_map
*propagation_want(eq_node
*node
, edge
*e1
, edge
*e2
)
804 new_want
= isl_map_copy(node
->want
);
806 new_want
= isl_map_apply_domain(new_want
,
807 isl_map_copy(e1
->relation
));
809 new_want
= isl_map_apply_range(new_want
,
810 isl_map_copy(e2
->relation
));
812 if (e1
&& e2
&& e1
->type
== edge::expansion
)
813 new_want
= expansion_want(new_want
, e1
, e2
);
815 return isl_map_detect_equalities(new_want
);
818 static eq_node
*propagation_node(eq_node
*node
, edge
*e1
, edge
*e2
)
821 computation
*comp1
= e1
? e1
->source
: node
->comp
[0];
822 computation
*comp2
= e2
? e2
->source
: node
->comp
[1];
824 new_want
= propagation_want(node
, e1
, e2
);
825 eq_node
*child
= new eq_node(comp1
, comp2
, new_want
);
829 static isl_map
*set_to_empty(isl_map
*map
)
832 space
= isl_map_get_space(map
);
834 return isl_map_empty(space
);
837 /* Propagate "lost" from child back to parent.
839 isl_map
*equivalence_checker::lost_from_propagation(eq_node
*parent
,
840 eq_node
*node
, edge
*e1
, edge
*e2
)
843 new_lost
= node
->get_lost();
846 new_lost
= isl_map_apply_domain(new_lost
,
847 isl_map_reverse(isl_map_copy(e1
->relation
)));
849 new_lost
= isl_map_apply_range(new_lost
,
850 isl_map_reverse(isl_map_copy(e2
->relation
)));
852 new_lost
= isl_map_intersect(new_lost
, isl_map_copy(parent
->want
));
854 extend_trace_propagation(parent
, node
, e1
, e2
);
859 void equivalence_checker::handle_copy(eq_node
*node
, int i
)
861 std::vector
<edge
*>::iterator iter
;
862 computation
*copy
= node
->comp
[i
];
863 isl_map
*want
= node
->want
;
866 lost
= isl_map_empty(isl_map_get_space(want
));
868 for (iter
= copy
->edges
.begin(); iter
!= copy
->edges
.end(); ++iter
) {
872 child
= propagation_node(node
, e
, NULL
);
874 child
= propagation_node(node
, NULL
, e
);
883 new_lost
= lost_from_propagation(node
, child
, e
, NULL
);
885 new_lost
= lost_from_propagation(node
, child
, NULL
, e
);
888 lost
= isl_map_union_disjoint(lost
, new_lost
);
890 node
->set_lost(lost
);
893 /* Compute and return the part of "want" that is lost when propagating
894 * over all edges of argument position pos1 in the first program
895 * and argument position pos2 in the second program.
896 * Since the domains of the dependence mappings on edges with the same
897 * argument position partition the domain of the computation (and are
898 * therefore disjoint), we simply need to take the disjoint union
899 * of all losts over all pairs of edges.
901 isl_map
*equivalence_checker::lost_at_pos(eq_node
*node
, int pos1
, int pos2
)
904 lost
= isl_map_empty(isl_map_get_space(node
->want
));
906 for (int i
= 0; i
< node
->comp
[0]->edges
.size(); ++i
) {
907 edge
*e1
= node
->comp
[0]->edges
[i
];
910 for (int j
= 0; j
< node
->comp
[1]->edges
.size(); ++j
) {
911 edge
*e2
= node
->comp
[1]->edges
[j
];
916 isl_map
*new_lost
= NULL
;
918 child
= propagation_node(node
, e1
, e2
);
920 new_lost
= lost_from_propagation(node
, child
, e1
, e2
);
922 lost
= isl_map_union_disjoint(lost
, new_lost
);
924 std::set
<eq_node
*>::iterator k
;
925 for (k
= child
->assumed
.begin();
926 k
!= child
->assumed
.end(); ++k
)
927 node
->assumed
.insert(*k
);
934 /* Compute the lost that results from propagation on a pair of
935 * computations with a commutative operation.
937 * We first compute the losts for each pair of argument positions
938 * and store the result in lost[pos1][pos2].
939 * Then we perform a backtracking search over all permutations
940 * of the arguments. For each permutation, we compute the lost
941 * relation as the union of the losts over all arguments.
942 * The final lost is the intersection of all these losts over all
945 void equivalence_checker::handle_propagation_comm(eq_node
*node
)
947 int trace_error
= options
->trace_error
;
950 unsigned r
= node
->comp
[0]->arity
;
952 std::vector
<std::vector
<isl_map
*> > lost
;
954 for (int i
= 0; i
< r
; ++i
) {
955 std::vector
<isl_map
*> row
;
956 for (int j
= 0; j
< r
; ++j
) {
958 pos_lost
= lost_at_pos(node
, i
, j
);
959 row
.push_back(pos_lost
);
965 std::vector
<int> perm
;
966 std::vector
<isl_map
*> lost_at
;
967 for (level
= 0; level
< r
; ++level
) {
969 lost_at
.push_back(NULL
);
973 total_lost
= isl_map_copy(node
->want
);
977 if (perm
[level
] == r
) {
985 for (l
= 0; l
< level
; ++l
)
986 if (perm
[l
] == perm
[level
])
993 isl_map_free(lost_at
[level
]);
994 lost_at
[level
] = isl_map_copy(lost
[level
][perm
[level
]]);
996 lost_at
[level
] = isl_map_union(lost_at
[level
],
997 isl_map_copy(lost_at
[level
- 1]));
1004 lost_at
[level
] = isl_map_coalesce(lost_at
[level
]);
1005 total_lost
= isl_map_intersect(total_lost
,
1006 isl_map_copy(lost_at
[level
]));
1010 for (int i
= 0; i
< r
; ++i
)
1011 isl_map_free(lost_at
[i
]);
1013 for (int i
= 0; i
< r
; ++i
)
1014 for (int j
= 0; j
< r
; ++j
)
1015 isl_map_free(lost
[i
][j
]);
1017 node
->set_lost(total_lost
);
1019 options
->trace_error
= trace_error
;
1023 /* Compute the lost that results from propagation on a pair of
1026 * First, for functions of zero arity (i.e., constants), equivalence
1027 * always holds and the lost relation is empty.
1028 * For commutative operations, the computation is delegated
1029 * to handle_propagation_comm.
1030 * Otherwise, we simply take the union of the losts over each
1031 * argument position (always taking the same argument position
1032 * in both programs).
1034 void equivalence_checker::handle_propagation_same(eq_node
*node
)
1036 unsigned r
= node
->comp
[0]->arity
;
1038 node
->set_lost(isl_map_empty(isl_map_get_space(node
->want
)));
1041 if (is_commutative(node
->comp
[0]->operation
)) {
1042 handle_propagation_comm(node
);
1047 lost
= isl_map_empty(isl_map_get_space(node
->want
));
1048 for (int i
= 0; i
< r
; ++i
) {
1050 pos_lost
= lost_at_pos(node
, i
, i
);
1051 lost
= isl_map_union(lost
, pos_lost
);
1053 lost
= isl_map_coalesce(lost
);
1054 node
->set_lost(lost
);
1057 void equivalence_checker::handle_widening(eq_node
*node
, eq_node
*a
)
1062 wants
= isl_map_union(isl_map_copy(node
->want
), isl_map_copy(a
->want
));
1063 aff
= isl_map_from_basic_map(isl_map_affine_hull(wants
));
1064 aff
= isl_map_intersect_domain(aff
,
1065 isl_set_copy(node
->comp
[0]->domain
));
1066 aff
= isl_map_intersect_range(aff
,
1067 isl_set_copy(node
->comp
[1]->domain
));
1069 eq_node
*child
= new eq_node(node
->comp
[0], node
->comp
[1], aff
);
1070 child
->widening
= 1;
1073 node
->set_lost(isl_map_intersect(child
->get_lost(),
1074 isl_map_copy(node
->want
)));
1075 extend_trace_widening(node
, child
);
1076 std::set
<eq_node
*>::iterator i
;
1077 for (i
= child
->assumed
.begin(); i
!= child
->assumed
.end(); ++i
)
1078 node
->assumed
.insert(*i
);
1082 /* Perform induction, if possible, and widening if we have to.
1084 void equivalence_checker::handle_with_ancestor(eq_node
*node
, eq_node
*a
)
1086 if (a
->narrowing
|| isl_map_is_subset(node
->want
, a
->want
)) {
1088 need
= isl_map_intersect(isl_map_copy(node
->want
),
1089 isl_map_copy(a
->want
));
1090 node
->set_lost(isl_map_subtract(isl_map_copy(node
->want
),
1091 isl_map_copy(a
->want
)));
1092 node
->assumed
.insert(a
);
1093 a
->need
= isl_map_union(a
->need
, need
);
1095 handle_widening(node
, a
);
1099 struct narrowing_data
{
1101 equivalence_checker
*ec
;
1105 static isl_stat
basic_handle_narrowing(__isl_take isl_basic_map
*bmap
,
1108 narrowing_data
*data
= (narrowing_data
*)user
;
1111 child
= new eq_node(data
->node
->comp
[0], data
->node
->comp
[1],
1112 isl_map_from_basic_map(bmap
));
1113 child
->narrowing
= 1;
1114 data
->ec
->narrowings
++;
1115 data
->ec
->handle(child
);
1116 data
->new_got
= isl_map_union(data
->new_got
, child
->get_got());
1118 std::set
<eq_node
*>::iterator i
;
1119 for (i
= child
->assumed
.begin(); i
!= child
->assumed
.end(); ++i
)
1120 data
->node
->assumed
.insert(*i
);
1121 data
->ec
->dismiss(child
);
1126 /* Construct and handle narrowing nodes for the given node.
1128 * If the node itself was already a narrowing node, then we
1129 * simply return the empty relation.
1130 * Otherwise, we consider each basic relation in the obtained
1131 * relation, construct a new node with that basic relation as
1132 * requested relation and take the union of all obtained relations.
1134 void equivalence_checker::handle_narrowing(eq_node
*node
)
1137 node
->assumed
.clear();
1139 isl_map
*want
= node
->want
;
1140 isl_map
*new_got
= isl_map_empty(isl_map_get_space(want
));
1141 if (!options
->narrowing
|| node
->narrowing
) {
1142 node
->set_got(new_got
);
1146 narrowing_data data
= { node
, this, new_got
};
1147 isl_map_foreach_basic_map(node
->peek_got(), &basic_handle_narrowing
,
1149 node
->set_got(data
.new_got
);
1152 static __isl_give isl_union_set
*update_result(__isl_take isl_union_set
*res
,
1153 const char *array_name
, isl_map
*map
, int first
, int n
)
1155 if (isl_map_is_empty(map
)) {
1160 isl_set
*range
= isl_map_range(map
);
1161 range
= isl_set_remove_dims(range
, isl_dim_set
, first
, n
);
1162 range
= isl_set_coalesce(range
);
1163 range
= isl_set_set_tuple_name(range
, array_name
);
1164 res
= isl_union_set_add_set(res
, range
);
1169 struct check_equivalence_data
{
1170 dependence_graph
*dg1
;
1171 dependence_graph
*dg2
;
1172 equivalence_checker
*ec
;
1177 static isl_stat
basic_check(__isl_take isl_basic_map
*bmap
, void *user
)
1179 check_equivalence_data
*data
= (check_equivalence_data
*)user
;
1181 eq_node
*root
= new eq_node(data
->dg1
->out
, data
->dg2
->out
,
1182 isl_map_from_basic_map(bmap
));
1183 data
->ec
->handle(root
);
1184 data
->got
= isl_map_union_disjoint(data
->got
, root
->get_got());
1185 data
->lost
= isl_map_union_disjoint(data
->lost
, root
->get_lost());
1186 data
->ec
->dismiss(root
);
1191 static int check_equivalence_array(isl_ctx
*ctx
, equivalence_checker
*ec
,
1192 dependence_graph
*dg1
, dependence_graph
*dg2
, int array1
, int array2
,
1193 isl_union_set
**proved
, isl_union_set
**not_proved
)
1196 const char *array_name
= dg1
->output_arrays
[array1
];
1197 isl_set
*out1
= isl_set_copy(dg1
->out
->domain
);
1198 isl_set
*out2
= isl_set_copy(dg2
->out
->domain
);
1199 unsigned dim1
= isl_set_n_dim(out1
);
1200 unsigned dim2
= isl_set_n_dim(out2
);
1201 unsigned array_dim
= dg1
->output_array_dims
[array1
];
1202 out1
= isl_set_fix_dim_si(out1
, dim1
-1, array1
);
1203 out2
= isl_set_fix_dim_si(out2
, dim2
-1, array2
);
1204 out1
= isl_set_remove_dims(out1
, isl_dim_set
, dim1
- 1, 1);
1205 out2
= isl_set_remove_dims(out2
, isl_dim_set
, dim2
- 1, 1);
1206 int equal
= isl_set_is_equal(out1
, out2
);
1211 fprintf(stderr
, "different output domains for array %s\n",
1216 out1
= isl_set_copy(dg1
->out
->domain
);
1217 out2
= isl_set_copy(dg2
->out
->domain
);
1218 out1
= isl_set_fix_dim_si(out1
, dim1
-1, array1
);
1219 out2
= isl_set_fix_dim_si(out2
, dim2
-1, array2
);
1220 out1
= isl_set_coalesce(out1
);
1221 out2
= isl_set_coalesce(out2
);
1222 isl_space
*dim
= isl_space_map_from_set(isl_set_get_space(out2
));
1223 isl_map
*id
= isl_map_identity(dim
);
1224 n_in
= isl_map_dim(id
, isl_dim_in
);
1225 id
= isl_map_remove_dims(id
, isl_dim_in
, n_in
- 1, 1);
1226 id
= isl_map_apply_domain(id
, isl_map_copy(id
));
1227 id
= isl_map_intersect_domain(id
, out1
);
1228 id
= isl_map_intersect_range(id
, out2
);
1230 isl_map
*got
= isl_map_empty(isl_map_get_space(id
));
1231 isl_map
*lost
= isl_map_copy(got
);
1232 check_equivalence_data data
= { dg1
, dg2
, ec
, got
, lost
};
1233 isl_map_foreach_basic_map(id
, &basic_check
, &data
);
1235 *proved
= update_result(*proved
,
1236 array_name
, data
.got
, array_dim
, dim2
- array_dim
);
1237 *not_proved
= update_result(*not_proved
,
1238 array_name
, data
.lost
, array_dim
, dim2
- array_dim
);
1242 /* The input arrays of the two programs are supposed to be the same,
1243 * so they should at least have the same dimension. Make sure
1244 * this is true, because we depend on it later on.
1246 static int check_input_arrays(dependence_graph
*dg1
, dependence_graph
*dg2
)
1248 for (int i
= 0; i
< dg1
->input_computations
.size(); ++i
)
1249 for (int j
= 0; j
< dg2
->input_computations
.size(); ++j
) {
1250 if (strcmp(dg1
->input_computations
[i
]->operation
,
1251 dg2
->input_computations
[j
]->operation
))
1253 if (dg1
->input_computations
[i
]->dim
==
1254 dg2
->input_computations
[j
]->dim
)
1257 "input arrays \"%s\" do not have the same dimension\n",
1258 dg1
->input_computations
[i
]->operation
);
1265 static __isl_give isl_union_set
*add_array(__isl_take isl_union_set
*only
,
1266 dependence_graph
*dg
, int array
)
1271 dim
= isl_union_set_get_space(only
);
1272 dim
= isl_space_add_dims(dim
, isl_dim_set
, dg
->output_array_dims
[array
]);
1273 dim
= isl_space_set_tuple_name(dim
, isl_dim_set
, dg
->output_arrays
[array
]);
1274 set
= isl_set_universe(dim
);
1275 only
= isl_union_set_add_set(only
, set
);
1280 static void print_results(const char *str
, __isl_keep isl_union_set
*only
)
1284 if (isl_union_set_is_empty(only
))
1287 fprintf(stdout
, "%s: '", str
);
1288 prn
= isl_printer_to_file(isl_union_set_get_ctx(only
), stdout
);
1289 prn
= isl_printer_print_union_set(prn
, only
);
1290 isl_printer_free(prn
);
1291 fprintf(stdout
, "'\n");
1294 static int check_equivalence(isl_ctx
*ctx
,
1295 dependence_graph
*dg1
, dependence_graph
*dg2
, options
*options
)
1299 isl_union_set
*only1
, *only2
, *proved
, *not_proved
;
1300 dg1
->flatten_associative_operators(options
->ops
->associative
);
1301 dg2
->flatten_associative_operators(options
->ops
->associative
);
1302 equivalence_checker
ec(options
);
1305 if (check_input_arrays(dg1
, dg2
))
1308 dim
= isl_space_set_alloc(ctx
, 0, 0);
1309 proved
= isl_union_set_empty(isl_space_copy(dim
));
1310 not_proved
= isl_union_set_empty(isl_space_copy(dim
));
1311 only1
= isl_union_set_empty(isl_space_copy(dim
));
1312 only2
= isl_union_set_empty(dim
);
1314 while (i1
< dg1
->output_arrays
.size() || i2
< dg2
->output_arrays
.size()) {
1316 cmp
= i1
== dg1
->output_arrays
.size() ? 1 :
1317 i2
== dg2
->output_arrays
.size() ? -1 :
1318 strcmp(dg1
->output_arrays
[i1
], dg2
->output_arrays
[i2
]);
1320 only1
= add_array(only1
, dg1
, i1
);
1322 } else if (cmp
> 0) {
1323 only2
= add_array(only2
, dg2
, i2
);
1326 check_equivalence_array(ctx
, &ec
, dg1
, dg2
, i1
, i2
,
1327 &proved
, ¬_proved
);
1333 context
= isl_set_union(isl_set_copy(dg1
->context
),
1334 isl_set_copy(dg2
->context
));
1335 proved
= isl_union_set_gist_params(proved
, isl_set_copy(context
));
1336 not_proved
= isl_union_set_gist_params(not_proved
, context
);
1338 print_results("Equivalence proved", proved
);
1339 print_results("Equivalence NOT proved", not_proved
);
1340 print_results("Only in program 1", only1
);
1341 print_results("Only in program 2", only2
);
1343 isl_union_set_free(proved
);
1344 isl_union_set_free(not_proved
);
1345 isl_union_set_free(only1
);
1346 isl_union_set_free(only2
);
1348 if (options
->print_stats
) {
1349 fprintf(stderr
, "widenings: %d\n", ec
.widenings
);
1350 if (options
->narrowing
)
1351 fprintf(stderr
, "narrowings: %d\n", ec
.narrowings
);
1357 static void dump_vertex(FILE *out
, computation
*comp
)
1359 fprintf(out
, "ND_%p [label = \"%d,%s/%d\"];\n",
1360 comp
, comp
->location
, comp
->operation
, comp
->arity
);
1361 for (int i
= 0; i
< comp
->edges
.size(); ++i
)
1362 fprintf(out
, "ND_%p -> ND_%p%s;\n",
1363 comp
, comp
->edges
[i
]->source
,
1364 comp
->edges
[i
]->type
== edge::expansion
?
1365 " [color=\"blue\"]" : "");
1368 static void dump_graph(FILE *out
, dependence_graph
*dg
)
1370 fprintf(out
, "digraph dummy {\n");
1371 dump_vertex(out
, dg
->out
);
1372 for (int i
= 0; i
< dg
->vertices
.size(); ++i
)
1373 dump_vertex(out
, dg
->vertices
[i
]);
1374 fprintf(out
, "}\n");
1377 static void dump_graphs(dependence_graph
**dg
, struct options
*options
)
1380 char path
[PATH_MAX
];
1382 if (!options
->dump_graphs
)
1385 for (i
= 0; i
< 2; ++i
) {
1388 s
= snprintf(path
, sizeof(path
), "%s.dot", options
->program
[i
]);
1389 assert(s
< sizeof(path
));
1390 out
= fopen(path
, "w");
1392 dump_graph(out
, dg
[i
]);
1397 void parse_ops(struct options
*options
) {
1400 if (options
->associative
) {
1401 tok
= strtok(options
->associative
, ",");
1402 options
->ops
->associative
.push_back(strdup(tok
));
1403 while ((tok
= strtok(NULL
, ",")) != NULL
)
1404 options
->ops
->associative
.push_back(strdup(tok
));
1407 if (options
->commutative
) {
1408 tok
= strtok(options
->commutative
, ",");
1409 options
->ops
->commutative
.push_back(strdup(tok
));
1410 while ((tok
= strtok(NULL
, ",")) != NULL
)
1411 options
->ops
->commutative
.push_back(strdup(tok
));
1415 int main(int argc
, char *argv
[])
1417 struct options
*options
= options_new_with_defaults();
1418 struct isl_ctx
*ctx
;
1419 isl_set
*context
= NULL
;
1420 dependence_graph
*dg
[2];
1422 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
1425 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
1427 fprintf(stderr
, "Unable to allocate ctx\n");
1431 if (options
->context
)
1432 context
= isl_set_read_from_str(ctx
, options
->context
);
1435 unsigned out_dim
= 0;
1436 for (int i
= 0; i
< 2; ++i
) {
1438 in
= fopen(options
->program
[i
], "r");
1440 fprintf(stderr
, "Unable to open %s\n", options
->program
[i
]);
1443 pdg
[i
] = yaml::Load
<pdg::PDG
>(in
, ctx
);
1446 fprintf(stderr
, "Unable to read %s\n", options
->program
[i
]);
1449 out_dim
= update_out_dim(pdg
[i
], out_dim
);
1452 pdg
[i
]->params
.size() != isl_set_dim(context
, isl_dim_param
)) {
1454 "Parameter dimension mismatch; context ignored\n");
1455 isl_set_free(context
);
1460 for (int i
= 0; i
< 2; ++i
) {
1461 dg
[i
] = pdg_to_dg(pdg
[i
], out_dim
, isl_set_copy(context
));
1466 dump_graphs(dg
, options
);
1468 int res
= check_equivalence(ctx
, dg
[0], dg
[1], options
);
1470 for (int i
= 0; i
< 2; ++i
)
1472 isl_set_free(context
);