2 * Copyright 2012-2014 Ecole Normale Superieure. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY ECOLE NORMALE SUPERIEURE ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECOLE NORMALE SUPERIEURE OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Ecole Normale Superieure.
39 #include <isl/options.h>
41 #include <isl/union_map.h>
42 #include <isl/id_to_pw_aff.h>
46 struct isl_options
*isl
;
47 struct pet_options
*pet
;
52 ISL_ARGS_START(struct options
, options_args
)
53 ISL_ARG_CHILD(struct options
, isl
, "isl", &isl_options_args
, "isl options")
54 ISL_ARG_CHILD(struct options
, pet
, NULL
, &pet_options_args
, "pet options")
55 ISL_ARG_ARG(struct options
, schedule
, "schedule", NULL
)
56 ISL_ARG_ARG(struct options
, code
, "code", NULL
)
59 ISL_ARG_DEF(options
, struct options
, options_args
)
61 static __isl_give isl_pw_aff
*expr_extract_pw_aff(struct pet_expr
*expr
,
62 __isl_keep isl_id_to_pw_aff
*assignments
);
64 /* Extract an affine expression from the call to floord in "expr",
65 * possibly exploiting "assignments".
67 static __isl_give isl_pw_aff
*expr_extract_floord(struct pet_expr
*expr
,
68 __isl_keep isl_id_to_pw_aff
*assignments
)
70 isl_pw_aff
*lhs
, *rhs
;
72 lhs
= expr_extract_pw_aff(expr
->args
[0], assignments
);
73 rhs
= expr_extract_pw_aff(expr
->args
[1], assignments
);
74 return isl_pw_aff_floor(isl_pw_aff_div(lhs
, rhs
));
77 /* Extract an affine expression from the call in "expr",
78 * possibly exploiting "assignments".
80 * We only support calls to the "floord" function for now.
82 static __isl_give isl_pw_aff
*call_expr_extract_pw_aff(struct pet_expr
*expr
,
83 __isl_keep isl_id_to_pw_aff
*assignments
)
85 assert(!strcmp(expr
->name
, "floord"));
87 return expr_extract_floord(expr
, assignments
);
90 /* Is the variable accessed by "index" assigned in "assignments"?
92 * The assignments map variable identifiers to functions of the form
96 static int is_assigned(__isl_keep isl_multi_pw_aff
*index
,
97 __isl_keep isl_id_to_pw_aff
*assignments
)
102 var
= isl_multi_pw_aff_get_tuple_id(index
, isl_dim_out
);
103 assigned
= isl_id_to_pw_aff_has(assignments
, var
);
109 /* Apply the appropriate assignment in "assignments"
110 * to the index expression "index".
112 * "index" is of the form
114 * { access_domain -> variable }
116 * "assignments" maps variable identifiers to functions of the form
118 * { assignment_domain -> value }
120 * We assume the assignment precedes the access in the code.
121 * In particular, we assume that the loops around the assignment
122 * are the same as the first loops around the access.
126 * { access_domain -> assignment_domain }
128 * equating the iterators of assignment_domain to the corresponding iterators
129 * in access_domain and then plug that into the assigned value, obtaining
131 * { access_domain -> value }
133 static __isl_give isl_pw_aff
*apply_assignment(
134 __isl_take isl_multi_pw_aff
*index
,
135 __isl_keep isl_id_to_pw_aff
*assignments
)
141 isl_space
*space
, *dom_space
;
145 id
= isl_multi_pw_aff_get_tuple_id(index
, isl_dim_out
);
146 dom
= isl_multi_pw_aff_domain(index
);
147 val
= isl_id_to_pw_aff_get(assignments
, id
);
148 space
= isl_pw_aff_get_domain_space(val
);
149 dom_space
= isl_set_get_space(dom
);
150 space
= isl_space_map_from_domain_and_range(dom_space
, space
);
151 ma
= isl_multi_aff_zero(space
);
152 ls
= isl_local_space_from_space(isl_set_get_space(dom
));
153 n
= isl_multi_aff_dim(ma
, isl_dim_out
);
154 for (i
= 0; i
< n
; ++i
) {
157 aff
= isl_aff_var_on_domain(isl_local_space_copy(ls
),
159 ma
= isl_multi_aff_set_aff(ma
, i
, aff
);
161 isl_local_space_free(ls
);
163 val
= isl_pw_aff_pullback_multi_aff(val
, ma
);
164 val
= isl_pw_aff_intersect_domain(val
, dom
);
169 /* Extract an affine expression from the access to a named space in "index",
170 * possibly exploiting "assignments".
172 * If the variable has been assigned a value, we return the corresponding
173 * assignment. Otherwise, we assume we are accessing a 0D space and
174 * we turn that into an expression equal to a parameter of the same name.
176 static __isl_give isl_pw_aff
*resolve_access(__isl_take isl_multi_pw_aff
*index
,
177 __isl_keep isl_id_to_pw_aff
*assignments
)
185 if (is_assigned(index
, assignments
))
186 return apply_assignment(index
, assignments
);
188 id
= isl_multi_pw_aff_get_tuple_id(index
, isl_dim_out
);
189 dom
= isl_multi_pw_aff_domain(index
);
190 dom
= isl_set_insert_dims(dom
, isl_dim_param
, 0, 1);
191 dom
= isl_set_set_dim_id(dom
, isl_dim_param
, 0, id
);
192 ls
= isl_local_space_from_space(isl_set_get_space(dom
));
193 aff
= isl_aff_var_on_domain(ls
, isl_dim_param
, 0);
194 pa
= isl_pw_aff_alloc(dom
, aff
);
199 /* Extract an affine expression from the access expression "expr",
200 * possibly exploiting "assignments".
202 * If we are accessing a (1D) anonymous space, then we are actually
203 * computing an affine expression and we simply return that expression.
204 * Otherwise, we try and convert the access to an affine expression in
207 static __isl_give isl_pw_aff
*access_expr_extract_pw_aff(struct pet_expr
*expr
,
208 __isl_keep isl_id_to_pw_aff
*assignments
)
212 if (isl_multi_pw_aff_has_tuple_id(expr
->acc
.index
, isl_dim_out
)) {
213 isl_multi_pw_aff
*index
;
214 index
= isl_multi_pw_aff_copy(expr
->acc
.index
);
215 pa
= resolve_access(index
, assignments
);
217 pa
= isl_multi_pw_aff_get_pw_aff(expr
->acc
.index
, 0);
221 /* Extract an affine expression from "expr", possibly exploiting "assignments",
222 * in the form of an isl_pw_aff.
224 * We only handle the kinds of expressions that we would expect
225 * as arguments to a function call in code generated by isl.
227 static __isl_give isl_pw_aff
*expr_extract_pw_aff(struct pet_expr
*expr
,
228 __isl_keep isl_id_to_pw_aff
*assignments
)
230 isl_pw_aff
*pa
, *pa1
, *pa2
;
232 switch (expr
->type
) {
233 case pet_expr_access
:
234 return access_expr_extract_pw_aff(expr
, assignments
);
236 if (expr
->op
== pet_op_minus
) {
237 pa
= expr_extract_pw_aff(expr
->args
[0], assignments
);
238 return isl_pw_aff_neg(pa
);
241 case pet_expr_binary
:
242 pa1
= expr_extract_pw_aff(expr
->args
[0], assignments
);
243 pa2
= expr_extract_pw_aff(expr
->args
[1], assignments
);
246 pa
= isl_pw_aff_mul(pa1
, pa2
);
249 pa
= isl_pw_aff_add(pa1
, pa2
);
252 pa
= isl_pw_aff_sub(pa1
, pa2
);
255 pa
= isl_pw_aff_tdiv_q(pa1
, pa2
);
258 pa
= isl_pw_aff_tdiv_r(pa1
, pa2
);
265 return call_expr_extract_pw_aff(expr
, assignments
);
266 case pet_expr_ternary
:
267 pa
= expr_extract_pw_aff(expr
->args
[0], assignments
);
268 pa1
= expr_extract_pw_aff(expr
->args
[1], assignments
);
269 pa2
= expr_extract_pw_aff(expr
->args
[2], assignments
);
270 return isl_pw_aff_cond(pa
, pa1
, pa2
);
272 case pet_expr_double
:
277 /* Extract an affine expression from "expr", possibly exploiting "assignments",
278 * in the form of an isl_map.
280 static __isl_give isl_map
*expr_extract_map(struct pet_expr
*expr
,
281 __isl_keep isl_id_to_pw_aff
*assignments
)
283 return isl_map_from_pw_aff(expr_extract_pw_aff(expr
, assignments
));
286 /* Extract a call from "stmt", possibly exploiting "assignments".
288 * The returned map is of the form
290 * { domain -> function[arguments] }
292 static __isl_give isl_map
*stmt_extract_call(struct pet_stmt
*stmt
,
293 __isl_keep isl_id_to_pw_aff
*assignments
)
299 domain
= isl_set_copy(stmt
->domain
);
300 call
= isl_map_from_domain(domain
);
302 assert(stmt
->body
->type
== pet_expr_call
);
304 for (i
= 0; i
< stmt
->body
->n_arg
; ++i
) {
307 arg
= expr_extract_map(stmt
->body
->args
[i
], assignments
);
308 call
= isl_map_flat_range_product(call
, arg
);
311 call
= isl_map_set_tuple_name(call
, isl_dim_out
, stmt
->body
->name
);
316 /* Add the assignment in "stmt" to "assignments".
318 * We extract the accessed variable identifier "var"
319 * and the assigned value
321 * { domain -> value }
323 * and map "var" to this value in "assignments", replacing
324 * any possible previously assigned value to the same variable.
326 static __isl_give isl_id_to_pw_aff
*add_assignment(
327 __isl_take isl_id_to_pw_aff
*assignments
, struct pet_stmt
*stmt
)
332 assert(stmt
->body
->op
== pet_op_assign
);
333 assert(stmt
->body
->args
[0]->type
== pet_expr_access
);
334 var
= isl_map_get_tuple_id(stmt
->body
->args
[0]->acc
.access
,
336 val
= expr_extract_pw_aff(stmt
->body
->args
[1], assignments
);
338 assignments
= isl_id_to_pw_aff_set(assignments
, var
, val
);
343 /* Extract a mapping from the iterations domains of "scop" to
344 * the calls in the corresponding statements.
346 * While scanning "scop", we keep track of assignments to variables
347 * so that we can plug them in in the arguments of the calls.
348 * Note that we do not perform any dependence analysis on the assigned
349 * variables. In code generated by isl, such assignments should only
350 * appear immediately before they are used.
352 * The assignments are kept as an associative array between
353 * variable identifiers and assignments of the form
355 * { domain -> value }
357 * We skip kill statements.
358 * Other than assignments and kill statements, all statements are assumed
359 * to be function calls.
361 static __isl_give isl_union_map
*scop_collect_calls(struct pet_scop
*scop
)
366 isl_id_to_pw_aff
*assignments
;
372 call
= isl_union_map_empty(isl_set_get_space(scop
->context
));
373 ctx
= isl_set_get_ctx(scop
->context
);
374 assignments
= isl_id_to_pw_aff_alloc(ctx
, 0);
376 for (i
= 0; i
< scop
->n_stmt
; ++i
) {
377 struct pet_stmt
*stmt
;
379 stmt
= scop
->stmts
[i
];
380 if (pet_stmt_is_assign(stmt
)) {
381 assignments
= add_assignment(assignments
, stmt
);
384 if (pet_stmt_is_kill(stmt
))
386 call_i
= stmt_extract_call(scop
->stmts
[i
], assignments
);
387 call
= isl_union_map_add_map(call
, call_i
);
390 isl_id_to_pw_aff_free(assignments
);
395 /* Extract a schedule on the original domains from "scop".
396 * The original domain elements appear as calls in "scop".
398 * We first extract a schedule on the code iteration domains
399 * and a mapping from the code iteration domains to the calls
400 * (i.e., the original domain) and then combine the two.
402 static __isl_give isl_union_map
*extract_code_schedule(struct pet_scop
*scop
)
404 isl_union_map
*schedule
;
405 isl_union_map
*calls
;
407 schedule
= pet_scop_collect_schedule(scop
);
409 calls
= scop_collect_calls(scop
);
411 schedule
= isl_union_map_apply_domain(schedule
, calls
);
416 /* Check that schedule and code_schedule have the same domain,
417 * i.e., that they execute the same statement instances.
419 static int check_domain(__isl_keep isl_union_map
*schedule
,
420 __isl_keep isl_union_map
*code_schedule
)
422 isl_union_set
*dom1
, *dom2
;
428 dom1
= isl_union_map_domain(isl_union_map_copy(schedule
));
429 dom2
= isl_union_map_domain(isl_union_map_copy(code_schedule
));
430 equal
= isl_union_set_is_equal(dom1
, dom2
);
435 isl_union_set_dump(dom1
);
436 isl_union_set_dump(dom2
);
437 isl_die(isl_union_map_get_ctx(schedule
), isl_error_unknown
,
438 "domains not identical", r
= -1);
441 isl_union_set_free(dom1
);
442 isl_union_set_free(dom2
);
447 /* Check that the relative order specified by the input schedule is respected
448 * by the schedule extracted from the code, in case the original schedule
451 * In particular, check that there is no pair of statement instances
452 * such that the first should be scheduled _before_ the second,
453 * but is actually scheduled _after_ the second in the code.
455 static int check_order_sv(__isl_keep isl_union_map
*schedule
,
456 __isl_keep isl_union_map
*code_schedule
)
462 t1
= isl_union_map_lex_lt_union_map(isl_union_map_copy(schedule
),
463 isl_union_map_copy(schedule
));
464 t2
= isl_union_map_lex_gt_union_map(isl_union_map_copy(code_schedule
),
465 isl_union_map_copy(code_schedule
));
466 t1
= isl_union_map_intersect(t1
, t2
);
467 empty
= isl_union_map_is_empty(t1
);
468 isl_union_map_free(t1
);
473 isl_die(isl_union_map_get_ctx(schedule
), isl_error_unknown
,
474 "order not respected", return -1);
479 /* Check that the relative order specified by the input schedule is respected
480 * by the schedule extracted from the code, in case the original schedule
481 * is not single valued.
483 * In particular, check that the order imposed by the schedules on pairs
484 * of statement instances is the same.
486 static int check_order_not_sv(__isl_keep isl_union_map
*schedule
,
487 __isl_keep isl_union_map
*code_schedule
)
493 t1
= isl_union_map_lex_lt_union_map(isl_union_map_copy(schedule
),
494 isl_union_map_copy(schedule
));
495 t2
= isl_union_map_lex_lt_union_map(isl_union_map_copy(code_schedule
),
496 isl_union_map_copy(code_schedule
));
497 equal
= isl_union_map_is_equal(t1
, t2
);
498 isl_union_map_free(t1
);
499 isl_union_map_free(t2
);
504 isl_die(isl_union_map_get_ctx(schedule
), isl_error_unknown
,
505 "order not respected", return -1);
510 /* Check that the relative order specified by the input schedule is respected
511 * by the schedule extracted from the code.
513 * "sv" indicated whether the original schedule is single valued.
514 * If so, we use a cheaper test. Otherwise, we fall back on a more
517 static int check_order(__isl_keep isl_union_map
*schedule
,
518 __isl_keep isl_union_map
*code_schedule
, int sv
)
521 return check_order_sv(schedule
, code_schedule
);
523 return check_order_not_sv(schedule
, code_schedule
);
526 /* If the original schedule was single valued ("sv" is set),
527 * then the schedule extracted from the code should be single valued as well.
529 static int check_single_valued(__isl_keep isl_union_map
*code_schedule
, int sv
)
534 sv
= isl_union_map_is_single_valued(code_schedule
);
539 isl_die(isl_union_map_get_ctx(code_schedule
), isl_error_unknown
,
540 "schedule not single valued", return -1);
545 /* Read a schedule and a context from the first argument and
546 * C code from the second argument and check that the C code
547 * corresponds to the schedule on the context.
549 * In particular, check that
550 * - the domains are identical, i.e., the calls in the C code
551 * correspond to the domain elements of the schedule
552 * - no function is called twice with the same arguments, provided
553 * the schedule is single-valued
554 * - the calls are performed in an order that is compatible
557 * If the schedule is not single-valued then we would have to check
558 * that each function with a given set of arguments is called
559 * the same number of times as there are images in the schedule,
560 * but this is considerably more difficult.
562 int main(int argc
, char **argv
)
566 isl_union_map
*schedule
, *code_schedule
;
567 struct pet_scop
*scop
;
568 struct options
*options
;
573 options
= options_new_with_defaults();
575 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
576 pet_options_set_signed_overflow(ctx
, PET_OVERFLOW_IGNORE
);
577 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
579 file
= fopen(options
->schedule
, "r");
581 schedule
= isl_union_map_read_from_file(ctx
, file
);
582 context
= isl_set_read_from_file(ctx
, file
);
585 scop
= pet_scop_extract_from_C_source(ctx
, options
->code
, NULL
);
587 schedule
= isl_union_map_intersect_params(schedule
,
588 isl_set_copy(context
));
589 code_schedule
= extract_code_schedule(scop
);
590 code_schedule
= isl_union_map_intersect_params(code_schedule
, context
);
592 sv
= isl_union_map_is_single_valued(schedule
);
594 check_domain(schedule
, code_schedule
) ||
595 check_single_valued(code_schedule
, sv
) ||
596 check_order(schedule
, code_schedule
, sv
);
599 isl_union_map_free(schedule
);
600 isl_union_map_free(code_schedule
);