pet_check_code: use isl_id_to_pw_aff for keeping track of assignments
[pet.git] / pet_check_code.c
blob1eddc28340bb65e3ef6d8b35e9eed12cbd1d5e15
1 /*
2 * Copyright 2012 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
6 * are met:
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.
34 #include <assert.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <isl/arg.h>
38 #include <isl/aff.h>
39 #include <isl/set.h>
40 #include <isl/union_map.h>
41 #include <isl/id_to_pw_aff.h>
42 #include <pet.h>
44 struct options {
45 struct pet_options *pet;
46 char *schedule;
47 char *code;
50 ISL_ARGS_START(struct options, options_args)
51 ISL_ARG_CHILD(struct options, pet, NULL, &pet_options_args, "pet options")
52 ISL_ARG_ARG(struct options, schedule, "schedule", NULL)
53 ISL_ARG_ARG(struct options, code, "code", NULL)
54 ISL_ARGS_END
56 ISL_ARG_DEF(options, struct options, options_args)
58 static __isl_give isl_pw_aff *expr_extract_pw_aff(struct pet_expr *expr,
59 __isl_keep isl_id_to_pw_aff *assignments);
61 /* Extract an affine expression from the call to floord in "expr",
62 * possibly exploiting "assignments".
64 static __isl_give isl_pw_aff *expr_extract_floord(struct pet_expr *expr,
65 __isl_keep isl_id_to_pw_aff *assignments)
67 isl_pw_aff *lhs, *rhs;
69 lhs = expr_extract_pw_aff(expr->args[0], assignments);
70 rhs = expr_extract_pw_aff(expr->args[1], assignments);
71 return isl_pw_aff_floor(isl_pw_aff_div(lhs, rhs));
74 /* Extract an affine expression from the call in "expr",
75 * possibly exploiting "assignments".
77 * We only support calls to the "floord" function for now.
79 static __isl_give isl_pw_aff *call_expr_extract_pw_aff(struct pet_expr *expr,
80 __isl_keep isl_id_to_pw_aff *assignments)
82 assert(!strcmp(expr->name, "floord"));
84 return expr_extract_floord(expr, assignments);
87 /* Is the variable accessed by "access" assigned in "assignments"?
89 * The assignments map variable identifiers to functions of the form
91 * { domain -> value }
93 static int is_assigned(__isl_keep isl_map *access,
94 __isl_keep isl_id_to_pw_aff *assignments)
96 isl_id *var;
97 int assigned;
99 var = isl_map_get_tuple_id(access, isl_dim_out);
100 assigned = isl_id_to_pw_aff_has(assignments, var);
101 isl_id_free(var);
103 return assigned;
106 /* Apply the appropriate assignment in "assignments" to the access "map".
108 * "map" is of the form
110 * { access_domain -> variable }
112 * "assignments" maps variable identifiers to functions of the form
114 * { assignment_domain -> value }
116 * We assume the assignment precedes the access in the code.
117 * In particular, we assume that the loops around the assignment
118 * are the same as the first loops around the access.
120 * We compute
122 * { access_domain -> assignment_domain }
124 * equating the iterators of assignment_domain to the corresponding iterators
125 * in access_domain and then plug that into the assigned value, obtaining
127 * { access_domain -> value }
129 static __isl_give isl_map *apply_assignment(__isl_take isl_map *map,
130 __isl_keep isl_id_to_pw_aff *assignments)
132 isl_id *id;
133 isl_set *dom;
134 isl_pw_aff *val;
135 isl_multi_aff *ma;
136 isl_space *space, *dom_space;
137 isl_local_space *ls;
138 int i, n;
140 id = isl_map_get_tuple_id(map, isl_dim_out);
141 dom = isl_map_domain(map);
142 val = isl_id_to_pw_aff_get(assignments, id);
143 space = isl_pw_aff_get_domain_space(val);
144 dom_space = isl_set_get_space(dom);
145 space = isl_space_map_from_domain_and_range(dom_space, space);
146 ma = isl_multi_aff_zero(space);
147 ls = isl_local_space_from_space(isl_set_get_space(dom));
148 n = isl_multi_aff_dim(ma, isl_dim_out);
149 for (i = 0; i < n; ++i) {
150 isl_aff *aff;
152 aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
153 isl_dim_set, i);
154 ma = isl_multi_aff_set_aff(ma, i, aff);
156 isl_local_space_free(ls);
158 val = isl_pw_aff_pullback_multi_aff(val, ma);
159 val = isl_pw_aff_intersect_domain(val, dom);
161 return isl_map_from_pw_aff(val);
164 /* Extract an affine expression from the access to a named space in "access",
165 * possibly exploiting "assignments".
167 * If the variable has been assigned a value, we return the corresponding
168 * assignment. Otherwise, we assume we are accessing a 0D space and
169 * we turn that into an expression equal to a parameter of the same name.
171 static __isl_give isl_map *resolve_access(__isl_take isl_map *access,
172 __isl_keep isl_id_to_pw_aff *assignments)
174 isl_id *id;
176 if (is_assigned(access, assignments))
177 return apply_assignment(access, assignments);
179 id = isl_map_get_tuple_id(access, isl_dim_out);
180 access = isl_map_insert_dims(access, isl_dim_param, 0, 1);
181 access = isl_map_set_dim_id(access, isl_dim_param, 0, id);
182 access = isl_map_insert_dims(access, isl_dim_out, 0, 1);
183 access = isl_map_equate(access, isl_dim_param, 0, isl_dim_out, 0);
185 return access;
188 /* Extract an affine expression from the access expression "expr",
189 * possibly exploiting "assignments".
191 * If we are accessing a (1D) anonymous space, then we are actually
192 * computing an affine expression and we simply return that expression.
193 * Otherwise, we try and convert the access to an affine expression in
194 * resolve_access().
196 static __isl_give isl_pw_aff *access_expr_extract_pw_aff(struct pet_expr *expr,
197 __isl_keep isl_id_to_pw_aff *assignments)
199 isl_map *map;
200 isl_pw_aff *pa;
201 isl_pw_multi_aff *pma;
203 map = isl_map_copy(expr->acc.access);
204 if (isl_map_has_tuple_id(map, isl_dim_out))
205 map = resolve_access(map, assignments);
206 pma = isl_pw_multi_aff_from_map(map);
207 pa = isl_pw_multi_aff_get_pw_aff(pma, 0);
208 isl_pw_multi_aff_free(pma);
209 return pa;
212 /* Extract an affine expression from "expr", possibly exploiting "assignments",
213 * in the form of an isl_pw_aff.
215 * We only handle the kinds of expressions that we would expect
216 * as arguments to a function call in code generated by isl.
218 static __isl_give isl_pw_aff *expr_extract_pw_aff(struct pet_expr *expr,
219 __isl_keep isl_id_to_pw_aff *assignments)
221 isl_pw_aff *pa, *pa1, *pa2;
223 switch (expr->type) {
224 case pet_expr_access:
225 return access_expr_extract_pw_aff(expr, assignments);
226 case pet_expr_unary:
227 if (expr->op == pet_op_minus) {
228 pa = expr_extract_pw_aff(expr->args[0], assignments);
229 return isl_pw_aff_neg(pa);
231 assert(0);
232 case pet_expr_binary:
233 pa1 = expr_extract_pw_aff(expr->args[0], assignments);
234 pa2 = expr_extract_pw_aff(expr->args[1], assignments);
235 switch (expr->op) {
236 case pet_op_mul:
237 pa = isl_pw_aff_mul(pa1, pa2);
238 break;
239 case pet_op_add:
240 pa = isl_pw_aff_add(pa1, pa2);
241 break;
242 case pet_op_sub:
243 pa = isl_pw_aff_sub(pa1, pa2);
244 break;
245 case pet_op_div:
246 pa = isl_pw_aff_tdiv_q(pa1, pa2);
247 break;
248 case pet_op_mod:
249 pa = isl_pw_aff_tdiv_r(pa1, pa2);
250 break;
251 default:
252 assert(0);
254 return pa;
255 case pet_expr_call:
256 return call_expr_extract_pw_aff(expr, assignments);
257 case pet_expr_ternary:
258 pa = expr_extract_pw_aff(expr->args[0], assignments);
259 pa1 = expr_extract_pw_aff(expr->args[1], assignments);
260 pa2 = expr_extract_pw_aff(expr->args[2], assignments);
261 return isl_pw_aff_cond(pa, pa1, pa2);
262 case pet_expr_cast:
263 case pet_expr_double:
264 assert(0);
268 /* Extract an affine expression from "expr", possibly exploiting "assignments",
269 * in the form of an isl_map.
271 static __isl_give isl_map *expr_extract_map(struct pet_expr *expr,
272 __isl_keep isl_id_to_pw_aff *assignments)
274 return isl_map_from_pw_aff(expr_extract_pw_aff(expr, assignments));
277 /* Extract a call from "stmt", possibly exploiting "assignments".
279 * The returned map is of the form
281 * { domain -> function[arguments] }
283 static __isl_give isl_map *stmt_extract_call(struct pet_stmt *stmt,
284 __isl_keep isl_id_to_pw_aff *assignments)
286 int i;
287 isl_set *domain;
288 isl_map *call;
290 domain = isl_set_copy(stmt->domain);
291 call = isl_map_from_domain(domain);
293 assert(stmt->body->type == pet_expr_call);
295 for (i = 0; i < stmt->body->n_arg; ++i) {
296 isl_map *arg;
298 arg = expr_extract_map(stmt->body->args[i], assignments);
299 call = isl_map_flat_range_product(call, arg);
302 call = isl_map_set_tuple_name(call, isl_dim_out, stmt->body->name);
304 return call;
307 /* Add the assignment in "stmt" to "assignments".
309 * We extract the accessed variable identifier "var"
310 * and the assigned value
312 * { domain -> value }
314 * and map "var" to this value in "assignments", replacing
315 * any possible previously assigned value to the same variable.
317 static __isl_give isl_id_to_pw_aff *add_assignment(
318 __isl_take isl_id_to_pw_aff *assignments, struct pet_stmt *stmt)
320 isl_id *var;
321 isl_pw_aff *val;
323 assert(stmt->body->op == pet_op_assign);
324 assert(stmt->body->args[0]->type == pet_expr_access);
325 var = isl_map_get_tuple_id(stmt->body->args[0]->acc.access,
326 isl_dim_out);
327 val = expr_extract_pw_aff(stmt->body->args[1], assignments);
329 assignments = isl_id_to_pw_aff_set(assignments, var, val);
331 return assignments;
334 /* Is "stmt" a kill statement?
336 static int is_kill(struct pet_stmt *stmt)
338 if (stmt->body->type != pet_expr_unary)
339 return 0;
340 return stmt->body->op == pet_op_kill;
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)
363 int i;
364 isl_ctx *ctx;
365 isl_map *call_i;
366 isl_id_to_pw_aff *assignments;
367 isl_union_map *call;
369 if (!scop)
370 return NULL;
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 (stmt->body->type == pet_expr_binary) {
381 assignments = add_assignment(assignments, stmt);
382 continue;
384 if (is_kill(stmt))
385 continue;
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);
392 return call;
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);
413 return schedule;
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;
423 int equal;
424 isl_set *s1, *s2;;
425 isl_id *id1, *id2;
427 dom1 = isl_union_map_domain(isl_union_map_copy(schedule));
428 dom2 = isl_union_map_domain(isl_union_map_copy(code_schedule));
429 equal = isl_union_set_is_equal(dom1, dom2);
430 isl_union_set_free(dom1);
431 isl_union_set_free(dom2);
433 if (equal < 0)
434 return -1;
435 if (!equal)
436 isl_die(isl_union_map_get_ctx(schedule), isl_error_unknown,
437 "domains not identical", return -1);
439 return 0;
442 /* Check that the relative order specified by the input schedule is respected
443 * by the schedule extracted from the code, in case the original schedule
444 * is single valued.
446 * In particular, check that there is no pair of statement instances
447 * such that the first should be scheduled _before_ the second,
448 * but is actually scheduled _after_ the second in the code.
450 static int check_order_sv(__isl_keep isl_union_map *schedule,
451 __isl_keep isl_union_map *code_schedule)
453 isl_union_map *t1;
454 isl_union_map *t2;
455 int empty;
457 t1 = isl_union_map_lex_lt_union_map(isl_union_map_copy(schedule),
458 isl_union_map_copy(schedule));
459 t2 = isl_union_map_lex_gt_union_map(isl_union_map_copy(code_schedule),
460 isl_union_map_copy(code_schedule));
461 t1 = isl_union_map_intersect(t1, t2);
462 empty = isl_union_map_is_empty(t1);
463 isl_union_map_free(t1);
465 if (empty < 0)
466 return -1;
467 if (!empty)
468 isl_die(isl_union_map_get_ctx(schedule), isl_error_unknown,
469 "order not respected", return -1);
471 return 0;
474 /* Check that the relative order specified by the input schedule is respected
475 * by the schedule extracted from the code, in case the original schedule
476 * is not single valued.
478 * In particular, check that the order imposed by the schedules on pairs
479 * of statement instances is the same.
481 static int check_order_not_sv(__isl_keep isl_union_map *schedule,
482 __isl_keep isl_union_map *code_schedule)
484 isl_union_map *t1;
485 isl_union_map *t2;
486 int equal;
488 t1 = isl_union_map_lex_lt_union_map(isl_union_map_copy(schedule),
489 isl_union_map_copy(schedule));
490 t2 = isl_union_map_lex_lt_union_map(isl_union_map_copy(code_schedule),
491 isl_union_map_copy(code_schedule));
492 equal = isl_union_map_is_equal(t1, t2);
493 isl_union_map_free(t1);
494 isl_union_map_free(t2);
496 if (equal < 0)
497 return -1;
498 if (!equal)
499 isl_die(isl_union_map_get_ctx(schedule), isl_error_unknown,
500 "order not respected", return -1);
502 return 0;
505 /* Check that the relative order specified by the input schedule is respected
506 * by the schedule extracted from the code.
508 * "sv" indicated whether the original schedule is single valued.
509 * If so, we use a cheaper test. Otherwise, we fall back on a more
510 * expensive test.
512 static int check_order(__isl_keep isl_union_map *schedule,
513 __isl_keep isl_union_map *code_schedule, int sv)
515 if (sv)
516 return check_order_sv(schedule, code_schedule);
517 else
518 return check_order_not_sv(schedule, code_schedule);
521 /* If the original schedule was single valued ("sv" is set),
522 * then the schedule extracted from the code should be single valued as well.
524 static int check_single_valued(__isl_keep isl_union_map *code_schedule, int sv)
526 if (!sv)
527 return 0;
529 sv = isl_union_map_is_single_valued(code_schedule);
530 if (sv < 0)
531 return -1;
533 if (!sv)
534 isl_die(isl_union_map_get_ctx(code_schedule), isl_error_unknown,
535 "schedule not single valued", return -1);
537 return 0;
540 /* Read a schedule and a context from the first argument and
541 * C code from the second argument and check that the C code
542 * corresponds to the schedule on the context.
544 * In particular, check that
545 * - the domains are identical, i.e., the calls in the C code
546 * correspond to the domain elements of the schedule
547 * - no function is called twice with the same arguments, provided
548 * the schedule is single-valued
549 * - the calls are performed in an order that is compatible
550 * with the schedule
552 * If the schedule is not single-valued then we would have to check
553 * that each function with a given set of arguments is called
554 * the same number of times as there are images in the schedule,
555 * but this is considerably more difficult.
557 int main(int argc, char **argv)
559 isl_ctx *ctx;
560 isl_set *context;
561 isl_union_map *schedule, *code_schedule;
562 struct pet_scop *scop;
563 struct options *options;
564 FILE *file;
565 int r;
566 int sv;
568 options = options_new_with_defaults();
569 assert(options);
570 ctx = isl_ctx_alloc_with_options(&options_args, options);
571 pet_options_set_signed_overflow(ctx, PET_OVERFLOW_IGNORE);
572 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
574 file = fopen(options->schedule, "r");
575 assert(file);
576 schedule = isl_union_map_read_from_file(ctx, file);
577 context = isl_set_read_from_file(ctx, file);
578 fclose(file);
580 scop = pet_scop_extract_from_C_source(ctx, options->code, NULL);
582 schedule = isl_union_map_intersect_params(schedule,
583 isl_set_copy(context));
584 code_schedule = extract_code_schedule(scop);
585 code_schedule = isl_union_map_intersect_params(code_schedule, context);
587 sv = isl_union_map_is_single_valued(schedule);
588 r = sv < 0 ||
589 check_domain(schedule, code_schedule) ||
590 check_single_valued(code_schedule, sv) ||
591 check_order(schedule, code_schedule, sv);
593 pet_scop_free(scop);
594 isl_union_map_free(schedule);
595 isl_union_map_free(code_schedule);
596 isl_ctx_free(ctx);
598 return r;