| 1 | /* |
| 2 | * Copyright 2010-2011 INRIA Saclay |
| 3 | * Copyright 2012-2013 Ecole Normale Superieure |
| 4 | * |
| 5 | * Use of this software is governed by the MIT license |
| 6 | * |
| 7 | * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, |
| 8 | * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, |
| 9 | * 91893 Orsay, France |
| 10 | * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France |
| 11 | */ |
| 12 | |
| 13 | #include <isl/val.h> |
| 14 | #include <isl/space.h> |
| 15 | #include <isl_map_private.h> |
| 16 | #include <isl_aff_private.h> |
| 17 | #include <isl/constraint.h> |
| 18 | #include <isl/ilp.h> |
| 19 | #include <isl/fixed_box.h> |
| 20 | |
| 21 | /* Representation of a box of fixed size containing the elements |
| 22 | * [offset, offset + size). |
| 23 | * "size" lives in the target space of "offset". |
| 24 | * |
| 25 | * If any of the "offsets" is NaN, then the object represents |
| 26 | * the failure of finding a fixed-size box. |
| 27 | */ |
| 28 | struct isl_fixed_box { |
| 29 | isl_multi_aff *offset; |
| 30 | isl_multi_val *size; |
| 31 | }; |
| 32 | |
| 33 | /* Free "box" and return NULL. |
| 34 | */ |
| 35 | __isl_null isl_fixed_box *isl_fixed_box_free(__isl_take isl_fixed_box *box) |
| 36 | { |
| 37 | if (!box) |
| 38 | return NULL; |
| 39 | isl_multi_aff_free(multi: box->offset); |
| 40 | isl_multi_val_free(multi: box->size); |
| 41 | free(ptr: box); |
| 42 | return NULL; |
| 43 | } |
| 44 | |
| 45 | /* Construct an isl_fixed_box with the given offset and size. |
| 46 | */ |
| 47 | static __isl_give isl_fixed_box *isl_fixed_box_alloc( |
| 48 | __isl_take isl_multi_aff *offset, __isl_take isl_multi_val *size) |
| 49 | { |
| 50 | isl_ctx *ctx; |
| 51 | isl_fixed_box *box; |
| 52 | |
| 53 | if (!offset || !size) |
| 54 | goto error; |
| 55 | ctx = isl_multi_aff_get_ctx(multi: offset); |
| 56 | box = isl_alloc_type(ctx, struct isl_fixed_box); |
| 57 | if (!box) |
| 58 | goto error; |
| 59 | box->offset = offset; |
| 60 | box->size = size; |
| 61 | |
| 62 | return box; |
| 63 | error: |
| 64 | isl_multi_aff_free(multi: offset); |
| 65 | isl_multi_val_free(multi: size); |
| 66 | return NULL; |
| 67 | } |
| 68 | |
| 69 | /* Construct an initial isl_fixed_box with zero offsets |
| 70 | * in the given space and zero corresponding sizes. |
| 71 | */ |
| 72 | static __isl_give isl_fixed_box *isl_fixed_box_init( |
| 73 | __isl_take isl_space *space) |
| 74 | { |
| 75 | isl_multi_aff *offset; |
| 76 | isl_multi_val *size; |
| 77 | |
| 78 | offset = isl_multi_aff_zero(space: isl_space_copy(space)); |
| 79 | space = isl_space_drop_all_params(space: isl_space_range(space)); |
| 80 | size = isl_multi_val_zero(space); |
| 81 | return isl_fixed_box_alloc(offset, size); |
| 82 | } |
| 83 | |
| 84 | /* Return a copy of "box". |
| 85 | */ |
| 86 | __isl_give isl_fixed_box *isl_fixed_box_copy(__isl_keep isl_fixed_box *box) |
| 87 | { |
| 88 | isl_multi_aff *offset; |
| 89 | isl_multi_val *size; |
| 90 | |
| 91 | offset = isl_fixed_box_get_offset(box); |
| 92 | size = isl_fixed_box_get_size(box); |
| 93 | return isl_fixed_box_alloc(offset, size); |
| 94 | } |
| 95 | |
| 96 | /* Replace the offset and size in direction "pos" by "offset" and "size" |
| 97 | * (without checking whether "box" is a valid box). |
| 98 | */ |
| 99 | static __isl_give isl_fixed_box *isl_fixed_box_set_extent( |
| 100 | __isl_take isl_fixed_box *box, int pos, __isl_keep isl_aff *offset, |
| 101 | __isl_keep isl_val *size) |
| 102 | { |
| 103 | if (!box) |
| 104 | return NULL; |
| 105 | box->offset = isl_multi_aff_set_aff(multi: box->offset, pos, |
| 106 | el: isl_aff_copy(aff: offset)); |
| 107 | box->size = isl_multi_val_set_val(multi: box->size, pos, el: isl_val_copy(v: size)); |
| 108 | if (!box->offset || !box->size) |
| 109 | return isl_fixed_box_free(box); |
| 110 | return box; |
| 111 | } |
| 112 | |
| 113 | /* Replace the offset and size in direction "pos" by "offset" and "size", |
| 114 | * if "box" is a valid box. |
| 115 | */ |
| 116 | static __isl_give isl_fixed_box *isl_fixed_box_set_valid_extent( |
| 117 | __isl_take isl_fixed_box *box, int pos, __isl_keep isl_aff *offset, |
| 118 | __isl_keep isl_val *size) |
| 119 | { |
| 120 | isl_bool valid; |
| 121 | |
| 122 | valid = isl_fixed_box_is_valid(box); |
| 123 | if (valid < 0 || !valid) |
| 124 | return box; |
| 125 | return isl_fixed_box_set_extent(box, pos, offset, size); |
| 126 | } |
| 127 | |
| 128 | /* Replace "box" by an invalid box, by setting all offsets to NaN |
| 129 | * (and all sizes to infinity). |
| 130 | */ |
| 131 | static __isl_give isl_fixed_box *isl_fixed_box_invalidate( |
| 132 | __isl_take isl_fixed_box *box) |
| 133 | { |
| 134 | int i; |
| 135 | isl_size n; |
| 136 | isl_space *space; |
| 137 | isl_val *infty; |
| 138 | isl_aff *nan; |
| 139 | |
| 140 | if (!box) |
| 141 | return NULL; |
| 142 | n = isl_multi_val_dim(multi: box->size, type: isl_dim_set); |
| 143 | if (n < 0) |
| 144 | return isl_fixed_box_free(box); |
| 145 | |
| 146 | infty = isl_val_infty(ctx: isl_fixed_box_get_ctx(box)); |
| 147 | space = isl_space_domain(space: isl_fixed_box_get_space(box)); |
| 148 | nan = isl_aff_nan_on_domain(ls: isl_local_space_from_space(space)); |
| 149 | for (i = 0; i < n; ++i) |
| 150 | box = isl_fixed_box_set_extent(box, pos: i, offset: nan, size: infty); |
| 151 | isl_aff_free(aff: nan); |
| 152 | isl_val_free(v: infty); |
| 153 | |
| 154 | if (!box->offset || !box->size) |
| 155 | return isl_fixed_box_free(box); |
| 156 | return box; |
| 157 | } |
| 158 | |
| 159 | /* Project the domain of the fixed box onto its parameter space. |
| 160 | * In particular, project out the domain of the offset. |
| 161 | */ |
| 162 | static __isl_give isl_fixed_box *isl_fixed_box_project_domain_on_params( |
| 163 | __isl_take isl_fixed_box *box) |
| 164 | { |
| 165 | isl_bool valid; |
| 166 | |
| 167 | valid = isl_fixed_box_is_valid(box); |
| 168 | if (valid < 0) |
| 169 | return isl_fixed_box_free(box); |
| 170 | if (!valid) |
| 171 | return box; |
| 172 | |
| 173 | box->offset = isl_multi_aff_project_domain_on_params(multi: box->offset); |
| 174 | if (!box->offset) |
| 175 | return isl_fixed_box_free(box); |
| 176 | |
| 177 | return box; |
| 178 | } |
| 179 | |
| 180 | /* Return the isl_ctx to which "box" belongs. |
| 181 | */ |
| 182 | isl_ctx *isl_fixed_box_get_ctx(__isl_keep isl_fixed_box *box) |
| 183 | { |
| 184 | if (!box) |
| 185 | return NULL; |
| 186 | return isl_multi_aff_get_ctx(multi: box->offset); |
| 187 | } |
| 188 | |
| 189 | /* Return the space in which "box" lives. |
| 190 | */ |
| 191 | __isl_give isl_space *isl_fixed_box_get_space(__isl_keep isl_fixed_box *box) |
| 192 | { |
| 193 | if (!box) |
| 194 | return NULL; |
| 195 | return isl_multi_aff_get_space(multi: box->offset); |
| 196 | } |
| 197 | |
| 198 | /* Does "box" contain valid information? |
| 199 | */ |
| 200 | isl_bool isl_fixed_box_is_valid(__isl_keep isl_fixed_box *box) |
| 201 | { |
| 202 | if (!box) |
| 203 | return isl_bool_error; |
| 204 | return isl_bool_not(b: isl_multi_aff_involves_nan(multi: box->offset)); |
| 205 | } |
| 206 | |
| 207 | /* Return the offsets of the box "box". |
| 208 | */ |
| 209 | __isl_give isl_multi_aff *isl_fixed_box_get_offset( |
| 210 | __isl_keep isl_fixed_box *box) |
| 211 | { |
| 212 | if (!box) |
| 213 | return NULL; |
| 214 | return isl_multi_aff_copy(multi: box->offset); |
| 215 | } |
| 216 | |
| 217 | /* Return the sizes of the box "box". |
| 218 | */ |
| 219 | __isl_give isl_multi_val *isl_fixed_box_get_size(__isl_keep isl_fixed_box *box) |
| 220 | { |
| 221 | if (!box) |
| 222 | return NULL; |
| 223 | return isl_multi_val_copy(multi: box->size); |
| 224 | } |
| 225 | |
| 226 | /* Data used in set_dim_extent and compute_size_in_direction. |
| 227 | * |
| 228 | * "bset" is a wrapped copy of the basic map that has the selected |
| 229 | * output dimension as range. |
| 230 | * "pos" is the position of the variable representing the output dimension, |
| 231 | * i.e., the variable for which the size should be computed. This variable |
| 232 | * is also the last variable in "bset". |
| 233 | * "size" is the best size found so far |
| 234 | * (infinity if no offset was found so far). |
| 235 | * "offset" is the offset corresponding to the best size |
| 236 | * (NULL if no offset was found so far). |
| 237 | */ |
| 238 | struct isl_size_info { |
| 239 | isl_basic_set *bset; |
| 240 | isl_size pos; |
| 241 | isl_val *size; |
| 242 | isl_aff *offset; |
| 243 | }; |
| 244 | |
| 245 | /* Is "c" a suitable bound on dimension "pos" for use as a lower bound |
| 246 | * of a fixed-size range. |
| 247 | * In particular, it needs to be a lower bound on "pos". |
| 248 | * In order for the final offset not to be too complicated, |
| 249 | * the constraint itself should also not involve any integer divisions. |
| 250 | */ |
| 251 | static isl_bool is_suitable_bound(__isl_keep isl_constraint *c, unsigned pos) |
| 252 | { |
| 253 | isl_size n_div; |
| 254 | isl_bool is_bound, any_divs; |
| 255 | |
| 256 | is_bound = isl_constraint_is_lower_bound(constraint: c, type: isl_dim_set, pos); |
| 257 | if (is_bound < 0 || !is_bound) |
| 258 | return is_bound; |
| 259 | |
| 260 | n_div = isl_constraint_dim(constraint: c, type: isl_dim_div); |
| 261 | if (n_div < 0) |
| 262 | return isl_bool_error; |
| 263 | any_divs = isl_constraint_involves_dims(constraint: c, type: isl_dim_div, first: 0, n: n_div); |
| 264 | return isl_bool_not(b: any_divs); |
| 265 | } |
| 266 | |
| 267 | /* Given a constraint from the basic set describing the bounds on |
| 268 | * an array index, check if it is a lower bound, say m i >= b(x), and, |
| 269 | * if so, check whether the expression "i - ceil(b(x)/m) + 1" has a constant |
| 270 | * upper bound. If so, and if this bound is smaller than any bound |
| 271 | * derived from earlier constraints, set the size to this bound on |
| 272 | * the expression and the lower bound to ceil(b(x)/m). |
| 273 | */ |
| 274 | static isl_stat compute_size_in_direction(__isl_take isl_constraint *c, |
| 275 | void *user) |
| 276 | { |
| 277 | struct isl_size_info *info = user; |
| 278 | isl_val *v; |
| 279 | isl_aff *aff; |
| 280 | isl_aff *lb; |
| 281 | isl_bool is_bound, better; |
| 282 | |
| 283 | is_bound = is_suitable_bound(c, pos: info->pos); |
| 284 | if (is_bound < 0 || !is_bound) { |
| 285 | isl_constraint_free(c); |
| 286 | return is_bound < 0 ? isl_stat_error : isl_stat_ok; |
| 287 | } |
| 288 | |
| 289 | aff = isl_constraint_get_bound(constraint: c, type: isl_dim_set, pos: info->pos); |
| 290 | aff = isl_aff_ceil(aff); |
| 291 | |
| 292 | lb = isl_aff_copy(aff); |
| 293 | |
| 294 | aff = isl_aff_neg(aff); |
| 295 | aff = isl_aff_add_coefficient_si(aff, type: isl_dim_in, pos: info->pos, v: 1); |
| 296 | |
| 297 | v = isl_basic_set_max_val(bset: info->bset, obj: aff); |
| 298 | isl_aff_free(aff); |
| 299 | |
| 300 | v = isl_val_add_ui(v1: v, v2: 1); |
| 301 | better = isl_val_lt(v1: v, v2: info->size); |
| 302 | if (better >= 0 && better) { |
| 303 | isl_val_free(v: info->size); |
| 304 | info->size = isl_val_copy(v); |
| 305 | lb = isl_aff_domain_factor_domain(aff: lb); |
| 306 | isl_aff_free(aff: info->offset); |
| 307 | info->offset = isl_aff_copy(aff: lb); |
| 308 | } |
| 309 | isl_val_free(v); |
| 310 | isl_aff_free(aff: lb); |
| 311 | |
| 312 | isl_constraint_free(c); |
| 313 | |
| 314 | return better < 0 ? isl_stat_error : isl_stat_ok; |
| 315 | } |
| 316 | |
| 317 | /* Look for a fixed-size range of values for the output dimension "pos" |
| 318 | * of "map", by looking for a lower-bound expression in the parameters |
| 319 | * and input dimensions such that the range of the output dimension |
| 320 | * is a constant shifted by this expression. |
| 321 | * |
| 322 | * In particular, look through the explicit lower bounds on the output dimension |
| 323 | * for candidate expressions and pick the one that results in the smallest size. |
| 324 | * Initialize the size with infinity and if no better size is found |
| 325 | * then invalidate the box. Otherwise, set the offset and size |
| 326 | * in the given direction by those that correspond to the smallest size. |
| 327 | * |
| 328 | * Note that while evaluating the size corresponding to a lower bound, |
| 329 | * an affine expression is constructed from the lower bound. |
| 330 | * This lower bound may therefore not have any unknown local variables. |
| 331 | * Eliminate any unknown local variables up front. |
| 332 | * No such restriction needs to be imposed on the set over which |
| 333 | * the size is computed. |
| 334 | */ |
| 335 | static __isl_give isl_fixed_box *set_dim_extent(__isl_take isl_fixed_box *box, |
| 336 | __isl_keep isl_map *map, int pos) |
| 337 | { |
| 338 | struct isl_size_info info; |
| 339 | isl_bool valid; |
| 340 | isl_ctx *ctx; |
| 341 | isl_basic_set *bset; |
| 342 | |
| 343 | if (!box || !map) |
| 344 | return isl_fixed_box_free(box); |
| 345 | |
| 346 | ctx = isl_map_get_ctx(map); |
| 347 | map = isl_map_copy(map); |
| 348 | map = isl_map_project_onto(map, type: isl_dim_out, first: pos, n: 1); |
| 349 | info.size = isl_val_infty(ctx); |
| 350 | info.offset = NULL; |
| 351 | info.pos = isl_map_dim(map, type: isl_dim_in); |
| 352 | info.bset = isl_basic_map_wrap(bmap: isl_map_simple_hull(map)); |
| 353 | bset = isl_basic_set_copy(bset: info.bset); |
| 354 | bset = isl_basic_set_remove_unknown_divs(bset); |
| 355 | if (info.pos < 0) |
| 356 | bset = isl_basic_set_free(bset); |
| 357 | if (isl_basic_set_foreach_constraint(bset, |
| 358 | fn: &compute_size_in_direction, user: &info) < 0) |
| 359 | box = isl_fixed_box_free(box); |
| 360 | isl_basic_set_free(bset); |
| 361 | valid = isl_val_is_int(v: info.size); |
| 362 | if (valid < 0) |
| 363 | box = isl_fixed_box_free(box); |
| 364 | else if (valid) |
| 365 | box = isl_fixed_box_set_valid_extent(box, pos, |
| 366 | offset: info.offset, size: info.size); |
| 367 | else |
| 368 | box = isl_fixed_box_invalidate(box); |
| 369 | isl_val_free(v: info.size); |
| 370 | isl_aff_free(aff: info.offset); |
| 371 | isl_basic_set_free(bset: info.bset); |
| 372 | |
| 373 | return box; |
| 374 | } |
| 375 | |
| 376 | /* Try and construct a fixed-size rectangular box with an offset |
| 377 | * in terms of the domain of "map" that contains the range of "map". |
| 378 | * If no such box can be constructed, then return an invalidated box, |
| 379 | * i.e., one where isl_fixed_box_is_valid returns false. |
| 380 | * |
| 381 | * Iterate over the dimensions in the range |
| 382 | * setting the corresponding offset and extent. |
| 383 | */ |
| 384 | __isl_give isl_fixed_box *isl_map_get_range_simple_fixed_box_hull( |
| 385 | __isl_keep isl_map *map) |
| 386 | { |
| 387 | int i; |
| 388 | isl_size n; |
| 389 | isl_space *space; |
| 390 | isl_fixed_box *box; |
| 391 | |
| 392 | n = isl_map_dim(map, type: isl_dim_out); |
| 393 | if (n < 0) |
| 394 | return NULL; |
| 395 | space = isl_map_get_space(map); |
| 396 | box = isl_fixed_box_init(space); |
| 397 | |
| 398 | map = isl_map_detect_equalities(map: isl_map_copy(map)); |
| 399 | for (i = 0; i < n; ++i) { |
| 400 | isl_bool valid; |
| 401 | |
| 402 | box = set_dim_extent(box, map, pos: i); |
| 403 | valid = isl_fixed_box_is_valid(box); |
| 404 | if (valid < 0 || !valid) |
| 405 | break; |
| 406 | } |
| 407 | isl_map_free(map); |
| 408 | |
| 409 | return box; |
| 410 | } |
| 411 | |
| 412 | /* Compute a fixed box from "set" using "map_box" by treating it as a map |
| 413 | * with a zero-dimensional domain and |
| 414 | * project out the domain again from the result. |
| 415 | */ |
| 416 | static __isl_give isl_fixed_box *fixed_box_as_map(__isl_keep isl_set *set, |
| 417 | __isl_give isl_fixed_box *(*map_box)(__isl_keep isl_map *map)) |
| 418 | { |
| 419 | isl_map *map; |
| 420 | isl_fixed_box *box; |
| 421 | |
| 422 | map = isl_map_from_range(set: isl_set_copy(set)); |
| 423 | box = map_box(map); |
| 424 | isl_map_free(map); |
| 425 | box = isl_fixed_box_project_domain_on_params(box); |
| 426 | |
| 427 | return box; |
| 428 | } |
| 429 | |
| 430 | /* Try and construct a fixed-size rectangular box with an offset |
| 431 | * in terms of the parameters of "set" that contains "set". |
| 432 | * If no such box can be constructed, then return an invalidated box, |
| 433 | * i.e., one where isl_fixed_box_is_valid returns false. |
| 434 | * |
| 435 | * Compute the box using isl_map_get_range_simple_fixed_box_hull |
| 436 | * by constructing a map from the set and |
| 437 | * project out the domain again from the result. |
| 438 | */ |
| 439 | __isl_give isl_fixed_box *isl_set_get_simple_fixed_box_hull( |
| 440 | __isl_keep isl_set *set) |
| 441 | { |
| 442 | return fixed_box_as_map(set, map_box: &isl_map_get_range_simple_fixed_box_hull); |
| 443 | } |
| 444 | |
| 445 | /* Check whether the output elements lie on a rectangular lattice, |
| 446 | * possibly depending on the parameters and the input dimensions. |
| 447 | * Return a tile in this lattice. |
| 448 | * If no stride information can be found, then return a tile of size 1 |
| 449 | * (and offset 0). |
| 450 | * |
| 451 | * Obtain stride information in each output dimension separately and |
| 452 | * combine the results. |
| 453 | */ |
| 454 | __isl_give isl_fixed_box *isl_map_get_range_lattice_tile( |
| 455 | __isl_keep isl_map *map) |
| 456 | { |
| 457 | int i; |
| 458 | isl_size n; |
| 459 | isl_space *space; |
| 460 | isl_fixed_box *box; |
| 461 | |
| 462 | n = isl_map_dim(map, type: isl_dim_out); |
| 463 | if (n < 0) |
| 464 | return NULL; |
| 465 | space = isl_map_get_space(map); |
| 466 | box = isl_fixed_box_init(space); |
| 467 | |
| 468 | for (i = 0; i < n; ++i) { |
| 469 | isl_val *stride; |
| 470 | isl_aff *offset; |
| 471 | isl_stride_info *si; |
| 472 | |
| 473 | si = isl_map_get_range_stride_info(map, pos: i); |
| 474 | stride = isl_stride_info_get_stride(si); |
| 475 | offset = isl_stride_info_get_offset(si); |
| 476 | isl_stride_info_free(si); |
| 477 | |
| 478 | box = isl_fixed_box_set_valid_extent(box, pos: i, offset, size: stride); |
| 479 | |
| 480 | isl_aff_free(aff: offset); |
| 481 | isl_val_free(v: stride); |
| 482 | } |
| 483 | |
| 484 | return box; |
| 485 | } |
| 486 | |
| 487 | /* Check whether the elements lie on a rectangular lattice, |
| 488 | * possibly depending on the parameters. |
| 489 | * Return a tile in this lattice. |
| 490 | * If no stride information can be found, then return a tile of size 1 |
| 491 | * (and offset 0). |
| 492 | * |
| 493 | * Consider the set as a map with a zero-dimensional domain and |
| 494 | * obtain a lattice tile of that map. |
| 495 | */ |
| 496 | __isl_give isl_fixed_box *isl_set_get_lattice_tile(__isl_keep isl_set *set) |
| 497 | { |
| 498 | return fixed_box_as_map(set, map_box: &isl_map_get_range_lattice_tile); |
| 499 | } |
| 500 | |
| 501 | #undef BASE |
| 502 | #define BASE multi_val |
| 503 | #include "print_yaml_field_templ.c" |
| 504 | |
| 505 | #undef BASE |
| 506 | #define BASE multi_aff |
| 507 | #include "print_yaml_field_templ.c" |
| 508 | |
| 509 | /* Print the information contained in "box" to "p". |
| 510 | * The information is printed as a YAML document. |
| 511 | */ |
| 512 | __isl_give isl_printer *isl_printer_print_fixed_box( |
| 513 | __isl_take isl_printer *p, __isl_keep isl_fixed_box *box) |
| 514 | { |
| 515 | if (!box) |
| 516 | return isl_printer_free(printer: p); |
| 517 | |
| 518 | p = isl_printer_yaml_start_mapping(p); |
| 519 | p = print_yaml_field_multi_aff(p, name: "offset" , val: box->offset); |
| 520 | p = print_yaml_field_multi_val(p, name: "size" , val: box->size); |
| 521 | p = isl_printer_yaml_end_mapping(p); |
| 522 | |
| 523 | return p; |
| 524 | } |
| 525 | |
| 526 | #undef BASE |
| 527 | #define BASE fixed_box |
| 528 | #include <print_templ_yaml.c> |
| 529 | |