nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright 2005-2007 Universiteit Leiden |
||
3 | * Copyright 2008-2009 Katholieke Universiteit Leuven |
||
4 | * Copyright 2010 INRIA Saclay |
||
5 | * Copyright 2012 Universiteit Leiden |
||
6 | * |
||
7 | * Use of this software is governed by the GNU LGPLv2.1 license |
||
8 | * |
||
9 | * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science, |
||
10 | * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands |
||
11 | * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A, |
||
12 | * B-3001 Leuven, Belgium |
||
13 | * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, |
||
14 | * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France |
||
15 | */ |
||
16 | |||
17 | #include <isl/set.h> |
||
18 | #include <isl/map.h> |
||
19 | #include <isl/flow.h> |
||
20 | #include <isl_qsort.h> |
||
21 | |||
22 | enum isl_restriction_type { |
||
23 | isl_restriction_type_empty, |
||
24 | isl_restriction_type_none, |
||
25 | isl_restriction_type_input, |
||
26 | isl_restriction_type_output |
||
27 | }; |
||
28 | |||
29 | struct isl_restriction { |
||
30 | enum isl_restriction_type type; |
||
31 | |||
32 | isl_set *source; |
||
33 | isl_set *sink; |
||
34 | }; |
||
35 | |||
36 | /* Create a restriction that doesn't restrict anything. |
||
37 | */ |
||
38 | __isl_give isl_restriction *isl_restriction_none(__isl_take isl_map *source_map) |
||
39 | { |
||
40 | isl_ctx *ctx; |
||
41 | isl_restriction *restr; |
||
42 | |||
43 | if (!source_map) |
||
44 | return NULL; |
||
45 | |||
46 | ctx = isl_map_get_ctx(source_map); |
||
47 | restr = isl_calloc_type(ctx, struct isl_restriction); |
||
48 | if (!restr) |
||
49 | goto error; |
||
50 | |||
51 | restr->type = isl_restriction_type_none; |
||
52 | |||
53 | isl_map_free(source_map); |
||
54 | return restr; |
||
55 | error: |
||
56 | isl_map_free(source_map); |
||
57 | return NULL; |
||
58 | } |
||
59 | |||
60 | /* Create a restriction that removes everything. |
||
61 | */ |
||
62 | __isl_give isl_restriction *isl_restriction_empty( |
||
63 | __isl_take isl_map *source_map) |
||
64 | { |
||
65 | isl_ctx *ctx; |
||
66 | isl_restriction *restr; |
||
67 | |||
68 | if (!source_map) |
||
69 | return NULL; |
||
70 | |||
71 | ctx = isl_map_get_ctx(source_map); |
||
72 | restr = isl_calloc_type(ctx, struct isl_restriction); |
||
73 | if (!restr) |
||
74 | goto error; |
||
75 | |||
76 | restr->type = isl_restriction_type_empty; |
||
77 | |||
78 | isl_map_free(source_map); |
||
79 | return restr; |
||
80 | error: |
||
81 | isl_map_free(source_map); |
||
82 | return NULL; |
||
83 | } |
||
84 | |||
85 | /* Create a restriction on the input of the maximization problem |
||
86 | * based on the given source and sink restrictions. |
||
87 | */ |
||
88 | __isl_give isl_restriction *isl_restriction_input( |
||
89 | __isl_take isl_set *source_restr, __isl_take isl_set *sink_restr) |
||
90 | { |
||
91 | isl_ctx *ctx; |
||
92 | isl_restriction *restr; |
||
93 | |||
94 | if (!source_restr || !sink_restr) |
||
95 | goto error; |
||
96 | |||
97 | ctx = isl_set_get_ctx(source_restr); |
||
98 | restr = isl_calloc_type(ctx, struct isl_restriction); |
||
99 | if (!restr) |
||
100 | goto error; |
||
101 | |||
102 | restr->type = isl_restriction_type_input; |
||
103 | restr->source = source_restr; |
||
104 | restr->sink = sink_restr; |
||
105 | |||
106 | return restr; |
||
107 | error: |
||
108 | isl_set_free(source_restr); |
||
109 | isl_set_free(sink_restr); |
||
110 | return NULL; |
||
111 | } |
||
112 | |||
113 | /* Create a restriction on the output of the maximization problem |
||
114 | * based on the given source restriction. |
||
115 | */ |
||
116 | __isl_give isl_restriction *isl_restriction_output( |
||
117 | __isl_take isl_set *source_restr) |
||
118 | { |
||
119 | isl_ctx *ctx; |
||
120 | isl_restriction *restr; |
||
121 | |||
122 | if (!source_restr) |
||
123 | return NULL; |
||
124 | |||
125 | ctx = isl_set_get_ctx(source_restr); |
||
126 | restr = isl_calloc_type(ctx, struct isl_restriction); |
||
127 | if (!restr) |
||
128 | goto error; |
||
129 | |||
130 | restr->type = isl_restriction_type_output; |
||
131 | restr->source = source_restr; |
||
132 | |||
133 | return restr; |
||
134 | error: |
||
135 | isl_set_free(source_restr); |
||
136 | return NULL; |
||
137 | } |
||
138 | |||
139 | void *isl_restriction_free(__isl_take isl_restriction *restr) |
||
140 | { |
||
141 | if (!restr) |
||
142 | return NULL; |
||
143 | |||
144 | isl_set_free(restr->source); |
||
145 | isl_set_free(restr->sink); |
||
146 | free(restr); |
||
147 | return NULL; |
||
148 | } |
||
149 | |||
150 | isl_ctx *isl_restriction_get_ctx(__isl_keep isl_restriction *restr) |
||
151 | { |
||
152 | return restr ? isl_set_get_ctx(restr->source) : NULL; |
||
153 | } |
||
154 | |||
155 | /* A private structure to keep track of a mapping together with |
||
156 | * a user-specified identifier and a boolean indicating whether |
||
157 | * the map represents a must or may access/dependence. |
||
158 | */ |
||
159 | struct isl_labeled_map { |
||
160 | struct isl_map *map; |
||
161 | void *data; |
||
162 | int must; |
||
163 | }; |
||
164 | |||
165 | /* A structure containing the input for dependence analysis: |
||
166 | * - a sink |
||
167 | * - n_must + n_may (<= max_source) sources |
||
168 | * - a function for determining the relative order of sources and sink |
||
169 | * The must sources are placed before the may sources. |
||
170 | * |
||
171 | * domain_map is an auxiliary map that maps the sink access relation |
||
172 | * to the domain of this access relation. |
||
173 | * |
||
174 | * restrict_fn is a callback that (if not NULL) will be called |
||
175 | * right before any lexicographical maximization. |
||
176 | */ |
||
177 | struct isl_access_info { |
||
178 | isl_map *domain_map; |
||
179 | struct isl_labeled_map sink; |
||
180 | isl_access_level_before level_before; |
||
181 | |||
182 | isl_access_restrict restrict_fn; |
||
183 | void *restrict_user; |
||
184 | |||
185 | int max_source; |
||
186 | int n_must; |
||
187 | int n_may; |
||
188 | struct isl_labeled_map source[1]; |
||
189 | }; |
||
190 | |||
191 | /* A structure containing the output of dependence analysis: |
||
192 | * - n_source dependences |
||
193 | * - a wrapped subset of the sink for which definitely no source could be found |
||
194 | * - a wrapped subset of the sink for which possibly no source could be found |
||
195 | */ |
||
196 | struct isl_flow { |
||
197 | isl_set *must_no_source; |
||
198 | isl_set *may_no_source; |
||
199 | int n_source; |
||
200 | struct isl_labeled_map *dep; |
||
201 | }; |
||
202 | |||
203 | /* Construct an isl_access_info structure and fill it up with |
||
204 | * the given data. The number of sources is set to 0. |
||
205 | */ |
||
206 | __isl_give isl_access_info *isl_access_info_alloc(__isl_take isl_map *sink, |
||
207 | void *sink_user, isl_access_level_before fn, int max_source) |
||
208 | { |
||
209 | isl_ctx *ctx; |
||
210 | struct isl_access_info *acc; |
||
211 | |||
212 | if (!sink) |
||
213 | return NULL; |
||
214 | |||
215 | ctx = isl_map_get_ctx(sink); |
||
216 | isl_assert(ctx, max_source >= 0, goto error); |
||
217 | |||
218 | acc = isl_calloc(ctx, struct isl_access_info, |
||
219 | sizeof(struct isl_access_info) + |
||
220 | (max_source - 1) * sizeof(struct isl_labeled_map)); |
||
221 | if (!acc) |
||
222 | goto error; |
||
223 | |||
224 | acc->sink.map = sink; |
||
225 | acc->sink.data = sink_user; |
||
226 | acc->level_before = fn; |
||
227 | acc->max_source = max_source; |
||
228 | acc->n_must = 0; |
||
229 | acc->n_may = 0; |
||
230 | |||
231 | return acc; |
||
232 | error: |
||
233 | isl_map_free(sink); |
||
234 | return NULL; |
||
235 | } |
||
236 | |||
237 | /* Free the given isl_access_info structure. |
||
238 | */ |
||
239 | void isl_access_info_free(__isl_take isl_access_info *acc) |
||
240 | { |
||
241 | int i; |
||
242 | |||
243 | if (!acc) |
||
244 | return; |
||
245 | isl_map_free(acc->domain_map); |
||
246 | isl_map_free(acc->sink.map); |
||
247 | for (i = 0; i < acc->n_must + acc->n_may; ++i) |
||
248 | isl_map_free(acc->source[i].map); |
||
249 | free(acc); |
||
250 | } |
||
251 | |||
252 | isl_ctx *isl_access_info_get_ctx(__isl_keep isl_access_info *acc) |
||
253 | { |
||
254 | return acc ? isl_map_get_ctx(acc->sink.map) : NULL; |
||
255 | } |
||
256 | |||
257 | __isl_give isl_access_info *isl_access_info_set_restrict( |
||
258 | __isl_take isl_access_info *acc, isl_access_restrict fn, void *user) |
||
259 | { |
||
260 | if (!acc) |
||
261 | return NULL; |
||
262 | acc->restrict_fn = fn; |
||
263 | acc->restrict_user = user; |
||
264 | return acc; |
||
265 | } |
||
266 | |||
267 | /* Add another source to an isl_access_info structure, making |
||
268 | * sure the "must" sources are placed before the "may" sources. |
||
269 | * This function may be called at most max_source times on a |
||
270 | * given isl_access_info structure, with max_source as specified |
||
271 | * in the call to isl_access_info_alloc that constructed the structure. |
||
272 | */ |
||
273 | __isl_give isl_access_info *isl_access_info_add_source( |
||
274 | __isl_take isl_access_info *acc, __isl_take isl_map *source, |
||
275 | int must, void *source_user) |
||
276 | { |
||
277 | isl_ctx *ctx; |
||
278 | |||
279 | if (!acc) |
||
280 | return NULL; |
||
281 | ctx = isl_map_get_ctx(acc->sink.map); |
||
282 | isl_assert(ctx, acc->n_must + acc->n_may < acc->max_source, goto error); |
||
283 | |||
284 | if (must) { |
||
285 | if (acc->n_may) |
||
286 | acc->source[acc->n_must + acc->n_may] = |
||
287 | acc->source[acc->n_must]; |
||
288 | acc->source[acc->n_must].map = source; |
||
289 | acc->source[acc->n_must].data = source_user; |
||
290 | acc->source[acc->n_must].must = 1; |
||
291 | acc->n_must++; |
||
292 | } else { |
||
293 | acc->source[acc->n_must + acc->n_may].map = source; |
||
294 | acc->source[acc->n_must + acc->n_may].data = source_user; |
||
295 | acc->source[acc->n_must + acc->n_may].must = 0; |
||
296 | acc->n_may++; |
||
297 | } |
||
298 | |||
299 | return acc; |
||
300 | error: |
||
301 | isl_map_free(source); |
||
302 | isl_access_info_free(acc); |
||
303 | return NULL; |
||
304 | } |
||
305 | |||
306 | /* Return -n, 0 or n (with n a positive value), depending on whether |
||
307 | * the source access identified by p1 should be sorted before, together |
||
308 | * or after that identified by p2. |
||
309 | * |
||
310 | * If p1 appears before p2, then it should be sorted first. |
||
311 | * For more generic initial schedules, it is possible that neither |
||
312 | * p1 nor p2 appears before the other, or at least not in any obvious way. |
||
313 | * We therefore also check if p2 appears before p1, in which case p2 |
||
314 | * should be sorted first. |
||
315 | * If not, we try to order the two statements based on the description |
||
316 | * of the iteration domains. This results in an arbitrary, but fairly |
||
317 | * stable ordering. |
||
318 | */ |
||
319 | static int access_sort_cmp(const void *p1, const void *p2, void *user) |
||
320 | { |
||
321 | isl_access_info *acc = user; |
||
322 | const struct isl_labeled_map *i1, *i2; |
||
323 | int level1, level2; |
||
324 | uint32_t h1, h2; |
||
325 | i1 = (const struct isl_labeled_map *) p1; |
||
326 | i2 = (const struct isl_labeled_map *) p2; |
||
327 | |||
328 | level1 = acc->level_before(i1->data, i2->data); |
||
329 | if (level1 % 2) |
||
330 | return -1; |
||
331 | |||
332 | level2 = acc->level_before(i2->data, i1->data); |
||
333 | if (level2 % 2) |
||
334 | return 1; |
||
335 | |||
336 | h1 = isl_map_get_hash(i1->map); |
||
337 | h2 = isl_map_get_hash(i2->map); |
||
338 | return h1 > h2 ? 1 : h1 < h2 ? -1 : 0; |
||
339 | } |
||
340 | |||
341 | /* Sort the must source accesses in their textual order. |
||
342 | */ |
||
343 | static __isl_give isl_access_info *isl_access_info_sort_sources( |
||
344 | __isl_take isl_access_info *acc) |
||
345 | { |
||
346 | if (!acc) |
||
347 | return NULL; |
||
348 | if (acc->n_must <= 1) |
||
349 | return acc; |
||
350 | |||
351 | isl_quicksort(acc->source, acc->n_must, sizeof(struct isl_labeled_map), |
||
352 | access_sort_cmp, acc); |
||
353 | |||
354 | return acc; |
||
355 | } |
||
356 | |||
357 | /* Align the parameters of the two spaces if needed and then call |
||
358 | * isl_space_join. |
||
359 | */ |
||
360 | static __isl_give isl_space *space_align_and_join(__isl_take isl_space *left, |
||
361 | __isl_take isl_space *right) |
||
362 | { |
||
363 | if (isl_space_match(left, isl_dim_param, right, isl_dim_param)) |
||
364 | return isl_space_join(left, right); |
||
365 | |||
366 | left = isl_space_align_params(left, isl_space_copy(right)); |
||
367 | right = isl_space_align_params(right, isl_space_copy(left)); |
||
368 | return isl_space_join(left, right); |
||
369 | } |
||
370 | |||
371 | /* Initialize an empty isl_flow structure corresponding to a given |
||
372 | * isl_access_info structure. |
||
373 | * For each must access, two dependences are created (initialized |
||
374 | * to the empty relation), one for the resulting must dependences |
||
375 | * and one for the resulting may dependences. May accesses can |
||
376 | * only lead to may dependences, so only one dependence is created |
||
377 | * for each of them. |
||
378 | * This function is private as isl_flow structures are only supposed |
||
379 | * to be created by isl_access_info_compute_flow. |
||
380 | */ |
||
381 | static __isl_give isl_flow *isl_flow_alloc(__isl_keep isl_access_info *acc) |
||
382 | { |
||
383 | int i; |
||
384 | struct isl_ctx *ctx; |
||
385 | struct isl_flow *dep; |
||
386 | |||
387 | if (!acc) |
||
388 | return NULL; |
||
389 | |||
390 | ctx = isl_map_get_ctx(acc->sink.map); |
||
391 | dep = isl_calloc_type(ctx, struct isl_flow); |
||
392 | if (!dep) |
||
393 | return NULL; |
||
394 | |||
395 | dep->dep = isl_calloc_array(ctx, struct isl_labeled_map, |
||
396 | 2 * acc->n_must + acc->n_may); |
||
397 | if (!dep->dep) |
||
398 | goto error; |
||
399 | |||
400 | dep->n_source = 2 * acc->n_must + acc->n_may; |
||
401 | for (i = 0; i < acc->n_must; ++i) { |
||
402 | isl_space *dim; |
||
403 | dim = space_align_and_join( |
||
404 | isl_map_get_space(acc->source[i].map), |
||
405 | isl_space_reverse(isl_map_get_space(acc->sink.map))); |
||
406 | dep->dep[2 * i].map = isl_map_empty(dim); |
||
407 | dep->dep[2 * i + 1].map = isl_map_copy(dep->dep[2 * i].map); |
||
408 | dep->dep[2 * i].data = acc->source[i].data; |
||
409 | dep->dep[2 * i + 1].data = acc->source[i].data; |
||
410 | dep->dep[2 * i].must = 1; |
||
411 | dep->dep[2 * i + 1].must = 0; |
||
412 | if (!dep->dep[2 * i].map || !dep->dep[2 * i + 1].map) |
||
413 | goto error; |
||
414 | } |
||
415 | for (i = acc->n_must; i < acc->n_must + acc->n_may; ++i) { |
||
416 | isl_space *dim; |
||
417 | dim = space_align_and_join( |
||
418 | isl_map_get_space(acc->source[i].map), |
||
419 | isl_space_reverse(isl_map_get_space(acc->sink.map))); |
||
420 | dep->dep[acc->n_must + i].map = isl_map_empty(dim); |
||
421 | dep->dep[acc->n_must + i].data = acc->source[i].data; |
||
422 | dep->dep[acc->n_must + i].must = 0; |
||
423 | if (!dep->dep[acc->n_must + i].map) |
||
424 | goto error; |
||
425 | } |
||
426 | |||
427 | return dep; |
||
428 | error: |
||
429 | isl_flow_free(dep); |
||
430 | return NULL; |
||
431 | } |
||
432 | |||
433 | /* Iterate over all sources and for each resulting flow dependence |
||
434 | * that is not empty, call the user specfied function. |
||
435 | * The second argument in this function call identifies the source, |
||
436 | * while the third argument correspond to the final argument of |
||
437 | * the isl_flow_foreach call. |
||
438 | */ |
||
439 | int isl_flow_foreach(__isl_keep isl_flow *deps, |
||
440 | int (*fn)(__isl_take isl_map *dep, int must, void *dep_user, void *user), |
||
441 | void *user) |
||
442 | { |
||
443 | int i; |
||
444 | |||
445 | if (!deps) |
||
446 | return -1; |
||
447 | |||
448 | for (i = 0; i < deps->n_source; ++i) { |
||
449 | if (isl_map_plain_is_empty(deps->dep[i].map)) |
||
450 | continue; |
||
451 | if (fn(isl_map_copy(deps->dep[i].map), deps->dep[i].must, |
||
452 | deps->dep[i].data, user) < 0) |
||
453 | return -1; |
||
454 | } |
||
455 | |||
456 | return 0; |
||
457 | } |
||
458 | |||
459 | /* Return a copy of the subset of the sink for which no source could be found. |
||
460 | */ |
||
461 | __isl_give isl_map *isl_flow_get_no_source(__isl_keep isl_flow *deps, int must) |
||
462 | { |
||
463 | if (!deps) |
||
464 | return NULL; |
||
465 | |||
466 | if (must) |
||
467 | return isl_set_unwrap(isl_set_copy(deps->must_no_source)); |
||
468 | else |
||
469 | return isl_set_unwrap(isl_set_copy(deps->may_no_source)); |
||
470 | } |
||
471 | |||
472 | void isl_flow_free(__isl_take isl_flow *deps) |
||
473 | { |
||
474 | int i; |
||
475 | |||
476 | if (!deps) |
||
477 | return; |
||
478 | isl_set_free(deps->must_no_source); |
||
479 | isl_set_free(deps->may_no_source); |
||
480 | if (deps->dep) { |
||
481 | for (i = 0; i < deps->n_source; ++i) |
||
482 | isl_map_free(deps->dep[i].map); |
||
483 | free(deps->dep); |
||
484 | } |
||
485 | free(deps); |
||
486 | } |
||
487 | |||
488 | isl_ctx *isl_flow_get_ctx(__isl_keep isl_flow *deps) |
||
489 | { |
||
490 | return deps ? isl_set_get_ctx(deps->must_no_source) : NULL; |
||
491 | } |
||
492 | |||
493 | /* Return a map that enforces that the domain iteration occurs after |
||
494 | * the range iteration at the given level. |
||
495 | * If level is odd, then the domain iteration should occur after |
||
496 | * the target iteration in their shared level/2 outermost loops. |
||
497 | * In this case we simply need to enforce that these outermost |
||
498 | * loop iterations are the same. |
||
499 | * If level is even, then the loop iterator of the domain should |
||
500 | * be greater than the loop iterator of the range at the last |
||
501 | * of the level/2 shared loops, i.e., loop level/2 - 1. |
||
502 | */ |
||
503 | static __isl_give isl_map *after_at_level(__isl_take isl_space *dim, int level) |
||
504 | { |
||
505 | struct isl_basic_map *bmap; |
||
506 | |||
507 | if (level % 2) |
||
508 | bmap = isl_basic_map_equal(dim, level/2); |
||
509 | else |
||
510 | bmap = isl_basic_map_more_at(dim, level/2 - 1); |
||
511 | |||
512 | return isl_map_from_basic_map(bmap); |
||
513 | } |
||
514 | |||
515 | /* Compute the partial lexicographic maximum of "dep" on domain "sink", |
||
516 | * but first check if the user has set acc->restrict_fn and if so |
||
517 | * update either the input or the output of the maximization problem |
||
518 | * with respect to the resulting restriction. |
||
519 | * |
||
520 | * Since the user expects a mapping from sink iterations to source iterations, |
||
521 | * whereas the domain of "dep" is a wrapped map, mapping sink iterations |
||
522 | * to accessed array elements, we first need to project out the accessed |
||
523 | * sink array elements by applying acc->domain_map. |
||
524 | * Similarly, the sink restriction specified by the user needs to be |
||
525 | * converted back to the wrapped map. |
||
526 | */ |
||
527 | static __isl_give isl_map *restricted_partial_lexmax( |
||
528 | __isl_keep isl_access_info *acc, __isl_take isl_map *dep, |
||
529 | int source, __isl_take isl_set *sink, __isl_give isl_set **empty) |
||
530 | { |
||
531 | isl_map *source_map; |
||
532 | isl_restriction *restr; |
||
533 | isl_set *sink_domain; |
||
534 | isl_set *sink_restr; |
||
535 | isl_map *res; |
||
536 | |||
537 | if (!acc->restrict_fn) |
||
538 | return isl_map_partial_lexmax(dep, sink, empty); |
||
539 | |||
540 | source_map = isl_map_copy(dep); |
||
541 | source_map = isl_map_apply_domain(source_map, |
||
542 | isl_map_copy(acc->domain_map)); |
||
543 | sink_domain = isl_set_copy(sink); |
||
544 | sink_domain = isl_set_apply(sink_domain, isl_map_copy(acc->domain_map)); |
||
545 | restr = acc->restrict_fn(source_map, sink_domain, |
||
546 | acc->source[source].data, acc->restrict_user); |
||
547 | isl_set_free(sink_domain); |
||
548 | isl_map_free(source_map); |
||
549 | |||
550 | if (!restr) |
||
551 | goto error; |
||
552 | if (restr->type == isl_restriction_type_input) { |
||
553 | dep = isl_map_intersect_range(dep, isl_set_copy(restr->source)); |
||
554 | sink_restr = isl_set_copy(restr->sink); |
||
555 | sink_restr = isl_set_apply(sink_restr, |
||
556 | isl_map_reverse(isl_map_copy(acc->domain_map))); |
||
557 | sink = isl_set_intersect(sink, sink_restr); |
||
558 | } else if (restr->type == isl_restriction_type_empty) { |
||
559 | isl_space *space = isl_map_get_space(dep); |
||
560 | isl_map_free(dep); |
||
561 | dep = isl_map_empty(space); |
||
562 | } |
||
563 | |||
564 | res = isl_map_partial_lexmax(dep, sink, empty); |
||
565 | |||
566 | if (restr->type == isl_restriction_type_output) |
||
567 | res = isl_map_intersect_range(res, isl_set_copy(restr->source)); |
||
568 | |||
569 | isl_restriction_free(restr); |
||
570 | return res; |
||
571 | error: |
||
572 | isl_map_free(dep); |
||
573 | isl_set_free(sink); |
||
574 | *empty = NULL; |
||
575 | return NULL; |
||
576 | } |
||
577 | |||
578 | /* Compute the last iteration of must source j that precedes the sink |
||
579 | * at the given level for sink iterations in set_C. |
||
580 | * The subset of set_C for which no such iteration can be found is returned |
||
581 | * in *empty. |
||
582 | */ |
||
583 | static struct isl_map *last_source(struct isl_access_info *acc, |
||
584 | struct isl_set *set_C, |
||
585 | int j, int level, struct isl_set **empty) |
||
586 | { |
||
587 | struct isl_map *read_map; |
||
588 | struct isl_map *write_map; |
||
589 | struct isl_map *dep_map; |
||
590 | struct isl_map *after; |
||
591 | struct isl_map *result; |
||
592 | |||
593 | read_map = isl_map_copy(acc->sink.map); |
||
594 | write_map = isl_map_copy(acc->source[j].map); |
||
595 | write_map = isl_map_reverse(write_map); |
||
596 | dep_map = isl_map_apply_range(read_map, write_map); |
||
597 | after = after_at_level(isl_map_get_space(dep_map), level); |
||
598 | dep_map = isl_map_intersect(dep_map, after); |
||
599 | result = restricted_partial_lexmax(acc, dep_map, j, set_C, empty); |
||
600 | result = isl_map_reverse(result); |
||
601 | |||
602 | return result; |
||
603 | } |
||
604 | |||
605 | /* For a given mapping between iterations of must source j and iterations |
||
606 | * of the sink, compute the last iteration of must source k preceding |
||
607 | * the sink at level before_level for any of the sink iterations, |
||
608 | * but following the corresponding iteration of must source j at level |
||
609 | * after_level. |
||
610 | */ |
||
611 | static struct isl_map *last_later_source(struct isl_access_info *acc, |
||
612 | struct isl_map *old_map, |
||
613 | int j, int before_level, |
||
614 | int k, int after_level, |
||
615 | struct isl_set **empty) |
||
616 | { |
||
617 | isl_space *dim; |
||
618 | struct isl_set *set_C; |
||
619 | struct isl_map *read_map; |
||
620 | struct isl_map *write_map; |
||
621 | struct isl_map *dep_map; |
||
622 | struct isl_map *after_write; |
||
623 | struct isl_map *before_read; |
||
624 | struct isl_map *result; |
||
625 | |||
626 | set_C = isl_map_range(isl_map_copy(old_map)); |
||
627 | read_map = isl_map_copy(acc->sink.map); |
||
628 | write_map = isl_map_copy(acc->source[k].map); |
||
629 | |||
630 | write_map = isl_map_reverse(write_map); |
||
631 | dep_map = isl_map_apply_range(read_map, write_map); |
||
632 | dim = space_align_and_join(isl_map_get_space(acc->source[k].map), |
||
633 | isl_space_reverse(isl_map_get_space(acc->source[j].map))); |
||
634 | after_write = after_at_level(dim, after_level); |
||
635 | after_write = isl_map_apply_range(after_write, old_map); |
||
636 | after_write = isl_map_reverse(after_write); |
||
637 | dep_map = isl_map_intersect(dep_map, after_write); |
||
638 | before_read = after_at_level(isl_map_get_space(dep_map), before_level); |
||
639 | dep_map = isl_map_intersect(dep_map, before_read); |
||
640 | result = restricted_partial_lexmax(acc, dep_map, k, set_C, empty); |
||
641 | result = isl_map_reverse(result); |
||
642 | |||
643 | return result; |
||
644 | } |
||
645 | |||
646 | /* Given a shared_level between two accesses, return 1 if the |
||
647 | * the first can precede the second at the requested target_level. |
||
648 | * If the target level is odd, i.e., refers to a statement level |
||
649 | * dimension, then first needs to precede second at the requested |
||
650 | * level, i.e., shared_level must be equal to target_level. |
||
651 | * If the target level is odd, then the two loops should share |
||
652 | * at least the requested number of outer loops. |
||
653 | */ |
||
654 | static int can_precede_at_level(int shared_level, int target_level) |
||
655 | { |
||
656 | if (shared_level < target_level) |
||
657 | return 0; |
||
658 | if ((target_level % 2) && shared_level > target_level) |
||
659 | return 0; |
||
660 | return 1; |
||
661 | } |
||
662 | |||
663 | /* Given a possible flow dependence temp_rel[j] between source j and the sink |
||
664 | * at level sink_level, remove those elements for which |
||
665 | * there is an iteration of another source k < j that is closer to the sink. |
||
666 | * The flow dependences temp_rel[k] are updated with the improved sources. |
||
667 | * Any improved source needs to precede the sink at the same level |
||
668 | * and needs to follow source j at the same or a deeper level. |
||
669 | * The lower this level, the later the execution date of source k. |
||
670 | * We therefore consider lower levels first. |
||
671 | * |
||
672 | * If temp_rel[j] is empty, then there can be no improvement and |
||
673 | * we return immediately. |
||
674 | */ |
||
675 | static int intermediate_sources(__isl_keep isl_access_info *acc, |
||
676 | struct isl_map **temp_rel, int j, int sink_level) |
||
677 | { |
||
678 | int k, level; |
||
679 | int depth = 2 * isl_map_dim(acc->source[j].map, isl_dim_in) + 1; |
||
680 | |||
681 | if (isl_map_plain_is_empty(temp_rel[j])) |
||
682 | return 0; |
||
683 | |||
684 | for (k = j - 1; k >= 0; --k) { |
||
685 | int plevel, plevel2; |
||
686 | plevel = acc->level_before(acc->source[k].data, acc->sink.data); |
||
687 | if (!can_precede_at_level(plevel, sink_level)) |
||
688 | continue; |
||
689 | |||
690 | plevel2 = acc->level_before(acc->source[j].data, |
||
691 | acc->source[k].data); |
||
692 | |||
693 | for (level = sink_level; level <= depth; ++level) { |
||
694 | struct isl_map *T; |
||
695 | struct isl_set *trest; |
||
696 | struct isl_map *copy; |
||
697 | |||
698 | if (!can_precede_at_level(plevel2, level)) |
||
699 | continue; |
||
700 | |||
701 | copy = isl_map_copy(temp_rel[j]); |
||
702 | T = last_later_source(acc, copy, j, sink_level, k, |
||
703 | level, &trest); |
||
704 | if (isl_map_plain_is_empty(T)) { |
||
705 | isl_set_free(trest); |
||
706 | isl_map_free(T); |
||
707 | continue; |
||
708 | } |
||
709 | temp_rel[j] = isl_map_intersect_range(temp_rel[j], trest); |
||
710 | temp_rel[k] = isl_map_union_disjoint(temp_rel[k], T); |
||
711 | } |
||
712 | } |
||
713 | |||
714 | return 0; |
||
715 | } |
||
716 | |||
717 | /* Compute all iterations of may source j that precedes the sink at the given |
||
718 | * level for sink iterations in set_C. |
||
719 | */ |
||
720 | static __isl_give isl_map *all_sources(__isl_keep isl_access_info *acc, |
||
721 | __isl_take isl_set *set_C, int j, int level) |
||
722 | { |
||
723 | isl_map *read_map; |
||
724 | isl_map *write_map; |
||
725 | isl_map *dep_map; |
||
726 | isl_map *after; |
||
727 | |||
728 | read_map = isl_map_copy(acc->sink.map); |
||
729 | read_map = isl_map_intersect_domain(read_map, set_C); |
||
730 | write_map = isl_map_copy(acc->source[acc->n_must + j].map); |
||
731 | write_map = isl_map_reverse(write_map); |
||
732 | dep_map = isl_map_apply_range(read_map, write_map); |
||
733 | after = after_at_level(isl_map_get_space(dep_map), level); |
||
734 | dep_map = isl_map_intersect(dep_map, after); |
||
735 | |||
736 | return isl_map_reverse(dep_map); |
||
737 | } |
||
738 | |||
739 | /* For a given mapping between iterations of must source k and iterations |
||
740 | * of the sink, compute the all iteration of may source j preceding |
||
741 | * the sink at level before_level for any of the sink iterations, |
||
742 | * but following the corresponding iteration of must source k at level |
||
743 | * after_level. |
||
744 | */ |
||
745 | static __isl_give isl_map *all_later_sources(__isl_keep isl_access_info *acc, |
||
746 | __isl_keep isl_map *old_map, |
||
747 | int j, int before_level, int k, int after_level) |
||
748 | { |
||
749 | isl_space *dim; |
||
750 | isl_set *set_C; |
||
751 | isl_map *read_map; |
||
752 | isl_map *write_map; |
||
753 | isl_map *dep_map; |
||
754 | isl_map *after_write; |
||
755 | isl_map *before_read; |
||
756 | |||
757 | set_C = isl_map_range(isl_map_copy(old_map)); |
||
758 | read_map = isl_map_copy(acc->sink.map); |
||
759 | read_map = isl_map_intersect_domain(read_map, set_C); |
||
760 | write_map = isl_map_copy(acc->source[acc->n_must + j].map); |
||
761 | |||
762 | write_map = isl_map_reverse(write_map); |
||
763 | dep_map = isl_map_apply_range(read_map, write_map); |
||
764 | dim = isl_space_join(isl_map_get_space(acc->source[acc->n_must + j].map), |
||
765 | isl_space_reverse(isl_map_get_space(acc->source[k].map))); |
||
766 | after_write = after_at_level(dim, after_level); |
||
767 | after_write = isl_map_apply_range(after_write, old_map); |
||
768 | after_write = isl_map_reverse(after_write); |
||
769 | dep_map = isl_map_intersect(dep_map, after_write); |
||
770 | before_read = after_at_level(isl_map_get_space(dep_map), before_level); |
||
771 | dep_map = isl_map_intersect(dep_map, before_read); |
||
772 | return isl_map_reverse(dep_map); |
||
773 | } |
||
774 | |||
775 | /* Given the must and may dependence relations for the must accesses |
||
776 | * for level sink_level, check if there are any accesses of may access j |
||
777 | * that occur in between and return their union. |
||
778 | * If some of these accesses are intermediate with respect to |
||
779 | * (previously thought to be) must dependences, then these |
||
780 | * must dependences are turned into may dependences. |
||
781 | */ |
||
782 | static __isl_give isl_map *all_intermediate_sources( |
||
783 | __isl_keep isl_access_info *acc, __isl_take isl_map *map, |
||
784 | struct isl_map **must_rel, struct isl_map **may_rel, |
||
785 | int j, int sink_level) |
||
786 | { |
||
787 | int k, level; |
||
788 | int depth = 2 * isl_map_dim(acc->source[acc->n_must + j].map, |
||
789 | isl_dim_in) + 1; |
||
790 | |||
791 | for (k = 0; k < acc->n_must; ++k) { |
||
792 | int plevel; |
||
793 | |||
794 | if (isl_map_plain_is_empty(may_rel[k]) && |
||
795 | isl_map_plain_is_empty(must_rel[k])) |
||
796 | continue; |
||
797 | |||
798 | plevel = acc->level_before(acc->source[k].data, |
||
799 | acc->source[acc->n_must + j].data); |
||
800 | |||
801 | for (level = sink_level; level <= depth; ++level) { |
||
802 | isl_map *T; |
||
803 | isl_map *copy; |
||
804 | isl_set *ran; |
||
805 | |||
806 | if (!can_precede_at_level(plevel, level)) |
||
807 | continue; |
||
808 | |||
809 | copy = isl_map_copy(may_rel[k]); |
||
810 | T = all_later_sources(acc, copy, j, sink_level, k, level); |
||
811 | map = isl_map_union(map, T); |
||
812 | |||
813 | copy = isl_map_copy(must_rel[k]); |
||
814 | T = all_later_sources(acc, copy, j, sink_level, k, level); |
||
815 | ran = isl_map_range(isl_map_copy(T)); |
||
816 | map = isl_map_union(map, T); |
||
817 | may_rel[k] = isl_map_union_disjoint(may_rel[k], |
||
818 | isl_map_intersect_range(isl_map_copy(must_rel[k]), |
||
819 | isl_set_copy(ran))); |
||
820 | T = isl_map_from_domain_and_range( |
||
821 | isl_set_universe( |
||
822 | isl_space_domain(isl_map_get_space(must_rel[k]))), |
||
823 | ran); |
||
824 | must_rel[k] = isl_map_subtract(must_rel[k], T); |
||
825 | } |
||
826 | } |
||
827 | |||
828 | return map; |
||
829 | } |
||
830 | |||
831 | /* Compute dependences for the case where all accesses are "may" |
||
832 | * accesses, which boils down to computing memory based dependences. |
||
833 | * The generic algorithm would also work in this case, but it would |
||
834 | * be overkill to use it. |
||
835 | */ |
||
836 | static __isl_give isl_flow *compute_mem_based_dependences( |
||
837 | __isl_keep isl_access_info *acc) |
||
838 | { |
||
839 | int i; |
||
840 | isl_set *mustdo; |
||
841 | isl_set *maydo; |
||
842 | isl_flow *res; |
||
843 | |||
844 | res = isl_flow_alloc(acc); |
||
845 | if (!res) |
||
846 | return NULL; |
||
847 | |||
848 | mustdo = isl_map_domain(isl_map_copy(acc->sink.map)); |
||
849 | maydo = isl_set_copy(mustdo); |
||
850 | |||
851 | for (i = 0; i < acc->n_may; ++i) { |
||
852 | int plevel; |
||
853 | int is_before; |
||
854 | isl_space *dim; |
||
855 | isl_map *before; |
||
856 | isl_map *dep; |
||
857 | |||
858 | plevel = acc->level_before(acc->source[i].data, acc->sink.data); |
||
859 | is_before = plevel & 1; |
||
860 | plevel >>= 1; |
||
861 | |||
862 | dim = isl_map_get_space(res->dep[i].map); |
||
863 | if (is_before) |
||
864 | before = isl_map_lex_le_first(dim, plevel); |
||
865 | else |
||
866 | before = isl_map_lex_lt_first(dim, plevel); |
||
867 | dep = isl_map_apply_range(isl_map_copy(acc->source[i].map), |
||
868 | isl_map_reverse(isl_map_copy(acc->sink.map))); |
||
869 | dep = isl_map_intersect(dep, before); |
||
870 | mustdo = isl_set_subtract(mustdo, |
||
871 | isl_map_range(isl_map_copy(dep))); |
||
872 | res->dep[i].map = isl_map_union(res->dep[i].map, dep); |
||
873 | } |
||
874 | |||
875 | res->may_no_source = isl_set_subtract(maydo, isl_set_copy(mustdo)); |
||
876 | res->must_no_source = mustdo; |
||
877 | |||
878 | return res; |
||
879 | } |
||
880 | |||
881 | /* Compute dependences for the case where there is at least one |
||
882 | * "must" access. |
||
883 | * |
||
884 | * The core algorithm considers all levels in which a source may precede |
||
885 | * the sink, where a level may either be a statement level or a loop level. |
||
886 | * The outermost statement level is 1, the first loop level is 2, etc... |
||
887 | * The algorithm basically does the following: |
||
888 | * for all levels l of the read access from innermost to outermost |
||
889 | * for all sources w that may precede the sink access at that level |
||
890 | * compute the last iteration of the source that precedes the sink access |
||
891 | * at that level |
||
892 | * add result to possible last accesses at level l of source w |
||
893 | * for all sources w2 that we haven't considered yet at this level that may |
||
894 | * also precede the sink access |
||
895 | * for all levels l2 of w from l to innermost |
||
896 | * for all possible last accesses dep of w at l |
||
897 | * compute last iteration of w2 between the source and sink |
||
898 | * of dep |
||
899 | * add result to possible last accesses at level l of write w2 |
||
900 | * and replace possible last accesses dep by the remainder |
||
901 | * |
||
902 | * |
||
903 | * The above algorithm is applied to the must access. During the course |
||
904 | * of the algorithm, we keep track of sink iterations that still |
||
905 | * need to be considered. These iterations are split into those that |
||
906 | * haven't been matched to any source access (mustdo) and those that have only |
||
907 | * been matched to may accesses (maydo). |
||
908 | * At the end of each level, we also consider the may accesses. |
||
909 | * In particular, we consider may accesses that precede the remaining |
||
910 | * sink iterations, moving elements from mustdo to maydo when appropriate, |
||
911 | * and may accesses that occur between a must source and a sink of any |
||
912 | * dependences found at the current level, turning must dependences into |
||
913 | * may dependences when appropriate. |
||
914 | * |
||
915 | */ |
||
916 | static __isl_give isl_flow *compute_val_based_dependences( |
||
917 | __isl_keep isl_access_info *acc) |
||
918 | { |
||
919 | isl_ctx *ctx; |
||
920 | isl_flow *res; |
||
921 | isl_set *mustdo = NULL; |
||
922 | isl_set *maydo = NULL; |
||
923 | int level, j; |
||
924 | int depth; |
||
925 | isl_map **must_rel = NULL; |
||
926 | isl_map **may_rel = NULL; |
||
927 | |||
928 | if (!acc) |
||
929 | return NULL; |
||
930 | |||
931 | res = isl_flow_alloc(acc); |
||
932 | if (!res) |
||
933 | goto error; |
||
934 | ctx = isl_map_get_ctx(acc->sink.map); |
||
935 | |||
936 | depth = 2 * isl_map_dim(acc->sink.map, isl_dim_in) + 1; |
||
937 | mustdo = isl_map_domain(isl_map_copy(acc->sink.map)); |
||
938 | maydo = isl_set_empty_like(mustdo); |
||
939 | if (!mustdo || !maydo) |
||
940 | goto error; |
||
941 | if (isl_set_plain_is_empty(mustdo)) |
||
942 | goto done; |
||
943 | |||
944 | must_rel = isl_alloc_array(ctx, struct isl_map *, acc->n_must); |
||
945 | may_rel = isl_alloc_array(ctx, struct isl_map *, acc->n_must); |
||
946 | if (!must_rel || !may_rel) |
||
947 | goto error; |
||
948 | |||
949 | for (level = depth; level >= 1; --level) { |
||
950 | for (j = acc->n_must-1; j >=0; --j) { |
||
951 | must_rel[j] = isl_map_empty_like(res->dep[j].map); |
||
952 | may_rel[j] = isl_map_copy(must_rel[j]); |
||
953 | } |
||
954 | |||
955 | for (j = acc->n_must - 1; j >= 0; --j) { |
||
956 | struct isl_map *T; |
||
957 | struct isl_set *rest; |
||
958 | int plevel; |
||
959 | |||
960 | plevel = acc->level_before(acc->source[j].data, |
||
961 | acc->sink.data); |
||
962 | if (!can_precede_at_level(plevel, level)) |
||
963 | continue; |
||
964 | |||
965 | T = last_source(acc, mustdo, j, level, &rest); |
||
966 | must_rel[j] = isl_map_union_disjoint(must_rel[j], T); |
||
967 | mustdo = rest; |
||
968 | |||
969 | intermediate_sources(acc, must_rel, j, level); |
||
970 | |||
971 | T = last_source(acc, maydo, j, level, &rest); |
||
972 | may_rel[j] = isl_map_union_disjoint(may_rel[j], T); |
||
973 | maydo = rest; |
||
974 | |||
975 | intermediate_sources(acc, may_rel, j, level); |
||
976 | |||
977 | if (isl_set_plain_is_empty(mustdo) && |
||
978 | isl_set_plain_is_empty(maydo)) |
||
979 | break; |
||
980 | } |
||
981 | for (j = j - 1; j >= 0; --j) { |
||
982 | int plevel; |
||
983 | |||
984 | plevel = acc->level_before(acc->source[j].data, |
||
985 | acc->sink.data); |
||
986 | if (!can_precede_at_level(plevel, level)) |
||
987 | continue; |
||
988 | |||
989 | intermediate_sources(acc, must_rel, j, level); |
||
990 | intermediate_sources(acc, may_rel, j, level); |
||
991 | } |
||
992 | |||
993 | for (j = 0; j < acc->n_may; ++j) { |
||
994 | int plevel; |
||
995 | isl_map *T; |
||
996 | isl_set *ran; |
||
997 | |||
998 | plevel = acc->level_before(acc->source[acc->n_must + j].data, |
||
999 | acc->sink.data); |
||
1000 | if (!can_precede_at_level(plevel, level)) |
||
1001 | continue; |
||
1002 | |||
1003 | T = all_sources(acc, isl_set_copy(maydo), j, level); |
||
1004 | res->dep[2 * acc->n_must + j].map = |
||
1005 | isl_map_union(res->dep[2 * acc->n_must + j].map, T); |
||
1006 | T = all_sources(acc, isl_set_copy(mustdo), j, level); |
||
1007 | ran = isl_map_range(isl_map_copy(T)); |
||
1008 | res->dep[2 * acc->n_must + j].map = |
||
1009 | isl_map_union(res->dep[2 * acc->n_must + j].map, T); |
||
1010 | mustdo = isl_set_subtract(mustdo, isl_set_copy(ran)); |
||
1011 | maydo = isl_set_union_disjoint(maydo, ran); |
||
1012 | |||
1013 | T = res->dep[2 * acc->n_must + j].map; |
||
1014 | T = all_intermediate_sources(acc, T, must_rel, may_rel, |
||
1015 | j, level); |
||
1016 | res->dep[2 * acc->n_must + j].map = T; |
||
1017 | } |
||
1018 | |||
1019 | for (j = acc->n_must - 1; j >= 0; --j) { |
||
1020 | res->dep[2 * j].map = |
||
1021 | isl_map_union_disjoint(res->dep[2 * j].map, |
||
1022 | must_rel[j]); |
||
1023 | res->dep[2 * j + 1].map = |
||
1024 | isl_map_union_disjoint(res->dep[2 * j + 1].map, |
||
1025 | may_rel[j]); |
||
1026 | } |
||
1027 | |||
1028 | if (isl_set_plain_is_empty(mustdo) && |
||
1029 | isl_set_plain_is_empty(maydo)) |
||
1030 | break; |
||
1031 | } |
||
1032 | |||
1033 | free(must_rel); |
||
1034 | free(may_rel); |
||
1035 | done: |
||
1036 | res->must_no_source = mustdo; |
||
1037 | res->may_no_source = maydo; |
||
1038 | return res; |
||
1039 | error: |
||
1040 | isl_flow_free(res); |
||
1041 | isl_set_free(mustdo); |
||
1042 | isl_set_free(maydo); |
||
1043 | free(must_rel); |
||
1044 | free(may_rel); |
||
1045 | return NULL; |
||
1046 | } |
||
1047 | |||
1048 | /* Given a "sink" access, a list of n "source" accesses, |
||
1049 | * compute for each iteration of the sink access |
||
1050 | * and for each element accessed by that iteration, |
||
1051 | * the source access in the list that last accessed the |
||
1052 | * element accessed by the sink access before this sink access. |
||
1053 | * Each access is given as a map from the loop iterators |
||
1054 | * to the array indices. |
||
1055 | * The result is a list of n relations between source and sink |
||
1056 | * iterations and a subset of the domain of the sink access, |
||
1057 | * corresponding to those iterations that access an element |
||
1058 | * not previously accessed. |
||
1059 | * |
||
1060 | * To deal with multi-valued sink access relations, the sink iteration |
||
1061 | * domain is first extended with dimensions that correspond to the data |
||
1062 | * space. After the computation is finished, these extra dimensions are |
||
1063 | * projected out again. |
||
1064 | */ |
||
1065 | __isl_give isl_flow *isl_access_info_compute_flow(__isl_take isl_access_info *acc) |
||
1066 | { |
||
1067 | int j; |
||
1068 | struct isl_flow *res = NULL; |
||
1069 | |||
1070 | if (!acc) |
||
1071 | return NULL; |
||
1072 | |||
1073 | acc->domain_map = isl_map_domain_map(isl_map_copy(acc->sink.map)); |
||
1074 | acc->sink.map = isl_map_range_map(acc->sink.map); |
||
1075 | if (!acc->sink.map) |
||
1076 | goto error; |
||
1077 | |||
1078 | if (acc->n_must == 0) |
||
1079 | res = compute_mem_based_dependences(acc); |
||
1080 | else { |
||
1081 | acc = isl_access_info_sort_sources(acc); |
||
1082 | res = compute_val_based_dependences(acc); |
||
1083 | } |
||
1084 | if (!res) |
||
1085 | goto error; |
||
1086 | |||
1087 | for (j = 0; j < res->n_source; ++j) { |
||
1088 | res->dep[j].map = isl_map_apply_range(res->dep[j].map, |
||
1089 | isl_map_copy(acc->domain_map)); |
||
1090 | if (!res->dep[j].map) |
||
1091 | goto error; |
||
1092 | } |
||
1093 | if (!res->must_no_source || !res->may_no_source) |
||
1094 | goto error; |
||
1095 | |||
1096 | isl_access_info_free(acc); |
||
1097 | return res; |
||
1098 | error: |
||
1099 | isl_access_info_free(acc); |
||
1100 | isl_flow_free(res); |
||
1101 | return NULL; |
||
1102 | } |
||
1103 | |||
1104 | |||
1105 | /* Keep track of some information about a schedule for a given |
||
1106 | * access. In particular, keep track of which dimensions |
||
1107 | * have a constant value and of the actual constant values. |
||
1108 | */ |
||
1109 | struct isl_sched_info { |
||
1110 | int *is_cst; |
||
1111 | isl_vec *cst; |
||
1112 | }; |
||
1113 | |||
1114 | static void sched_info_free(__isl_take struct isl_sched_info *info) |
||
1115 | { |
||
1116 | if (!info) |
||
1117 | return; |
||
1118 | isl_vec_free(info->cst); |
||
1119 | free(info->is_cst); |
||
1120 | free(info); |
||
1121 | } |
||
1122 | |||
1123 | /* Extract information on the constant dimensions of the schedule |
||
1124 | * for a given access. The "map" is of the form |
||
1125 | * |
||
1126 | * [S -> D] -> A |
||
1127 | * |
||
1128 | * with S the schedule domain, D the iteration domain and A the data domain. |
||
1129 | */ |
||
1130 | static __isl_give struct isl_sched_info *sched_info_alloc( |
||
1131 | __isl_keep isl_map *map) |
||
1132 | { |
||
1133 | isl_ctx *ctx; |
||
1134 | isl_space *dim; |
||
1135 | struct isl_sched_info *info; |
||
1136 | int i, n; |
||
1137 | isl_int v; |
||
1138 | |||
1139 | if (!map) |
||
1140 | return NULL; |
||
1141 | |||
1142 | dim = isl_space_unwrap(isl_space_domain(isl_map_get_space(map))); |
||
1143 | if (!dim) |
||
1144 | return NULL; |
||
1145 | n = isl_space_dim(dim, isl_dim_in); |
||
1146 | isl_space_free(dim); |
||
1147 | |||
1148 | ctx = isl_map_get_ctx(map); |
||
1149 | info = isl_alloc_type(ctx, struct isl_sched_info); |
||
1150 | if (!info) |
||
1151 | return NULL; |
||
1152 | info->is_cst = isl_alloc_array(ctx, int, n); |
||
1153 | info->cst = isl_vec_alloc(ctx, n); |
||
1154 | if (!info->is_cst || !info->cst) |
||
1155 | goto error; |
||
1156 | |||
1157 | isl_int_init(v); |
||
1158 | for (i = 0; i < n; ++i) { |
||
1159 | info->is_cst[i] = isl_map_plain_is_fixed(map, isl_dim_in, i, |
||
1160 | &v); |
||
1161 | info->cst = isl_vec_set_element(info->cst, i, v); |
||
1162 | } |
||
1163 | isl_int_clear(v); |
||
1164 | |||
1165 | return info; |
||
1166 | error: |
||
1167 | sched_info_free(info); |
||
1168 | return NULL; |
||
1169 | } |
||
1170 | |||
1171 | struct isl_compute_flow_data { |
||
1172 | isl_union_map *must_source; |
||
1173 | isl_union_map *may_source; |
||
1174 | isl_union_map *must_dep; |
||
1175 | isl_union_map *may_dep; |
||
1176 | isl_union_map *must_no_source; |
||
1177 | isl_union_map *may_no_source; |
||
1178 | |||
1179 | int count; |
||
1180 | int must; |
||
1181 | isl_space *dim; |
||
1182 | struct isl_sched_info *sink_info; |
||
1183 | struct isl_sched_info **source_info; |
||
1184 | isl_access_info *accesses; |
||
1185 | }; |
||
1186 | |||
1187 | static int count_matching_array(__isl_take isl_map *map, void *user) |
||
1188 | { |
||
1189 | int eq; |
||
1190 | isl_space *dim; |
||
1191 | struct isl_compute_flow_data *data; |
||
1192 | |||
1193 | data = (struct isl_compute_flow_data *)user; |
||
1194 | |||
1195 | dim = isl_space_range(isl_map_get_space(map)); |
||
1196 | |||
1197 | eq = isl_space_is_equal(dim, data->dim); |
||
1198 | |||
1199 | isl_space_free(dim); |
||
1200 | isl_map_free(map); |
||
1201 | |||
1202 | if (eq < 0) |
||
1203 | return -1; |
||
1204 | if (eq) |
||
1205 | data->count++; |
||
1206 | |||
1207 | return 0; |
||
1208 | } |
||
1209 | |||
1210 | static int collect_matching_array(__isl_take isl_map *map, void *user) |
||
1211 | { |
||
1212 | int eq; |
||
1213 | isl_space *dim; |
||
1214 | struct isl_sched_info *info; |
||
1215 | struct isl_compute_flow_data *data; |
||
1216 | |||
1217 | data = (struct isl_compute_flow_data *)user; |
||
1218 | |||
1219 | dim = isl_space_range(isl_map_get_space(map)); |
||
1220 | |||
1221 | eq = isl_space_is_equal(dim, data->dim); |
||
1222 | |||
1223 | isl_space_free(dim); |
||
1224 | |||
1225 | if (eq < 0) |
||
1226 | goto error; |
||
1227 | if (!eq) { |
||
1228 | isl_map_free(map); |
||
1229 | return 0; |
||
1230 | } |
||
1231 | |||
1232 | info = sched_info_alloc(map); |
||
1233 | data->source_info[data->count] = info; |
||
1234 | |||
1235 | data->accesses = isl_access_info_add_source(data->accesses, |
||
1236 | map, data->must, info); |
||
1237 | |||
1238 | data->count++; |
||
1239 | |||
1240 | return 0; |
||
1241 | error: |
||
1242 | isl_map_free(map); |
||
1243 | return -1; |
||
1244 | } |
||
1245 | |||
1246 | /* Determine the shared nesting level and the "textual order" of |
||
1247 | * the given accesses. |
||
1248 | * |
||
1249 | * We first determine the minimal schedule dimension for both accesses. |
||
1250 | * |
||
1251 | * If among those dimensions, we can find one where both have a fixed |
||
1252 | * value and if moreover those values are different, then the previous |
||
1253 | * dimension is the last shared nesting level and the textual order |
||
1254 | * is determined based on the order of the fixed values. |
||
1255 | * If no such fixed values can be found, then we set the shared |
||
1256 | * nesting level to the minimal schedule dimension, with no textual ordering. |
||
1257 | */ |
||
1258 | static int before(void *first, void *second) |
||
1259 | { |
||
1260 | struct isl_sched_info *info1 = first; |
||
1261 | struct isl_sched_info *info2 = second; |
||
1262 | int n1, n2; |
||
1263 | int i; |
||
1264 | isl_int v1, v2; |
||
1265 | |||
1266 | n1 = isl_vec_size(info1->cst); |
||
1267 | n2 = isl_vec_size(info2->cst); |
||
1268 | |||
1269 | if (n2 < n1) |
||
1270 | n1 = n2; |
||
1271 | |||
1272 | isl_int_init(v1); |
||
1273 | isl_int_init(v2); |
||
1274 | for (i = 0; i < n1; ++i) { |
||
1275 | int r; |
||
1276 | |||
1277 | if (!info1->is_cst[i]) |
||
1278 | continue; |
||
1279 | if (!info2->is_cst[i]) |
||
1280 | continue; |
||
1281 | isl_vec_get_element(info1->cst, i, &v1); |
||
1282 | isl_vec_get_element(info2->cst, i, &v2); |
||
1283 | if (isl_int_eq(v1, v2)) |
||
1284 | continue; |
||
1285 | |||
1286 | r = 2 * i + isl_int_lt(v1, v2); |
||
1287 | |||
1288 | isl_int_clear(v1); |
||
1289 | isl_int_clear(v2); |
||
1290 | return r; |
||
1291 | } |
||
1292 | isl_int_clear(v1); |
||
1293 | isl_int_clear(v2); |
||
1294 | |||
1295 | return 2 * n1; |
||
1296 | } |
||
1297 | |||
1298 | /* Given a sink access, look for all the source accesses that access |
||
1299 | * the same array and perform dataflow analysis on them using |
||
1300 | * isl_access_info_compute_flow. |
||
1301 | */ |
||
1302 | static int compute_flow(__isl_take isl_map *map, void *user) |
||
1303 | { |
||
1304 | int i; |
||
1305 | isl_ctx *ctx; |
||
1306 | struct isl_compute_flow_data *data; |
||
1307 | isl_flow *flow; |
||
1308 | |||
1309 | data = (struct isl_compute_flow_data *)user; |
||
1310 | |||
1311 | ctx = isl_map_get_ctx(map); |
||
1312 | |||
1313 | data->accesses = NULL; |
||
1314 | data->sink_info = NULL; |
||
1315 | data->source_info = NULL; |
||
1316 | data->count = 0; |
||
1317 | data->dim = isl_space_range(isl_map_get_space(map)); |
||
1318 | |||
1319 | if (isl_union_map_foreach_map(data->must_source, |
||
1320 | &count_matching_array, data) < 0) |
||
1321 | goto error; |
||
1322 | if (isl_union_map_foreach_map(data->may_source, |
||
1323 | &count_matching_array, data) < 0) |
||
1324 | goto error; |
||
1325 | |||
1326 | data->sink_info = sched_info_alloc(map); |
||
1327 | data->source_info = isl_calloc_array(ctx, struct isl_sched_info *, |
||
1328 | data->count); |
||
1329 | |||
1330 | data->accesses = isl_access_info_alloc(isl_map_copy(map), |
||
1331 | data->sink_info, &before, data->count); |
||
1332 | if (!data->sink_info || !data->source_info || !data->accesses) |
||
1333 | goto error; |
||
1334 | data->count = 0; |
||
1335 | data->must = 1; |
||
1336 | if (isl_union_map_foreach_map(data->must_source, |
||
1337 | &collect_matching_array, data) < 0) |
||
1338 | goto error; |
||
1339 | data->must = 0; |
||
1340 | if (isl_union_map_foreach_map(data->may_source, |
||
1341 | &collect_matching_array, data) < 0) |
||
1342 | goto error; |
||
1343 | |||
1344 | flow = isl_access_info_compute_flow(data->accesses); |
||
1345 | data->accesses = NULL; |
||
1346 | |||
1347 | if (!flow) |
||
1348 | goto error; |
||
1349 | |||
1350 | data->must_no_source = isl_union_map_union(data->must_no_source, |
||
1351 | isl_union_map_from_map(isl_flow_get_no_source(flow, 1))); |
||
1352 | data->may_no_source = isl_union_map_union(data->may_no_source, |
||
1353 | isl_union_map_from_map(isl_flow_get_no_source(flow, 0))); |
||
1354 | |||
1355 | for (i = 0; i < flow->n_source; ++i) { |
||
1356 | isl_union_map *dep; |
||
1357 | dep = isl_union_map_from_map(isl_map_copy(flow->dep[i].map)); |
||
1358 | if (flow->dep[i].must) |
||
1359 | data->must_dep = isl_union_map_union(data->must_dep, dep); |
||
1360 | else |
||
1361 | data->may_dep = isl_union_map_union(data->may_dep, dep); |
||
1362 | } |
||
1363 | |||
1364 | isl_flow_free(flow); |
||
1365 | |||
1366 | sched_info_free(data->sink_info); |
||
1367 | if (data->source_info) { |
||
1368 | for (i = 0; i < data->count; ++i) |
||
1369 | sched_info_free(data->source_info[i]); |
||
1370 | free(data->source_info); |
||
1371 | } |
||
1372 | isl_space_free(data->dim); |
||
1373 | isl_map_free(map); |
||
1374 | |||
1375 | return 0; |
||
1376 | error: |
||
1377 | isl_access_info_free(data->accesses); |
||
1378 | sched_info_free(data->sink_info); |
||
1379 | if (data->source_info) { |
||
1380 | for (i = 0; i < data->count; ++i) |
||
1381 | sched_info_free(data->source_info[i]); |
||
1382 | free(data->source_info); |
||
1383 | } |
||
1384 | isl_space_free(data->dim); |
||
1385 | isl_map_free(map); |
||
1386 | |||
1387 | return -1; |
||
1388 | } |
||
1389 | |||
1390 | /* Given a collection of "sink" and "source" accesses, |
||
1391 | * compute for each iteration of a sink access |
||
1392 | * and for each element accessed by that iteration, |
||
1393 | * the source access in the list that last accessed the |
||
1394 | * element accessed by the sink access before this sink access. |
||
1395 | * Each access is given as a map from the loop iterators |
||
1396 | * to the array indices. |
||
1397 | * The result is a relations between source and sink |
||
1398 | * iterations and a subset of the domain of the sink accesses, |
||
1399 | * corresponding to those iterations that access an element |
||
1400 | * not previously accessed. |
||
1401 | * |
||
1402 | * We first prepend the schedule dimensions to the domain |
||
1403 | * of the accesses so that we can easily compare their relative order. |
||
1404 | * Then we consider each sink access individually in compute_flow. |
||
1405 | */ |
||
1406 | int isl_union_map_compute_flow(__isl_take isl_union_map *sink, |
||
1407 | __isl_take isl_union_map *must_source, |
||
1408 | __isl_take isl_union_map *may_source, |
||
1409 | __isl_take isl_union_map *schedule, |
||
1410 | __isl_give isl_union_map **must_dep, __isl_give isl_union_map **may_dep, |
||
1411 | __isl_give isl_union_map **must_no_source, |
||
1412 | __isl_give isl_union_map **may_no_source) |
||
1413 | { |
||
1414 | isl_space *dim; |
||
1415 | isl_union_map *range_map = NULL; |
||
1416 | struct isl_compute_flow_data data; |
||
1417 | |||
1418 | sink = isl_union_map_align_params(sink, |
||
1419 | isl_union_map_get_space(must_source)); |
||
1420 | sink = isl_union_map_align_params(sink, |
||
1421 | isl_union_map_get_space(may_source)); |
||
1422 | sink = isl_union_map_align_params(sink, |
||
1423 | isl_union_map_get_space(schedule)); |
||
1424 | dim = isl_union_map_get_space(sink); |
||
1425 | must_source = isl_union_map_align_params(must_source, isl_space_copy(dim)); |
||
1426 | may_source = isl_union_map_align_params(may_source, isl_space_copy(dim)); |
||
1427 | schedule = isl_union_map_align_params(schedule, isl_space_copy(dim)); |
||
1428 | |||
1429 | schedule = isl_union_map_reverse(schedule); |
||
1430 | range_map = isl_union_map_range_map(schedule); |
||
1431 | schedule = isl_union_map_reverse(isl_union_map_copy(range_map)); |
||
1432 | sink = isl_union_map_apply_domain(sink, isl_union_map_copy(schedule)); |
||
1433 | must_source = isl_union_map_apply_domain(must_source, |
||
1434 | isl_union_map_copy(schedule)); |
||
1435 | may_source = isl_union_map_apply_domain(may_source, schedule); |
||
1436 | |||
1437 | data.must_source = must_source; |
||
1438 | data.may_source = may_source; |
||
1439 | data.must_dep = must_dep ? |
||
1440 | isl_union_map_empty(isl_space_copy(dim)) : NULL; |
||
1441 | data.may_dep = may_dep ? isl_union_map_empty(isl_space_copy(dim)) : NULL; |
||
1442 | data.must_no_source = must_no_source ? |
||
1443 | isl_union_map_empty(isl_space_copy(dim)) : NULL; |
||
1444 | data.may_no_source = may_no_source ? |
||
1445 | isl_union_map_empty(isl_space_copy(dim)) : NULL; |
||
1446 | |||
1447 | isl_space_free(dim); |
||
1448 | |||
1449 | if (isl_union_map_foreach_map(sink, &compute_flow, &data) < 0) |
||
1450 | goto error; |
||
1451 | |||
1452 | isl_union_map_free(sink); |
||
1453 | isl_union_map_free(must_source); |
||
1454 | isl_union_map_free(may_source); |
||
1455 | |||
1456 | if (must_dep) { |
||
1457 | data.must_dep = isl_union_map_apply_domain(data.must_dep, |
||
1458 | isl_union_map_copy(range_map)); |
||
1459 | data.must_dep = isl_union_map_apply_range(data.must_dep, |
||
1460 | isl_union_map_copy(range_map)); |
||
1461 | *must_dep = data.must_dep; |
||
1462 | } |
||
1463 | if (may_dep) { |
||
1464 | data.may_dep = isl_union_map_apply_domain(data.may_dep, |
||
1465 | isl_union_map_copy(range_map)); |
||
1466 | data.may_dep = isl_union_map_apply_range(data.may_dep, |
||
1467 | isl_union_map_copy(range_map)); |
||
1468 | *may_dep = data.may_dep; |
||
1469 | } |
||
1470 | if (must_no_source) { |
||
1471 | data.must_no_source = isl_union_map_apply_domain( |
||
1472 | data.must_no_source, isl_union_map_copy(range_map)); |
||
1473 | *must_no_source = data.must_no_source; |
||
1474 | } |
||
1475 | if (may_no_source) { |
||
1476 | data.may_no_source = isl_union_map_apply_domain( |
||
1477 | data.may_no_source, isl_union_map_copy(range_map)); |
||
1478 | *may_no_source = data.may_no_source; |
||
1479 | } |
||
1480 | |||
1481 | isl_union_map_free(range_map); |
||
1482 | |||
1483 | return 0; |
||
1484 | error: |
||
1485 | isl_union_map_free(range_map); |
||
1486 | isl_union_map_free(sink); |
||
1487 | isl_union_map_free(must_source); |
||
1488 | isl_union_map_free(may_source); |
||
1489 | isl_union_map_free(data.must_dep); |
||
1490 | isl_union_map_free(data.may_dep); |
||
1491 | isl_union_map_free(data.must_no_source); |
||
1492 | isl_union_map_free(data.may_no_source); |
||
1493 | |||
1494 | if (must_dep) |
||
1495 | *must_dep = NULL; |
||
1496 | if (may_dep) |
||
1497 | *may_dep = NULL; |
||
1498 | if (must_no_source) |
||
1499 | *must_no_source = NULL; |
||
1500 | if (may_no_source) |
||
1501 | *may_no_source = NULL; |
||
1502 | return -1; |
||
1503 | } |