allow specification of maximal amount of shared memory
[ppcg.git] / cuda.c
blobfbb9ffe7b008ddfc07706b1423ced7f7051014e1
1 /*
2 * Copyright 2010-2011 INRIA Saclay
4 * Use of this software is governed by the GNU LGPLv2.1 license
6 * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
7 * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
8 * 91893 Orsay, France
9 */
11 #include <assert.h>
12 #include <stdlib.h>
14 #include <isl/polynomial.h>
15 #include <isl/union_set.h>
16 #include <isl/aff.h>
17 #include <isl/ilp.h>
18 #include <isl/flow.h>
19 #include <isl/band.h>
20 #include <isl/schedule.h>
21 #include <isl/options.h>
22 #include <cloog/isl/cloog.h>
24 #include "cuda.h"
25 #include "cuda_common.h"
26 #include "gpucode.h"
27 #include "schedule.h"
28 #include "ppcg_options.h"
30 /* The fields stride, shift and shift_map only contain valid information
31 * if shift != NULL.
32 * If so, they express that current index is such that if you add shift,
33 * then the result is always a multiple of stride.
34 * shift_map contains the mapping
36 * i -> (i + shift)/stride
38 struct cuda_array_bound {
39 isl_int size;
40 isl_aff *lb;
42 isl_int stride;
43 isl_aff *shift;
44 isl_basic_map *shift_map;
47 struct cuda_array_info;
49 /* A group of array references in a kernel that should be handled together.
50 * If private_bound is not NULL, then it is mapped to registers.
51 * Otherwise, if shared_bound is not NULL, it is mapped to shared memory.
52 * Otherwise, it is accessed from global memory.
54 struct cuda_array_ref_group {
55 /* The references in this group access this array. */
56 struct cuda_array_info *array;
57 /* Position of this group in the list of reference groups of array. */
58 int nr;
60 /* The following fields are use during the construction of the groups.
61 * access is the combined access relation relative to the shared
62 * memory tiling.
63 * write is set if any access in the group is a write.
65 isl_map *access;
66 int write;
68 /* For each index, size and offset of piece in shared memory. */
69 struct cuda_array_bound *shared_bound;
71 /* For each index, size and offset of piece in private memory. */
72 struct cuda_array_bound *private_bound;
74 /* References in this group; point to elements of a linked list. */
75 int n_ref;
76 struct cuda_stmt_access **refs;
79 struct cuda_array_info {
80 isl_space *dim;
81 /* Element type. */
82 char *type;
83 /* Element size. */
84 int size;
85 /* Name of the array. */
86 char *name;
87 /* Number of indices. */
88 unsigned n_index;
89 /* For each index, a bound on the array in that direction. */
90 isl_pw_aff **bound;
91 /* For each index, bound[i] specialized to the current kernel. */
92 isl_pw_aff **local_bound;
94 /* All references to this array; point to elements of a linked list. */
95 int n_ref;
96 struct cuda_stmt_access **refs;
98 /* The reference groups associated to this array. */
99 int n_group;
100 struct cuda_array_ref_group **groups;
102 /* For scalars, is this scalar read-only within the entire program? */
103 int read_only;
105 /* Last shared memory tile dimension that affects tile of this array. */
106 int last_shared;
107 /* Dimension at which copying to/from shared memory is printed.
108 * if >= 0, then the value is >= last_shared
109 * if -1, then the copying is done at the leaf level.
111 int print_shared_level;
114 /* Print the name of the local copy of a given group of array references.
116 static void print_array_name(FILE *out, struct cuda_array_ref_group *group)
118 int global = 0;
120 if (group->private_bound)
121 fprintf(out, "private_");
122 else if (group->shared_bound)
123 fprintf(out, "shared_");
124 else
125 global = 1;
126 fprintf(out, "%s", group->array->name);
127 if (!global && group->array->n_group > 1)
128 fprintf(out, "_%d", group->nr);
131 /* Collect all references to the given array and store pointers to them
132 * in array->refs.
134 static void collect_references(struct cuda_gen *gen,
135 struct cuda_array_info *array)
137 int i;
138 int n;
140 n = 0;
141 for (i = 0; i < gen->n_stmts; ++i) {
142 struct cuda_stmt *stmt = &gen->stmts[i];
143 struct cuda_stmt_access *access;
145 for (access = stmt->accesses; access; access = access->next) {
146 const char *name;
147 name = isl_map_get_tuple_name(access->access,
148 isl_dim_out);
149 if (name && !strcmp(array->name, name))
150 n++;
154 array->n_ref = n;
155 array->refs = isl_alloc_array(gen->ctx, struct cuda_stmt_access *, n);
156 assert(array->refs);
158 n = 0;
159 for (i = 0; i < gen->n_stmts; ++i) {
160 struct cuda_stmt *stmt = &gen->stmts[i];
161 struct cuda_stmt_access *access;
163 for (access = stmt->accesses; access; access = access->next) {
164 const char *name;
165 name = isl_map_get_tuple_name(access->access,
166 isl_dim_out);
167 if (!name || strcmp(array->name, name))
168 continue;
170 array->refs[n++] = access;
175 static struct cuda_array_bound *create_bound_list(isl_ctx *ctx, int n_index)
177 int i;
178 struct cuda_array_bound *bound;
180 bound = isl_alloc_array(ctx, struct cuda_array_bound, n_index);
181 assert(bound);
183 for (i = 0; i < n_index; ++i) {
184 isl_int_init(bound[i].size);
185 bound[i].lb = NULL;
186 isl_int_init(bound[i].stride);
187 bound[i].shift = NULL;
188 bound[i].shift_map = NULL;
191 return bound;
194 static void free_bound_list(struct cuda_array_bound *bound, int n_index)
196 int j;
198 if (!bound)
199 return;
201 for (j = 0; j < n_index; ++j) {
202 isl_int_clear(bound[j].size);
203 isl_int_clear(bound[j].stride);
204 isl_aff_free(bound[j].lb);
205 isl_aff_free(bound[j].shift);
206 isl_basic_map_free(bound[j].shift_map);
208 free(bound);
211 static struct pet_array *find_array(struct pet_scop *scop,
212 __isl_keep isl_set *accessed)
214 int i;
215 isl_id *id;
217 id = isl_set_get_tuple_id(accessed);
219 for (i = 0; i < scop->n_array; ++i) {
220 isl_id *id_i;
222 id_i = isl_set_get_tuple_id(scop->arrays[i]->extent);
223 isl_id_free(id_i);
224 if (id == id_i)
225 break;
227 isl_id_free(id);
229 return i < scop->n_array ? scop->arrays[i] : NULL;
232 /* Compute bounds on the host arrays based on the accessed elements
233 * and collect all references to the array.
235 * If the array is zero-dimensional, i.e., a scalar, we check
236 * whether it is read-only.
238 static int extract_array_info(__isl_take isl_set *array, void *user)
240 int i;
241 struct cuda_gen *gen = (struct cuda_gen *)user;
242 const char *name;
243 int n_index;
244 isl_pw_aff **bounds;
245 isl_pw_aff **local_bounds;
246 struct pet_array *pa;
248 n_index = isl_set_dim(array, isl_dim_set);
249 name = isl_set_get_tuple_name(array);
250 bounds = isl_alloc_array(isl_set_get_ctx(array),
251 isl_pw_aff *, n_index);
252 assert(bounds);
253 local_bounds = isl_calloc_array(isl_set_get_ctx(array),
254 isl_pw_aff *, n_index);
255 assert(local_bounds);
256 gen->array[gen->n_array].dim = isl_set_get_space(array);
257 gen->array[gen->n_array].name = strdup(name);
258 gen->array[gen->n_array].n_index = n_index;
259 gen->array[gen->n_array].bound = bounds;
260 gen->array[gen->n_array].local_bound = local_bounds;
262 pa = find_array(gen->scop, array);
263 assert(pa);
265 gen->array[gen->n_array].type = strdup(pa->element_type);
266 gen->array[gen->n_array].size = pa->element_size;
268 if (n_index == 0) {
269 isl_set *space;
270 isl_union_map *write;
271 int empty;
273 write = isl_union_map_copy(gen->write);
274 space = isl_set_universe(isl_set_get_space(array));
275 write = isl_union_map_intersect_range(write,
276 isl_union_set_from_set(space));
277 empty = isl_union_map_is_empty(write);
278 isl_union_map_free(write);
280 gen->array[gen->n_array].read_only = empty;
283 for (i = 0; i < n_index; ++i) {
284 isl_set *dom;
285 isl_local_space *ls;
286 isl_aff *one;
287 isl_pw_aff *bound;
288 isl_set *size = i == 0 ? array : pa->extent;
290 bound = isl_set_dim_max(isl_set_copy(size), i);
291 assert(bound);
292 dom = isl_pw_aff_domain(isl_pw_aff_copy(bound));
293 ls = isl_local_space_from_space(isl_set_get_space(dom));
294 one = isl_aff_zero_on_domain(ls);
295 one = isl_aff_add_constant_si(one, 1);
296 bound = isl_pw_aff_add(bound, isl_pw_aff_alloc(dom, one));
297 bound = isl_pw_aff_gist(bound, isl_set_copy(gen->context));
299 bounds[i] = bound;
302 collect_references(gen, &gen->array[gen->n_array]);
304 gen->n_array++;
306 isl_set_free(array);
307 return 0;
310 void collect_array_info(struct cuda_gen *gen)
312 isl_union_set *arrays;
314 arrays = isl_union_map_range(isl_union_map_copy(gen->read));
315 arrays = isl_union_set_union(arrays,
316 isl_union_map_range(isl_union_map_copy(gen->write)));
317 arrays = isl_union_set_coalesce(arrays);
319 gen->n_array = isl_union_set_n_set(arrays);
320 gen->array = isl_alloc_array(gen->ctx,
321 struct cuda_array_info, gen->n_array);
322 assert(gen->array);
323 gen->n_array = 0;
324 isl_union_set_foreach_set(arrays, &extract_array_info, gen);
325 isl_union_set_free(arrays);
328 static void free_array_info(struct cuda_gen *gen)
330 int i, j;
332 for (i = 0; i < gen->n_array; ++i) {
333 int n_index = gen->array[i].n_index;
334 free(gen->array[i].type);
335 free(gen->array[i].name);
336 for (j = 0; j < n_index; ++j) {
337 isl_pw_aff_free(gen->array[i].bound[j]);
338 isl_pw_aff_free(gen->array[i].local_bound[j]);
340 isl_space_free(gen->array[i].dim);
341 free(gen->array[i].bound);
342 free(gen->array[i].local_bound);
343 free(gen->array[i].refs);
345 free(gen->array);
348 /* Check if a cuda array is a scalar. A scalar is a value that is not stored
349 * as an array or through a pointer reference, but as single data element. At
350 * the moment, scalars are represented as zero dimensional arrays.
352 static int cuda_array_is_scalar(struct cuda_array_info *array)
354 return (array->n_index == 0);
357 /* Is "array" a read-only scalar?
359 static int cuda_array_is_read_only_scalar(struct cuda_array_info *array)
361 return cuda_array_is_scalar(array) && array->read_only;
364 static void declare_device_arrays(struct cuda_gen *gen)
366 int i;
368 for (i = 0; i < gen->n_array; ++i) {
369 if (cuda_array_is_read_only_scalar(&gen->array[i]))
370 continue;
371 fprintf(gen->cuda.host_c, "%s *dev_%s;\n",
372 gen->array[i].type, gen->array[i].name);
374 fprintf(gen->cuda.host_c, "\n");
377 static void print_array_size(struct cuda_gen *gen, FILE *out,
378 struct cuda_array_info *array)
380 int i;
381 isl_printer *prn;
383 prn = isl_printer_to_file(gen->ctx, out);
384 prn = isl_printer_set_output_format(prn, ISL_FORMAT_C);
385 for (i = 0; i < array->n_index; ++i) {
386 prn = isl_printer_print_str(prn, "(");
387 prn = isl_printer_print_pw_aff(prn, array->bound[i]);
388 prn = isl_printer_print_str(prn, ") * ");
390 prn = isl_printer_print_str(prn, "sizeof(");
391 prn = isl_printer_print_str(prn, array->type);
392 prn = isl_printer_print_str(prn, ")");
393 isl_printer_free(prn);
396 static void allocate_device_arrays(struct cuda_gen *gen)
398 int i;
400 for (i = 0; i < gen->n_array; ++i) {
401 if (cuda_array_is_read_only_scalar(&gen->array[i]))
402 continue;
403 fprintf(gen->cuda.host_c,
404 "cudaCheckReturn(cudaMalloc((void **) &dev_%s, ",
405 gen->array[i].name);
406 print_array_size(gen, gen->cuda.host_c, &gen->array[i]);
407 fprintf(gen->cuda.host_c, "));\n");
409 fprintf(gen->cuda.host_c, "\n");
412 static void free_device_arrays(struct cuda_gen *gen)
414 int i;
416 for (i = 0; i < gen->n_array; ++i) {
417 if (cuda_array_is_read_only_scalar(&gen->array[i]))
418 continue;
419 fprintf(gen->cuda.host_c, "cudaCheckReturn(cudaFree(dev_%s));\n",
420 gen->array[i].name);
424 static void copy_arrays_to_device(struct cuda_gen *gen)
426 int i;
428 for (i = 0; i < gen->n_array; ++i) {
429 isl_space *dim;
430 isl_set *read_i;
431 int empty;
433 if (cuda_array_is_read_only_scalar(&gen->array[i]))
434 continue;
436 dim = isl_space_copy(gen->array[i].dim);
437 read_i = isl_union_set_extract_set(gen->copy_in, dim);
438 empty = isl_set_fast_is_empty(read_i);
439 isl_set_free(read_i);
440 if (empty)
441 continue;
443 fprintf(gen->cuda.host_c, "cudaCheckReturn(cudaMemcpy(dev_%s,",
444 gen->array[i].name);
446 if (cuda_array_is_scalar(&(gen->array[i])))
447 fprintf(gen->cuda.host_c, " &%s, ",
448 gen->array[i].name);
449 else
450 fprintf(gen->cuda.host_c, " %s, ", gen->array[i].name);
452 print_array_size(gen, gen->cuda.host_c, &gen->array[i]);
453 fprintf(gen->cuda.host_c, ", cudaMemcpyHostToDevice));\n");
455 fprintf(gen->cuda.host_c, "\n");
458 static void copy_arrays_from_device(struct cuda_gen *gen)
460 int i;
461 isl_union_set *write;
462 write = isl_union_map_range(isl_union_map_copy(gen->write));
464 for (i = 0; i < gen->n_array; ++i) {
465 isl_space *dim;
466 isl_set *write_i;
467 int empty;
469 dim = isl_space_copy(gen->array[i].dim);
470 write_i = isl_union_set_extract_set(write, dim);
471 empty = isl_set_fast_is_empty(write_i);
472 isl_set_free(write_i);
473 if (empty)
474 continue;
476 fprintf(gen->cuda.host_c, "cudaCheckReturn(cudaMemcpy(");
477 if (cuda_array_is_scalar(&gen->array[i]))
478 fprintf(gen->cuda.host_c, "&%s, ", gen->array[i].name);
479 else
480 fprintf(gen->cuda.host_c, "%s, ", gen->array[i].name);
481 fprintf(gen->cuda.host_c, "dev_%s, ", gen->array[i].name);
482 print_array_size(gen, gen->cuda.host_c, &gen->array[i]);
483 fprintf(gen->cuda.host_c, ", cudaMemcpyDeviceToHost));\n");
486 isl_union_set_free(write);
487 fprintf(gen->cuda.host_c, "\n");
490 static void read_sizes_from_file(struct cuda_gen *gen, const char *filename,
491 int *sizes, int len)
493 int i;
494 FILE *file;
496 file = fopen(filename, "r");
497 if (!file)
498 return;
500 for (i = 0; i < len; ++i)
501 if (fscanf(file, "%d", &sizes[i]) < 1)
502 break;
504 fclose(file);
507 /* Internal data structure for extract_size_of_type.
508 * "type" specifies the name of the space that we want to extract.
509 * "res" is used to store the subset of that space.
511 struct ppcg_extract_size_data {
512 const char *type;
513 isl_set *res;
516 /* This function is called for each set in a union_set.
517 * If the name of the set matches data->type, we store the
518 * set in data->res.
520 static int extract_size_of_type(__isl_take isl_set *size, void *user)
522 struct ppcg_extract_size_data *data = user;
523 const char *name;
525 name = isl_set_get_tuple_name(size);
526 if (name && !strcmp(name, data->type)) {
527 data->res = size;
528 return -1;
531 isl_set_free(size);
532 return 0;
535 /* Given a union map { kernel[i] -> *[...] },
536 * return the range in the space called "type" for the kernel with
537 * sequence number "id".
539 static __isl_give isl_set *extract_sizes(__isl_keep isl_union_map *sizes,
540 const char *type, int id)
542 isl_space *space;
543 isl_set *dom;
544 isl_union_set *local_sizes;
545 struct ppcg_extract_size_data data = { type, NULL };
547 if (!sizes)
548 return NULL;
550 space = isl_union_map_get_space(sizes);
551 space = isl_space_set_from_params(space);
552 space = isl_space_add_dims(space, isl_dim_set, 1);
553 space = isl_space_set_tuple_name(space, isl_dim_set, "kernel");
554 dom = isl_set_universe(space);
555 dom = isl_set_fix_si(dom, isl_dim_set, 0, id);
557 local_sizes = isl_union_set_apply(isl_union_set_from_set(dom),
558 isl_union_map_copy(sizes));
559 isl_union_set_foreach_set(local_sizes, &extract_size_of_type, &data);
560 isl_union_set_free(local_sizes);
561 return data.res;
564 /* Given a singleton set, extract the first (at most *len) elements
565 * of the single integer tuple into *sizes and update *len if needed.
567 static void read_sizes_from_set(__isl_take isl_set *set, int *sizes, int *len)
569 int i;
570 int dim;
571 isl_int v;
573 if (!set)
574 return;
576 dim = isl_set_dim(set, isl_dim_set);
577 if (dim < *len)
578 *len = dim;
580 isl_int_init(v);
582 for (i = 0; i < *len; ++i) {
583 int ok;
585 ok = isl_set_plain_is_fixed(set, isl_dim_set, i, &v);
586 assert(ok);
588 sizes[i] = isl_int_get_si(v);
591 isl_int_clear(v);
593 isl_set_free(set);
596 /* Extract user specified "tile" sizes from the "sizes" command line option,
597 * defaulting to option->tile_size in each dimension.
599 static void read_tile_sizes(struct cuda_gen *gen)
601 int n;
602 isl_set *size;
604 gen->tile_size = isl_alloc_array(gen->ctx, int, gen->tile_len);
605 assert(gen->tile_size);
606 for (n = 0; n < gen->tile_len; ++n)
607 gen->tile_size[n] = gen->options->tile_size;
609 size = extract_sizes(gen->sizes, "tile", gen->kernel_id);
610 read_sizes_from_set(size, gen->tile_size, &gen->tile_len);
612 if (gen->n_parallel > gen->tile_len)
613 gen->n_parallel = gen->tile_len;
616 /* Extract user specified "block" sizes from the "sizes" command line option,
617 * after filling in some potentially useful defaults.
619 static void read_block_sizes(struct cuda_gen *gen)
621 int n;
622 isl_set *size;
624 n = gen->n_parallel;
625 gen->n_block = (n <= 3) ? n : 3;
626 switch (gen->n_block) {
627 case 1:
628 gen->block_dim[0] = 512;
629 break;
630 case 2:
631 gen->block_dim[0] = 32;
632 gen->block_dim[1] = 16;
633 break;
634 default:
635 gen->block_dim[0] = 32;
636 gen->block_dim[1] = 4;
637 gen->block_dim[2] = 4;
638 break;
641 size = extract_sizes(gen->sizes, "block", gen->kernel_id);
642 read_sizes_from_set(size, gen->block_dim, &gen->n_block);
645 /* Extract user specified "grid" sizes from the "sizes" command line option,
646 * after filling in some potentially useful defaults.
648 static void read_grid_sizes(struct cuda_gen *gen)
650 int n = gen->n_parallel;
651 isl_set *size;
653 gen->n_grid = (n <= 2) ? n : 2;
654 switch (gen->n_grid) {
655 case 1:
656 gen->grid_dim[0] = 32768;
657 break;
658 default:
659 gen->grid_dim[0] = 256;
660 gen->grid_dim[1] = 256;
661 break;
664 size = extract_sizes(gen->sizes, "grid", gen->kernel_id);
665 read_sizes_from_set(size, gen->grid_dim, &gen->n_grid);
668 /* Extract user specified sizes from the "sizes" command line option
669 * after filling in some potentially useful defaults.
671 static void read_sizes(struct cuda_gen *gen)
673 read_tile_sizes(gen);
674 read_block_sizes(gen);
675 read_grid_sizes(gen);
678 static void free_stmts(struct cuda_stmt *stmts, int n)
680 int i;
682 for (i = 0; i < n; ++i) {
683 struct cuda_stmt_access *access, *next;
685 for (access = stmts[i].accesses; access; access = next) {
686 next = access->next;
687 isl_map_free(access->access);
688 free(access);
691 isl_set_free(stmts[i].domain);
693 free(stmts);
696 void clear_cuda_gen(struct cuda_gen *gen)
698 free_stmts(gen->stmts, gen->n_stmts);
699 free_array_info(gen);
700 isl_union_map_free(gen->sizes);
701 isl_set_free(gen->context);
702 isl_union_set_free(gen->copy_in);
703 isl_union_map_free(gen->sched);
704 isl_union_map_free(gen->read);
705 isl_union_map_free(gen->write);
708 static void print_reverse_list(FILE *out, int len, int *list)
710 int i;
712 if (len == 0)
713 return;
715 fprintf(out, "(");
716 for (i = 0; i < len; ++i) {
717 if (i)
718 fprintf(out, ", ");
719 fprintf(out, "%d", list[len - 1 - i]);
721 fprintf(out, ")");
724 static void print_kernel_launch(struct cuda_gen *gen,
725 __isl_keep isl_union_set *arrays)
727 int i;
728 int first = 1;
729 unsigned nparam;
730 isl_space *dim;
732 print_indent(gen->code.dst, gen->code.indent);
733 fprintf(gen->code.dst, "kernel%d <<<k%d_dimGrid, k%d_dimBlock>>> (",
734 gen->kernel_id, gen->kernel_id, gen->kernel_id);
735 fprintf(gen->cuda.kernel_c, "__global__ void kernel%d(",
736 gen->kernel_id);
737 fprintf(gen->cuda.kernel_h, "__global__ void kernel%d(",
738 gen->kernel_id);
740 for (i = 0; i < gen->n_array; ++i) {
741 isl_space *dim;
742 isl_set *arr;
743 int empty;
745 dim = isl_space_copy(gen->array[i].dim);
746 arr = isl_union_set_extract_set(arrays, dim);
747 empty = isl_set_fast_is_empty(arr);
748 isl_set_free(arr);
749 if (empty)
750 continue;
752 if (!first) {
753 fprintf(gen->code.dst, ", ");
754 fprintf(gen->cuda.kernel_c, ", ");
755 fprintf(gen->cuda.kernel_h, ", ");
758 if (cuda_array_is_read_only_scalar(&gen->array[i])) {
759 fprintf(gen->code.dst, "%s", gen->array[i].name);
760 fprintf(gen->cuda.kernel_c, "%s %s",
761 gen->array[i].type, gen->array[i].name);
762 fprintf(gen->cuda.kernel_h, "%s %s",
763 gen->array[i].type, gen->array[i].name);
764 } else {
765 fprintf(gen->code.dst, "dev_%s", gen->array[i].name);
766 fprintf(gen->cuda.kernel_c, "%s *%s",
767 gen->array[i].type, gen->array[i].name);
768 fprintf(gen->cuda.kernel_h, "%s *%s",
769 gen->array[i].type, gen->array[i].name);
772 first = 0;
775 dim = isl_union_set_get_space(arrays);
776 nparam = isl_space_dim(dim, isl_dim_param);
777 for (i = 0; i < nparam; ++i) {
778 const char *name = isl_space_get_dim_name(dim, isl_dim_param, i);
779 if (!first) {
780 fprintf(gen->code.dst, ", ");
781 fprintf(gen->cuda.kernel_c, ", ");
782 fprintf(gen->cuda.kernel_h, ", ");
784 fprintf(gen->code.dst, "%s", name);
785 fprintf(gen->cuda.kernel_c, "int %s", name);
786 fprintf(gen->cuda.kernel_h, "int %s", name);
787 first = 0;
789 isl_space_free(dim);
791 for (i = 0; i < gen->tile_first; ++i) {
792 if (!first) {
793 fprintf(gen->code.dst, ", ");
794 fprintf(gen->cuda.kernel_c, ", ");
795 fprintf(gen->cuda.kernel_h, ", ");
797 fprintf(gen->code.dst, "h%d", i);
798 fprintf(gen->cuda.kernel_c, "int h%d", i);
799 fprintf(gen->cuda.kernel_h, "int h%d", i);
800 first = 0;
803 fprintf(gen->code.dst, ");\n");
804 fprintf(gen->cuda.kernel_c, ")\n");
805 fprintf(gen->cuda.kernel_h, ");\n");
807 fprintf(gen->code.dst, "cudaCheckKernel();\n");
810 /* Construct a map from a domain of dimensionality "len"
811 * to a domain of dimensionality "len" + "tile_len" that tiles
812 * the "tile_len" coordinates starting at "first".
813 * In particular, [s_i] -> [s_i / tile_size[i], s_i % tile_size[i]].
814 * "dim" prescribes the parameters.
816 static __isl_give isl_map *tile(__isl_take isl_space *dim, int len,
817 int first, int tile_len, int *tile_size)
819 int i;
820 isl_int v;
821 isl_basic_map *bmap;
822 isl_constraint *c;
823 isl_local_space *ls;
825 isl_int_init(v);
827 dim = isl_space_add_dims(dim, isl_dim_in, len);
828 dim = isl_space_add_dims(dim, isl_dim_out, len + tile_len);
829 bmap = isl_basic_map_universe(isl_space_copy(dim));
830 ls = isl_local_space_from_space(dim);
832 for (i = 0; i < len - tile_len; ++i) {
833 int j = i < first ? i : i + tile_len;
834 int k = i < first ? i : i + 2 * tile_len;
836 c = isl_equality_alloc(isl_local_space_copy(ls));
837 isl_int_set_si(v, -1);
838 isl_constraint_set_coefficient(c, isl_dim_in, j, v);
839 isl_int_set_si(v, 1);
840 isl_constraint_set_coefficient(c, isl_dim_out, k, v);
841 bmap = isl_basic_map_add_constraint(bmap, c);
844 for (i = 0; i < tile_len; ++i) {
845 c = isl_equality_alloc(isl_local_space_copy(ls));
846 isl_int_set_si(v, -1);
847 isl_constraint_set_coefficient(c, isl_dim_in, first + i, v);
848 isl_int_set_si(v, tile_size[i]);
849 isl_constraint_set_coefficient(c, isl_dim_out, first + i, v);
850 isl_int_set_si(v, 1);
851 isl_constraint_set_coefficient(c, isl_dim_out,
852 first + i + tile_len, v);
853 bmap = isl_basic_map_add_constraint(bmap, c);
855 c = isl_inequality_alloc(isl_local_space_copy(ls));
856 isl_int_set_si(v, 1);
857 isl_constraint_set_coefficient(c, isl_dim_out,
858 first + i + tile_len, v);
859 bmap = isl_basic_map_add_constraint(bmap, c);
861 c = isl_inequality_alloc(isl_local_space_copy(ls));
862 isl_int_set_si(v, -1);
863 isl_constraint_set_coefficient(c, isl_dim_out,
864 first + i + tile_len, v);
865 isl_int_set_si(v, tile_size[i] - 1);
866 isl_constraint_set_constant(c, v);
867 bmap = isl_basic_map_add_constraint(bmap, c);
870 isl_local_space_free(ls);
871 isl_int_clear(v);
873 return isl_map_from_basic_map(bmap);
876 /* Construct a map from a domain of dimensionality "len"
877 * to a domain of dimensionality "len" + "wrap_len" that "wraps"
878 * the "wrap_len" coordinates starting at "first" according to "wrap_size".
879 * In particular, [s_i] -> [s_i, s_i % wrap_size[i]].
880 * To do so, we need extra variables corresponding to [s_i / wrap_size[i]],
881 * that are projected out at the end.
882 * "dim" prescribes the parameters.
884 static __isl_give isl_map *wrap(__isl_take isl_space *dim, int len,
885 int first, int wrap_len, int *wrap_size)
887 int i;
888 isl_basic_map *bmap;
889 isl_constraint *c;
890 isl_local_space *ls;
892 dim = isl_space_add_dims(dim, isl_dim_in, len);
893 dim = isl_space_add_dims(dim, isl_dim_out, len + 2 * wrap_len);
894 bmap = isl_basic_map_universe(isl_space_copy(dim));
895 ls = isl_local_space_from_space(dim);
897 for (i = 0; i < len; ++i) {
898 int k = i < first + wrap_len ? i : i + 2 * wrap_len;
900 c = isl_equality_alloc(isl_local_space_copy(ls));
901 isl_constraint_set_coefficient_si(c, isl_dim_in, i, -1);
902 isl_constraint_set_coefficient_si(c, isl_dim_out, k, 1);
903 bmap = isl_basic_map_add_constraint(bmap, c);
906 for (i = 0; i < wrap_len; ++i) {
907 c = isl_equality_alloc(isl_local_space_copy(ls));
908 isl_constraint_set_coefficient_si(c, isl_dim_out,
909 first + i, -1);
910 isl_constraint_set_coefficient_si(c, isl_dim_out,
911 first + wrap_len + i, 1);
912 isl_constraint_set_coefficient_si(c, isl_dim_out,
913 first + 2 * wrap_len + i, wrap_size[i]);
914 bmap = isl_basic_map_add_constraint(bmap, c);
916 c = isl_inequality_alloc(isl_local_space_copy(ls));
917 isl_constraint_set_coefficient_si(c, isl_dim_out,
918 first + wrap_len + i, 1);
919 bmap = isl_basic_map_add_constraint(bmap, c);
921 c = isl_inequality_alloc(isl_local_space_copy(ls));
922 isl_constraint_set_coefficient_si(c, isl_dim_out,
923 first + wrap_len + i, -1);
924 isl_constraint_set_constant_si(c, wrap_size[i] - 1);
925 bmap = isl_basic_map_add_constraint(bmap, c);
928 isl_local_space_free(ls);
930 bmap = isl_basic_map_project_out(bmap, isl_dim_out,
931 first + 2 * wrap_len, wrap_len);
933 return isl_map_from_basic_map(bmap);
936 /* Add "n" parameters named prefix%d.
938 static __isl_give isl_set *add_params( __isl_take isl_set *set,
939 int n, const char *prefix)
941 int i;
942 unsigned nparam;
943 char name[20];
945 nparam = isl_set_dim(set, isl_dim_param);
946 set = isl_set_add_dims(set, isl_dim_param, n);
948 for (i = 0; i < n; ++i) {
949 snprintf(name, sizeof(name), "%s%d", prefix, i);
950 set = isl_set_set_dim_name(set, isl_dim_param,
951 nparam + i, name);
954 return set;
957 /* Equate the "n" dimensions of "set" starting at "first" to
958 * freshly created parameters named prefix%d.
960 static __isl_give isl_set *parametrize(__isl_take isl_set *set,
961 int first, int n, const char *prefix)
963 int i;
964 unsigned nparam;
965 isl_int v;
966 isl_space *dim;
967 isl_basic_set *bset;
968 isl_constraint *c;
969 isl_local_space *ls;
971 nparam = isl_set_dim(set, isl_dim_param);
973 set = add_params(set, n, prefix);
975 dim = isl_set_get_space(set);
976 bset = isl_basic_set_universe(isl_space_copy(dim));
977 ls = isl_local_space_from_space(dim);
979 isl_int_init(v);
981 for (i = 0; i < n; ++i) {
982 c = isl_equality_alloc(isl_local_space_copy(ls));
983 isl_int_set_si(v, -1);
984 isl_constraint_set_coefficient(c, isl_dim_param, nparam + i, v);
985 isl_int_set_si(v, 1);
986 isl_constraint_set_coefficient(c, isl_dim_set, first + i, v);
987 bset = isl_basic_set_add_constraint(bset, c);
990 isl_int_clear(v);
991 isl_local_space_free(ls);
993 return isl_set_intersect(set, isl_set_from_basic_set(bset));
996 static __isl_give isl_set *parametrization(__isl_take isl_space *dim,
997 int len, int first, int n, const char *prefix)
999 isl_set *set;
1001 dim = isl_space_add_dims(dim, isl_dim_set, len);
1002 set = isl_set_universe(dim);
1004 return parametrize(set, first, n, prefix);
1007 /* Tile the B loops over the tile sizes and then tile/wrap
1008 * the T1 loops over the blocks.
1010 static __isl_give isl_union_map *tile_schedule(struct cuda_gen *gen,
1011 __isl_take isl_union_map *sched)
1013 isl_space *dim;
1014 isl_map *tiling, *block_tiling;
1016 dim = isl_union_map_get_space(sched);
1017 tiling = tile(isl_space_copy(dim), gen->untiled_len,
1018 gen->tile_first, gen->tile_len, gen->tile_size);
1020 if (gen->options->wrap)
1021 block_tiling = wrap(dim, gen->untiled_len + gen->tile_len,
1022 gen->tile_first, gen->n_grid, gen->grid_dim);
1023 else
1024 block_tiling = tile(dim, gen->untiled_len + gen->tile_len,
1025 gen->tile_first, gen->n_grid, gen->grid_dim);
1027 gen->tiled_len = gen->untiled_len + gen->tile_len + gen->n_grid;
1029 tiling = isl_map_apply_range(tiling, block_tiling);
1031 sched = isl_union_map_apply_range(sched,
1032 isl_union_map_from_map(tiling));
1034 gen->shared_len = gen->tile_first + gen->tile_len + gen->n_grid;
1036 return sched;
1039 static __isl_give isl_union_map *parametrize_tiled_schedule(
1040 struct cuda_gen *gen, __isl_take isl_union_map *sched)
1042 isl_space *dim;
1043 isl_set *par;
1045 dim = isl_union_map_get_space(sched);
1046 par = parametrization(dim, gen->tiled_len, 0, gen->tile_first, "h");
1047 sched = isl_union_map_intersect_range(sched,
1048 isl_union_set_from_set(par));
1050 dim = isl_union_map_get_space(sched);
1051 par = parametrization(dim, gen->tiled_len,
1052 gen->tile_first + gen->n_grid, gen->n_grid, "b");
1053 sched = isl_union_map_intersect_range(sched,
1054 isl_union_set_from_set(par));
1056 return sched;
1059 /* Tile/wrap the P1 loops over the threads.
1061 static __isl_give isl_union_map *thread_tile_schedule(struct cuda_gen *gen,
1062 __isl_take isl_union_map *sched)
1064 isl_space *dim;
1065 isl_map *tiling;
1066 isl_set *par;
1068 dim = isl_union_map_get_space(sched);
1070 if (gen->options->wrap)
1071 tiling = wrap(isl_space_copy(dim), gen->tiled_len,
1072 gen->shared_len, gen->n_block, gen->block_dim);
1073 else
1074 tiling = tile(isl_space_copy(dim), gen->tiled_len,
1075 gen->shared_len, gen->n_block, gen->block_dim);
1076 gen->thread_tiled_len = gen->tiled_len + gen->n_block;
1078 sched = isl_union_map_apply_range(sched,
1079 isl_union_map_from_map(tiling));
1081 par = parametrization(dim, gen->thread_tiled_len,
1082 gen->tile_first + gen->tile_len + gen->n_grid + gen->n_block,
1083 gen->n_block, "t");
1084 sched = isl_union_map_intersect_range(sched,
1085 isl_union_set_from_set(par));
1087 gen->shared_len = gen->tile_first + gen->tile_len + gen->n_grid;
1089 return sched;
1092 /* If the user asked for it, scale the shared memory tile loops
1093 * (T1T and T2) of "sched" by gen->tile_size[i].
1094 * If we are not performing "wrapping", then additionally scale the T1P
1095 * loops by gen->grid_dim[i].
1097 static __isl_give isl_union_map *scale_tile_loops(struct cuda_gen *gen,
1098 __isl_take isl_union_map *sched)
1100 int i;
1101 isl_space *dim;
1102 isl_basic_map *scale;
1103 isl_constraint *c;
1104 isl_local_space *ls;
1106 if (!gen->options->scale_tile_loops)
1107 return sched;
1109 dim = isl_union_map_get_space(sched);
1110 dim = isl_space_add_dims(dim, isl_dim_in, gen->tiled_len);
1111 dim = isl_space_add_dims(dim, isl_dim_out, gen->tiled_len);
1112 scale = isl_basic_map_universe(isl_space_copy(dim));
1113 ls = isl_local_space_from_space(dim);
1115 for (i = 0; i < gen->tiled_len; ++i) {
1116 int f = 1;
1118 if (i >= gen->tile_first && i < gen->tile_first + gen->n_grid) {
1119 f = gen->tile_size[i - gen->tile_first];
1120 if (!gen->options->wrap)
1121 f *= gen->grid_dim[i - gen->tile_first];
1122 } else if (i >= gen->tile_first + gen->n_grid &&
1123 i < gen->tile_first + gen->n_grid + gen->tile_len) {
1124 f = gen->tile_size[i - (gen->tile_first + gen->n_grid)];
1127 c = isl_equality_alloc(isl_local_space_copy(ls));
1128 isl_constraint_set_coefficient_si(c, isl_dim_in, i, f);
1129 isl_constraint_set_coefficient_si(c, isl_dim_out, i, -1);
1130 scale = isl_basic_map_add_constraint(scale, c);
1133 isl_local_space_free(ls);
1135 sched = isl_union_map_apply_range(sched,
1136 isl_union_map_from_map(isl_map_from_basic_map(scale)));
1138 return sched;
1141 /* If we are not performing "wrapping" and if the user asked for it,
1142 * scale the thread tile loops (P1T) of "sched" by gen->block_dim[i].
1144 static __isl_give isl_union_map *scale_thread_tile_loops(struct cuda_gen *gen,
1145 __isl_take isl_union_map *sched)
1147 int i;
1148 isl_space *dim;
1149 isl_basic_map *scale;
1150 isl_constraint *c;
1151 isl_local_space *ls;
1153 if (gen->options->wrap)
1154 return sched;
1155 if (!gen->options->scale_tile_loops)
1156 return sched;
1158 dim = isl_union_map_get_space(sched);
1159 dim = isl_space_add_dims(dim, isl_dim_in, gen->thread_tiled_len);
1160 dim = isl_space_add_dims(dim, isl_dim_out, gen->thread_tiled_len);
1161 scale = isl_basic_map_universe(isl_space_copy(dim));
1162 ls = isl_local_space_from_space(dim);
1164 for (i = 0; i < gen->thread_tiled_len; ++i) {
1165 int f = 1;
1167 if (i >= gen->shared_len &&
1168 i < gen->shared_len + gen->n_block)
1169 f = gen->block_dim[i - gen->shared_len];
1171 c = isl_equality_alloc(isl_local_space_copy(ls));
1172 isl_constraint_set_coefficient_si(c, isl_dim_in, i, f);
1173 isl_constraint_set_coefficient_si(c, isl_dim_out, i, -1);
1174 scale = isl_basic_map_add_constraint(scale, c);
1177 isl_local_space_free(ls);
1179 sched = isl_union_map_apply_range(sched,
1180 isl_union_map_from_map(isl_map_from_basic_map(scale)));
1182 return sched;
1185 /* If we are not performing "wrapping" and if the user asked for it,
1186 * scale the "n_tile" loops starting at "first" of "sched" by gen->block_dim[i].
1188 static __isl_give isl_union_map *scale_access_tile_loops(struct cuda_gen *gen,
1189 __isl_take isl_union_map *sched, int len, int first, int n_tile)
1191 int i;
1192 isl_space *dim;
1193 isl_basic_map *scale;
1194 isl_constraint *c;
1195 isl_local_space *ls;
1197 if (gen->options->wrap)
1198 return sched;
1199 if (!gen->options->scale_tile_loops)
1200 return sched;
1202 dim = isl_union_map_get_space(sched);
1203 dim = isl_space_add_dims(dim, isl_dim_in, len);
1204 dim = isl_space_add_dims(dim, isl_dim_out, len);
1205 scale = isl_basic_map_universe(isl_space_copy(dim));
1206 ls = isl_local_space_from_space(dim);
1208 for (i = 0; i < len; ++i) {
1209 int f = 1;
1211 if (i >= first && i < first + n_tile)
1212 f = gen->block_dim[i - first];
1214 c = isl_equality_alloc(isl_local_space_copy(ls));
1215 isl_constraint_set_coefficient_si(c, isl_dim_in, i, f);
1216 isl_constraint_set_coefficient_si(c, isl_dim_out, i, -1);
1217 scale = isl_basic_map_add_constraint(scale, c);
1220 isl_local_space_free(ls);
1222 sched = isl_union_map_apply_range(sched,
1223 isl_union_map_from_map(isl_map_from_basic_map(scale)));
1225 return sched;
1228 /* If print_user_stmt is set, we want to print the statements ourselves,
1229 * instead of relying on the C preprocessor. If so, we need to use
1230 * the stop option so that the domains will be saved on the statement
1231 * nodes.
1233 static void print_cloog_shared_body(struct cuda_gen *gen,
1234 __isl_keep isl_set *context, __isl_keep isl_union_map *sched, int len,
1235 void (*print_user_stmt)(struct gpucode_info *info,
1236 struct clast_user_stmt *s),
1237 int first_unroll)
1239 int i;
1240 CloogOptions *options;
1241 CloogDomain *cloog_context;
1242 CloogUnionDomain *ud;
1243 CloogInput *input;
1244 struct clast_stmt *stmt;
1245 char name[20];
1247 sched = isl_union_map_copy(sched);
1248 sched = isl_union_map_align_params(sched, isl_set_get_space(context));
1250 options = cloog_options_malloc(gen->state);
1251 options->language = CLOOG_LANGUAGE_C;
1252 options->strides = 1;
1253 options->sh = 1;
1254 options->f = len;
1255 options->l = -1;
1256 options->override = 1;
1257 options->save_domains = 1;
1258 options->noscalars = 1;
1259 options->first_unroll = first_unroll;
1261 ud = cloog_union_domain_from_isl_union_map(sched);
1262 for (i = 0; i < len; ++i) {
1263 snprintf(name, sizeof(name), "c%d", i);
1264 ud = cloog_union_domain_set_name(ud, CLOOG_SCAT, i, name);
1266 cloog_context = cloog_domain_from_isl_set(isl_set_copy(context));
1267 input = cloog_input_alloc(cloog_context, ud);
1269 stmt = cloog_clast_create_from_input(input, options);
1271 gen->stmt_code.indent = gen->kernel_code.indent;
1272 gen->stmt_code.dst = gen->cuda.kernel_c;
1273 gen->stmt_code.print_user_stmt = print_user_stmt;
1274 gen->stmt_code.print_user_stmt_list = NULL;
1275 gen->stmt_code.print_for_head = NULL;
1276 gen->stmt_code.print_for_foot = NULL;
1277 gen->stmt_code.user = gen;
1278 gpu_print_host_stmt(&gen->stmt_code, stmt);
1280 cloog_clast_free(stmt);
1281 cloog_options_free(options);
1284 /* Add "len" parameters p[i] called prefix%d,
1285 * with bounds to 0 <= p[i] < size[i].
1287 __isl_give isl_set *add_bounded_parameters(__isl_take isl_set *set,
1288 int len, int *size, const char *prefix)
1290 int i;
1291 unsigned nparam;
1292 isl_int v;
1293 isl_space *dim;
1294 isl_basic_set *bset;
1295 isl_constraint *c;
1296 isl_local_space *ls;
1297 char name[20];
1299 nparam = isl_set_dim(set, isl_dim_param);
1300 set = isl_set_add_dims(set, isl_dim_param, len);
1302 for (i = 0; i < len; ++i) {
1303 snprintf(name, sizeof(name), "%s%d", prefix, i);
1304 set = isl_set_set_dim_name(set, isl_dim_param,
1305 nparam + i, name);
1308 dim = isl_set_get_space(set);
1309 bset = isl_basic_set_universe(isl_space_copy(dim));
1310 ls = isl_local_space_from_space(dim);
1312 isl_int_init(v);
1314 for (i = 0; i < len; ++i) {
1315 c = isl_inequality_alloc(isl_local_space_copy(ls));
1316 isl_int_set_si(v, 1);
1317 isl_constraint_set_coefficient(c, isl_dim_param, nparam + i, v);
1318 bset = isl_basic_set_add_constraint(bset, c);
1320 c = isl_inequality_alloc(isl_local_space_copy(ls));
1321 isl_int_set_si(v, -1);
1322 isl_constraint_set_coefficient(c, isl_dim_param, nparam + i, v);
1323 isl_int_set_si(v, size[i] - 1);
1324 isl_constraint_set_constant(c, v);
1325 bset = isl_basic_set_add_constraint(bset, c);
1328 isl_int_clear(v);
1329 isl_local_space_free(ls);
1331 return isl_set_intersect(set, isl_set_from_basic_set(bset));
1334 static void print_shared_body(struct cuda_gen *gen,
1335 __isl_keep isl_set *shared_domain, __isl_keep isl_union_map *sched,
1336 int len, void (*print_user_stmt)(struct gpucode_info *info,
1337 struct clast_user_stmt *s),
1338 int first_unroll)
1340 isl_set *context;
1342 context = isl_set_copy(shared_domain);
1343 context = parametrize(context, 0, gen->shared_len, "g");
1344 context = isl_set_project_out(context, isl_dim_set, 0, gen->shared_len);
1345 context = add_bounded_parameters(context,
1346 gen->n_block, gen->block_dim, "t");
1348 print_cloog_shared_body(gen, context, sched, len, print_user_stmt,
1349 first_unroll);
1351 isl_set_free(context);
1354 /* Given a tile of an array, construct a map that maps each element
1355 * of the tile to a copy of the tile shifted to the origin
1356 * (based on the lower bounds in group->private_bound or group->shared_bound).
1357 * If any of the indices is strided, then {private,shared}_bound[i].shift_map
1358 * is applied to the index first.
1359 * The domain of the resulting map is "access",
1360 * while the range space is anonymous.
1362 static __isl_give isl_map *shift_access(__isl_take isl_set *access,
1363 struct cuda_array_ref_group *group)
1365 int i;
1366 isl_space *dim;
1367 isl_basic_set *bset;
1368 isl_basic_map *bmap;
1369 isl_aff *lb;
1370 isl_basic_set *offset;
1371 isl_basic_map *shift;
1372 isl_basic_map *pre_shift;
1373 isl_map *sched;
1374 const char *name;
1375 struct cuda_array_bound *bounds;
1376 int n_index = group->array->n_index;
1378 bounds = group->private_bound;
1379 if (!bounds)
1380 bounds = group->shared_bound;
1382 dim = isl_set_get_space(access);
1383 dim = isl_space_drop_dims(dim, isl_dim_set, 0, n_index);
1384 offset = isl_basic_set_universe(dim);
1385 for (i = 0; i < n_index; ++i) {
1386 lb = isl_aff_copy(bounds[i].lb);
1387 bmap = isl_basic_map_from_aff(lb);
1388 bset = isl_basic_map_range(bmap);
1389 offset = isl_basic_set_flat_product(offset, bset);
1391 offset = isl_basic_set_neg(offset);
1393 dim = isl_space_map_from_set(isl_set_get_space(access));
1394 shift = isl_basic_map_identity(dim);
1395 shift = isl_basic_map_set_tuple_name(shift, isl_dim_out, NULL);
1397 bset = isl_basic_set_universe(isl_set_get_space(access));
1398 bmap = isl_basic_map_from_domain_and_range(bset, offset);
1400 shift = isl_basic_map_sum(shift, bmap);
1402 dim = isl_set_get_space(access);
1403 dim = isl_space_drop_dims(dim, isl_dim_set, 0, n_index);
1404 dim = isl_space_map_from_set(dim);
1405 pre_shift = isl_basic_map_universe(isl_space_copy(dim));
1406 dim = isl_space_add_dims(dim, isl_dim_in, 1);
1407 dim = isl_space_add_dims(dim, isl_dim_out, 1);
1408 for (i = 0; i < n_index; ++i) {
1409 if (!bounds[i].shift_map)
1410 bmap = isl_basic_map_identity(isl_space_copy(dim));
1411 else
1412 bmap = isl_basic_map_copy(bounds[i].shift_map);
1413 pre_shift = isl_basic_map_flat_product(pre_shift, bmap);
1415 isl_space_free(dim);
1416 name = isl_basic_map_get_tuple_name(shift, isl_dim_in);
1417 pre_shift = isl_basic_map_set_tuple_name(pre_shift, isl_dim_in, name);
1418 pre_shift = isl_basic_map_set_tuple_name(pre_shift, isl_dim_out, name);
1419 shift = isl_basic_map_apply_range(pre_shift, shift);
1421 sched = isl_map_from_basic_map(shift);
1422 sched = isl_map_intersect_domain(sched, access);
1424 return sched;
1427 /* Construct a schedule for iterating over all elements in the given
1428 * piece of an array. The schedule iterates over a copy of the piece
1429 * that is shifted to the origin.
1430 * We subsequently also perform the tiling/wrapping over the threads.
1432 * In particular, we tile the final iterators so that the final thread
1433 * dimension runs over the final array dimension.
1434 * However, if those final iterators have only a single iteration,
1435 * we try to tile earlier iterators instead.
1437 static __isl_give isl_union_map *access_schedule(struct cuda_gen *gen,
1438 __isl_take isl_set *access, struct cuda_array_ref_group *group)
1440 isl_space *dim;
1441 isl_map *sched;
1442 isl_union_map *usched;
1443 isl_map *tiling;
1444 isl_set *par;
1445 unsigned nvar = isl_set_dim(access, isl_dim_set);
1446 int n_tile;
1447 int first;
1449 sched = shift_access(access, group);
1451 n_tile = gen->n_block;
1452 if (n_tile > nvar) {
1453 int i;
1454 sched = isl_map_insert_dims(sched,
1455 isl_dim_out, 0, n_tile - nvar);
1456 for (i = 0; i < n_tile - nvar; ++i)
1457 sched = isl_map_fix_si(sched, isl_dim_out, i, 0);
1458 nvar = n_tile;
1461 first = nvar - n_tile;
1463 for (; first > 0; first --)
1464 if (!isl_map_plain_is_fixed(sched, isl_dim_out,
1465 first + n_tile - 1, NULL))
1466 break;
1468 dim = isl_map_get_space(sched);
1469 dim = isl_space_params(dim);
1470 if (gen->options->wrap)
1471 tiling = wrap(isl_space_copy(dim), nvar, first,
1472 n_tile, gen->block_dim);
1473 else
1474 tiling = tile(isl_space_copy(dim), nvar, first,
1475 n_tile, gen->block_dim);
1476 sched = isl_map_apply_range(sched, tiling);
1478 par = parametrization(dim, nvar + n_tile, first + n_tile, n_tile, "t");
1479 usched = isl_union_map_from_map(sched);
1480 usched = isl_union_map_intersect_range(usched,
1481 isl_union_set_from_set(par));
1483 usched = scale_access_tile_loops(gen, usched, nvar + n_tile,
1484 first, n_tile);
1486 return usched;
1489 /* Print an access to the element in the global memory copy of the
1490 * given array that corresponds to the element described by "pma".
1491 * of the original array.
1492 * The copy in global memory has been linearized, so we need to take
1493 * the array size into account.
1495 static void print_global_index(FILE *out,
1496 struct cuda_array_info *array, __isl_keep isl_pw_multi_aff *pma,
1497 __isl_keep isl_set *domain)
1499 int i;
1500 isl_ctx *ctx = isl_pw_multi_aff_get_ctx(pma);
1501 isl_printer *prn;
1503 if (cuda_array_is_scalar(array)) {
1504 fprintf(out, "*%s", array->name);
1505 return;
1508 fprintf(out, "%s[", array->name);
1509 prn = isl_printer_to_file(ctx, out);
1510 prn = isl_printer_set_output_format(prn, ISL_FORMAT_C);
1511 for (i = 0; i + 1 < array->n_index; ++i)
1512 prn = isl_printer_print_str(prn, "(");
1513 for (i = 0; i < array->n_index; ++i) {
1514 isl_pw_aff *pa = isl_pw_multi_aff_get_pw_aff(pma, i);
1515 pa = isl_pw_aff_coalesce(pa);
1516 pa = isl_pw_aff_gist(pa, isl_set_copy(domain));
1517 if (i) {
1518 prn = isl_printer_print_str(prn, ") * (");
1519 prn = isl_printer_print_pw_aff(prn,
1520 array->local_bound[i]);
1521 prn = isl_printer_print_str(prn, ") + ");
1523 prn = isl_printer_print_pw_aff(prn, pa);
1524 isl_pw_aff_free(pa);
1526 isl_printer_free(prn);
1527 fprintf(out, "]");
1530 /* Given an index expression into a tile of an array, adjust the expression
1531 * to a shift of the tile to the origin
1532 * (based on the lower bounds in array->shared_bound).
1533 * If the index is strided, then we first add
1534 * bound->shift and divide by bound->stride.
1536 static __isl_give isl_pw_aff *shift_index(__isl_take isl_pw_aff *pa,
1537 struct cuda_array_info *array,
1538 struct cuda_array_bound *bound, __isl_take isl_set *domain)
1540 isl_aff *lb;
1541 isl_pw_aff *tmp;
1543 if (bound->shift) {
1544 isl_aff *shift;
1545 shift = bound->shift;
1546 shift = isl_aff_copy(shift);
1547 shift = isl_aff_project_domain_on_params(shift);
1548 shift = isl_aff_align_params(shift, isl_pw_aff_get_space(pa));
1549 tmp = isl_pw_aff_alloc(isl_set_copy(domain), shift);
1550 pa = isl_pw_aff_add(pa, tmp);
1551 pa = isl_pw_aff_scale_down(pa, bound->stride);
1554 lb = isl_aff_copy(bound->lb);
1555 lb = isl_aff_project_domain_on_params(lb);
1557 lb = isl_aff_align_params(lb, isl_pw_aff_get_space(pa));
1559 tmp = isl_pw_aff_alloc(isl_set_copy(domain), lb);
1560 pa = isl_pw_aff_sub(pa, tmp);
1561 pa = isl_pw_aff_coalesce(pa);
1562 pa = isl_pw_aff_gist(pa, domain);
1564 return pa;
1567 /* Print an access to the element in the private/shared memory copy of the
1568 * given array reference group that corresponds to the element described
1569 * by "pma" of the original array.
1570 * Since the array in private/shared memory is just a shifted copy of part
1571 * of the original array, we simply need to subtract the lower bound,
1572 * which was computed in can_tile_for_shared_memory.
1573 * If any of the indices is strided, then we first add
1574 * bounds[i].shift and divide by bounds[i].stride.
1576 static void print_local_index(FILE *out,
1577 struct cuda_array_ref_group *group, struct cuda_array_bound *bounds,
1578 __isl_keep isl_pw_multi_aff *pma, __isl_keep isl_set *domain)
1580 int i;
1581 isl_ctx *ctx = isl_pw_multi_aff_get_ctx(pma);
1582 isl_printer *prn;
1583 struct cuda_array_info *array = group->array;
1585 print_array_name(out, group);
1586 for (i = 0; i < array->n_index; ++i) {
1587 isl_pw_aff *pa = isl_pw_multi_aff_get_pw_aff(pma, i);
1589 pa = shift_index(pa, array, &bounds[i], isl_set_copy(domain));
1591 fprintf(out, "[");
1592 prn = isl_printer_to_file(ctx, out);
1593 prn = isl_printer_set_output_format(prn, ISL_FORMAT_C);
1594 prn = isl_printer_print_pw_aff(prn, pa);
1595 isl_printer_free(prn);
1596 fprintf(out, "]");
1597 isl_pw_aff_free(pa);
1601 /* This function is called for each leaf in the clast of the code
1602 * for copying to or from shared/private memory.
1603 * The statement name is {read,write}_{shared,private}_<array>.
1605 * The schedule iterates over the array elements, so we can use
1606 * the domain of copy_sched at the current scheduling position
1607 * as the index of the array.
1609 static void print_copy_statement(struct gpucode_info *code,
1610 struct clast_user_stmt *u)
1612 struct cuda_gen *gen = code->user;
1613 isl_set *domain;
1614 isl_map *sched;
1615 struct cuda_array_ref_group *group = gen->copy_group;
1616 struct cuda_array_bound *bounds = gen->copy_bound;
1617 int i;
1618 unsigned n_in;
1619 unsigned n_out;
1620 isl_space *dim;
1621 isl_set *param;
1622 isl_set *index;
1623 isl_pw_multi_aff *pma;
1624 int read;
1626 read = !strncmp(u->statement->name, "read", 4);
1628 domain = extract_host_domain(u);
1629 assert(domain);
1631 sched = isl_map_copy(gen->copy_sched);
1632 sched = isl_map_reverse(sched);
1633 sched = isl_map_intersect_domain(sched, domain);
1634 n_in = isl_map_dim(sched, isl_dim_in);
1635 n_out = isl_map_dim(sched, isl_dim_out);
1636 dim = isl_map_get_space(sched);
1637 dim = isl_space_drop_dims(dim, isl_dim_in, 0, n_in);
1638 dim = isl_space_drop_dims(dim, isl_dim_out, 0, n_out);
1639 param = parametrization(dim, n_in, 0, n_in, "c");
1640 sched = isl_map_align_params(sched, isl_set_get_space(param));
1641 sched = isl_map_intersect_domain(sched, param);
1642 index = isl_map_range(sched);
1643 domain = isl_set_copy(index);
1644 pma = isl_pw_multi_aff_from_set(index);
1645 pma = isl_pw_multi_aff_coalesce(pma);
1646 domain = isl_set_params(domain);
1648 print_indent(code->dst, code->indent);
1649 if (read) {
1650 print_local_index(code->dst, group, bounds, pma, domain);
1651 fprintf(code->dst, " = ");
1652 print_global_index(code->dst, group->array, pma, domain);
1653 } else {
1654 print_global_index(code->dst, group->array, pma, domain);
1655 fprintf(code->dst, " = ");
1656 print_local_index(code->dst, group, bounds, pma, domain);
1658 fprintf(code->dst, ";\n");
1660 isl_pw_multi_aff_free(pma);
1661 isl_set_free(domain);
1664 static void print_shared_access(struct cuda_gen *gen,
1665 __isl_keep isl_set *shared_domain, __isl_take isl_set *access,
1666 const char *type, struct cuda_array_ref_group *group)
1668 const char *array_name;
1669 char *name;
1670 isl_ctx *ctx;
1671 isl_union_map *sched;
1672 unsigned nvar = isl_set_dim(access, isl_dim_set);
1673 int n_tile;
1675 ctx = isl_set_get_ctx(access);
1676 array_name = isl_set_get_tuple_name(access);
1677 name = isl_alloc_array(ctx, char,
1678 strlen(type) + sizeof("_shared_") + strlen(array_name) + 20);
1679 if (group->array->n_group > 1)
1680 sprintf(name, "%s_shared_%s_%d", type, array_name, group->nr);
1681 else
1682 sprintf(name, "%s_shared_%s", type, array_name);
1683 access = isl_set_set_tuple_name(access, name);
1684 free(name);
1686 sched = access_schedule(gen, access, group);
1688 n_tile = gen->n_block;
1689 if (n_tile > nvar)
1690 n_tile = nvar;
1692 gen->copy_sched = isl_map_from_union_map(isl_union_map_copy(sched));
1693 gen->copy_group = group;
1694 gen->copy_bound = group->shared_bound;
1696 print_shared_body(gen, shared_domain, sched, nvar + n_tile,
1697 &print_copy_statement, -1);
1699 isl_union_map_free(sched);
1700 isl_map_free(gen->copy_sched);
1703 /* Return the union of all read (read = 1) and/or write (write = 1)
1704 * access relations in the group.
1706 static __isl_give isl_union_map *group_access_relation(
1707 struct cuda_array_ref_group *group, int read, int write)
1709 int i;
1710 isl_union_map *access;
1712 access = isl_union_map_empty(isl_map_get_space(group->access));
1713 for (i = 0; i < group->n_ref; ++i) {
1714 isl_map *map_i;
1716 if (!((read && group->refs[i]->read) ||
1717 (write && group->refs[i]->write)))
1718 continue;
1719 map_i = isl_map_copy(group->refs[i]->access);
1720 access = isl_union_map_union(access,
1721 isl_union_map_from_map(map_i));
1724 return access;
1727 /* Check that none of the shared memory tiles involve any strides.
1729 static int no_strides(struct cuda_array_ref_group *group)
1731 int i;
1732 int n_index = group->array->n_index;
1734 for (i = 0; i < n_index; ++i)
1735 if (group->shared_bound[i].shift)
1736 return 0;
1738 return 1;
1741 /* Return a set containing the values of the given index i
1742 * of the elements in the array tile in global memory that corresponds
1743 * to the shared memory copy.
1744 * In particular, if a is the index, we return a set with constraints
1746 * tile_offset <= a <= tile_offset + tile_size - 1
1748 * and
1750 * 0 <= a <= array_size - 1
1753 static __isl_give isl_set *group_tile_dim(struct cuda_array_ref_group *group,
1754 int i)
1756 isl_basic_set *tile;
1757 isl_aff *aff;
1758 isl_constraint *c;
1759 isl_local_space *ls;
1760 isl_pw_aff *bound;
1761 isl_set *dom;
1762 isl_set *tile_set;
1764 aff = isl_aff_copy(group->shared_bound[i].lb);
1765 aff = isl_aff_add_dims(aff, isl_dim_in, 1);
1766 ls = isl_aff_get_domain_local_space(aff);
1767 aff = isl_aff_neg(aff);
1768 aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1);
1769 c = isl_inequality_from_aff(isl_aff_copy(aff));
1770 tile = isl_basic_set_from_constraint(c);
1772 aff = isl_aff_neg(aff);
1773 aff = isl_aff_add_constant(aff, group->shared_bound[i].size);
1774 aff = isl_aff_add_constant_si(aff, -1);
1775 c = isl_inequality_from_aff(aff);
1776 tile = isl_basic_set_add_constraint(tile, c);
1778 aff = isl_aff_zero_on_domain(ls);
1779 aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1);
1780 c = isl_inequality_from_aff(aff);
1781 tile = isl_basic_set_add_constraint(tile, c);
1783 bound = isl_pw_aff_copy(group->array->bound[i]);
1784 bound = isl_pw_aff_add_dims(bound, isl_dim_in, 1);
1785 ls = isl_local_space_from_space(isl_pw_aff_get_domain_space(bound));
1786 aff = isl_aff_zero_on_domain(ls);
1787 aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1);
1788 aff = isl_aff_add_constant_si(aff, 1);
1789 dom = isl_pw_aff_domain(isl_pw_aff_copy(bound));
1791 tile_set = isl_pw_aff_ge_set(bound, isl_pw_aff_alloc(dom, aff));
1792 tile_set = isl_set_align_params(tile_set, isl_basic_set_get_space(tile));
1793 tile_set = isl_set_intersect(tile_set, isl_set_from_basic_set(tile));
1795 return tile_set;
1798 /* Return a set containing the elements in the array tile in
1799 * global memory that corresponds to the shared memory copy.
1801 static __isl_give isl_set *group_tile(struct cuda_array_ref_group *group)
1803 int i;
1804 int n_index = group->array->n_index;
1805 isl_set *tile;
1807 tile = group_tile_dim(group, 0);
1808 for (i = 1; i < n_index; ++i) {
1809 isl_set *tile_i;
1811 tile_i = group_tile_dim(group, i);
1812 tile = isl_set_flat_product(tile, tile_i);
1815 tile = isl_set_set_tuple_name(tile, group->array->name);
1817 return tile;
1820 /* Print code for reading into or writing from shared memory
1821 * the given array reference group.
1823 * sched maps the original iteration domains to the shared memory tile loops.
1825 * If we are performing a read from global memory to shared memory,
1826 * if the array involved is not a scalar and if the definition of the
1827 * shared memory tiles does not involve any strides, then we copy
1828 * the entire tile to shared memory. This may result in some extra
1829 * elements getting copied, but it should lead to simpler code
1830 * (which means that fewer registers may be needed) and less divergence.
1832 * Otherwise, we only copy the elements that will be read or have been written
1833 * in the kernel.
1835 * Note that the absence of stride requirement can easily be lifted.
1836 * We would just need to add constraints of the form
1838 * shift + a = stride * alpha
1840 static int print_group_shared_accesses(struct cuda_gen *gen,
1841 struct cuda_array_ref_group *group, const char *type,
1842 __isl_keep isl_set *shared_domain, __isl_keep isl_union_map *sched)
1844 int read;
1845 isl_union_map *access;
1846 isl_union_set *uset;
1847 isl_set *access_set;
1849 if (group->private_bound)
1850 return 0;
1851 if (!group->shared_bound)
1852 return 0;
1854 read = !strcmp(type, "read");
1856 access = group_access_relation(group, read, !read);
1857 access = isl_union_map_apply_domain(access, isl_union_map_copy(sched));
1858 uset = isl_union_map_range(access);
1860 if (isl_union_set_is_empty(uset)) {
1861 isl_union_set_free(uset);
1862 return 0;
1865 if (read && group->array->n_index > 0 && no_strides(group)) {
1866 isl_union_set_free(uset);
1867 access_set = group_tile(group);
1868 print_shared_access(gen, shared_domain, access_set,
1869 type, group);
1870 return 1;
1873 access_set = isl_set_from_union_set(uset);
1874 access_set = isl_set_coalesce(access_set);
1876 print_shared_access(gen, shared_domain, access_set, type, group);
1878 return 1;
1881 /* Print code for reading into or writing from shared memory at
1882 * the given level (-1 for innermost).
1884 * If we are not printing at the innermost level, then the dimensionality
1885 * of shared_domain may be smaller than gen->shared_len.
1886 * As the rest of the code assumes that the domain of access has
1887 * gen->shared_len dimensions, we therefore may need to embed this domain
1888 * in a higher dimensional space after intersection with shared_domain.
1890 static void print_shared_accesses(struct cuda_gen *gen,
1891 __isl_keep isl_set *shared_domain, __isl_keep isl_union_map *access,
1892 const char *type, int level)
1894 int i, j;
1895 isl_space *dim;
1896 isl_map *proj;
1897 isl_set *par;
1898 int shared_len = isl_set_dim(shared_domain, isl_dim_set);
1899 int sync = 0;
1900 isl_union_map *sched;
1902 shared_domain = isl_set_copy(shared_domain);
1903 sched = isl_union_map_copy(gen->tiled_sched);
1904 dim = isl_union_map_get_space(sched);
1905 proj = projection(dim, gen->tiled_len, shared_len);
1906 sched = isl_union_map_apply_range(sched, isl_union_map_from_map(proj));
1907 sched = isl_union_map_intersect_range(sched,
1908 isl_union_set_from_set(isl_set_copy(shared_domain)));
1909 if (shared_len != gen->shared_len) {
1910 dim = isl_union_map_get_space(sched);
1911 proj = projection(dim, gen->shared_len, shared_len);
1912 proj = isl_map_reverse(proj);
1913 shared_domain = isl_set_apply(shared_domain,
1914 isl_map_copy(proj));
1915 sched = isl_union_map_apply_range(sched,
1916 isl_union_map_from_map(proj));
1919 dim = isl_union_map_get_space(sched);
1920 par = parametrization(dim, gen->shared_len, 0, gen->shared_len, "g");
1921 sched = isl_union_map_intersect_range(sched,
1922 isl_union_set_from_set(par));
1924 for (i = 0; i < gen->n_array; ++i) {
1925 struct cuda_array_info *array = &gen->array[i];
1927 if (gen->array[i].print_shared_level != level)
1928 continue;
1930 for (j = 0; j < array->n_group; ++j) {
1931 if (print_group_shared_accesses(gen, array->groups[j],
1932 type, shared_domain, sched))
1933 sync = 1;
1937 isl_union_map_free(sched);
1938 isl_set_free(shared_domain);
1940 if (sync) {
1941 print_indent(gen->cuda.kernel_c, gen->kernel_code.indent);
1942 fprintf(gen->cuda.kernel_c, "__syncthreads();\n");
1946 /* This function is called for each access to an array in some statement
1947 * in the original code.
1948 * Replace that access by an access to shared or (linearized) global memory.
1949 * Since the array in shared memory is just
1950 * a shifted copy of part of the original array, we simply need
1951 * to subtract the lower bound, which was computed
1952 * in can_tile_for_shared_memory.
1953 * If any of the indices is strided, then we first add
1954 * shared_bound[i].shift and divide by shared_bound[i].stride.
1956 * If the given array is accessed directly from global memory,
1957 * we don't need to perform any shifting and simply simplify
1958 * expression in the context of the domain instead.
1960 * If the array space (range of access) has no name, then we are
1961 * accessing an iterator in the original program.
1963 static void print_access(struct cuda_gen *gen, __isl_take isl_map *access,
1964 int group_nr)
1966 int i;
1967 const char *name;
1968 unsigned n_index;
1969 struct cuda_array_info *array = NULL;
1970 isl_printer *prn;
1971 isl_pw_multi_aff *pma;
1972 isl_set *data_set;
1973 isl_set *domain;
1974 struct cuda_array_bound *bounds = NULL;
1976 access = isl_map_align_params(access,
1977 isl_set_get_space(gen->stmt_domain));
1979 data_set = isl_set_apply(isl_set_copy(gen->stmt_domain), access);
1981 name = isl_set_get_tuple_name(data_set);
1983 if (!name)
1984 fprintf(gen->cuda.kernel_c, "(");
1985 else {
1986 struct cuda_array_ref_group *group;
1988 for (i = 0; i < gen->n_array; ++i) {
1989 if (strcmp(name, gen->array[i].name))
1990 continue;
1991 array = &gen->array[i];
1993 assert(array);
1994 group = array->groups[group_nr];
1995 bounds = group->private_bound;
1996 if (!bounds)
1997 bounds = group->shared_bound;
1999 if (!bounds && cuda_array_is_scalar(array) && !array->read_only)
2000 fprintf(gen->cuda.kernel_c, "*");
2001 print_array_name(gen->cuda.kernel_c, group);
2003 if (cuda_array_is_scalar(array)) {
2004 isl_set_free(data_set);
2005 return;
2008 fprintf(gen->cuda.kernel_c, "[");
2012 n_index = isl_set_dim(data_set, isl_dim_set);
2013 pma = isl_pw_multi_aff_from_set(data_set);
2014 pma = isl_pw_multi_aff_coalesce(pma);
2016 prn = isl_printer_to_file(gen->ctx, gen->cuda.kernel_c);
2017 prn = isl_printer_set_output_format(prn, ISL_FORMAT_C);
2019 if (!bounds)
2020 for (i = 0; i + 1 < n_index; ++i)
2021 prn = isl_printer_print_str(prn, "(");
2023 for (i = 0; i < n_index; ++i) {
2024 isl_pw_aff *index;
2026 index = isl_pw_multi_aff_get_pw_aff(pma, i);
2028 if (!array) {
2029 prn = isl_printer_print_pw_aff(prn, index);
2030 isl_pw_aff_free(index);
2031 continue;
2034 domain = isl_set_copy(gen->stmt_domain);
2035 domain = isl_set_params(domain);
2036 if (!bounds) {
2037 index = isl_pw_aff_coalesce(index);
2038 index = isl_pw_aff_gist(index, domain);
2039 } else
2040 index = shift_index(index, array, &bounds[i], domain);
2042 if (i) {
2043 if (!bounds) {
2044 prn = isl_printer_print_str(prn, ") * (");
2045 prn = isl_printer_print_pw_aff(prn,
2046 array->local_bound[i]);
2047 prn = isl_printer_print_str(prn, ") + ");
2048 } else
2049 prn = isl_printer_print_str(prn, "][");
2051 prn = isl_printer_print_pw_aff(prn, index);
2052 isl_pw_aff_free(index);
2054 if (!name)
2055 prn = isl_printer_print_str(prn, ")");
2056 else
2057 prn = isl_printer_print_str(prn, "]");
2058 isl_printer_free(prn);
2060 isl_pw_multi_aff_free(pma);
2063 static struct cuda_stmt_access *print_expr(struct cuda_gen *gen, FILE *out,
2064 struct pet_expr *expr, struct cuda_stmt_access *access, int outer)
2066 int i;
2068 switch (expr->type) {
2069 case pet_expr_double:
2070 fprintf(out, "%g", expr->d);
2071 break;
2072 case pet_expr_access:
2073 print_access(gen, isl_map_copy(access->access), access->group);
2074 access = access->next;
2075 break;
2076 case pet_expr_unary:
2077 if (!outer)
2078 fprintf(out, "(");
2079 fprintf(out, " %s ", pet_op_str(expr->op));
2080 access = print_expr(gen, out, expr->args[pet_un_arg],
2081 access, 0);
2082 if (!outer)
2083 fprintf(out, ")");
2084 break;
2085 case pet_expr_binary:
2086 if (!outer)
2087 fprintf(out, "(");
2088 access = print_expr(gen, out, expr->args[pet_bin_lhs],
2089 access, 0);
2090 fprintf(out, " %s ", pet_op_str(expr->op));
2091 access = print_expr(gen, out, expr->args[pet_bin_rhs],
2092 access, 0);
2093 if (!outer)
2094 fprintf(out, ")");
2095 break;
2096 case pet_expr_ternary:
2097 if (!outer)
2098 fprintf(out, "(");
2099 access = print_expr(gen, out, expr->args[pet_ter_cond],
2100 access, 0);
2101 fprintf(out, " ? ");
2102 access = print_expr(gen, out, expr->args[pet_ter_true],
2103 access, 0);
2104 fprintf(out, " : ");
2105 access = print_expr(gen, out, expr->args[pet_ter_false],
2106 access, 0);
2107 if (!outer)
2108 fprintf(out, ")");
2109 break;
2110 case pet_expr_call:
2111 fprintf(out, "%s(", expr->name);
2112 for (i = 0; i < expr->n_arg; ++i) {
2113 if (i)
2114 fprintf(out, ", ");
2115 access = print_expr(gen, out, expr->args[i],
2116 access, 1);
2118 fprintf(out, ")");
2120 return access;
2123 static void print_stmt_body(struct cuda_gen *gen,
2124 FILE *out, struct cuda_stmt *stmt)
2126 print_expr(gen, out, stmt->body, stmt->accesses, 1);
2127 fprintf(out, ";\n");
2130 /* This function is called for each leaf in the innermost clast,
2131 * i.e., for each statement.
2132 * We print the statement body, simplifying the accesses based
2133 * on the schedule.
2135 static void print_statement(struct gpucode_info *code,
2136 struct clast_user_stmt *u)
2138 struct cuda_gen *gen = code->user;
2139 isl_space *dim;
2140 isl_set *par;
2141 isl_set *stmt_domain;
2142 isl_union_map *stmt_sched;
2143 isl_union_set *uset;
2144 int nr;
2145 struct cuda_stmt *stmt;
2147 nr = atoi(u->statement->name + 2);
2148 stmt = &gen->stmts[nr];
2150 stmt_domain = extract_host_domain(u);
2152 stmt_sched = isl_union_map_intersect_range(
2153 isl_union_map_copy(gen->local_sched),
2154 isl_union_set_from_set(extend(stmt_domain,
2155 gen->thread_tiled_len)));
2156 dim = isl_union_map_get_space(stmt_sched);
2157 par = parametrization(dim, gen->thread_tiled_len, 0,
2158 gen->thread_tiled_len, "c");
2159 stmt_sched = isl_union_map_intersect_range(stmt_sched,
2160 isl_union_set_from_set(par));
2162 uset = isl_union_map_domain(stmt_sched);
2163 dim = isl_union_set_get_space(uset);
2164 dim = isl_space_add_dims(dim, isl_dim_set,
2165 isl_set_dim(stmt->domain, isl_dim_set));
2166 dim = isl_space_set_tuple_name(dim, isl_dim_set, u->statement->name);
2167 gen->stmt_domain = isl_union_set_extract_set(uset, dim);
2168 isl_union_set_free(uset);
2170 print_indent(code->dst, code->indent);
2171 print_stmt_body(gen, code->dst, stmt);
2173 isl_set_free(gen->stmt_domain);
2176 static void print_private_access(struct cuda_gen *gen,
2177 __isl_keep isl_set *shared_domain, __isl_take isl_set *access,
2178 const char *type, struct cuda_array_ref_group *group)
2180 const char *array_name;
2181 char *name;
2182 isl_ctx *ctx;
2183 unsigned nvar = isl_set_dim(access, isl_dim_set);
2184 isl_union_map *usched;
2186 if (isl_set_fast_is_empty(access)) {
2187 isl_set_free(access);
2188 return;
2191 ctx = isl_set_get_ctx(access);
2192 array_name = isl_set_get_tuple_name(access);
2193 name = isl_alloc_array(ctx, char,
2194 strlen(type) + sizeof("_private_") + strlen(array_name) + 20);
2195 if (group->array->n_group > 1)
2196 sprintf(name, "%s_private_%s_%d", type, array_name, group->nr);
2197 else
2198 sprintf(name, "%s_private_%s", type, array_name);
2199 access = isl_set_set_tuple_name(access, name);
2200 free(name);
2202 gen->copy_sched = shift_access(access, group);
2203 gen->copy_group = group;
2204 gen->copy_bound = group->private_bound;
2206 usched = isl_union_map_from_map(isl_map_copy(gen->copy_sched));
2207 print_shared_body(gen, shared_domain, usched, nvar,
2208 &print_copy_statement, 1);
2209 isl_union_map_free(usched);
2211 isl_map_free(gen->copy_sched);
2214 /* Print code for reading into or writing from private memory
2215 * the given array reference group.
2217 * sched maps the original iteration domains to the shared memory tile loops.
2219 static void print_group_private_accesses(struct cuda_gen *gen,
2220 struct cuda_array_ref_group *group,
2221 const char *type, __isl_keep isl_set *shared_domain,
2222 unsigned first_shared, int shared_len, __isl_keep isl_union_map *sched)
2224 int read;
2225 isl_union_map *access;
2226 isl_union_set *uset;
2227 isl_set *access_set;
2229 if (!group->private_bound)
2230 return;
2232 read = !strcmp(type, "read");
2234 access = group_access_relation(group, read, !read);
2235 access = isl_union_map_apply_domain(access, isl_union_map_copy(sched));
2236 access = isl_union_map_intersect(access,
2237 isl_union_map_copy(gen->private_access));
2238 uset = isl_union_map_range(access);
2240 if (isl_union_set_is_empty(uset)) {
2241 isl_union_set_free(uset);
2242 return;
2245 access_set = isl_set_from_union_set(uset);
2246 access_set = isl_set_coalesce(access_set);
2247 access_set = isl_set_eliminate(access_set, isl_dim_param,
2248 first_shared + shared_len,
2249 gen->shared_len - shared_len);
2251 print_private_access(gen, shared_domain, access_set, type, group);
2254 /* Print code for reading into or writing from private memory at
2255 * the given level (-1 for innermost).
2257 * If we are not printing at the innermost level, then the dimensionality
2258 * of shared_domain may be smaller than gen->shared_len.
2259 * As the rest of the code assumes that the domain of access has
2260 * gen->shared_len dimensions, we therefore may need to embed this domain
2261 * in a higher dimensional space after intersection with shared_domain.
2263 * This code is very similar to print_shared_accesses.
2264 * The main difference is that we to take into account gen->private_access.
2266 static void print_private_accesses(struct cuda_gen *gen,
2267 __isl_keep isl_set *shared_domain, __isl_keep isl_union_map *access,
2268 const char *type, int level)
2270 int i, j;
2271 isl_space *dim;
2272 isl_map *proj;
2273 int shared_len = isl_set_dim(shared_domain, isl_dim_set);
2274 unsigned first_shared;
2275 isl_union_map *sched;
2277 shared_domain = isl_set_copy(shared_domain);
2278 sched = isl_union_map_copy(gen->tiled_sched);
2279 dim = isl_union_map_get_space(sched);
2280 first_shared = isl_space_dim(dim, isl_dim_param);
2281 proj = projection(dim, gen->tiled_len, shared_len);
2282 sched = isl_union_map_apply_range(sched, isl_union_map_from_map(proj));
2283 sched = isl_union_map_intersect_range(sched,
2284 isl_union_set_from_set(isl_set_copy(shared_domain)));
2285 if (shared_len != gen->shared_len) {
2286 dim = isl_union_map_get_space(sched);
2287 proj = projection(dim, gen->shared_len, shared_len);
2288 proj = isl_map_reverse(proj);
2289 shared_domain = isl_set_apply(shared_domain,
2290 isl_map_copy(proj));
2291 sched = isl_union_map_apply_range(sched,
2292 isl_union_map_from_map(proj));
2295 for (i = 0; i < gen->n_array; ++i) {
2296 struct cuda_array_info *array = &gen->array[i];
2298 if (gen->array[i].print_shared_level != level)
2299 continue;
2301 for (j = 0; j < array->n_group; ++j)
2302 print_group_private_accesses(gen, array->groups[j],
2303 type, shared_domain,
2304 first_shared, shared_len, sched);
2307 isl_union_map_free(sched);
2308 isl_set_free(shared_domain);
2311 /* Set unroll[j] if the input dimension j is involved in
2312 * the index expression represented by bmap.
2314 static int check_unroll(__isl_take isl_basic_map *bmap, void *user)
2316 int i, j;
2317 int n_in = isl_basic_map_dim(bmap, isl_dim_in);
2318 int n_out = isl_basic_map_dim(bmap, isl_dim_out);
2319 int *unroll = user;
2321 for (i = 0; i < n_out; ++i) {
2322 isl_constraint *c;
2323 int ok;
2325 ok = isl_basic_map_has_defining_equality(bmap,
2326 isl_dim_out, i, &c);
2327 assert(ok);
2328 for (j = 0; j < n_in; ++j)
2329 if (isl_constraint_involves_dims(c, isl_dim_in, j, 1))
2330 unroll[j] = 1;
2331 isl_constraint_free(c);
2334 isl_basic_map_free(bmap);
2335 return 0;
2338 /* Given an array pos mapping input dimensions to the corresponding
2339 * output dimension, construct the corresponding map.
2341 static __isl_give isl_map *permutation(__isl_take isl_space *dim,
2342 int *pos, int len)
2344 int i;
2345 isl_constraint *c;
2346 isl_basic_map *bmap;
2347 isl_local_space *ls;
2349 dim = isl_space_add_dims(dim, isl_dim_in, len);
2350 dim = isl_space_add_dims(dim, isl_dim_out, len);
2351 bmap = isl_basic_map_universe(isl_space_copy(dim));
2352 ls = isl_local_space_from_space(dim);
2354 for (i = 0; i < len; ++i) {
2355 c = isl_equality_alloc(isl_local_space_copy(ls));
2356 isl_constraint_set_coefficient_si(c, isl_dim_in, i, -1);
2357 isl_constraint_set_coefficient_si(c, isl_dim_out, pos[i], 1);
2358 bmap = isl_basic_map_add_constraint(bmap, c);
2360 isl_local_space_free(ls);
2362 return isl_map_from_basic_map(bmap);
2365 /* Find all loops involved in any of the index expressions for any of
2366 * the private accesses, move them innermost and then mark them as
2367 * requiring unrolling by setting gen->first_unroll.
2368 * The loops involved should all be parallel because of the checks
2369 * we performed in check_private_group_access. Moving them innermost
2370 * is therefore a valid transformation.
2372 static __isl_give isl_union_map *interchange_for_unroll(struct cuda_gen *gen,
2373 __isl_take isl_union_map *sched)
2375 int i, j;
2376 int unroll[gen->thread_tiled_len];
2377 int perm[gen->thread_tiled_len];
2378 isl_space *dim;
2379 isl_map *permute;
2380 int len = gen->shared_len + gen->n_parallel + gen->n_block;
2382 gen->first_unroll = -1;
2384 for (i = 0; i < gen->thread_tiled_len; ++i)
2385 unroll[i] = 0;
2386 for (i = 0; i < gen->n_array; ++i) {
2387 struct cuda_array_info *array = &gen->array[i];
2389 for (j = 0; j < array->n_group; ++j) {
2390 isl_union_map *access;
2391 isl_map *acc;
2393 if (!array->groups[j]->private_bound)
2394 continue;
2396 access = group_access_relation(array->groups[j], 1, 1);
2397 access = isl_union_map_apply_domain(access,
2398 isl_union_map_copy(sched));
2400 acc = isl_map_from_union_map(access);
2401 isl_map_foreach_basic_map(acc, &check_unroll, unroll);
2403 isl_map_free(acc);
2407 for (i = 0; i < gen->shared_len; ++i)
2408 if (unroll[i])
2409 return sched;
2411 for (i = gen->shared_len; i < len; ++i)
2412 if (unroll[i])
2413 break;
2415 if (i >= len)
2416 return sched;
2418 for (i = len; i < gen->thread_tiled_len; ++i)
2419 if (unroll[i])
2420 return sched;
2422 j = 0;
2423 for (i = 0; i < gen->thread_tiled_len; ++i)
2424 if (!unroll[i])
2425 perm[i] = j++;
2426 gen->first_unroll = 1 + j;
2427 for (i = 0; i < len; ++i)
2428 if (unroll[i])
2429 perm[i] = j++;
2431 dim = isl_union_map_get_space(sched);
2432 permute = permutation(dim, perm, gen->thread_tiled_len);
2433 sched = isl_union_map_apply_range(sched,
2434 isl_union_map_from_map(permute));
2436 return sched;
2439 /* This function is called for each leaf in the clast of the kernel code.
2440 * We first specialize the schedule to the site of the leaf and
2441 * print code for reading into shared memory, performing the actual
2442 * computations and writing from shared memory, with the required
2443 * synchronizations.
2445 static void print_kernel_user(struct gpucode_info *code,
2446 struct clast_user_stmt *u)
2448 struct cuda_gen *gen = code->user;
2449 isl_set *shared_domain;
2451 shared_domain = extract_entire_host_domain(u);
2453 print_shared_accesses(gen, shared_domain, gen->read, "read", -1);
2455 print_private_accesses(gen, shared_domain, gen->read, "read", -1);
2457 print_shared_body(gen, shared_domain, gen->local_sched,
2458 gen->thread_tiled_len, &print_statement,
2459 gen->first_unroll);
2461 print_private_accesses(gen, shared_domain, gen->write, "write", -1);
2463 print_indent(gen->cuda.kernel_c, gen->kernel_code.indent);
2464 fprintf(gen->cuda.kernel_c, "__syncthreads();\n");
2466 print_shared_accesses(gen, shared_domain, gen->write, "write", -1);
2468 isl_set_free(shared_domain);
2471 /* Check if we need to perform any copying to shared memory at this level
2472 * and if so, print the copying instructions.
2473 * Any array for which we are allowed to print copying instructions at
2474 * this level, but haven't done so already, is printed.
2476 static void copy_to_local(struct cuda_gen *gen, __isl_keep isl_set *domain)
2478 int i;
2479 int level;
2480 int print = 0;
2482 level = isl_set_dim(domain, isl_dim_set);
2484 for (i = 0; i < gen->n_array; ++i) {
2485 if (gen->array[i].print_shared_level >= 0)
2486 continue;
2487 if (gen->array[i].last_shared >= level)
2488 continue;
2489 gen->array[i].print_shared_level = level;
2490 print = 1;
2493 if (print) {
2494 print_shared_accesses(gen, domain, gen->read, "read", level);
2495 print_private_accesses(gen, domain, gen->read, "read", level);
2500 /* This function is called for each for loop in the clast,
2501 * right after the opening brace has been printed.
2503 * Print copying instructions to shared or private memory if needed.
2505 static void print_kernel_for_head(struct gpucode_info *code,
2506 struct clast_for *f)
2508 struct cuda_gen *gen = code->user;
2509 isl_set *domain;
2511 domain = isl_set_from_cloog_domain(cloog_domain_copy(f->domain));
2512 copy_to_local(gen, domain);
2514 isl_set_free(domain);
2517 /* Print instructions for copying from shared memory for each array
2518 * for which print_kernel_for_head has added copying instructions
2519 * to shared memory.
2521 static void copy_from_local(struct cuda_gen *gen, __isl_keep isl_set *domain)
2523 int i;
2524 int level;
2525 int print = 0;
2527 level = isl_set_dim(domain, isl_dim_set);
2529 for (i = 0; i < gen->n_array; ++i) {
2530 if (gen->array[i].print_shared_level != level)
2531 continue;
2532 print = 1;
2533 break;
2536 if (print) {
2537 print_private_accesses(gen, domain, gen->write, "write", level);
2538 print_shared_accesses(gen, domain, gen->write, "write", level);
2542 /* This function is called for each for loop in the clast,
2543 * right before the closing brace is printed.
2545 * Print copying instructions from shared or private memory if needed.
2547 static void print_kernel_for_foot(struct gpucode_info *code,
2548 struct clast_for *f)
2550 struct cuda_gen *gen = code->user;
2551 isl_set *domain;
2553 domain = isl_set_from_cloog_domain(cloog_domain_copy(f->domain));
2554 copy_from_local(gen, domain);
2556 isl_set_free(domain);
2559 /* Use CLooG to generate code for the outer gen->shared_first loops
2560 * of the local schedule "sched".
2561 * The pretty printing of this code is handled by gpu_print_host_stmt,
2562 * which calls print_kernel_user for each iteration of the shared tile loops.
2564 static void print_cloog_kernel_body(struct cuda_gen *gen,
2565 __isl_keep isl_set *context, __isl_keep isl_union_map *sched)
2567 int i;
2568 CloogOptions *options;
2569 CloogDomain *cloog_context;
2570 CloogUnionDomain *ud;
2571 CloogInput *input;
2572 struct clast_stmt *stmt;
2573 char name[20];
2575 sched = isl_union_map_copy(sched);
2576 sched = isl_union_map_align_params(sched, isl_set_get_space(context));
2578 options = cloog_options_malloc(gen->state);
2579 options->language = CLOOG_LANGUAGE_C;
2580 options->strides = 1;
2581 options->sh = 1;
2582 options->stop = gen->shared_len;
2583 options->f = gen->tiled_len;
2584 options->l = gen->tiled_len;
2585 options->save_domains = 1;
2586 options->noscalars = 1;
2588 ud = cloog_union_domain_from_isl_union_map(sched);
2589 for (i = 0; i < gen->shared_len; ++i) {
2590 snprintf(name, sizeof(name), "g%d", i);
2591 ud = cloog_union_domain_set_name(ud, CLOOG_SCAT, i, name);
2593 cloog_context = cloog_domain_from_isl_set(isl_set_copy(context));
2594 input = cloog_input_alloc(cloog_context, ud);
2596 stmt = cloog_clast_create_from_input(input, options);
2598 gen->kernel_code.indent = 4;
2599 gen->kernel_code.dst = gen->cuda.kernel_c;
2600 gen->kernel_code.print_user_stmt = NULL;
2601 gen->kernel_code.print_user_stmt_list = &print_kernel_user;
2602 gen->kernel_code.print_for_head = &print_kernel_for_head;
2603 gen->kernel_code.print_for_foot = &print_kernel_for_foot;
2604 gen->kernel_code.user = gen;
2605 copy_to_local(gen, context);
2606 gpu_print_host_stmt(&gen->kernel_code, stmt);
2607 copy_from_local(gen, context);
2609 cloog_clast_free(stmt);
2610 cloog_options_free(options);
2613 static void print_kernel_iterators(struct cuda_gen *gen)
2615 int i;
2616 const char *block_dims[] = { "blockIdx.x", "blockIdx.y" };
2617 const char *thread_dims[] = { "threadIdx.x", "threadIdx.y",
2618 "threadIdx.z" };
2620 if (gen->n_grid > 0) {
2621 print_indent(gen->cuda.kernel_c, 4);
2622 fprintf(gen->cuda.kernel_c, "int ");
2623 for (i = 0; i < gen->n_grid; ++i) {
2624 if (i)
2625 fprintf(gen->cuda.kernel_c, ", ");
2626 fprintf(gen->cuda.kernel_c, "b%d = %s",
2627 i, block_dims[gen->n_grid - 1 - i]);
2629 fprintf(gen->cuda.kernel_c, ";\n");
2632 if (gen->n_block > 0) {
2633 print_indent(gen->cuda.kernel_c, 4);
2634 fprintf(gen->cuda.kernel_c, "int ");
2635 for (i = 0; i < gen->n_block; ++i) {
2636 if (i)
2637 fprintf(gen->cuda.kernel_c, ", ");
2638 fprintf(gen->cuda.kernel_c, "t%d = %s",
2639 i, thread_dims[gen->n_block - 1 - i]);
2641 fprintf(gen->cuda.kernel_c, ";\n");
2645 static void print_group_shared_array(struct cuda_gen *gen,
2646 struct cuda_array_ref_group *group)
2648 int j;
2649 struct cuda_array_bound *bounds;
2651 bounds = group->private_bound;
2652 if (!bounds)
2653 bounds = group->shared_bound;
2654 if (!bounds)
2655 return;
2657 print_indent(gen->cuda.kernel_c, 4);
2658 fprintf(gen->cuda.kernel_c, "%s%s ",
2659 group->private_bound ? "" : "__shared__ ", group->array->type);
2660 print_array_name(gen->cuda.kernel_c, group);
2661 for (j = 0; j < group->array->n_index; ++j) {
2662 fprintf(gen->cuda.kernel_c, "[");
2663 isl_int_print(gen->cuda.kernel_c, bounds[j].size, 0);
2664 fprintf(gen->cuda.kernel_c, "]");
2666 fprintf(gen->cuda.kernel_c, ";\n");
2669 static void print_shared_arrays(struct cuda_gen *gen)
2671 int i, j;
2673 for (i = 0; i < gen->n_array; ++i) {
2674 struct cuda_array_info *array = &gen->array[i];
2676 for (j = 0; j < array->n_group; ++j)
2677 print_group_shared_array(gen, array->groups[j]);
2681 static void print_kernel_body(struct cuda_gen *gen,
2682 __isl_keep isl_set *host_domain, __isl_keep isl_union_map *sched)
2684 isl_set *context;
2686 context = isl_set_copy(host_domain);
2687 context = parametrize(context, 0, gen->tile_first, "h");
2688 context = isl_set_project_out(context, isl_dim_set, 0, gen->tile_first);
2689 context = add_bounded_parameters(context,
2690 gen->n_grid, gen->grid_dim, "b");
2692 print_kernel_iterators(gen);
2693 print_shared_arrays(gen);
2695 fprintf(gen->cuda.kernel_c, "\n");
2697 print_cloog_kernel_body(gen, context, sched);
2699 isl_set_free(context);
2702 /* Given a constraint
2704 * a(p,i) + j = g f(e)
2706 * or -a(p,i) - j = g f(e) if sign < 0,
2707 * store a(p,i) in bound->shift and g (stride) in bound->stride.
2708 * a(p,i) is assumed to be an expression in only the parameters.
2710 static void extract_stride(__isl_keep isl_constraint *c,
2711 struct cuda_array_bound *bound, isl_int stride, int sign)
2713 int i;
2714 isl_int v;
2715 isl_space *dim;
2716 unsigned nparam;
2717 isl_aff *aff;
2719 isl_int_set(bound->stride, stride);
2721 dim = isl_constraint_get_space(c);
2722 dim = isl_space_params(dim);
2724 nparam = isl_space_dim(dim, isl_dim_param);
2726 isl_int_init(v);
2728 isl_constraint_get_constant(c, &v);
2729 if (sign < 0)
2730 isl_int_neg(v, v);
2731 aff = isl_aff_zero_on_domain(isl_local_space_from_space(dim));
2732 aff = isl_aff_set_constant(aff, v);
2734 for (i = 0; i < nparam; ++i) {
2735 isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
2736 if (isl_int_is_zero(v))
2737 continue;
2738 if (sign < 0)
2739 isl_int_neg(v, v);
2740 aff = isl_aff_add_coefficient(aff, isl_dim_param, i, v);
2743 isl_int_clear(v);
2745 bound->shift = aff;
2748 /* Given an equality constraint of a map with a single output dimension j,
2749 * check if the constraint is of the form
2751 * a(p,i) + j = g f(e)
2753 * with a(p,i) an expression in the parameters and input dimensions
2754 * and f(e) an expression in the existentially quantified variables.
2755 * If so, and if g is larger than any such g from a previously considered
2756 * constraint, then call extract_stride. to record the stride information
2757 * in bound.
2759 static int check_stride_constraint(__isl_take isl_constraint *c, void *user)
2761 int i;
2762 isl_int v, stride;
2763 unsigned n_div;
2764 struct cuda_array_bound *bound = user;
2766 isl_int_init(v);
2767 isl_int_init(stride);
2769 n_div = isl_constraint_dim(c, isl_dim_div);
2770 isl_constraint_get_coefficient(c, isl_dim_out, 0, &v);
2772 if (n_div && (isl_int_is_one(v) || isl_int_is_negone(v))) {
2773 int s = isl_int_sgn(v);
2774 isl_int_set_si(stride, 0);
2775 for (i = 0; i < n_div; ++i) {
2776 isl_constraint_get_coefficient(c, isl_dim_div, i, &v);
2777 isl_int_gcd(stride, stride, v);
2779 if (!isl_int_is_zero(stride) &&
2780 isl_int_gt(stride, bound->stride))
2781 extract_stride(c, bound, stride, s);
2784 isl_int_clear(stride);
2785 isl_int_clear(v);
2787 isl_constraint_free(c);
2788 return 0;
2791 /* Given contraints on an array index i, check if we can find
2792 * a shift a(p) and a stride g such that
2794 * a(p) + i = 0 mod g
2796 * If so, record the information in bound and apply the mapping
2797 * i -> (i + a(p))/g to the array index in bounds and return
2798 * the new constraints.
2799 * If not, simply return the original constraints.
2801 static __isl_give isl_basic_map *check_stride(struct cuda_gen *gen,
2802 struct cuda_array_bound *bound, __isl_take isl_basic_map *bounds)
2804 isl_basic_map *aff;
2805 isl_basic_map *shift;
2806 isl_aff *aff_shift;
2808 isl_int_set_si(bound->stride, -1);
2810 aff = isl_basic_map_affine_hull(isl_basic_map_copy(bounds));
2812 isl_basic_map_foreach_constraint(aff, &check_stride_constraint, bound);
2814 isl_basic_map_free(aff);
2816 if (isl_int_is_neg(bound->stride))
2817 return bounds;
2819 aff_shift = isl_aff_copy(bound->shift);
2820 aff_shift = isl_aff_add_dims(aff_shift, isl_dim_in, 1);
2821 aff_shift = isl_aff_add_coefficient_si(aff_shift, isl_dim_in, 0, 1);
2822 aff_shift = isl_aff_scale_down(aff_shift, bound->stride);
2823 shift = isl_basic_map_from_aff(aff_shift);
2825 bound->shift_map = isl_basic_map_copy(shift);
2826 bounds = isl_basic_map_apply_range(bounds, shift);
2828 return bounds;
2831 struct cuda_size_info {
2832 isl_basic_set *bset;
2833 struct cuda_array_bound *bound;
2834 int pos;
2837 /* Given a constraint from the basic set describing the bounds on
2838 * an array index, check if it is a lower bound, say m i >= b(x), and,
2839 * if so, check whether the expression "i - ceil(b(x)/m) + 1" has a constant
2840 * upper bound. If so, and if this bound is smaller than any bound
2841 * derived from earlier constraints, set the size to this bound on
2842 * the expression and the lower bound to ceil(b(x)/m).
2844 static int compute_size_in_direction(__isl_take isl_constraint *c, void *user)
2846 struct cuda_size_info *size = user;
2847 unsigned nparam;
2848 unsigned n_div;
2849 isl_int v;
2851 nparam = isl_basic_set_dim(size->bset, isl_dim_param);
2852 n_div = isl_constraint_dim(c, isl_dim_div);
2854 if (isl_constraint_involves_dims(c, isl_dim_div, 0, n_div)) {
2855 isl_constraint_free(c);
2856 return 0;
2859 isl_int_init(v);
2861 isl_constraint_get_coefficient(c, isl_dim_set, size->pos, &v);
2863 if (isl_int_is_pos(v)) {
2864 isl_aff *aff;
2865 isl_aff *lb;
2866 enum isl_lp_result res;
2868 aff = isl_constraint_get_bound(c, isl_dim_set, size->pos);
2869 aff = isl_aff_ceil(aff);
2871 lb = isl_aff_copy(aff);
2873 aff = isl_aff_neg(aff);
2874 aff = isl_aff_add_coefficient_si(aff, isl_dim_in, size->pos, 1);
2876 res = isl_basic_set_max(size->bset, aff, &v);
2877 isl_aff_free(aff);
2879 if (res == isl_lp_ok) {
2880 isl_int_add_ui(v, v, 1);
2881 if (isl_int_is_neg(size->bound->size) ||
2882 isl_int_lt(v, size->bound->size)) {
2883 isl_int_set(size->bound->size, v);
2884 lb = isl_aff_drop_dims(lb, isl_dim_in,
2885 0, size->pos + 1);
2886 isl_aff_free(size->bound->lb);
2887 size->bound->lb = isl_aff_copy(lb);
2890 isl_aff_free(lb);
2893 isl_int_clear(v);
2894 isl_constraint_free(c);
2896 return 0;
2899 /* Given a basic map "bounds" that maps parameters and input dimensions
2900 * to a single output dimension, look for an expression in the parameters
2901 * and input dimensions such that the range of the output dimension shifted
2902 * by this expression is a constant.
2904 * In particular, we currently only consider lower bounds on the output
2905 * dimension as candidate expressions.
2907 static int compute_array_dim_size(struct cuda_gen *gen,
2908 struct cuda_array_bound *bound, __isl_take isl_basic_map *bounds)
2910 struct cuda_size_info size;
2912 bounds = isl_basic_map_detect_equalities(bounds);
2913 bounds = check_stride(gen, bound, bounds);
2915 isl_int_set_si(bound->size, -1);
2916 bound->lb = NULL;
2918 size.bound = bound;
2919 size.pos = isl_basic_map_dim(bounds, isl_dim_in);
2920 size.bset = isl_basic_map_wrap(bounds);
2921 size.bset = isl_basic_set_flatten(size.bset);
2922 size.bset = isl_set_simple_hull(isl_basic_set_compute_divs(size.bset));
2923 isl_basic_set_foreach_constraint(size.bset, &compute_size_in_direction,
2924 &size);
2925 isl_basic_set_free(size.bset);
2927 return isl_int_is_nonneg(bound->size) ? 0 : -1;
2930 /* Check if we can find a shared memory tile for the given array
2931 * based on the given accesses, and if so, put the results
2932 * in array->shared_bound.
2934 * We project the accesses on each index in turn and look for a parametric
2935 * offset such that the size is constant.
2937 static int can_tile_for_shared_memory(struct cuda_gen *gen,
2938 struct cuda_array_info *array, __isl_keep isl_map *access,
2939 struct cuda_array_bound *bounds)
2941 int i;
2943 for (i = 0; i < array->n_index; ++i) {
2944 isl_map *access_i;
2945 isl_basic_map *hull;
2947 access_i = isl_map_copy(access);
2948 access_i = isl_map_project_out(access_i, isl_dim_out, 0, i);
2949 access_i = isl_map_project_out(access_i, isl_dim_out,
2950 1, array->n_index - (i + 1));
2951 access_i = isl_map_compute_divs(access_i);
2952 hull = isl_map_simple_hull(access_i);
2953 if (compute_array_dim_size(gen, &bounds[i], hull) < 0)
2954 return 0;
2957 return 1;
2960 /* Construct a map with input the shared tile loops and the loops that
2961 * will be wrapped around the threads that relates these later loops
2962 * to the thread indices and the projects them out.
2964 static __isl_give isl_map *compute_privatization(struct cuda_gen *gen)
2966 isl_map *priv;
2967 isl_map *tiling;
2968 isl_map *proj;
2969 isl_set *par;
2970 isl_space *dim;
2972 dim = isl_union_map_get_space(gen->shared_sched);
2974 if (gen->options->wrap)
2975 tiling = wrap(isl_space_copy(dim), gen->shared_len + gen->n_block,
2976 gen->shared_len, gen->n_block, gen->block_dim);
2977 else
2978 tiling = tile(isl_space_copy(dim), gen->shared_len + gen->n_block,
2979 gen->shared_len, gen->n_block, gen->block_dim);
2981 priv = tiling;
2983 par = parametrization(dim, gen->shared_len + 2 * gen->n_block,
2984 gen->tile_first + gen->tile_len + gen->n_grid + gen->n_block,
2985 gen->n_block, "t");
2987 priv = isl_map_align_params(priv, isl_set_get_space(par));
2988 priv = isl_map_intersect_range(priv, par);
2990 dim = isl_map_get_space(priv);
2991 dim = isl_space_drop_dims(dim, isl_dim_in, 0, isl_space_dim(dim, isl_dim_in));
2992 dim = isl_space_drop_dims(dim, isl_dim_out, 0, isl_space_dim(dim, isl_dim_out));
2993 proj = projection(dim, gen->shared_len + 2 * gen->n_block,
2994 gen->shared_len);
2996 priv = isl_map_apply_range(priv, proj);
2998 return priv;
3001 /* Construct a map from domain_dim to domain_dim that increments
3002 * the dimension at position "pos" and leaves all other dimensions
3003 * constant.
3005 static __isl_give isl_map *next(__isl_take isl_space *domain_dim, int pos)
3007 int i;
3008 int len = isl_space_dim(domain_dim, isl_dim_set);
3009 isl_space *dim;
3010 isl_basic_map *next;
3011 isl_local_space *ls;
3013 dim = isl_space_map_from_set(domain_dim);
3014 next = isl_basic_map_universe(isl_space_copy(dim));
3015 ls = isl_local_space_from_space(dim);
3017 for (i = 0; i < len; ++i) {
3018 isl_constraint *c;
3020 c = isl_equality_alloc(isl_local_space_copy(ls));
3021 isl_constraint_set_coefficient_si(c, isl_dim_in, i, 1);
3022 isl_constraint_set_coefficient_si(c, isl_dim_out, i, -1);
3023 if (i == pos)
3024 isl_constraint_set_constant_si(c, 1);
3025 next = isl_basic_map_add_constraint(next, c);
3028 isl_local_space_free(ls);
3030 return isl_map_from_basic_map(next);
3033 /* Check if the given access is coalesced.
3034 * That is, check whether incrementing the dimension that will get
3035 * wrapped over the last thread index results in incrementing
3036 * the last array index.
3038 * This function is only called for access relations without reuse.
3040 static int access_is_coalesced(struct cuda_gen *gen,
3041 __isl_keep isl_union_map *access)
3043 isl_space *dim;
3044 isl_map *access_map;
3045 isl_map *next_thread_x;
3046 isl_map *next_element;
3047 isl_map *map;
3048 int coalesced;
3050 access = isl_union_map_copy(access);
3051 access = isl_union_map_apply_domain(access,
3052 isl_union_map_copy(gen->tiled_sched));
3053 access_map = isl_map_from_union_map(access);
3055 dim = isl_map_get_space(access_map);
3056 dim = isl_space_domain(dim);
3057 next_thread_x = next(dim, gen->shared_len + gen->n_block - 1);
3059 dim = isl_map_get_space(access_map);
3060 dim = isl_space_range(dim);
3061 next_element = next(dim, isl_space_dim(dim, isl_dim_set) - 1);
3063 map = isl_map_apply_domain(next_thread_x, isl_map_copy(access_map));
3064 map = isl_map_apply_range(map, access_map);
3066 coalesced = isl_map_is_subset(map, next_element);
3068 isl_map_free(next_element);
3069 isl_map_free(map);
3071 return coalesced;
3074 /* For the given array reference group, check whether the access is private
3075 * to the thread. That is, check that any given array element
3076 * is only accessed by a single thread.
3077 * We compute an access relation that maps the shared tile loop iterators
3078 * and the shared point loop iterators that will be wrapped over the
3079 * threads to the array elements.
3080 * We actually check that those iterators that will be wrapped
3081 * partition the array space. This check is stricter than necessary
3082 * since several iterations may be mapped onto the same thread
3083 * and then they could be allowed to access the same memory elements,
3084 * but our check does not allow this situation.
3086 * We also check that the index expression only depends on parallel
3087 * loops. That way, we can move those loops innermost and unroll them.
3088 * Again, we use a test that is stricter than necessary.
3089 * We actually check whether the index expression only depends
3090 * on the iterators that are wrapped over the threads.
3091 * These are necessarily parallel, but there may be more parallel loops.
3093 * Combining the injectivity of the first test with the single-valuedness
3094 * of the second test, we simply test for bijectivity.
3096 * If it turns out we can use registers, we compute the private memory
3097 * tile size using can_tile_for_shared_memory, after introducing a dependence
3098 * on the thread indices.
3100 * Before performing any of the above computations, we first check
3101 * if there is any reuse on the reference group. If not, we simply
3102 * return. If, moreover, the access is coalesced then we also remove
3103 * the shared memory tiling since we should just use global memory instead.
3105 static void check_private_group_access(struct cuda_gen *gen,
3106 struct cuda_array_ref_group *group)
3108 isl_map *acc;
3109 isl_union_map *access;
3110 int n_index = group->array->n_index;
3112 access = group_access_relation(group, 1, 1);
3113 if (isl_union_map_is_injective(access)) {
3114 if (group->shared_bound && access_is_coalesced(gen, access)) {
3115 free_bound_list(group->shared_bound, n_index);
3116 group->shared_bound = NULL;
3118 isl_union_map_free(access);
3119 return;
3121 access = isl_union_map_apply_domain(access,
3122 isl_union_map_copy(gen->shared_sched));
3124 acc = isl_map_from_union_map(access);
3126 if (!isl_map_is_bijective(acc)) {
3127 isl_map_free(acc);
3128 return;
3131 group->private_bound = create_bound_list(gen->ctx, n_index);
3132 acc = isl_map_align_params(acc, isl_map_get_space(gen->privatization));
3133 acc = isl_map_apply_domain(acc, isl_map_copy(gen->privatization));
3134 if (!can_tile_for_shared_memory(gen, group->array, acc,
3135 group->private_bound)) {
3136 free_bound_list(group->private_bound, n_index);
3137 group->private_bound = NULL;
3140 isl_map_free(acc);
3143 /* Look for the last shared tile loop that affects the offset of the
3144 * shared or private tile and store the result in array->last_shared.
3146 static void set_last_shared(struct cuda_gen *gen,
3147 struct cuda_array_ref_group *group)
3149 int i, j;
3150 struct cuda_array_bound *bounds;
3151 unsigned first_shared = gen->first_shared;
3152 int n_index = group->array->n_index;
3154 bounds = group->private_bound;
3155 if (!bounds)
3156 bounds = group->shared_bound;
3157 if (!bounds)
3158 return;
3160 for (j = gen->shared_len - 1; j >= 0; --j) {
3161 for (i = 0; i < n_index; ++i) {
3162 isl_aff *lb;
3163 isl_aff *shift;
3165 lb = bounds[i].lb;
3166 if (isl_aff_involves_dims(lb, isl_dim_param,
3167 first_shared + j, 1))
3168 break;
3170 shift = bounds[i].shift;
3171 if (!shift)
3172 continue;
3173 if (isl_aff_involves_dims(shift, isl_dim_param,
3174 first_shared + j, 1))
3175 break;
3177 if (i < n_index)
3178 break;
3180 group->array->last_shared = j;
3183 /* Compute the sizes of all private arrays for the current kernel,
3184 * as well as the offsets of the private pieces in the original arrays.
3185 * If we cannot or don't want to privatize a given array group,
3186 * we use the shared memory tile sizes computed in
3187 * compute_group_shared_bound instead.
3189 * If a given Array only has a single reference group and if we have
3190 * been able to find a privated or shared tile,
3191 * we also look for the last shared tile loop that affects the offset
3192 * (and therefore the array tile) and store the result in array->last_shared.
3194 * A privatized copy of all access relations from reference groups that
3195 * are mapped to private memory is stored in gen->privatization.
3197 static void compute_private_size(struct cuda_gen *gen)
3199 int i, j;
3200 isl_union_map *private;
3202 if (!gen->options->use_private_memory)
3203 return;
3205 private = isl_union_map_empty(isl_union_map_get_space(gen->shared_sched));
3207 for (i = 0; i < gen->n_array; ++i) {
3208 struct cuda_array_info *array = &gen->array[i];
3210 for (j = 0; j < array->n_group; ++j) {
3211 check_private_group_access(gen, array->groups[j]);
3213 if (!array->groups[j]->private_bound)
3214 continue;
3216 private = isl_union_map_union(private,
3217 group_access_relation(array->groups[j], 1, 1));
3220 array->last_shared = gen->shared_len - 1;
3221 array->print_shared_level = -1;
3223 if (array->n_group != 1)
3224 continue;
3225 set_last_shared(gen, array->groups[0]);
3228 if (isl_union_map_is_empty(private))
3229 isl_union_map_free(private);
3230 else {
3231 isl_union_map *priv;
3233 private = isl_union_map_apply_domain(private,
3234 isl_union_map_copy(gen->shared_sched));
3235 priv = isl_union_map_from_map(isl_map_copy(gen->privatization));
3236 private = isl_union_map_apply_domain(private, priv);
3237 gen->private_access = private;
3241 /* Compute the size of the tile specified by the list "bound" of n_index
3242 * cuda_array_bounds in number of elements and put the result in *size.
3244 static void tile_size(unsigned n_index, struct cuda_array_bound *bound,
3245 isl_int *size)
3247 int i;
3249 isl_int_set_si(*size, 1);
3251 for (i = 0; i < n_index; ++i)
3252 isl_int_mul(*size, *size, bound[i].size);
3255 /* If max_shared_memory is not set to infinity (-1), then make
3256 * sure that the total amount of shared memory required by the
3257 * array reference groups mapped to shared memory is no larger
3258 * than this maximum.
3260 * We apply a greedy approach and discard (keep in global memory)
3261 * those groups that would result in a total memory size that
3262 * is larger than the maximum.
3264 static void check_shared_memory_bound(struct cuda_gen *gen)
3266 int i, j;
3267 isl_int left, size;
3269 if (gen->options->max_shared_memory < 0)
3270 return;
3272 isl_int_init(left);
3273 isl_int_init(size);
3274 isl_int_set_si(left, gen->options->max_shared_memory);
3276 for (i = 0; i < gen->n_array; ++i) {
3277 struct cuda_array_info *array = &gen->array[i];
3279 for (j = 0; j < array->n_group; ++j) {
3280 struct cuda_array_ref_group *group;
3282 group = array->groups[j];
3283 if (!group->shared_bound)
3284 continue;
3286 tile_size(array->n_index, group->shared_bound, &size);
3287 isl_int_mul_ui(size, size, array->size);
3289 if (isl_int_le(size, left)) {
3290 isl_int_sub(left, left, size);
3291 continue;
3294 free_bound_list(group->shared_bound, array->n_index);
3295 group->shared_bound = NULL;
3299 isl_int_clear(size);
3300 isl_int_clear(left);
3303 /* Fill up the groups array with singleton groups, i.e., one group
3304 * per reference, initializing the array, access, write and refs fields.
3305 * In particular the access field is initialized to the scheduled
3306 * access relation of the array reference.
3308 * Return the number of elements initialized, i.e., the number of
3309 * active references in the current kernel.
3311 static int populate_array_references(struct cuda_gen *gen,
3312 struct cuda_array_info *array, __isl_keep isl_union_map *sched,
3313 struct cuda_array_ref_group **groups)
3315 int i;
3316 int n;
3317 isl_ctx *ctx = isl_union_map_get_ctx(sched);
3319 n = 0;
3320 for (i = 0; i < array->n_ref; ++i) {
3321 isl_union_map *umap;
3322 isl_map *map;
3323 struct cuda_array_ref_group *group;
3324 struct cuda_stmt_access *access = array->refs[i];
3326 map = isl_map_copy(access->access);
3327 umap = isl_union_map_from_map(map);
3328 umap = isl_union_map_apply_domain(umap,
3329 isl_union_map_copy(sched));
3331 if (isl_union_map_is_empty(umap)) {
3332 isl_union_map_free(umap);
3333 continue;
3336 map = isl_map_from_union_map(umap);
3338 group = isl_calloc_type(ctx, struct cuda_array_ref_group);
3339 assert(group);
3340 group->array = array;
3341 group->access = map;
3342 group->write = access->write;
3343 group->refs = &array->refs[i];
3345 groups[n++] = group;
3348 return n;
3351 static void free_array_ref_group(struct cuda_array_ref_group *group,
3352 int n_index)
3354 if (!group)
3355 return;
3356 free_bound_list(group->shared_bound, n_index);
3357 free_bound_list(group->private_bound, n_index);
3358 isl_map_free(group->access);
3359 free(group->refs);
3360 free(group);
3363 /* If two groups have overlapping access relations and if one of them
3364 * involves a write, then merge the two groups into one.
3366 * We keep track of the grouping in "leader". leader[j] points to
3367 * an earlier group array element that belongs to the same group,
3368 * or the array element j itself if this element is the first in the group.
3370 * Return the number of group leaders.
3372 static int group_overlapping_writes(int n,
3373 struct cuda_array_ref_group **groups, int *leader)
3375 int i, j;
3376 int n_group = n;
3378 for (i = 0; i < n; ++i) {
3379 int l = i;
3380 groups[l]->n_ref = 1;
3381 for (j = i - 1; j >= 0; --j) {
3382 isl_map *map;
3383 int empty;
3385 if (leader[j] != j)
3386 continue;
3387 if (!groups[l]->write && !groups[j]->write)
3388 continue;
3390 map = isl_map_intersect(isl_map_copy(groups[l]->access),
3391 isl_map_copy(groups[j]->access));
3392 empty = isl_map_is_empty(map);
3393 isl_map_free(map);
3395 if (empty)
3396 continue;
3398 groups[j]->access = isl_map_union(groups[j]->access,
3399 groups[l]->access);
3400 groups[j]->write = 1;
3401 groups[l]->access = NULL;
3402 groups[j]->n_ref += groups[l]->n_ref;
3403 l = leader[l] = j;
3404 n_group--;
3406 leader[i] = l;
3409 return n_group;
3412 /* Compute the size of the shared array corresponding to the given array
3413 * array refrence group, based on the accesses from the current kernel,
3414 * as well as the offset of the shared piece in the original array.
3416 static void compute_group_shared_bound(struct cuda_gen *gen,
3417 struct cuda_array_info *array, struct cuda_array_ref_group *group)
3419 isl_ctx *ctx = isl_space_get_ctx(array->dim);
3421 if (!gen->options->use_shared_memory)
3422 return;
3423 if (cuda_array_is_read_only_scalar(array))
3424 return;
3426 group->shared_bound = create_bound_list(ctx, array->n_index);
3427 if (!can_tile_for_shared_memory(gen, array, group->access,
3428 group->shared_bound)) {
3429 free_bound_list(group->shared_bound, array->n_index);
3430 group->shared_bound = NULL;
3434 /* Given an initial grouping of array references and shared memory tiles
3435 * for each group that allows for a shared memory tile, merge two groups
3436 * if both have a shared memory tile and if the merged group also has
3437 * a shared memory tile.
3439 * Return the number of group leaders after merging.
3441 static int group_common_shared_memory_tile(struct cuda_gen *gen,
3442 struct cuda_array_info *array, int n,
3443 struct cuda_array_ref_group **groups, int *leader, int n_group)
3445 int i, j;
3446 isl_ctx *ctx = isl_space_get_ctx(array->dim);
3448 for (i = 0; n_group > 1 && i < n; ++i) {
3449 int l = i;
3450 if (leader[i] != i)
3451 continue;
3452 if (!groups[i]->shared_bound)
3453 continue;
3454 for (j = i - 1; j >= 0; --j) {
3455 isl_map *map;
3456 int empty;
3457 struct cuda_array_bound *shared_bound;
3459 if (leader[j] != j)
3460 continue;
3461 if (!groups[j]->shared_bound)
3462 continue;
3464 map = isl_map_intersect(isl_map_copy(groups[l]->access),
3465 isl_map_copy(groups[j]->access));
3466 empty = isl_map_is_empty(map);
3467 isl_map_free(map);
3469 if (empty)
3470 continue;
3472 map = isl_map_union(isl_map_copy(groups[l]->access),
3473 isl_map_copy(groups[j]->access));
3474 shared_bound = create_bound_list(ctx, array->n_index);
3475 if (!can_tile_for_shared_memory(gen, array, map,
3476 shared_bound)) {
3477 isl_map_free(map);
3478 free_bound_list(shared_bound, array->n_index);
3479 continue;
3482 free_bound_list(groups[j]->shared_bound,
3483 array->n_index);
3484 groups[j]->shared_bound = shared_bound;
3485 isl_map_free(groups[j]->access);
3486 groups[j]->access = map;
3487 groups[j]->n_ref += groups[l]->n_ref;
3488 l = leader[l] = j;
3489 n_group--;
3493 return n_group;
3496 /* Extract an array of array reference groups from the array of references
3497 * and the grouping information in "leader".
3499 * Store the results in array->n_group and array->groups.
3501 static void extract_array_groups(isl_ctx *ctx, struct cuda_array_info *array,
3502 int n, struct cuda_array_ref_group **groups, int *leader, int n_group)
3504 int i, j;
3506 for (i = 2; i < n; ++i)
3507 leader[i] = leader[leader[i]];
3509 array->n_group = n_group;
3510 array->groups = isl_alloc_array(ctx, struct cuda_array_ref_group *,
3511 n_group);
3512 assert(array->groups);
3514 j = 0;
3515 for (i = 0; i < n; ++i) {
3516 int k, l;
3517 struct cuda_stmt_access **refs;
3519 if (leader[i] != i) {
3520 groups[i]->refs = NULL;
3521 free_array_ref_group(groups[i], array->n_index);
3522 continue;
3525 refs = isl_alloc_array(ctx, struct cuda_stmt_access *,
3526 groups[i]->n_ref);
3527 assert(refs);
3528 l = 0;
3529 for (k = i; k < n; ++k)
3530 if (leader[k] == i) {
3531 refs[l++] = *groups[k]->refs;
3532 (*groups[k]->refs)->group = j;
3535 groups[i]->refs = refs;
3536 groups[i]->nr = j;
3537 array->groups[j++] = groups[i];
3541 /* Group array references that should be considered together when
3542 * deciding whether to access them from private, shared or global memory.
3544 * In particular, if two array references overlap and if one of them
3545 * is a write, then the two references are grouped together.
3546 * Furthermore, if two groups admit a shared memory tile and if the
3547 * combination of the two also admits a shared memory tile, we merge
3548 * the two groups.
3550 * During the construction the group->refs field points to a single
3551 * array reference inside the array of array references, while
3552 * group->n_ref contains the number of element in leader that
3553 * (directly or indirectly) point to this group, provided the group
3554 * is a leader.
3556 static void group_array_references(struct cuda_gen *gen,
3557 struct cuda_array_info *array, __isl_keep isl_union_map *sched)
3559 int i;
3560 int n, n_group;
3561 isl_ctx *ctx = isl_union_map_get_ctx(sched);
3562 struct cuda_array_ref_group **groups;
3563 int *leader;
3565 groups = isl_calloc_array(ctx, struct cuda_array_ref_group *,
3566 array->n_ref);
3567 assert(groups);
3569 n = populate_array_references(gen, array, sched, groups);
3571 leader = isl_alloc_array(ctx, int, n);
3572 assert(leader);
3574 n_group = group_overlapping_writes(n, groups, leader);
3576 for (i = 0; i < n; ++i)
3577 if (leader[i] == i)
3578 compute_group_shared_bound(gen, array, groups[i]);
3580 n_group = group_common_shared_memory_tile(gen, array, n, groups,
3581 leader, n_group);
3583 extract_array_groups(ctx, array, n, groups, leader, n_group);
3585 free(leader);
3586 free(groups);
3589 /* Take tiled_sched, project it onto the shared tile loops and
3590 * the loops that will be wrapped over the threads,
3591 * parametrize the shared tile loops and store the result in gen->shared_sched.
3592 * The position of the first of these parameters is stored in gen->first_shared.
3593 * Also compute a projection that projects out the loops that will be
3594 * wrapped over the threads and store this projection in gen->shared_proj.
3596 static void compute_shared_sched(struct cuda_gen *gen)
3598 isl_space *dim;
3599 isl_map *proj;
3600 isl_set *par;
3601 isl_union_map *sched;
3603 sched = isl_union_map_copy(gen->tiled_sched);
3605 dim = isl_union_map_get_space(sched);
3606 gen->first_shared = isl_space_dim(dim, isl_dim_param);
3607 proj = projection(dim, gen->tiled_len, gen->shared_len + gen->n_block);
3608 sched = isl_union_map_apply_range(sched, isl_union_map_from_map(proj));
3610 dim = isl_union_map_get_space(sched);
3611 par = parametrization(dim, gen->shared_len + gen->n_block,
3612 0, gen->shared_len, "g");
3613 sched = isl_union_map_intersect_range(sched,
3614 isl_union_set_from_set(par));
3616 dim = isl_union_map_get_space(sched);
3617 proj = projection(dim, gen->shared_len + gen->n_block, gen->shared_len);
3619 gen->shared_sched = sched;
3620 gen->shared_proj = isl_union_map_from_map(proj);
3623 /* Group references of all arrays in the program.
3625 static void group_references(struct cuda_gen *gen)
3627 int i;
3628 isl_union_map *sched;
3630 sched = isl_union_map_apply_range(isl_union_map_copy(gen->shared_sched),
3631 isl_union_map_copy(gen->shared_proj));
3633 for (i = 0; i < gen->n_array; ++i)
3634 group_array_references(gen, &gen->array[i], sched);
3636 isl_union_map_free(sched);
3639 /* Free all array information that is local to the current kernel.
3641 static void free_local_array_info(struct cuda_gen *gen)
3643 int i, j;
3645 for (i = 0; i < gen->n_array; ++i) {
3646 struct cuda_array_info *array = &gen->array[i];
3648 for (j = 0; j < array->n_group; ++j)
3649 free_array_ref_group(array->groups[j], array->n_index);
3650 free(array->groups);
3652 if (array->n_group == 0)
3653 continue;
3654 for (j = 0; j < gen->array[i].n_index; ++j) {
3655 isl_pw_aff_free(gen->array[i].local_bound[j]);
3656 gen->array[i].local_bound[j] = NULL;
3661 static void print_iterator_list(FILE *out, int len, const char *prefix,
3662 int parens)
3664 int i;
3666 fprintf(out, "(");
3667 for (i = 0; i < len; ++i) {
3668 if (i)
3669 fprintf(out, ", ");
3670 if (parens)
3671 fprintf(out, "(%s%d)", prefix, i);
3672 else
3673 fprintf(out, "%s%d", prefix, i);
3675 fprintf(out, ")");
3678 /* The sizes of the arrays on the host that have been computed by
3679 * extract_array_info may depend on the parameters. Use the extra
3680 * constraints on the parameters that are valid at "host_domain"
3681 * to simplify these expressions.
3683 static void localize_bounds(struct cuda_gen *gen,
3684 __isl_keep isl_set *host_domain)
3686 int i, j;
3687 isl_set *context;
3689 context = isl_set_copy(host_domain);
3690 context = isl_set_params(host_domain);
3692 for (i = 0; i < gen->n_array; ++i) {
3693 struct cuda_array_info *array = &gen->array[i];
3695 if (array->n_group == 0)
3696 continue;
3698 for (j = 0; j < array->n_index; ++j) {
3699 isl_pw_aff *pwaff;
3701 pwaff = isl_pw_aff_copy(array->bound[j]);
3702 pwaff = isl_pw_aff_gist(pwaff, isl_set_copy(context));
3703 array->local_bound[j] = pwaff;
3706 isl_set_free(context);
3709 /* Set gen->tile_len and gen->n_parallel to those of the first statement
3710 * in the statement list u.
3711 * Because of the way the schedule is constructed, the other statements
3712 * in the list, if any, should have the same values for these properties.
3714 static void set_tile_len(struct cuda_gen *gen, struct clast_user_stmt *u)
3716 int nr;
3717 struct cuda_stmt *stmt;
3719 nr = atoi(u->statement->name + 2);
3720 stmt = &gen->stmts[nr];
3722 gen->tile_len = stmt->tile_len;
3723 gen->n_parallel = stmt->n_parallel;
3726 /* Extract a description of the grid, i.e., the possible values
3727 * of the block ids, from gen->tiled_sched.
3728 * The block ids are parameters in gen->tiled_sched.
3729 * We simply need to change them into set dimensions.
3731 static __isl_give isl_set *extract_grid(struct cuda_gen *gen)
3733 int i;
3734 isl_set *grid;
3736 grid = isl_union_map_params(isl_union_map_copy(gen->tiled_sched));
3737 grid = isl_set_from_params(grid);
3738 grid = isl_set_add_dims(grid, isl_dim_set, gen->n_grid);
3739 for (i = 0; i < gen->n_grid; ++i) {
3740 int pos;
3741 char name[20];
3743 snprintf(name, sizeof(name), "b%d", i);
3744 pos = isl_set_find_dim_by_name(grid, isl_dim_param, name);
3745 assert(pos >= 0);
3746 grid = isl_set_equate(grid, isl_dim_param, pos, isl_dim_set, i);
3747 grid = isl_set_project_out(grid, isl_dim_param, pos, 1);
3750 return grid;
3753 /* Print the effective grid size as a list of the sizes in each
3754 * dimension, from innermost to outermost.
3756 * The grid size specified by the user or set by default
3757 * in read_grid_sizes() and applied in tile_schedule(),
3758 * may be too large for the given code in the sense that
3759 * it may contain blocks that don't need to execute anything.
3760 * We therefore don't print this grid size, but instead the
3761 * smallest grid size that ensures that all blocks that actually
3762 * execute code are included in the grid.
3764 * For each block dimension, we compute the maximal value of the block id
3765 * and add one.
3767 static void print_grid_size(struct cuda_gen *gen, __isl_take isl_set *context)
3769 int i;
3770 isl_printer *prn;
3771 isl_set *grid;
3773 if (gen->n_grid == 0) {
3774 isl_set_free(context);
3775 return;
3778 grid = extract_grid(gen);
3780 prn = isl_printer_to_file(gen->ctx, gen->cuda.host_c);
3781 prn = isl_printer_set_output_format(prn, ISL_FORMAT_C);
3783 prn = isl_printer_print_str(prn, "(");
3784 for (i = gen->n_grid - 1; i >= 0; --i) {
3785 isl_space *space;
3786 isl_aff *one;
3787 isl_pw_aff *bound = isl_set_dim_max(isl_set_copy(grid), i);
3789 bound = isl_pw_aff_coalesce(bound);
3790 bound = isl_pw_aff_gist(bound, isl_set_copy(context));
3792 space = isl_pw_aff_get_domain_space(bound);
3793 one = isl_aff_zero_on_domain(isl_local_space_from_space(space));
3794 one = isl_aff_add_constant_si(one, 1);
3795 bound = isl_pw_aff_add(bound, isl_pw_aff_from_aff(one));
3796 prn = isl_printer_print_pw_aff(prn, bound);
3797 isl_pw_aff_free(bound);
3799 if (i > 0)
3800 prn = isl_printer_print_str(prn, ", ");
3802 prn = isl_printer_print_str(prn, ")");
3804 isl_printer_free(prn);
3805 isl_set_free(grid);
3806 isl_set_free(context);
3809 /* This function is called for each leaf in the clast of the host code.
3810 * We first specialize the schedule to the site of the leaf, compute
3811 * the size of shared memory and then print the body of host code
3812 * and the associated kernel (through a call to print_kernel_body).
3814 static void print_host_user(struct gpucode_info *code,
3815 struct clast_user_stmt *u)
3817 struct cuda_gen *gen = code->user;
3818 isl_space *dim;
3819 isl_set *par;
3820 isl_set *host_domain;
3821 isl_union_map *access;
3822 isl_union_map *local_sched;
3823 isl_union_set *arrays;
3825 set_tile_len(gen, u);
3826 read_sizes(gen);
3828 host_domain = extract_entire_host_domain(u);
3830 local_sched = isl_union_map_intersect_range(
3831 isl_union_map_copy(gen->sched),
3832 isl_union_set_from_set(extend(isl_set_copy(host_domain),
3833 gen->untiled_len)));
3834 access = isl_union_map_union(isl_union_map_copy(gen->read),
3835 isl_union_map_copy(gen->write));
3836 access = isl_union_map_apply_domain(access,
3837 isl_union_map_copy(local_sched));
3838 arrays = isl_union_map_range(access);
3840 print_indent(code->dst, code->indent);
3841 fprintf(code->dst, "dim3 k%d_dimBlock", gen->kernel_id);
3842 print_reverse_list(code->dst, gen->n_block, gen->block_dim);
3843 fprintf(code->dst, ";\n");
3845 gen->tiled_sched = tile_schedule(gen, local_sched);
3846 gen->tiled_sched = parametrize_tiled_schedule(gen, gen->tiled_sched);
3847 gen->tiled_sched = scale_tile_loops(gen, gen->tiled_sched);
3849 print_indent(code->dst, code->indent);
3850 fprintf(code->dst, "dim3 k%d_dimGrid", gen->kernel_id);
3851 print_grid_size(gen, isl_set_params(isl_set_copy(host_domain)));
3852 fprintf(code->dst, ";\n");
3854 gen->local_sched = isl_union_map_copy(gen->tiled_sched);
3856 dim = isl_union_map_get_space(gen->local_sched);
3857 par = parametrization(dim, gen->tiled_len, 0, gen->shared_len, "g");
3858 gen->local_sched = isl_union_map_intersect_range(gen->local_sched,
3859 isl_union_set_from_set(par));
3861 gen->local_sched = thread_tile_schedule(gen, gen->local_sched);
3862 gen->local_sched = scale_thread_tile_loops(gen, gen->local_sched);
3864 gen->private_access = NULL;
3865 compute_shared_sched(gen);
3866 gen->privatization = compute_privatization(gen);
3867 group_references(gen);
3868 compute_private_size(gen);
3869 check_shared_memory_bound(gen);
3870 localize_bounds(gen, host_domain);
3872 gen->local_sched = interchange_for_unroll(gen, gen->local_sched);
3874 print_kernel_launch(gen, arrays);
3876 fprintf(gen->cuda.kernel_c, "{\n");
3878 print_kernel_body(gen, host_domain, gen->tiled_sched);
3880 fprintf(gen->cuda.kernel_c, "}\n");
3882 free_local_array_info(gen);
3883 isl_map_free(gen->privatization);
3884 isl_union_map_free(gen->private_access);
3885 isl_union_map_free(gen->local_sched);
3886 isl_union_map_free(gen->tiled_sched);
3887 isl_union_map_free(gen->shared_sched);
3888 isl_union_map_free(gen->shared_proj);
3889 isl_union_set_free(arrays);
3890 isl_set_free(host_domain);
3892 free(gen->tile_size);
3893 gen->kernel_id++;
3896 /* Use CLooG to generate code for the outer gen->tile_first loops
3897 * of the global schedule in gen->sched.
3898 * The pretty printing of this code is handled by gpu_print_host_stmt,
3899 * which calls print_host_user for each kernel invocation location.
3901 static void print_cloog_host_code(struct cuda_gen *gen)
3903 int i;
3904 isl_set *context;
3905 isl_union_map *sched;
3906 CloogOptions *options;
3907 CloogDomain *cloog_context;
3908 CloogUnionDomain *ud;
3909 CloogInput *input;
3910 struct clast_stmt *stmt;
3911 char name[20];
3913 options = cloog_options_malloc(gen->state);
3914 options->language = CLOOG_LANGUAGE_C;
3915 options->otl = 0;
3916 options->strides = 1;
3917 options->stop = gen->tile_first;
3918 options->f = gen->untiled_len;
3919 options->l = gen->untiled_len;
3920 options->save_domains = 1;
3921 options->noscalars = 1;
3923 sched = isl_union_map_copy(gen->sched);
3924 ud = cloog_union_domain_from_isl_union_map(sched);
3925 for (i = 0; i < options->stop; ++i) {
3926 snprintf(name, sizeof(name), "h%d", i);
3927 ud = cloog_union_domain_set_name(ud, CLOOG_SCAT, i, name);
3929 context = isl_set_copy(gen->context);
3930 cloog_context = cloog_domain_from_isl_set(context);
3931 input = cloog_input_alloc(cloog_context, ud);
3933 stmt = cloog_clast_create_from_input(input, options);
3935 gen->code.indent = 0;
3936 gen->code.dst = gen->cuda.host_c;
3937 gen->code.print_user_stmt = NULL;
3938 gen->code.print_user_stmt_list = &print_host_user;
3939 gen->code.print_for_head = NULL;
3940 gen->code.print_for_foot = NULL;
3941 gen->code.user = gen;
3942 gpu_print_host_stmt(&gen->code, stmt);
3944 cloog_clast_free(stmt);
3945 cloog_options_free(options);
3946 fprintf(gen->cuda.host_c, "\n");
3949 void print_cuda_macros(struct cuda_gen *gen)
3951 const char *macros =
3952 "#define cudaCheckReturn(ret) assert((ret) == cudaSuccess)\n"
3953 "#define cudaCheckKernel()"
3954 " assert(cudaGetLastError() == cudaSuccess)\n\n";
3955 fputs(macros, gen->cuda.host_c);
3958 void print_host_code(struct cuda_gen *gen)
3960 fprintf(gen->cuda.host_c, "{\n");
3961 print_cloog_macros(gen->cuda.host_c);
3962 print_cloog_macros(gen->cuda.kernel_c);
3964 print_cuda_macros(gen);
3966 declare_device_arrays(gen);
3968 allocate_device_arrays(gen);
3969 copy_arrays_to_device(gen);
3971 gen->kernel_id = 0;
3972 print_cloog_host_code(gen);
3974 copy_arrays_from_device(gen);
3975 free_device_arrays(gen);
3977 fprintf(gen->cuda.host_c, "}\n");
3980 __isl_give isl_set *add_context_from_str(__isl_take isl_set *set,
3981 const char *str)
3983 isl_ctx *ctx;
3984 isl_set *context;
3986 if (!str)
3987 return set;
3989 ctx = isl_set_get_ctx(set);
3990 context = isl_set_read_from_str(ctx, str);
3991 context = isl_set_align_params(context, isl_set_get_space(set));
3992 set = isl_set_intersect(set, context);
3994 return set;
3997 __isl_give isl_union_map *extract_sizes_from_str(isl_ctx *ctx, const char *str)
3999 if (!str)
4000 return NULL;
4001 return isl_union_map_read_from_str(ctx, str);
4004 /* Return the union of all iteration domains of the gen->stmts[i].
4006 static __isl_give isl_union_set *extract_domain(struct cuda_gen *gen)
4008 int i;
4009 isl_union_set *domain;
4011 domain = isl_union_set_empty(isl_set_get_space(gen->context));
4012 for (i = 0; i < gen->n_stmts; ++i) {
4013 isl_set *domain_i;
4015 domain_i = isl_set_copy(gen->stmts[i].domain);
4016 domain = isl_union_set_union(domain,
4017 isl_union_set_from_set(domain_i));
4020 return domain;
4023 /* Information about the outermost tilable bands in the forest of bands.
4025 * tile_len and n_parallel are only sets on band_info structures
4026 * that correspond to outermost bands. For other bands (in particular,
4027 * ancestors of the outermost bands), n_parallal is set to 0.
4029 * prefix is the (padded) schedule leading up to the outermost tilable bands.
4031 * tile_first is the number of schedule dimensions in prefix.
4033 * suffix is the schedule of the outermost tilable bands and their descendants.
4035 struct band_info {
4036 struct cuda_gen *gen;
4037 int tile_first;
4038 int tile_len;
4039 int n_parallel;
4040 isl_union_map *prefix;
4041 isl_union_map *suffix;
4044 /* Set tile_len and n_parallel of the statement to that of
4045 * their outermost band, recorded in the band_info.
4047 static int set_stmt_tile_len(__isl_take isl_map *map, void *user)
4049 struct band_info *info = user;
4050 int nr;
4051 struct cuda_stmt *stmt;
4053 nr = atoi(isl_map_get_tuple_name(map, isl_dim_in) + 2);
4054 stmt = &info->gen->stmts[nr];
4056 stmt->tile_len = info->tile_len;
4057 stmt->n_parallel = info->n_parallel;
4059 isl_map_free(map);
4061 return 0;
4064 static void list_select_outer_band(struct cuda_gen *gen,
4065 __isl_take isl_band_list *list, int pos, struct band_info *list_info);
4067 /* Check if this band has any parallel loops. If so, take it as
4068 * the outermost tilable band. If not, continue looking for the
4069 * outermost tilable band in the children of the current band.
4071 static void band_select_outer_band(struct cuda_gen *gen,
4072 __isl_take isl_band *band, int pos, struct band_info *info)
4074 int n = isl_band_n_member(band);
4075 int n_parallel;
4077 for (n_parallel = 0; n_parallel < n; ++n_parallel)
4078 if (!isl_band_member_is_zero_distance(band, n_parallel))
4079 break;
4081 info->n_parallel = n_parallel;
4082 if (n_parallel) {
4083 info->gen = gen;
4084 info->tile_first = pos;
4085 info->tile_len = n;
4086 info->prefix = isl_band_get_prefix_schedule(band);
4087 info->suffix = isl_union_map_flat_range_product(
4088 isl_band_get_partial_schedule(band),
4089 isl_band_get_suffix_schedule(band));
4090 isl_union_map_foreach_map(info->prefix,
4091 &set_stmt_tile_len, info);
4092 } else if (isl_band_has_children(band)) {
4093 isl_band_list *children;
4094 children = isl_band_get_children(band);
4095 list_select_outer_band(gen, children, pos + n, info);
4096 } else {
4097 info->gen = gen;
4098 info->tile_first = pos + n;
4099 info->tile_len = 0;
4100 info->prefix = isl_union_map_flat_range_product(
4101 isl_band_get_prefix_schedule(band),
4102 isl_band_get_partial_schedule(band));
4103 info->suffix = isl_band_get_suffix_schedule(band);
4104 isl_union_map_foreach_map(info->prefix,
4105 &set_stmt_tile_len, info);
4108 isl_band_free(band);
4111 /* Comparison function that returns a non-zero value for band_infos
4112 * with different tile_len fields or different n_parallel fields.
4114 static int cmp_band(const void *p1, const void *p2)
4116 const struct band_info *info1 = p1;
4117 const struct band_info *info2 = p2;
4119 if (info1->tile_len != info2->tile_len)
4120 return info1->tile_len - info2->tile_len;
4122 return info1->n_parallel - info2->n_parallel;
4125 /* Extend "umap" with coordinates with fixed value "val"
4126 * to a total length of "dst_len", assuming the original dimension is "src_len".
4128 static __isl_give isl_union_map *extend_range(__isl_take isl_union_map *umap,
4129 int src_len, int dst_len, int val)
4131 isl_space *dim;
4132 isl_map *map;
4133 int i;
4135 dim = isl_union_map_get_space(umap);
4136 map = isl_map_reverse(projection(dim, dst_len, src_len));
4137 for (i = src_len; i < dst_len; ++i)
4138 map = isl_map_fix_si(map, isl_dim_out, i, val);
4140 umap = isl_union_map_apply_range(umap, isl_union_map_from_map(map));
4142 return umap;
4145 /* Group bands with the same values for tile_len and n_parallel.
4146 * The prefix schedule is then extended with a fixed coordinate that
4147 * is different for each such group.
4148 * Note that the actual values for this coordinate are not important.
4149 * The bands have already been effectively separated at a higher level
4150 * or they are independent and may be executed in parallel.
4151 * The list of band_info has been sorted before this functions is called.
4153 static void separate_bands(struct band_info *info, int n)
4155 int i;
4156 int j = 0;
4158 for (i = 0; i < n; ++i) {
4159 int l = info[i].tile_first;
4161 if (i &&
4162 (info[i].tile_len != info[i - 1].tile_len ||
4163 info[i].n_parallel != info[i - 1].n_parallel))
4164 j++;
4166 info[i].prefix = extend_range(info[i].prefix,
4167 l, l + 1, j);
4168 info[i].tile_first = l + 1;
4172 /* Select the outermost bands in the elements of the list, align
4173 * their prefix schedules, separate bands with different values
4174 * for tile_len and/or n_parallel and then combine the resulting
4175 * prefix and suffix schedules into a single pair of prefix and
4176 * suffix schedules for the entire list.
4178 static void list_select_outer_band(struct cuda_gen *gen,
4179 __isl_take isl_band_list *list, int pos, struct band_info *list_info)
4181 isl_band *band;
4182 int i;
4183 int n = isl_band_list_n_band(list);
4184 isl_ctx *ctx = isl_band_list_get_ctx(list);
4185 struct band_info *info;
4186 int max_tile_first;
4187 isl_union_map *prefix;
4188 isl_union_map *suffix;
4190 assert(n >= 1);
4191 info = isl_calloc_array(ctx, struct band_info, n);
4192 assert(info);
4194 max_tile_first = 0;
4195 for (i = 0; i < n; ++i) {
4196 band = isl_band_list_get_band(list, i);
4197 band_select_outer_band(gen, band, pos, &info[i]);
4198 if (info[i].tile_first > max_tile_first)
4199 max_tile_first = info[i].tile_first;
4202 for (i = 0; i < n; ++i) {
4203 if (info[i].tile_first == max_tile_first)
4204 continue;
4205 info[i].prefix = extend_range(info[i].prefix,
4206 info[i].tile_first, max_tile_first, 0);
4207 info[i].tile_first = max_tile_first;
4210 qsort(info, n, sizeof(struct band_info), &cmp_band);
4212 for (i = 0; i < n - 1; ++i)
4213 if (info[i].tile_len != info[i + 1].tile_len ||
4214 info[i].n_parallel != info[i + 1].n_parallel)
4215 break;
4217 if (i < n -1)
4218 separate_bands(info, n);
4220 prefix = info[0].prefix;
4221 suffix = info[0].suffix;
4223 for (i = 1; i < n; ++i) {
4224 prefix = isl_union_map_union(prefix, info[i].prefix);
4225 suffix = isl_union_map_union(suffix, info[i].suffix);
4228 list_info->tile_first = info[0].tile_first;
4229 list_info->tile_len = -1;
4230 list_info->prefix = prefix;
4231 list_info->suffix = suffix;
4233 isl_band_list_free(list);
4234 free(info);
4237 /* Set max_out to the maximal number of output dimensions over
4238 * all maps.
4240 static int update_max_out(__isl_take isl_map *map, void *user)
4242 int *max_out = user;
4243 int n_out = isl_map_dim(map, isl_dim_out);
4245 if (n_out > *max_out)
4246 *max_out = n_out;
4248 isl_map_free(map);
4249 return 0;
4252 struct align_range_data {
4253 int max_out;
4254 isl_union_map *res;
4257 /* Extend the dimension of the range of the given map to data->max_out and
4258 * then add the result to data->res.
4260 static int map_align_range(__isl_take isl_map *map, void *user)
4262 struct align_range_data *data = user;
4263 int i;
4264 isl_space *dim;
4265 isl_map *proj;
4266 int n_out = isl_map_dim(map, isl_dim_out);
4268 dim = isl_union_map_get_space(data->res);
4269 proj = isl_map_reverse(projection(dim, data->max_out, n_out));
4270 for (i = n_out; i < data->max_out; ++i)
4271 proj = isl_map_fix_si(proj, isl_dim_out, i, 0);
4273 map = isl_map_apply_range(map, proj);
4275 data->res = isl_union_map_add_map(data->res, map);
4277 return 0;
4280 /* Extend the ranges of the maps in the union map such they all have
4281 * the same dimension.
4283 static __isl_give isl_union_map *align_range(__isl_take isl_union_map *umap)
4285 struct align_range_data data;
4287 data.max_out = 0;
4288 isl_union_map_foreach_map(umap, &update_max_out, &data.max_out);
4290 data.res = isl_union_map_empty(isl_union_map_get_space(umap));
4291 isl_union_map_foreach_map(umap, &map_align_range, &data);
4293 isl_union_map_free(umap);
4294 return data.res;
4297 /* Select the outermost tilable band that (by construction)
4298 * has at least one parallel loop.
4299 * The starting position of the aligned band is stored in the pair
4300 * gen->tile_first.
4301 * The sizes and number of parallel loops may be different in different
4302 * parts of the band forest and are therefore stored in the cuda_stmts.
4304 * Return the complete schedule, with the tilable bands aligned
4305 * at gen->tile_first and padded with zero, if needed.
4307 static __isl_give isl_union_map *select_outer_tilable_band(struct cuda_gen *gen,
4308 __isl_keep isl_schedule *schedule)
4310 isl_band_list *list;
4311 struct band_info info;
4313 gen->n_parallel = 0;
4314 gen->tile_len = -1;
4316 list = isl_schedule_get_band_forest(schedule);
4318 list_select_outer_band(gen, list, 0, &info);
4320 gen->tile_first = info.tile_first;
4321 info.suffix = align_range(info.suffix);
4323 return isl_union_map_flat_range_product(info.prefix, info.suffix);
4326 /* Set gen->untiled_len to the number of scheduling dimensions
4327 * for the schedule of the first domain.
4328 * We assume here that this number is the same for all domains.
4330 static int set_untiled_len(__isl_take isl_map *map, void *user)
4332 unsigned *untiled_len = user;
4334 *untiled_len = isl_map_dim(map, isl_dim_out);
4336 isl_map_free(map);
4337 return -1;
4340 /* Compute an appropriate schedule based on the accesses in
4341 * gen->read and gen->write.
4343 * We first compute dependences and then use those to compute
4344 * a schedule that has a parallel loop in each tilable band.
4345 * Finally, we select the outermost tilable band.
4347 static void compute_schedule(struct cuda_gen *gen,
4348 __isl_take isl_union_map *sched)
4350 isl_ctx *ctx = isl_union_map_get_ctx(sched);
4351 isl_union_set *domain;
4352 isl_union_map *empty;
4353 isl_union_map *dep_raw, *dep2, *dep3, *dep;
4354 isl_union_map *uninitialized;
4355 isl_schedule *schedule;
4357 empty = isl_union_map_empty(isl_union_map_get_space(sched));
4359 isl_union_map_compute_flow(isl_union_map_copy(gen->read),
4360 isl_union_map_copy(gen->write), empty,
4361 isl_union_map_copy(sched),
4362 &dep_raw, NULL, &uninitialized, NULL);
4363 isl_union_map_compute_flow(isl_union_map_copy(gen->write),
4364 isl_union_map_copy(gen->write),
4365 isl_union_map_copy(gen->read),
4366 isl_union_map_copy(sched),
4367 &dep2, &dep3, NULL, NULL);
4368 isl_union_map_free(sched);
4370 gen->copy_in = isl_union_map_range(uninitialized);
4372 dep = isl_union_map_union(dep2, dep3);
4373 dep = isl_union_map_union(dep, dep_raw);
4374 dep = isl_union_map_coalesce(dep);
4376 domain = extract_domain(gen);
4377 schedule = isl_union_set_compute_schedule(isl_union_set_copy(domain),
4378 isl_union_map_copy(dep), dep);
4380 sched = select_outer_tilable_band(gen, schedule);
4382 isl_union_map_foreach_map(sched, &set_untiled_len, &gen->untiled_len);
4383 sched = isl_union_map_intersect_domain(sched, domain);
4384 gen->sched = sched;
4386 isl_schedule_free(schedule);
4389 static struct cuda_stmt_access **expr_extract_access(struct pet_expr *expr,
4390 struct cuda_stmt_access **next_access)
4392 struct cuda_stmt_access *access;
4393 isl_ctx *ctx = isl_map_get_ctx(expr->acc.access);
4395 access = isl_alloc_type(ctx, struct cuda_stmt_access);
4396 assert(access);
4397 access->next = NULL;
4398 access->read = expr->acc.read;
4399 access->write = expr->acc.write;
4400 access->access = isl_map_copy(expr->acc.access);
4402 *next_access = access;
4403 next_access = &(*next_access)->next;
4404 return next_access;
4407 static struct cuda_stmt_access **expr_extract_accesses(struct pet_expr *expr,
4408 struct cuda_stmt_access **next_access)
4410 int i;
4412 for (i = 0; i < expr->n_arg; ++i)
4413 next_access = expr_extract_accesses(expr->args[i],
4414 next_access);
4416 if (expr->type == pet_expr_access)
4417 next_access = expr_extract_access(expr, next_access);
4419 return next_access;
4422 static void pet_stmt_extract_accesses(struct cuda_stmt *stmt)
4424 struct cuda_stmt_access **next_access = &stmt->accesses;
4426 stmt->accesses = NULL;
4427 expr_extract_accesses(stmt->body, next_access);
4430 /* Return an array of cuda_stmt representing the statements in "scop".
4432 static struct cuda_stmt *extract_stmts(isl_ctx *ctx, struct pet_scop *scop,
4433 __isl_keep isl_set *context)
4435 int i;
4436 struct cuda_stmt *stmts;
4438 stmts = isl_calloc_array(ctx, struct cuda_stmt, scop->n_stmt);
4439 assert(stmts);
4441 for (i = 0; i < scop->n_stmt; ++i) {
4442 struct cuda_stmt *s = &stmts[i];
4444 s->domain = isl_set_copy(scop->stmts[i]->domain);
4445 s->domain = isl_set_intersect_params(s->domain,
4446 isl_set_copy(context));
4447 s->body = scop->stmts[i]->body;
4448 pet_stmt_extract_accesses(s);
4451 return stmts;
4454 /* Replace the scop in the "input" file by equivalent code
4455 * that uses the GPU. "scop" is assumed to correspond to this scop.
4457 * We first compute a schedule that respects the dependences
4458 * of the original program and select the outermost band
4459 * of tilable dimensions that has at least one parallel loop.
4460 * We then have three blocks of dimensions
4462 * H B G
4464 * The tilable band "B" is first tiled according to "tile" sizes, resulting
4465 * in
4467 * H T P G
4469 * For each iteration of the T loop and for each array, we compute
4470 * the array elements accessed by that iteration, construct a rectangular
4471 * box around it and shift it to the origin. The result is used
4472 * as shared memory for the array.
4474 * We then split off at most 2 parallel loops from the T loops and
4475 * at most 3 parallel loops from the P loops
4477 * H T1 T2 P1 P2 G
4479 * The T1/P1 loops are then tiled or "wrapped" over the blocks/threads,
4480 * according to "grid"/"block" sizes.
4482 * H T1T T1P T2 P1T P1P P2 G
4484 * Finally, the T1P and P1P iterators are equated to the block and
4485 * thread dimensions respectively and so are effectively removed.
4486 * The H loops are run on the host. The T1T, T2, P1T, P2 and G loops
4487 * are run on the GPU.
4489 * Code is generated in three stages. We first generate code for the
4490 * host (the H loops), with iterators h%d. Then, for each leaf node
4491 * of the resulting AST, we generate code for the shared loops (up to
4492 * and including T2), with iterators g%d and after equating the H loops
4493 * to h%d parameters and the T1P loops to the block dimensions.
4494 * Finally, we generate code for the remaining loops in a similar fashion.
4496 int cuda_pet(isl_ctx *ctx, struct pet_scop *scop, struct ppcg_options *options,
4497 const char *input)
4499 isl_union_map *sched;
4500 struct cuda_gen gen;
4502 if (!scop)
4503 return -1;
4505 scop = pet_scop_align_params(scop);
4507 gen.ctx = ctx;
4508 gen.context = isl_set_copy(scop->context);
4509 gen.context = add_context_from_str(gen.context, options->ctx);
4510 gen.sizes = extract_sizes_from_str(ctx, options->sizes);
4511 gen.n_stmts = scop->n_stmt;
4512 gen.stmts = extract_stmts(ctx, scop, gen.context);
4513 gen.read = pet_scop_collect_reads(scop);
4514 gen.write = pet_scop_collect_writes(scop);
4515 gen.options = options;
4516 gen.state = cloog_isl_state_malloc(gen.ctx);
4517 gen.scop = scop;
4519 cuda_open_files(&gen.cuda, input);
4521 collect_array_info(&gen);
4523 sched = pet_scop_collect_schedule(scop);
4525 compute_schedule(&gen, sched);
4527 print_host_code(&gen);
4529 cloog_state_free(gen.state);
4530 clear_cuda_gen(&gen);
4532 cuda_close_files(&gen.cuda);
4534 return 0;