opencl_print_kernel: fix indent
[ppcg.git] / opencl.c
blobe7ebe4643254bb00a34cb87be3b80f44bb5734ad
1 /*
2 * Copyright 2013 Ecole Normale Superieure
4 * Use of this software is governed by the GNU LGPLv2.1 license
6 * Written by Sven Verdoolaege and Riyadh Baghdadi,
7 * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
8 */
10 #include <ctype.h>
11 #include <limits.h>
12 #include <string.h>
14 #include <isl/aff.h>
15 #include <isl/ast.h>
17 #include "opencl.h"
18 #include "gpu_print.h"
19 #include "gpu.h"
20 #include "ppcg.h"
21 #include "print.h"
22 #include "schedule.h"
24 #define min(a, b) (((a) < (b)) ? (a) : (b))
25 #define max(a, b) (((a) > (b)) ? (a) : (b))
27 /* options are the global options passed to generate_opencl.
28 * input is the name of the input file.
29 * output is the user-specified output file name and may be NULL
30 * if not specified by the user.
31 * kernel_c_name is the name of the kernel_c file.
32 * host_c is the generated source file for the host code. kernel_c is
33 * the generated source file for the kernel. kernel_h is the generated
34 * header file for the kernel.
36 struct opencl_info {
37 struct ppcg_options *options;
38 const char *input;
39 const char *output;
40 char kernel_c_name[PATH_MAX];
42 FILE *host_c;
43 FILE *kernel_c;
44 FILE *kernel_h;
47 /* Open the file called "name" for writing or print an error message.
49 static FILE *open_or_croak(const char *name)
51 FILE *file;
53 file = fopen(name, "w");
54 if (!file)
55 fprintf(stderr, "Failed to open \"%s\" for writing\n", name);
56 return file;
59 /* Open the host .c file and the kernel .h and .cl files for writing.
60 * Their names are derived from info->output (or info->input if
61 * the user did not specify an output file name).
62 * Add the necessary includes to these files, including those specified
63 * by the user.
65 * Return 0 on success and -1 on failure.
67 static int opencl_open_files(struct opencl_info *info)
69 char name[PATH_MAX];
70 int i;
71 int len;
73 if (info->output) {
74 const char *ext;
76 ext = strrchr(info->output, '.');
77 len = ext ? ext - info->output : strlen(info->output);
78 memcpy(name, info->output, len);
80 info->host_c = open_or_croak(info->output);
81 } else {
82 len = ppcg_extract_base_name(name, info->input);
84 strcpy(name + len, "_host.c");
85 info->host_c = open_or_croak(name);
88 memcpy(info->kernel_c_name, name, len);
89 strcpy(info->kernel_c_name + len, "_kernel.cl");
90 info->kernel_c = open_or_croak(info->kernel_c_name);
92 strcpy(name + len, "_kernel.h");
93 info->kernel_h = open_or_croak(name);
95 if (!info->host_c || !info->kernel_c || !info->host_c)
96 return -1;
98 fprintf(info->host_c, "#include <assert.h>\n");
99 fprintf(info->host_c, "#include <stdio.h>\n");
100 fprintf(info->host_c, "#include \"%s\"\n\n", ppcg_base_name(name));
101 fprintf(info->kernel_h, "#if defined(__APPLE__)\n");
102 fprintf(info->kernel_h, "#include <OpenCL/opencl.h>\n");
103 fprintf(info->kernel_h, "#else\n");
104 fprintf(info->kernel_h, "#include <CL/opencl.h>\n");
105 fprintf(info->kernel_h, "#endif\n\n");
106 fprintf(info->kernel_h, "cl_device_id opencl_create_device("
107 "int use_gpu);\n");
108 fprintf(info->kernel_h, "cl_program opencl_build_program("
109 "cl_context ctx, "
110 "cl_device_id dev, const char *filename, "
111 "const char *opencl_options);\n");
112 fprintf(info->kernel_h,
113 "const char *opencl_error_string(cl_int error);\n");
114 for (i = 0; i < info->options->opencl_n_include_file; ++i)
115 fprintf(info->kernel_c, "#include <%s>\n",
116 info->options->opencl_include_files[i]);
118 return 0;
121 /* Close all output files.
123 static void opencl_close_files(struct opencl_info *info)
125 if (info->kernel_c)
126 fclose(info->kernel_c);
127 if (info->kernel_h)
128 fclose(info->kernel_h);
129 if (info->host_c)
130 fclose(info->host_c);
133 static __isl_give isl_printer *opencl_print_host_macros(__isl_take isl_printer *p)
135 const char *macros =
136 "#define openclCheckReturn(ret) \\\n"
137 " if (ret != CL_SUCCESS) {\\\n"
138 " fprintf(stderr, \"OpenCL error: %s\\n\", "
139 "opencl_error_string(ret)); \\\n"
140 " fflush(stderr); \\\n"
141 " assert(ret == CL_SUCCESS);\\\n }\n";
143 p = isl_printer_start_line(p);
144 p = isl_printer_print_str(p, macros);
145 p = isl_printer_end_line(p);
147 p = isl_ast_op_type_print_macro(isl_ast_op_max, p);
149 return p;
152 static __isl_give isl_printer *opencl_declare_device_arrays(
153 __isl_take isl_printer *p, struct gpu_prog *prog)
155 int i;
157 for (i = 0; i < prog->n_array; ++i) {
158 if (gpu_array_is_read_only_scalar(&prog->array[i]))
159 continue;
160 p = isl_printer_start_line(p);
161 p = isl_printer_print_str(p, "cl_mem dev_");
162 p = isl_printer_print_str(p, prog->array[i].name);
163 p = isl_printer_print_str(p, ";");
164 p = isl_printer_end_line(p);
166 p = isl_printer_start_line(p);
167 p = isl_printer_end_line(p);
168 return p;
171 /* Given an array, check whether its positive size guard expression is
172 * trivial.
174 static int is_array_positive_size_guard_trivial(struct gpu_array_info *array)
176 isl_set *guard;
177 int is_trivial;
179 guard = gpu_array_positive_size_guard(array);
180 is_trivial = isl_set_plain_is_universe(guard);
181 isl_set_free(guard);
182 return is_trivial;
185 /* Allocate a device array for array and copy the contents to the device
186 * if copy is set.
188 * Emit a max-expression to ensure the device array can contain at least one
189 * element if the array's positive size guard expression is not trivial.
191 static __isl_give isl_printer *allocate_device_array(__isl_take isl_printer *p,
192 struct gpu_array_info *array, int copy)
194 int need_lower_bound;
196 p = ppcg_start_block(p);
198 p = isl_printer_start_line(p);
199 p = isl_printer_print_str(p, "dev_");
200 p = isl_printer_print_str(p, array->name);
201 p = isl_printer_print_str(p, " = clCreateBuffer(context, ");
202 p = isl_printer_print_str(p, "CL_MEM_READ_WRITE");
204 if (!copy)
205 p = isl_printer_print_str(p, ", ");
206 else
207 p = isl_printer_print_str(p, " | CL_MEM_COPY_HOST_PTR, ");
209 need_lower_bound = !is_array_positive_size_guard_trivial(array);
210 if (need_lower_bound) {
211 p = isl_printer_print_str(p, "max(sizeof(");
212 p = isl_printer_print_str(p, array->type);
213 p = isl_printer_print_str(p, "), ");
215 p = gpu_array_info_print_size(p, array);
216 if (need_lower_bound)
217 p = isl_printer_print_str(p, ")");
219 if (!copy)
220 p = isl_printer_print_str(p, ", NULL");
221 else if (gpu_array_is_scalar(array)) {
222 p = isl_printer_print_str(p, ", &");
223 p = isl_printer_print_str(p, array->name);
224 } else {
225 p = isl_printer_print_str(p, ", ");
226 p = isl_printer_print_str(p, array->name);
229 p = isl_printer_print_str(p, ", &err);");
230 p = isl_printer_end_line(p);
231 p = isl_printer_start_line(p);
232 p = isl_printer_print_str(p, "openclCheckReturn(err);");
233 p = isl_printer_end_line(p);
235 p = ppcg_end_block(p);
237 return p;
240 /* Allocate device arrays and copy the contents of copy_in arrays into device.
242 static __isl_give isl_printer *opencl_allocate_device_arrays(
243 __isl_take isl_printer *p, struct gpu_prog *prog)
245 int i, j;
247 for (i = 0; i < prog->n_array; ++i) {
248 struct gpu_array_info *array = &prog->array[i];
249 isl_space *space;
250 isl_set *read_i;
251 int empty;
253 if (gpu_array_is_read_only_scalar(array))
254 continue;
256 space = isl_space_copy(array->space);
257 read_i = isl_union_set_extract_set(prog->copy_in, space);
258 empty = isl_set_plain_is_empty(read_i);
259 isl_set_free(read_i);
261 p = allocate_device_array(p, array, !empty);
263 p = isl_printer_start_line(p);
264 p = isl_printer_end_line(p);
265 return p;
268 /* Print a call to the OpenCL clSetKernelArg() function which sets
269 * the arguments of the kernel. arg_name and arg_index are the name and the
270 * index of the kernel argument. The index of the leftmost argument of
271 * the kernel is 0 whereas the index of the rightmost argument of the kernel
272 * is n - 1, where n is the total number of the kernel arguments.
273 * read_only_scalar is a boolean that indicates whether the argument is a read
274 * only scalar.
276 static __isl_give isl_printer *opencl_set_kernel_argument(
277 __isl_take isl_printer *p, int kernel_id,
278 const char *arg_name, int arg_index, int read_only_scalar)
280 p = isl_printer_start_line(p);
281 p = isl_printer_print_str(p,
282 "openclCheckReturn(clSetKernelArg(kernel");
283 p = isl_printer_print_int(p, kernel_id);
284 p = isl_printer_print_str(p, ", ");
285 p = isl_printer_print_int(p, arg_index);
286 p = isl_printer_print_str(p, ", sizeof(");
288 if (read_only_scalar) {
289 p = isl_printer_print_str(p, arg_name);
290 p = isl_printer_print_str(p, "), &");
291 } else
292 p = isl_printer_print_str(p, "cl_mem), (void *) &dev_");
294 p = isl_printer_print_str(p, arg_name);
295 p = isl_printer_print_str(p, "));");
296 p = isl_printer_end_line(p);
298 return p;
301 /* Print the block sizes as a list of the sizes in each
302 * dimension.
304 static __isl_give isl_printer *opencl_print_block_sizes(
305 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
307 int i;
309 if (kernel->n_block > 0)
310 for (i = 0; i < kernel->n_block; ++i) {
311 if (i)
312 p = isl_printer_print_str(p, ", ");
313 p = isl_printer_print_int(p, kernel->block_dim[i]);
315 else
316 p = isl_printer_print_str(p, "1");
318 return p;
321 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
322 * clSetKernelArg() function for each kernel argument.
324 static __isl_give isl_printer *opencl_set_kernel_arguments(
325 __isl_take isl_printer *p, struct gpu_prog *prog,
326 struct ppcg_kernel *kernel)
328 int i, n, ro;
329 unsigned nparam;
330 isl_space *space;
331 const char *type;
332 int arg_index = 0;
334 for (i = 0; i < prog->n_array; ++i) {
335 isl_set *arr;
336 int empty;
338 space = isl_space_copy(prog->array[i].space);
339 arr = isl_union_set_extract_set(kernel->arrays, space);
340 empty = isl_set_plain_is_empty(arr);
341 isl_set_free(arr);
342 if (empty)
343 continue;
344 ro = gpu_array_is_read_only_scalar(&prog->array[i]);
345 opencl_set_kernel_argument(p, kernel->id, prog->array[i].name,
346 arg_index, ro);
347 arg_index++;
350 space = isl_union_set_get_space(kernel->arrays);
351 nparam = isl_space_dim(space, isl_dim_param);
352 for (i = 0; i < nparam; ++i) {
353 const char *name;
355 name = isl_space_get_dim_name(space, isl_dim_param, i);
356 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
357 arg_index++;
359 isl_space_free(space);
361 n = isl_space_dim(kernel->space, isl_dim_set);
362 for (i = 0; i < n; ++i) {
363 const char *name;
364 isl_id *id;
366 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
367 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
368 arg_index++;
371 return p;
374 /* Print the arguments to a kernel declaration or call. If "types" is set,
375 * then print a declaration (including the types of the arguments).
377 * The arguments are printed in the following order
378 * - the arrays accessed by the kernel
379 * - the parameters
380 * - the host loop iterators
382 static __isl_give isl_printer *opencl_print_kernel_arguments(
383 __isl_take isl_printer *p, struct gpu_prog *prog,
384 struct ppcg_kernel *kernel, int types)
386 int i, n;
387 int first = 1;
388 unsigned nparam;
389 isl_space *space;
390 const char *type;
392 for (i = 0; i < prog->n_array; ++i) {
393 isl_set *arr;
394 int empty;
396 space = isl_space_copy(prog->array[i].space);
397 arr = isl_union_set_extract_set(kernel->arrays, space);
398 empty = isl_set_plain_is_empty(arr);
399 isl_set_free(arr);
400 if (empty)
401 continue;
403 if (!first)
404 p = isl_printer_print_str(p, ", ");
406 if (types)
407 p = gpu_array_info_print_declaration_argument(p,
408 &prog->array[i], "__global");
409 else
410 p = gpu_array_info_print_call_argument(p,
411 &prog->array[i]);
413 first = 0;
416 space = isl_union_set_get_space(kernel->arrays);
417 nparam = isl_space_dim(space, isl_dim_param);
418 for (i = 0; i < nparam; ++i) {
419 const char *name;
421 name = isl_space_get_dim_name(space, isl_dim_param, i);
423 if (!first)
424 p = isl_printer_print_str(p, ", ");
425 if (types)
426 p = isl_printer_print_str(p, "int ");
427 p = isl_printer_print_str(p, name);
429 first = 0;
431 isl_space_free(space);
433 n = isl_space_dim(kernel->space, isl_dim_set);
434 type = isl_options_get_ast_iterator_type(prog->ctx);
435 for (i = 0; i < n; ++i) {
436 const char *name;
437 isl_id *id;
439 if (!first)
440 p = isl_printer_print_str(p, ", ");
441 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
442 if (types) {
443 p = isl_printer_print_str(p, type);
444 p = isl_printer_print_str(p, " ");
446 p = isl_printer_print_str(p, name);
448 first = 0;
451 return p;
454 /* Print the header of the given kernel.
456 static __isl_give isl_printer *opencl_print_kernel_header(
457 __isl_take isl_printer *p, struct gpu_prog *prog,
458 struct ppcg_kernel *kernel)
460 p = isl_printer_start_line(p);
461 p = isl_printer_print_str(p, "__kernel void kernel");
462 p = isl_printer_print_int(p, kernel->id);
463 p = isl_printer_print_str(p, "(");
464 p = opencl_print_kernel_arguments(p, prog, kernel, 1);
465 p = isl_printer_print_str(p, ")");
466 p = isl_printer_end_line(p);
468 return p;
471 /* Unlike the equivalent function in the CUDA backend which prints iterators
472 * in reverse order to promote coalescing, this function does not print
473 * iterators in reverse order. The OpenCL backend currently does not take
474 * into account any coalescing considerations.
476 static __isl_give isl_printer *opencl_print_kernel_iterators(
477 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
479 int i, n_grid;
480 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
481 const char *type;
483 type = isl_options_get_ast_iterator_type(ctx);
485 n_grid = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
486 if (n_grid > 0) {
487 p = isl_printer_start_line(p);
488 p = isl_printer_print_str(p, type);
489 p = isl_printer_print_str(p, " ");
490 for (i = 0; i < n_grid; ++i) {
491 if (i)
492 p = isl_printer_print_str(p, ", ");
493 p = isl_printer_print_str(p, "b");
494 p = isl_printer_print_int(p, i);
495 p = isl_printer_print_str(p, " = get_group_id(");
496 p = isl_printer_print_int(p, i);
497 p = isl_printer_print_str(p, ")");
499 p = isl_printer_print_str(p, ";");
500 p = isl_printer_end_line(p);
503 if (kernel->n_block > 0) {
504 p = isl_printer_start_line(p);
505 p = isl_printer_print_str(p, type);
506 p = isl_printer_print_str(p, " ");
507 for (i = 0; i < kernel->n_block; ++i) {
508 if (i)
509 p = isl_printer_print_str(p, ", ");
510 p = isl_printer_print_str(p, "t");
511 p = isl_printer_print_int(p, i);
512 p = isl_printer_print_str(p, " = get_local_id(");
513 p = isl_printer_print_int(p, i);
514 p = isl_printer_print_str(p, ")");
516 p = isl_printer_print_str(p, ";");
517 p = isl_printer_end_line(p);
520 return p;
523 static __isl_give isl_printer *opencl_print_kernel_var(
524 __isl_take isl_printer *p, struct ppcg_kernel_var *var)
526 int j;
527 isl_val *v;
529 p = isl_printer_start_line(p);
530 if (var->type == ppcg_access_shared)
531 p = isl_printer_print_str(p, "__local ");
532 p = isl_printer_print_str(p, var->array->type);
533 p = isl_printer_print_str(p, " ");
534 p = isl_printer_print_str(p, var->name);
535 for (j = 0; j < var->array->n_index; ++j) {
536 p = isl_printer_print_str(p, "[");
537 v = isl_vec_get_element_val(var->size, j);
538 p = isl_printer_print_val(p, v);
539 p = isl_printer_print_str(p, "]");
540 isl_val_free(v);
542 p = isl_printer_print_str(p, ";");
543 p = isl_printer_end_line(p);
545 return p;
548 static __isl_give isl_printer *opencl_print_kernel_vars(
549 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
551 int i;
553 for (i = 0; i < kernel->n_var; ++i)
554 p = opencl_print_kernel_var(p, &kernel->var[i]);
556 return p;
559 /* Print a call to barrier() which is a sync statement.
560 * All work-items in a work-group executing the kernel on a processor must
561 * execute the barrier() function before any are allowed to continue execution
562 * beyond the barrier.
563 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
564 * variables stored in local memory or queue a memory fence to ensure correct
565 * ordering of memory operations to local memory.
566 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
567 * fence to ensure correct ordering of memory operations to global memory.
569 static __isl_give isl_printer *opencl_print_sync(__isl_take isl_printer *p,
570 struct ppcg_kernel_stmt *stmt)
572 p = isl_printer_start_line(p);
573 p = isl_printer_print_str(p,
574 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
575 p = isl_printer_end_line(p);
577 return p;
580 /* This function is called for each user statement in the AST,
581 * i.e., for each kernel body statement, copy statement or sync statement.
583 static __isl_give isl_printer *opencl_print_kernel_stmt(
584 __isl_take isl_printer *p,
585 __isl_take isl_ast_print_options *print_options,
586 __isl_keep isl_ast_node *node, void *user)
588 isl_id *id;
589 struct ppcg_kernel_stmt *stmt;
591 id = isl_ast_node_get_annotation(node);
592 stmt = isl_id_get_user(id);
593 isl_id_free(id);
595 isl_ast_print_options_free(print_options);
597 switch (stmt->type) {
598 case ppcg_kernel_copy:
599 return ppcg_kernel_print_copy(p, stmt);
600 case ppcg_kernel_sync:
601 return opencl_print_sync(p, stmt);
602 case ppcg_kernel_domain:
603 return ppcg_kernel_print_domain(p, stmt);
606 return p;
609 /* Return true if there is a double array in prog->array or
610 * if any of the types in prog->scop involve any doubles.
611 * To check the latter condition, we simply search for the string "double"
612 * in the type definitions, which may result in false positives.
614 static __isl_give int any_double_elements(struct gpu_prog *prog)
616 int i;
618 for (i = 0; i < prog->n_array; ++i)
619 if (strcmp(prog->array[i].type, "double") == 0)
620 return 1;
622 for (i = 0; i < prog->scop->pet->n_type; ++i) {
623 struct pet_type *type = prog->scop->pet->types[i];
625 if (strstr(type->definition, "double"))
626 return 1;
629 return 0;
632 /* Prints a #pragma to enable support for double floating-point
633 * precision. OpenCL 1.0 adds support for double precision floating-point as
634 * an optional extension. An application that wants to use double will need to
635 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
636 * any double precision data type is declared in the kernel code.
638 static __isl_give isl_printer *opencl_enable_double_support(
639 __isl_take isl_printer *p)
641 int i;
643 p = isl_printer_start_line(p);
644 p = isl_printer_print_str(p, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
645 " enable");
646 p = isl_printer_end_line(p);
647 p = isl_printer_start_line(p);
648 p = isl_printer_end_line(p);
650 return p;
653 static void opencl_print_kernel(struct gpu_prog *prog,
654 struct ppcg_kernel *kernel, struct opencl_info *opencl)
656 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
657 isl_ast_print_options *print_options;
658 isl_printer *p;
660 p = isl_printer_to_file(ctx, opencl->kernel_c);
661 print_options = isl_ast_print_options_alloc(ctx);
662 print_options = isl_ast_print_options_set_print_user(print_options,
663 &opencl_print_kernel_stmt, NULL);
665 p = isl_printer_set_output_format(p, ISL_FORMAT_C);
666 p = opencl_print_kernel_header(p, prog, kernel);
667 p = isl_printer_print_str(p, "{");
668 p = isl_printer_end_line(p);
669 p = isl_printer_indent(p, 4);
670 p = opencl_print_kernel_iterators(p, kernel);
671 p = opencl_print_kernel_vars(p, kernel);
672 p = isl_printer_end_line(p);
673 p = gpu_print_macros(p, kernel->tree);
674 p = isl_ast_node_print(kernel->tree, p, print_options);
675 p = isl_printer_indent(p, -4);
676 p = isl_printer_start_line(p);
677 p = isl_printer_print_str(p, "}");
678 p = isl_printer_end_line(p);
679 isl_printer_free(p);
682 struct print_host_user_data_opencl {
683 struct opencl_info *opencl;
684 struct gpu_prog *prog;
687 /* This function prints the i'th block size multiplied by the i'th grid size,
688 * where i (a parameter to this function) is one of the possible dimensions of
689 * grid sizes and block sizes.
690 * If the dimension of block sizes is not equal to the dimension of grid sizes
691 * the output is calculated as follows:
693 * Suppose that:
694 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
695 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
697 * The output is:
698 * If (i > dim2) then the output is block_sizes[i]
699 * If (i > dim1) then the output is grid_sizes[i]
701 static __isl_give isl_printer *opencl_print_total_number_of_work_items_for_dim(
702 __isl_take isl_printer *p, struct ppcg_kernel *kernel, int i)
704 int grid_dim, block_dim;
706 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
707 block_dim = kernel->n_block;
709 isl_pw_aff *bound_grid;
711 if (i < min(grid_dim, block_dim)) {
712 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
713 p = isl_printer_print_str(p, "(");
714 p = isl_printer_print_pw_aff(p, bound_grid);
715 p = isl_printer_print_str(p, ") * ");
716 p = isl_printer_print_int(p, kernel->block_dim[i]);
717 isl_pw_aff_free(bound_grid);
718 } else if (i >= grid_dim)
719 p = isl_printer_print_int(p, kernel->block_dim[i]);
720 else {
721 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
722 p = isl_printer_print_pw_aff(p, bound_grid);
723 isl_pw_aff_free(bound_grid);
726 return p;
729 /* Print a list that represents the total number of work items. The list is
730 * constructed by performing an element-wise multiplication of the block sizes
731 * and the grid sizes. To explain how the list is constructed, suppose that:
732 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
733 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
735 * The output of this function is constructed as follows:
736 * If (dim1 > dim2) then the output is the following list:
737 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
738 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
740 * If (dim2 > dim1) then the output is the following list:
741 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
742 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
744 * To calculate the total number of work items out of the list constructed by
745 * this function, the user should multiply the elements of the list.
747 static __isl_give isl_printer *opencl_print_total_number_of_work_items_as_list(
748 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
750 int i;
751 int grid_dim, block_dim;
753 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
754 block_dim = kernel->n_block;
756 if ((grid_dim <= 0) || (block_dim <= 0)) {
757 p = isl_printer_print_str(p, "1");
758 return p;
761 for (i = 0; i <= max(grid_dim, block_dim) - 1; i++) {
762 if (i > 0)
763 p = isl_printer_print_str(p, ", ");
765 p = opencl_print_total_number_of_work_items_for_dim(p,
766 kernel, i);
769 return p;
772 /* Print the user statement of the host code to "p".
774 * In particular, print a block of statements that defines the grid
775 * and the work group and then launches the kernel.
777 * A grid is composed of many work groups (blocks), each work group holds
778 * many work-items (threads).
780 * global_work_size[kernel->n_block] represents the total number of work
781 * items. It points to an array of kernel->n_block unsigned
782 * values that describe the total number of work-items that will execute
783 * the kernel. The total number of work-items is computed as:
784 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
786 * The size of each work group (i.e. the number of work-items in each work
787 * group) is described using block_size[kernel->n_block]. The total
788 * number of work-items in a block (work-group) is computed as:
789 * block_size[0] *... * block_size[kernel->n_block - 1].
791 * For more information check:
792 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
794 static __isl_give isl_printer *opencl_print_host_user(
795 __isl_take isl_printer *p,
796 __isl_take isl_ast_print_options *print_options,
797 __isl_keep isl_ast_node *node, void *user)
799 isl_id *id;
800 struct ppcg_kernel *kernel;
801 struct print_host_user_data_opencl *data;
802 int i;
804 id = isl_ast_node_get_annotation(node);
805 kernel = isl_id_get_user(id);
806 isl_id_free(id);
808 data = (struct print_host_user_data_opencl *) user;
810 p = isl_printer_start_line(p);
811 p = isl_printer_print_str(p, "{");
812 p = isl_printer_end_line(p);
813 p = isl_printer_indent(p, 2);
815 p = isl_printer_start_line(p);
816 p = isl_printer_print_str(p, "size_t global_work_size[");
818 if (kernel->n_block > 0)
819 p = isl_printer_print_int(p, kernel->n_block);
820 else
821 p = isl_printer_print_int(p, 1);
823 p = isl_printer_print_str(p, "] = {");
824 p = opencl_print_total_number_of_work_items_as_list(p, kernel);
825 p = isl_printer_print_str(p, "};");
826 p = isl_printer_end_line(p);
828 p = isl_printer_start_line(p);
829 p = isl_printer_print_str(p, "size_t block_size[");
831 if (kernel->n_block > 0)
832 p = isl_printer_print_int(p, kernel->n_block);
833 else
834 p = isl_printer_print_int(p, 1);
836 p = isl_printer_print_str(p, "] = {");
837 p = opencl_print_block_sizes(p, kernel);
838 p = isl_printer_print_str(p, "};");
839 p = isl_printer_end_line(p);
841 p = isl_printer_start_line(p);
842 p = isl_printer_print_str(p, "cl_kernel kernel");
843 p = isl_printer_print_int(p, kernel->id);
844 p = isl_printer_print_str(p, " = clCreateKernel(program, \"kernel");
845 p = isl_printer_print_int(p, kernel->id);
846 p = isl_printer_print_str(p, "\", &err);");
847 p = isl_printer_end_line(p);
848 p = isl_printer_start_line(p);
849 p = isl_printer_print_str(p, "openclCheckReturn(err);");
850 p = isl_printer_end_line(p);
852 opencl_set_kernel_arguments(p, data->prog, kernel);
854 p = isl_printer_start_line(p);
855 p = isl_printer_print_str(p, "openclCheckReturn(clEnqueueNDRangeKernel"
856 "(queue, kernel");
857 p = isl_printer_print_int(p, kernel->id);
858 p = isl_printer_print_str(p, ", ");
859 if (kernel->n_block > 0)
860 p = isl_printer_print_int(p, kernel->n_block);
861 else
862 p = isl_printer_print_int(p, 1);
864 p = isl_printer_print_str(p, ", NULL, global_work_size, "
865 "block_size, "
866 "0, NULL, NULL));");
867 p = isl_printer_end_line(p);
868 p = isl_printer_start_line(p);
869 p = isl_printer_print_str(p, "openclCheckReturn("
870 "clReleaseKernel(kernel");
871 p = isl_printer_print_int(p, kernel->id);
872 p = isl_printer_print_str(p, "));");
873 p = isl_printer_end_line(p);
874 p = isl_printer_start_line(p);
875 p = isl_printer_print_str(p, "clFinish(queue);");
876 p = isl_printer_end_line(p);
877 p = isl_printer_indent(p, -2);
878 p = isl_printer_start_line(p);
879 p = isl_printer_print_str(p, "}");
880 p = isl_printer_end_line(p);
882 p = isl_printer_start_line(p);
883 p = isl_printer_end_line(p);
885 opencl_print_kernel(data->prog, kernel, data->opencl);
887 isl_ast_print_options_free(print_options);
889 return p;
892 static __isl_give isl_printer *opencl_print_host_code(
893 __isl_take isl_printer *p, struct gpu_prog *prog,
894 __isl_keep isl_ast_node *tree, struct opencl_info *opencl)
896 isl_ast_print_options *print_options;
897 isl_ctx *ctx = isl_ast_node_get_ctx(tree);
898 struct print_host_user_data_opencl data = { opencl, prog };
900 print_options = isl_ast_print_options_alloc(ctx);
901 print_options = isl_ast_print_options_set_print_user(print_options,
902 &opencl_print_host_user, &data);
904 p = gpu_print_macros(p, tree);
905 p = isl_ast_node_print(tree, p, print_options);
907 return p;
910 /* Copy "array" back from the GPU to the host.
912 static __isl_give isl_printer *copy_array_from_device(__isl_take isl_printer *p,
913 void *user)
915 struct gpu_array_info *array = user;
917 p = isl_printer_start_line(p);
918 p = isl_printer_print_str(p, "openclCheckReturn("
919 "clEnqueueReadBuffer(queue,"
920 " dev_");
921 p = isl_printer_print_str(p, array->name);
922 p = isl_printer_print_str(p, ", CL_TRUE, 0, ");
923 p = gpu_array_info_print_size(p, array);
925 if (gpu_array_is_scalar(array))
926 p = isl_printer_print_str(p, ", &");
927 else
928 p = isl_printer_print_str(p, ", ");
929 p = isl_printer_print_str(p, array->name);
930 p = isl_printer_print_str(p, ", 0, NULL, NULL));");
931 p = isl_printer_end_line(p);
933 return p;
936 /* Copy copy_out arrays back from the GPU to the host.
938 * Only perform the copying for arrays with strictly positive size.
940 static __isl_give isl_printer *opencl_copy_arrays_from_device(
941 __isl_take isl_printer *p, struct gpu_prog *prog)
943 int i;
944 isl_union_set *copy_out;
945 copy_out = isl_union_set_copy(prog->copy_out);
947 for (i = 0; i < prog->n_array; ++i) {
948 struct gpu_array_info *array = &prog->array[i];
949 isl_space *space;
950 isl_set *copy_out_i;
951 isl_set *guard;
952 int empty;
954 space = isl_space_copy(array->space);
955 copy_out_i = isl_union_set_extract_set(copy_out, space);
956 empty = isl_set_plain_is_empty(copy_out_i);
957 isl_set_free(copy_out_i);
958 if (empty)
959 continue;
961 guard = gpu_array_positive_size_guard(array);
962 p = ppcg_print_guarded(p, guard, isl_set_copy(prog->context),
963 &copy_array_from_device, array);
966 isl_union_set_free(copy_out);
967 p = isl_printer_start_line(p);
968 p = isl_printer_end_line(p);
969 return p;
972 /* Create an OpenCL device, context, command queue and build the kernel.
973 * input is the name of the input file provided to ppcg.
975 static __isl_give isl_printer *opencl_setup(__isl_take isl_printer *p,
976 const char *input, struct opencl_info *info)
978 int len;
980 p = isl_printer_start_line(p);
981 p = isl_printer_print_str(p, "cl_device_id device;");
982 p = isl_printer_end_line(p);
983 p = isl_printer_start_line(p);
984 p = isl_printer_print_str(p, "cl_context context;");
985 p = isl_printer_end_line(p);
986 p = isl_printer_start_line(p);
987 p = isl_printer_print_str(p, "cl_program program;");
988 p = isl_printer_end_line(p);
989 p = isl_printer_start_line(p);
990 p = isl_printer_print_str(p, "cl_command_queue queue;");
991 p = isl_printer_end_line(p);
992 p = isl_printer_start_line(p);
993 p = isl_printer_print_str(p, "cl_int err;");
994 p = isl_printer_end_line(p);
995 p = isl_printer_start_line(p);
996 p = isl_printer_print_str(p, "device = opencl_create_device(");
997 p = isl_printer_print_int(p, info->options->opencl_use_gpu);
998 p = isl_printer_print_str(p, ");");
999 p = isl_printer_end_line(p);
1000 p = isl_printer_start_line(p);
1001 p = isl_printer_print_str(p, "context = clCreateContext(NULL, 1, "
1002 "&device, NULL, NULL, &err);");
1003 p = isl_printer_end_line(p);
1004 p = isl_printer_start_line(p);
1005 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1006 p = isl_printer_end_line(p);
1007 p = isl_printer_start_line(p);
1008 p = isl_printer_print_str(p, "queue = clCreateCommandQueue"
1009 "(context, device, 0, &err);");
1010 p = isl_printer_end_line(p);
1011 p = isl_printer_start_line(p);
1012 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1013 p = isl_printer_end_line(p);
1015 p = isl_printer_start_line(p);
1016 p = isl_printer_print_str(p, "program = opencl_build_program("
1017 "context, device, \"");
1018 p = isl_printer_print_str(p, info->kernel_c_name);
1019 p = isl_printer_print_str(p, "\", \"");
1021 if (info->options->opencl_compiler_options)
1022 p = isl_printer_print_str(p,
1023 info->options->opencl_compiler_options);
1025 p = isl_printer_print_str(p, "\");");
1026 p = isl_printer_end_line(p);
1027 p = isl_printer_start_line(p);
1028 p = isl_printer_end_line(p);
1030 return p;
1033 static __isl_give isl_printer *opencl_release_cl_objects(
1034 __isl_take isl_printer *p, struct opencl_info *info)
1036 p = isl_printer_start_line(p);
1037 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseCommandQueue"
1038 "(queue));");
1039 p = isl_printer_end_line(p);
1040 p = isl_printer_start_line(p);
1041 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseProgram"
1042 "(program));");
1043 p = isl_printer_end_line(p);
1044 p = isl_printer_start_line(p);
1045 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseContext"
1046 "(context));");
1047 p = isl_printer_end_line(p);
1049 return p;
1052 /* Free the device array corresponding to "array"
1054 static __isl_give isl_printer *release_device_array(__isl_take isl_printer *p,
1055 struct gpu_array_info *array)
1057 p = isl_printer_start_line(p);
1058 p = isl_printer_print_str(p, "openclCheckReturn("
1059 "clReleaseMemObject(dev_");
1060 p = isl_printer_print_str(p, array->name);
1061 p = isl_printer_print_str(p, "));");
1062 p = isl_printer_end_line(p);
1064 return p;
1067 /* Free the device arrays.
1069 static __isl_give isl_printer *opencl_release_device_arrays(
1070 __isl_take isl_printer *p, struct gpu_prog *prog)
1072 int i, j;
1074 for (i = 0; i < prog->n_array; ++i) {
1075 struct gpu_array_info *array = &prog->array[i];
1076 if (gpu_array_is_read_only_scalar(array))
1077 continue;
1079 p = release_device_array(p, array);
1081 return p;
1084 /* Given a gpu_prog "prog" and the corresponding transformed AST
1085 * "tree", print the entire OpenCL code to "p".
1087 static __isl_give isl_printer *print_opencl(__isl_take isl_printer *p,
1088 struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
1089 struct gpu_types *types, void *user)
1091 struct opencl_info *opencl = user;
1092 isl_printer *kernel;
1094 kernel = isl_printer_to_file(isl_printer_get_ctx(p), opencl->kernel_c);
1095 kernel = isl_printer_set_output_format(kernel, ISL_FORMAT_C);
1096 if (any_double_elements(prog))
1097 kernel = opencl_enable_double_support(kernel);
1098 kernel = gpu_print_types(kernel, types, prog);
1099 isl_printer_free(kernel);
1101 if (!kernel)
1102 return isl_printer_free(p);
1104 p = ppcg_start_block(p);
1106 p = opencl_print_host_macros(p);
1108 p = opencl_declare_device_arrays(p, prog);
1109 p = opencl_setup(p, opencl->input, opencl);
1110 p = opencl_allocate_device_arrays(p, prog);
1112 p = opencl_print_host_code(p, prog, tree, opencl);
1114 p = opencl_copy_arrays_from_device(p, prog);
1115 p = opencl_release_device_arrays(p, prog);
1116 p = opencl_release_cl_objects(p, opencl);
1118 p = ppcg_end_block(p);
1120 return p;
1123 /* Transform the code in the file called "input" by replacing
1124 * all scops by corresponding OpenCL code.
1125 * The host code is written to "output" or a name derived from
1126 * "input" if "output" is NULL.
1127 * The kernel code is placed in separate files with names
1128 * derived from "output" or "input".
1130 * We let generate_gpu do all the hard work and then let it call
1131 * us back for printing the AST in print_cuda.
1133 * To prepare for this printing, we first open the output files
1134 * and we close them after generate_gpu has finished.
1136 int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
1137 const char *input, const char *output)
1139 struct opencl_info opencl = { options, input, output };
1140 int r;
1142 r = opencl_open_files(&opencl);
1144 if (r >= 0)
1145 r = generate_gpu(ctx, input, opencl.host_c, options,
1146 &print_opencl, &opencl);
1148 opencl_close_files(&opencl);
1150 return r;