PetScan::get_array_size: cache results
[pet.git] / context.c
blob5406b0714b2fef73e78ae847aaebc40ceb27f64a
1 /*
2 * Copyright 2011 Leiden University. All rights reserved.
3 * Copyright 2014 Ecole Normale Superieure. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY LEIDEN UNIVERSITY ''AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LEIDEN UNIVERSITY OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * The views and conclusions contained in the software and documentation
30 * are those of the authors and should not be interpreted as
31 * representing official policies, either expressed or implied, of
32 * Leiden University.
35 #include <isl/aff.h>
37 #include "aff.h"
38 #include "context.h"
39 #include "expr.h"
40 #include "expr_arg.h"
41 #include "nest.h"
43 /* A pet_context represents the context in which a pet_expr
44 * in converted to an affine expression.
46 * "domain" prescribes the domain of the affine expressions.
48 * "assignments" maps variable names to their currently known values.
49 * Internally, the domains of the values may be equal to some prefix
50 * of the space of "domain", but the domains are updated to be
51 * equal to the space of "domain" before passing them to the user.
52 * If a variable has been assigned an unknown value (possibly because
53 * it may be assigned a different expression in each iteration) or a value
54 * that is not an affine expression, then the corresponding isl_pw_aff
55 * is set to NaN.
57 * If "allow_nested" is set, then the affine expression created
58 * in this context may involve new parameters that encode a pet_expr.
60 struct pet_context {
61 int ref;
63 isl_set *domain;
64 isl_id_to_pw_aff *assignments;
65 int allow_nested;
68 /* Create a pet_context with the given domain, assignments,
69 * and value for allow_nested.
71 static __isl_give pet_context *context_alloc(__isl_take isl_set *domain,
72 __isl_take isl_id_to_pw_aff *assignments, int allow_nested)
74 pet_context *pc;
76 if (!domain || !assignments)
77 goto error;
79 pc = isl_calloc_type(isl_set_get_ctx(domain), struct pet_context);
80 if (!pc)
81 goto error;
83 pc->ref = 1;
84 pc->domain = domain;
85 pc->assignments = assignments;
86 pc->allow_nested = allow_nested;
88 return pc;
89 error:
90 isl_set_free(domain);
91 isl_id_to_pw_aff_free(assignments);
92 return NULL;
95 /* Create a pet_context with the given domain.
97 * Initially, there are no assigned values and parameters that
98 * encode a pet_expr are not allowed.
100 __isl_give pet_context *pet_context_alloc(__isl_take isl_set *domain)
102 isl_id_to_pw_aff *assignments;
104 if (!domain)
105 return NULL;
107 assignments = isl_id_to_pw_aff_alloc(isl_set_get_ctx(domain), 0);;
108 return context_alloc(domain, assignments, 0);
111 /* Return an independent duplicate of "pc".
113 static __isl_give pet_context *pet_context_dup(__isl_keep pet_context *pc)
115 pet_context *dup;
117 if (!pc)
118 return NULL;
120 dup = context_alloc(isl_set_copy(pc->domain),
121 isl_id_to_pw_aff_copy(pc->assignments),
122 pc->allow_nested);
124 return dup;
127 /* Return a pet_context that is equal to "pc" and that has only one reference.
129 static __isl_give pet_context *pet_context_cow(__isl_take pet_context *pc)
131 if (!pc)
132 return NULL;
134 if (pc->ref == 1)
135 return pc;
136 pc->ref--;
137 return pet_context_dup(pc);
140 /* Return an extra reference to "pc".
142 __isl_give pet_context *pet_context_copy(__isl_keep pet_context *pc)
144 if (!pc)
145 return NULL;
147 pc->ref++;
148 return pc;
151 /* Free a reference to "pc" and return NULL.
153 __isl_null pet_context *pet_context_free(__isl_take pet_context *pc)
155 if (!pc)
156 return NULL;
157 if (--pc->ref > 0)
158 return NULL;
160 isl_set_free(pc->domain);
161 isl_id_to_pw_aff_free(pc->assignments);
162 free(pc);
163 return NULL;
166 /* Return the isl_ctx in which "pc" was created.
168 isl_ctx *pet_context_get_ctx(__isl_keep pet_context *pc)
170 return pc ? isl_set_get_ctx(pc->domain) : NULL;
173 /* Return the domain of "pc".
175 __isl_give isl_set *pet_context_get_domain(__isl_keep pet_context *pc)
177 if (!pc)
178 return NULL;
179 return isl_set_copy(pc->domain);
182 /* Return the domain of "pc" in a form that is suitable
183 * for use as a gist context.
184 * In particular, remove all references to nested expression parameters
185 * so that they do not get introduced in the gisted expression.
187 __isl_give isl_set *pet_context_get_gist_domain(__isl_keep pet_context *pc)
189 isl_set *domain;
191 domain = pet_context_get_domain(pc);
192 domain = pet_nested_remove_from_set(domain);
193 return domain;
196 /* Return the domain space of "pc".
198 * The domain of "pc" may have constraints involving parameters
199 * that encode a pet_expr. These parameters are not relevant
200 * outside this domain. Furthermore, they need to be resolved
201 * from the final result, so we do not want to propagate them needlessly.
203 __isl_give isl_space *pet_context_get_space(__isl_keep pet_context *pc)
205 isl_space *space;
207 if (!pc)
208 return NULL;
210 space = isl_set_get_space(pc->domain);
211 space = pet_nested_remove_from_space(space);
212 return space;
215 /* Return the dimension of the domain space of "pc".
217 unsigned pet_context_dim(__isl_keep pet_context *pc)
219 if (!pc)
220 return 0;
221 return isl_set_dim(pc->domain, isl_dim_set);
224 /* Return the assignments of "pc".
226 __isl_give isl_id_to_pw_aff *pet_context_get_assignments(
227 __isl_keep pet_context *pc)
229 if (!pc)
230 return NULL;
231 return isl_id_to_pw_aff_copy(pc->assignments);
234 /* Is "id" assigned any value in "pc"?
236 int pet_context_is_assigned(__isl_keep pet_context *pc, __isl_keep isl_id *id)
238 if (!pc || !id)
239 return -1;
240 return isl_id_to_pw_aff_has(pc->assignments, id);
243 /* Return the value assigned to "id" in "pc".
245 * Some dimensions may have been added to pc->domain after the value
246 * associated to "id" was added. We therefore need to adjust the domain
247 * of the stored value to match pc->domain by adding the missing
248 * dimensions.
250 __isl_give isl_pw_aff *pet_context_get_value(__isl_keep pet_context *pc,
251 __isl_take isl_id *id)
253 int dim;
254 isl_pw_aff *pa;
255 isl_multi_aff *ma;
257 if (!pc || !id)
258 goto error;
260 pa = isl_id_to_pw_aff_get(pc->assignments, id);
261 dim = isl_pw_aff_dim(pa, isl_dim_in);
262 if (dim == isl_set_dim(pc->domain, isl_dim_set))
263 return pa;
265 ma = pet_prefix_projection(pet_context_get_space(pc), dim);
266 pa = isl_pw_aff_pullback_multi_aff(pa, ma);
268 return pa;
269 error:
270 isl_id_free(id);
271 return NULL;
274 /* Assign the value "value" to "id" in "pc", replacing the previously
275 * assigned value, if any.
277 __isl_give pet_context *pet_context_set_value(__isl_take pet_context *pc,
278 __isl_take isl_id *id, isl_pw_aff *value)
280 pc = pet_context_cow(pc);
281 if (!pc)
282 goto error;
283 pc->assignments = isl_id_to_pw_aff_set(pc->assignments, id, value);
284 if (!pc->assignments)
285 return pet_context_free(pc);
286 return pc;
287 error:
288 isl_id_free(id);
289 isl_pw_aff_free(value);
290 return NULL;
293 /* Remove any assignment to "id" in "pc".
295 __isl_give pet_context *pet_context_clear_value(__isl_keep pet_context *pc,
296 __isl_take isl_id *id)
298 pc = pet_context_cow(pc);
299 if (!pc)
300 goto error;
301 pc->assignments = isl_id_to_pw_aff_drop(pc->assignments, id);
302 if (!pc->assignments)
303 return pet_context_free(pc);
304 return pc;
305 error:
306 isl_id_free(id);
307 return NULL;
310 /* Return a piecewise affine expression defined on the specified domain
311 * that represents NaN.
313 static __isl_give isl_pw_aff *nan_on_domain(__isl_take isl_space *space)
315 return isl_pw_aff_nan_on_domain(isl_local_space_from_space(space));
318 /* Assign the value NaN to "id" in "pc", marked it as having an unknown
319 * value.
321 __isl_give pet_context *pet_context_mark_unknown(__isl_take pet_context *pc,
322 __isl_take isl_id *id)
324 isl_pw_aff *pa;
326 pa = nan_on_domain(pet_context_get_space(pc));
327 pc = pet_context_set_value(pc, id, pa);
329 return pc;
332 /* Are affine expressions created in this context allowed to involve
333 * parameters that encode a pet_expr?
335 int pet_context_allow_nesting(__isl_keep pet_context *pc)
337 if (!pc)
338 return -1;
339 return pc->allow_nested;
342 /* Allow affine expressions created in this context to involve
343 * parameters that encode a pet_expr based on the value of "allow_nested".
345 __isl_give pet_context *pet_context_set_allow_nested(__isl_take pet_context *pc,
346 int allow_nested)
348 if (!pc)
349 return NULL;
350 if (pc->allow_nested == allow_nested)
351 return pc;
352 pc = pet_context_cow(pc);
353 if (!pc)
354 return NULL;
355 pc->allow_nested = allow_nested;
356 return pc;
359 /* If the access expression "expr" writes to a (non-virtual) scalar,
360 * then mark the scalar as having an unknown value in "pc".
362 static int clear_write(__isl_keep pet_expr *expr, void *user)
364 isl_id *id;
365 pet_context **pc = (pet_context **) user;
367 if (!pet_expr_access_is_write(expr))
368 return 0;
369 if (!pet_expr_is_scalar_access(expr))
370 return 0;
372 id = pet_expr_access_get_id(expr);
373 if (isl_id_get_user(id))
374 *pc = pet_context_mark_unknown(*pc, id);
375 else
376 isl_id_free(id);
378 return 0;
381 /* Look for any writes to scalar variables in "expr" and
382 * mark them as having an unknown value in "pc".
384 __isl_give pet_context *pet_context_clear_writes_in_expr(
385 __isl_take pet_context *pc, __isl_keep pet_expr *expr)
387 if (pet_expr_foreach_access_expr(expr, &clear_write, &pc) < 0)
388 pc = pet_context_free(pc);
390 return pc;
393 /* Look for any writes to scalar variables in "tree" and
394 * mark them as having an unknown value in "pc".
396 __isl_give pet_context *pet_context_clear_writes_in_tree(
397 __isl_take pet_context *pc, __isl_keep pet_tree *tree)
399 if (pet_tree_foreach_access_expr(tree, &clear_write, &pc) < 0)
400 pc = pet_context_free(pc);
402 return pc;
405 /* Given an access expression, check if it reads a scalar variable
406 * that has a known value in "pc".
407 * If so, then replace the access by an access to that value.
409 static __isl_give pet_expr *access_plug_in_affine_read(
410 __isl_take pet_expr *expr, void *user)
412 pet_context *pc = user;
413 isl_pw_aff *pa;
415 if (pet_expr_access_is_write(expr))
416 return expr;
417 if (!pet_expr_is_scalar_access(expr))
418 return expr;
420 pa = pet_expr_extract_affine(expr, pc);
421 if (!pa)
422 return pet_expr_free(expr);
423 if (isl_pw_aff_involves_nan(pa)) {
424 isl_pw_aff_free(pa);
425 return expr;
428 pet_expr_free(expr);
429 expr = pet_expr_from_index(isl_multi_pw_aff_from_pw_aff(pa));
431 return expr;
434 /* Replace every read access in "expr" to a scalar variable
435 * that has a known value in "pc" by that known value.
437 static __isl_give pet_expr *plug_in_affine_read(__isl_take pet_expr *expr,
438 __isl_keep pet_context *pc)
440 return pet_expr_map_access(expr, &access_plug_in_affine_read, pc);
443 /* Evaluate "expr" in the context of "pc".
445 * In particular, we first make sure that all the access expressions
446 * inside "expr" have the same domain as "pc".
447 * Then, we plug in affine expressions for scalar reads and
448 * plug in the arguments of all access expressions in "expr".
450 __isl_give pet_expr *pet_context_evaluate_expr(__isl_keep pet_context *pc,
451 __isl_take pet_expr *expr)
453 expr = pet_expr_insert_domain(expr, pet_context_get_space(pc));
454 expr = plug_in_affine_read(expr, pc);
455 return pet_expr_plug_in_args(expr, pc);
458 /* Add an unbounded inner dimension "id" to pc->domain.
460 * The assignments are not adjusted here and therefore keep
461 * their original domain. These domains need to be adjusted before
462 * these assigned values can be used. This is taken care of by
463 * pet_context_get_value.
465 static __isl_give pet_context *extend_domain(__isl_take pet_context *pc,
466 __isl_take isl_id *id)
468 int pos;
470 pc = pet_context_cow(pc);
471 if (!pc || !id)
472 goto error;
474 pos = pet_context_dim(pc);
475 pc->domain = isl_set_add_dims(pc->domain, isl_dim_set, 1);
476 pc->domain = isl_set_set_dim_id(pc->domain, isl_dim_set, pos, id);
477 if (!pc->domain)
478 return pet_context_free(pc);
480 return pc;
481 error:
482 pet_context_free(pc);
483 isl_id_free(id);
484 return NULL;
487 /* Add an unbounded inner iterator "id" to pc->domain.
488 * Additionally, mark the variable "id" as having the value of this
489 * new inner iterator.
491 __isl_give pet_context *pet_context_add_inner_iterator(
492 __isl_take pet_context *pc, __isl_take isl_id *id)
494 int pos;
495 isl_space *space;
496 isl_local_space *ls;
497 isl_aff *aff;
498 isl_pw_aff *pa;
500 if (!pc || !id)
501 goto error;
503 pos = pet_context_dim(pc);
504 pc = extend_domain(pc, isl_id_copy(id));
505 if (!pc)
506 goto error;
508 space = pet_context_get_space(pc);
509 ls = isl_local_space_from_space(space);
510 aff = isl_aff_var_on_domain(ls, isl_dim_set, pos);
511 pa = isl_pw_aff_from_aff(aff);
513 pc = pet_context_set_value(pc, id, pa);
515 return pc;
516 error:
517 pet_context_free(pc);
518 isl_id_free(id);
519 return NULL;
522 /* Add an inner iterator to pc->domain.
523 * In particular, extend the domain with an inner loop { [t] : t >= 0 }.
525 __isl_give pet_context *pet_context_add_infinite_loop(
526 __isl_take pet_context *pc)
528 int dim;
529 isl_ctx *ctx;
530 isl_id *id;
532 if (!pc)
533 return NULL;
535 dim = pet_context_dim(pc);
536 ctx = pet_context_get_ctx(pc);
537 id = isl_id_alloc(ctx, "t", NULL);
538 pc = extend_domain(pc, id);
539 pc = pet_context_cow(pc);
540 if (!pc)
541 return NULL;
542 pc->domain = isl_set_lower_bound_si(pc->domain, isl_dim_set, dim, 0);
543 if (!pc->domain)
544 return pet_context_free(pc);
546 return pc;
549 /* Internal data structure for preimage_domain.
551 * "ma" is the function under which the preimage should be computed.
552 * "assignments" collects the results.
554 struct pet_preimage_domain_data {
555 isl_multi_aff *ma;
556 isl_id_to_pw_aff *assignments;
559 /* Add the assignment to "key" of the preimage of "val" under data->ma
560 * to data->assignments.
562 * Some dimensions may have been added to the domain of the enclosing
563 * pet_context after the value "val" was added. We therefore need to
564 * adjust the domain of "val" to match the range of data->ma (which
565 * in turn matches the domain of the pet_context), by adding the missing
566 * dimensions.
568 static int preimage_domain_pair(__isl_take isl_id *key,
569 __isl_take isl_pw_aff *val, void *user)
571 struct pet_preimage_domain_data *data = user;
572 int dim;
573 isl_multi_aff *ma;
575 ma = isl_multi_aff_copy(data->ma);
577 dim = isl_pw_aff_dim(val, isl_dim_in);
578 if (dim != isl_multi_aff_dim(data->ma, isl_dim_out)) {
579 isl_space *space;
580 isl_multi_aff *proj;
581 space = isl_multi_aff_get_space(data->ma);
582 space = isl_space_range(space);
583 proj = pet_prefix_projection(space, dim);
584 ma = isl_multi_aff_pullback_multi_aff(proj, ma);
587 val = isl_pw_aff_pullback_multi_aff(val, ma);
588 data->assignments = isl_id_to_pw_aff_set(data->assignments, key, val);
590 return 0;
593 /* Compute the preimage of "assignments" under the function represented by "ma".
594 * In other words, plug in "ma" in the domains of the assigned values.
596 static __isl_give isl_id_to_pw_aff *preimage_domain(
597 __isl_take isl_id_to_pw_aff *assignments, __isl_keep isl_multi_aff *ma)
599 struct pet_preimage_domain_data data = { ma };
600 isl_ctx *ctx;
602 ctx = isl_id_to_pw_aff_get_ctx(assignments);
603 data.assignments = isl_id_to_pw_aff_alloc(ctx, 0);
604 if (isl_id_to_pw_aff_foreach(assignments, &preimage_domain_pair,
605 &data) < 0)
606 data.assignments = isl_id_to_pw_aff_free(data.assignments);
607 isl_id_to_pw_aff_free(assignments);
609 return data.assignments;
612 /* Compute the preimage of "pc" under the function represented by "ma".
613 * In other words, plug in "ma" in the domain of "pc".
615 __isl_give pet_context *pet_context_preimage_domain(__isl_take pet_context *pc,
616 __isl_keep isl_multi_aff *ma)
618 pc = pet_context_cow(pc);
619 if (!pc)
620 return NULL;
621 pc->domain = isl_set_preimage_multi_aff(pc->domain,
622 isl_multi_aff_copy(ma));
623 pc->assignments = preimage_domain(pc->assignments, ma);
624 if (!pc->domain || !pc->assignments)
625 return pet_context_free(pc);
626 return pc;
629 /* Add the constraints of "set" to the domain of "pc".
631 __isl_give pet_context *pet_context_intersect_domain(__isl_take pet_context *pc,
632 __isl_take isl_set *set)
634 pc = pet_context_cow(pc);
635 if (!pc)
636 goto error;
637 pc->domain = isl_set_intersect(pc->domain, set);
638 if (!pc->domain)
639 return pet_context_free(pc);
640 return pc;
641 error:
642 pet_context_free(pc);
643 isl_set_free(set);
644 return NULL;
647 void pet_context_dump(__isl_keep pet_context *pc)
649 if (!pc)
650 return;
651 fprintf(stderr, "domain: ");
652 isl_set_dump(pc->domain);
653 fprintf(stderr, "assignments: ");
654 isl_id_to_pw_aff_dump(pc->assignments);
655 fprintf(stderr, "nesting allowed: %d\n", pc->allow_nested);