opencl backend: print kernel file using single printer
[ppcg.git] / opencl.c
blobdac0614ba84587427b4c68c7dcef15fb9e8d9407
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 * kprinter is an isl_printer for the kernel file.
33 * host_c is the generated source file for the host code. kernel_c is
34 * the generated source file for the kernel. kernel_h is the generated
35 * header file for the kernel.
37 struct opencl_info {
38 struct ppcg_options *options;
39 const char *input;
40 const char *output;
41 char kernel_c_name[PATH_MAX];
43 isl_printer *kprinter;
45 FILE *host_c;
46 FILE *kernel_c;
47 FILE *kernel_h;
50 /* Open the file called "name" for writing or print an error message.
52 static FILE *open_or_croak(const char *name)
54 FILE *file;
56 file = fopen(name, "w");
57 if (!file)
58 fprintf(stderr, "Failed to open \"%s\" for writing\n", name);
59 return file;
62 /* Open the host .c file and the kernel .h and .cl files for writing.
63 * Their names are derived from info->output (or info->input if
64 * the user did not specify an output file name).
65 * Add the necessary includes to these files, including those specified
66 * by the user.
68 * Return 0 on success and -1 on failure.
70 static int opencl_open_files(struct opencl_info *info)
72 char name[PATH_MAX];
73 int i;
74 int len;
76 if (info->output) {
77 const char *ext;
79 ext = strrchr(info->output, '.');
80 len = ext ? ext - info->output : strlen(info->output);
81 memcpy(name, info->output, len);
83 info->host_c = open_or_croak(info->output);
84 } else {
85 len = ppcg_extract_base_name(name, info->input);
87 strcpy(name + len, "_host.c");
88 info->host_c = open_or_croak(name);
91 memcpy(info->kernel_c_name, name, len);
92 strcpy(info->kernel_c_name + len, "_kernel.cl");
93 info->kernel_c = open_or_croak(info->kernel_c_name);
95 strcpy(name + len, "_kernel.h");
96 info->kernel_h = open_or_croak(name);
98 if (!info->host_c || !info->kernel_c || !info->host_c)
99 return -1;
101 fprintf(info->host_c, "#include <assert.h>\n");
102 fprintf(info->host_c, "#include <stdio.h>\n");
103 fprintf(info->host_c, "#include \"%s\"\n\n", ppcg_base_name(name));
104 fprintf(info->kernel_h, "#if defined(__APPLE__)\n");
105 fprintf(info->kernel_h, "#include <OpenCL/opencl.h>\n");
106 fprintf(info->kernel_h, "#else\n");
107 fprintf(info->kernel_h, "#include <CL/opencl.h>\n");
108 fprintf(info->kernel_h, "#endif\n\n");
109 fprintf(info->kernel_h, "cl_device_id opencl_create_device("
110 "int use_gpu);\n");
111 fprintf(info->kernel_h, "cl_program opencl_build_program("
112 "cl_context ctx, "
113 "cl_device_id dev, const char *filename, "
114 "const char *opencl_options);\n");
115 fprintf(info->kernel_h,
116 "const char *opencl_error_string(cl_int error);\n");
117 for (i = 0; i < info->options->opencl_n_include_file; ++i) {
118 info->kprinter = isl_printer_print_str(info->kprinter,
119 "#include <");
120 info->kprinter = isl_printer_print_str(info->kprinter,
121 info->options->opencl_include_files[i]);
122 info->kprinter = isl_printer_print_str(info->kprinter, ">\n");
125 return 0;
128 /* Write the code that we have accumulated in the kernel isl_printer to the
129 * kernel.cl file.
131 static void opencl_write_kernel_file(struct opencl_info *opencl)
133 char *raw = isl_printer_get_str(opencl->kprinter);
135 fprintf(opencl->kernel_c, "%s", raw);
137 free(raw);
140 /* Close all output files. Write the kernel contents to the kernel file before
141 * closing it.
143 static void opencl_close_files(struct opencl_info *info)
145 if (info->kernel_c) {
146 opencl_write_kernel_file(info);
147 fclose(info->kernel_c);
149 if (info->kernel_h)
150 fclose(info->kernel_h);
151 if (info->host_c)
152 fclose(info->host_c);
155 static __isl_give isl_printer *opencl_print_host_macros(__isl_take isl_printer *p)
157 const char *macros =
158 "#define openclCheckReturn(ret) \\\n"
159 " if (ret != CL_SUCCESS) {\\\n"
160 " fprintf(stderr, \"OpenCL error: %s\\n\", "
161 "opencl_error_string(ret)); \\\n"
162 " fflush(stderr); \\\n"
163 " assert(ret == CL_SUCCESS);\\\n }\n";
165 p = isl_printer_start_line(p);
166 p = isl_printer_print_str(p, macros);
167 p = isl_printer_end_line(p);
169 p = isl_ast_op_type_print_macro(isl_ast_op_max, p);
171 return p;
174 static __isl_give isl_printer *opencl_declare_device_arrays(
175 __isl_take isl_printer *p, struct gpu_prog *prog)
177 int i;
179 for (i = 0; i < prog->n_array; ++i) {
180 if (gpu_array_is_read_only_scalar(&prog->array[i]))
181 continue;
182 p = isl_printer_start_line(p);
183 p = isl_printer_print_str(p, "cl_mem dev_");
184 p = isl_printer_print_str(p, prog->array[i].name);
185 p = isl_printer_print_str(p, ";");
186 p = isl_printer_end_line(p);
188 p = isl_printer_start_line(p);
189 p = isl_printer_end_line(p);
190 return p;
193 /* Given an array, check whether its positive size guard expression is
194 * trivial.
196 static int is_array_positive_size_guard_trivial(struct gpu_array_info *array)
198 isl_set *guard;
199 int is_trivial;
201 guard = gpu_array_positive_size_guard(array);
202 is_trivial = isl_set_plain_is_universe(guard);
203 isl_set_free(guard);
204 return is_trivial;
207 /* Allocate a device array for array and copy the contents to the device
208 * if copy is set.
210 * Emit a max-expression to ensure the device array can contain at least one
211 * element if the array's positive size guard expression is not trivial.
213 static __isl_give isl_printer *allocate_device_array(__isl_take isl_printer *p,
214 struct gpu_array_info *array, int copy)
216 int need_lower_bound;
218 p = ppcg_start_block(p);
220 p = isl_printer_start_line(p);
221 p = isl_printer_print_str(p, "dev_");
222 p = isl_printer_print_str(p, array->name);
223 p = isl_printer_print_str(p, " = clCreateBuffer(context, ");
224 p = isl_printer_print_str(p, "CL_MEM_READ_WRITE");
226 if (!copy)
227 p = isl_printer_print_str(p, ", ");
228 else
229 p = isl_printer_print_str(p, " | CL_MEM_COPY_HOST_PTR, ");
231 need_lower_bound = !is_array_positive_size_guard_trivial(array);
232 if (need_lower_bound) {
233 p = isl_printer_print_str(p, "max(sizeof(");
234 p = isl_printer_print_str(p, array->type);
235 p = isl_printer_print_str(p, "), ");
237 p = gpu_array_info_print_size(p, array);
238 if (need_lower_bound)
239 p = isl_printer_print_str(p, ")");
241 if (!copy)
242 p = isl_printer_print_str(p, ", NULL");
243 else if (gpu_array_is_scalar(array)) {
244 p = isl_printer_print_str(p, ", &");
245 p = isl_printer_print_str(p, array->name);
246 } else {
247 p = isl_printer_print_str(p, ", ");
248 p = isl_printer_print_str(p, array->name);
251 p = isl_printer_print_str(p, ", &err);");
252 p = isl_printer_end_line(p);
253 p = isl_printer_start_line(p);
254 p = isl_printer_print_str(p, "openclCheckReturn(err);");
255 p = isl_printer_end_line(p);
257 p = ppcg_end_block(p);
259 return p;
262 /* Allocate device arrays and copy the contents of copy_in arrays into device.
264 static __isl_give isl_printer *opencl_allocate_device_arrays(
265 __isl_take isl_printer *p, struct gpu_prog *prog)
267 int i, j;
269 for (i = 0; i < prog->n_array; ++i) {
270 struct gpu_array_info *array = &prog->array[i];
271 isl_space *space;
272 isl_set *read_i;
273 int empty;
275 if (gpu_array_is_read_only_scalar(array))
276 continue;
278 space = isl_space_copy(array->space);
279 read_i = isl_union_set_extract_set(prog->copy_in, space);
280 empty = isl_set_plain_is_empty(read_i);
281 isl_set_free(read_i);
283 p = allocate_device_array(p, array, !empty);
285 p = isl_printer_start_line(p);
286 p = isl_printer_end_line(p);
287 return p;
290 /* Print a call to the OpenCL clSetKernelArg() function which sets
291 * the arguments of the kernel. arg_name and arg_index are the name and the
292 * index of the kernel argument. The index of the leftmost argument of
293 * the kernel is 0 whereas the index of the rightmost argument of the kernel
294 * is n - 1, where n is the total number of the kernel arguments.
295 * read_only_scalar is a boolean that indicates whether the argument is a read
296 * only scalar.
298 static __isl_give isl_printer *opencl_set_kernel_argument(
299 __isl_take isl_printer *p, int kernel_id,
300 const char *arg_name, int arg_index, int read_only_scalar)
302 p = isl_printer_start_line(p);
303 p = isl_printer_print_str(p,
304 "openclCheckReturn(clSetKernelArg(kernel");
305 p = isl_printer_print_int(p, kernel_id);
306 p = isl_printer_print_str(p, ", ");
307 p = isl_printer_print_int(p, arg_index);
308 p = isl_printer_print_str(p, ", sizeof(");
310 if (read_only_scalar) {
311 p = isl_printer_print_str(p, arg_name);
312 p = isl_printer_print_str(p, "), &");
313 } else
314 p = isl_printer_print_str(p, "cl_mem), (void *) &dev_");
316 p = isl_printer_print_str(p, arg_name);
317 p = isl_printer_print_str(p, "));");
318 p = isl_printer_end_line(p);
320 return p;
323 /* Print the block sizes as a list of the sizes in each
324 * dimension.
326 static __isl_give isl_printer *opencl_print_block_sizes(
327 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
329 int i;
331 if (kernel->n_block > 0)
332 for (i = 0; i < kernel->n_block; ++i) {
333 if (i)
334 p = isl_printer_print_str(p, ", ");
335 p = isl_printer_print_int(p, kernel->block_dim[i]);
337 else
338 p = isl_printer_print_str(p, "1");
340 return p;
343 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
344 * clSetKernelArg() function for each kernel argument.
346 static __isl_give isl_printer *opencl_set_kernel_arguments(
347 __isl_take isl_printer *p, struct gpu_prog *prog,
348 struct ppcg_kernel *kernel)
350 int i, n, ro;
351 unsigned nparam;
352 isl_space *space;
353 const char *type;
354 int arg_index = 0;
356 for (i = 0; i < prog->n_array; ++i) {
357 isl_set *arr;
358 int empty;
360 space = isl_space_copy(prog->array[i].space);
361 arr = isl_union_set_extract_set(kernel->arrays, space);
362 empty = isl_set_plain_is_empty(arr);
363 isl_set_free(arr);
364 if (empty)
365 continue;
366 ro = gpu_array_is_read_only_scalar(&prog->array[i]);
367 opencl_set_kernel_argument(p, kernel->id, prog->array[i].name,
368 arg_index, ro);
369 arg_index++;
372 space = isl_union_set_get_space(kernel->arrays);
373 nparam = isl_space_dim(space, isl_dim_param);
374 for (i = 0; i < nparam; ++i) {
375 const char *name;
377 name = isl_space_get_dim_name(space, isl_dim_param, i);
378 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
379 arg_index++;
381 isl_space_free(space);
383 n = isl_space_dim(kernel->space, isl_dim_set);
384 for (i = 0; i < n; ++i) {
385 const char *name;
386 isl_id *id;
388 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
389 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
390 arg_index++;
393 return p;
396 /* Print the arguments to a kernel declaration or call. If "types" is set,
397 * then print a declaration (including the types of the arguments).
399 * The arguments are printed in the following order
400 * - the arrays accessed by the kernel
401 * - the parameters
402 * - the host loop iterators
404 static __isl_give isl_printer *opencl_print_kernel_arguments(
405 __isl_take isl_printer *p, struct gpu_prog *prog,
406 struct ppcg_kernel *kernel, int types)
408 int i, n;
409 int first = 1;
410 unsigned nparam;
411 isl_space *space;
412 const char *type;
414 for (i = 0; i < prog->n_array; ++i) {
415 isl_set *arr;
416 int empty;
418 space = isl_space_copy(prog->array[i].space);
419 arr = isl_union_set_extract_set(kernel->arrays, space);
420 empty = isl_set_plain_is_empty(arr);
421 isl_set_free(arr);
422 if (empty)
423 continue;
425 if (!first)
426 p = isl_printer_print_str(p, ", ");
428 if (types)
429 p = gpu_array_info_print_declaration_argument(p,
430 &prog->array[i], "__global");
431 else
432 p = gpu_array_info_print_call_argument(p,
433 &prog->array[i]);
435 first = 0;
438 space = isl_union_set_get_space(kernel->arrays);
439 nparam = isl_space_dim(space, isl_dim_param);
440 for (i = 0; i < nparam; ++i) {
441 const char *name;
443 name = isl_space_get_dim_name(space, isl_dim_param, i);
445 if (!first)
446 p = isl_printer_print_str(p, ", ");
447 if (types)
448 p = isl_printer_print_str(p, "int ");
449 p = isl_printer_print_str(p, name);
451 first = 0;
453 isl_space_free(space);
455 n = isl_space_dim(kernel->space, isl_dim_set);
456 type = isl_options_get_ast_iterator_type(prog->ctx);
457 for (i = 0; i < n; ++i) {
458 const char *name;
459 isl_id *id;
461 if (!first)
462 p = isl_printer_print_str(p, ", ");
463 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
464 if (types) {
465 p = isl_printer_print_str(p, type);
466 p = isl_printer_print_str(p, " ");
468 p = isl_printer_print_str(p, name);
470 first = 0;
473 return p;
476 /* Print the header of the given kernel.
478 static __isl_give isl_printer *opencl_print_kernel_header(
479 __isl_take isl_printer *p, struct gpu_prog *prog,
480 struct ppcg_kernel *kernel)
482 p = isl_printer_start_line(p);
483 p = isl_printer_print_str(p, "__kernel void kernel");
484 p = isl_printer_print_int(p, kernel->id);
485 p = isl_printer_print_str(p, "(");
486 p = opencl_print_kernel_arguments(p, prog, kernel, 1);
487 p = isl_printer_print_str(p, ")");
488 p = isl_printer_end_line(p);
490 return p;
493 /* Unlike the equivalent function in the CUDA backend which prints iterators
494 * in reverse order to promote coalescing, this function does not print
495 * iterators in reverse order. The OpenCL backend currently does not take
496 * into account any coalescing considerations.
498 static __isl_give isl_printer *opencl_print_kernel_iterators(
499 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
501 int i, n_grid;
502 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
503 const char *type;
505 type = isl_options_get_ast_iterator_type(ctx);
507 n_grid = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
508 if (n_grid > 0) {
509 p = isl_printer_start_line(p);
510 p = isl_printer_print_str(p, type);
511 p = isl_printer_print_str(p, " ");
512 for (i = 0; i < n_grid; ++i) {
513 if (i)
514 p = isl_printer_print_str(p, ", ");
515 p = isl_printer_print_str(p, "b");
516 p = isl_printer_print_int(p, i);
517 p = isl_printer_print_str(p, " = get_group_id(");
518 p = isl_printer_print_int(p, i);
519 p = isl_printer_print_str(p, ")");
521 p = isl_printer_print_str(p, ";");
522 p = isl_printer_end_line(p);
525 if (kernel->n_block > 0) {
526 p = isl_printer_start_line(p);
527 p = isl_printer_print_str(p, type);
528 p = isl_printer_print_str(p, " ");
529 for (i = 0; i < kernel->n_block; ++i) {
530 if (i)
531 p = isl_printer_print_str(p, ", ");
532 p = isl_printer_print_str(p, "t");
533 p = isl_printer_print_int(p, i);
534 p = isl_printer_print_str(p, " = get_local_id(");
535 p = isl_printer_print_int(p, i);
536 p = isl_printer_print_str(p, ")");
538 p = isl_printer_print_str(p, ";");
539 p = isl_printer_end_line(p);
542 return p;
545 static __isl_give isl_printer *opencl_print_kernel_var(
546 __isl_take isl_printer *p, struct ppcg_kernel_var *var)
548 int j;
549 isl_val *v;
551 p = isl_printer_start_line(p);
552 if (var->type == ppcg_access_shared)
553 p = isl_printer_print_str(p, "__local ");
554 p = isl_printer_print_str(p, var->array->type);
555 p = isl_printer_print_str(p, " ");
556 p = isl_printer_print_str(p, var->name);
557 for (j = 0; j < var->array->n_index; ++j) {
558 p = isl_printer_print_str(p, "[");
559 v = isl_vec_get_element_val(var->size, j);
560 p = isl_printer_print_val(p, v);
561 p = isl_printer_print_str(p, "]");
562 isl_val_free(v);
564 p = isl_printer_print_str(p, ";");
565 p = isl_printer_end_line(p);
567 return p;
570 static __isl_give isl_printer *opencl_print_kernel_vars(
571 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
573 int i;
575 for (i = 0; i < kernel->n_var; ++i)
576 p = opencl_print_kernel_var(p, &kernel->var[i]);
578 return p;
581 /* Print a call to barrier() which is a sync statement.
582 * All work-items in a work-group executing the kernel on a processor must
583 * execute the barrier() function before any are allowed to continue execution
584 * beyond the barrier.
585 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
586 * variables stored in local memory or queue a memory fence to ensure correct
587 * ordering of memory operations to local memory.
588 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
589 * fence to ensure correct ordering of memory operations to global memory.
591 static __isl_give isl_printer *opencl_print_sync(__isl_take isl_printer *p,
592 struct ppcg_kernel_stmt *stmt)
594 p = isl_printer_start_line(p);
595 p = isl_printer_print_str(p,
596 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
597 p = isl_printer_end_line(p);
599 return p;
602 /* This function is called for each user statement in the AST,
603 * i.e., for each kernel body statement, copy statement or sync statement.
605 static __isl_give isl_printer *opencl_print_kernel_stmt(
606 __isl_take isl_printer *p,
607 __isl_take isl_ast_print_options *print_options,
608 __isl_keep isl_ast_node *node, void *user)
610 isl_id *id;
611 struct ppcg_kernel_stmt *stmt;
613 id = isl_ast_node_get_annotation(node);
614 stmt = isl_id_get_user(id);
615 isl_id_free(id);
617 isl_ast_print_options_free(print_options);
619 switch (stmt->type) {
620 case ppcg_kernel_copy:
621 return ppcg_kernel_print_copy(p, stmt);
622 case ppcg_kernel_sync:
623 return opencl_print_sync(p, stmt);
624 case ppcg_kernel_domain:
625 return ppcg_kernel_print_domain(p, stmt);
628 return p;
631 /* Return true if there is a double array in prog->array or
632 * if any of the types in prog->scop involve any doubles.
633 * To check the latter condition, we simply search for the string "double"
634 * in the type definitions, which may result in false positives.
636 static __isl_give int any_double_elements(struct gpu_prog *prog)
638 int i;
640 for (i = 0; i < prog->n_array; ++i)
641 if (strcmp(prog->array[i].type, "double") == 0)
642 return 1;
644 for (i = 0; i < prog->scop->pet->n_type; ++i) {
645 struct pet_type *type = prog->scop->pet->types[i];
647 if (strstr(type->definition, "double"))
648 return 1;
651 return 0;
654 /* Prints a #pragma to enable support for double floating-point
655 * precision. OpenCL 1.0 adds support for double precision floating-point as
656 * an optional extension. An application that wants to use double will need to
657 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
658 * any double precision data type is declared in the kernel code.
660 static __isl_give isl_printer *opencl_enable_double_support(
661 __isl_take isl_printer *p)
663 int i;
665 p = isl_printer_start_line(p);
666 p = isl_printer_print_str(p, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
667 " enable");
668 p = isl_printer_end_line(p);
669 p = isl_printer_start_line(p);
670 p = isl_printer_end_line(p);
672 return p;
675 static __isl_give isl_printer *opencl_print_kernel(struct gpu_prog *prog,
676 struct ppcg_kernel *kernel, __isl_take isl_printer *p)
678 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
679 isl_ast_print_options *print_options;
681 print_options = isl_ast_print_options_alloc(ctx);
682 print_options = isl_ast_print_options_set_print_user(print_options,
683 &opencl_print_kernel_stmt, NULL);
685 p = isl_printer_set_output_format(p, ISL_FORMAT_C);
686 p = opencl_print_kernel_header(p, prog, kernel);
687 p = isl_printer_print_str(p, "{");
688 p = isl_printer_end_line(p);
689 p = isl_printer_indent(p, 4);
690 p = opencl_print_kernel_iterators(p, kernel);
691 p = opencl_print_kernel_vars(p, kernel);
692 p = isl_printer_end_line(p);
693 p = gpu_print_macros(p, kernel->tree);
694 p = isl_ast_node_print(kernel->tree, p, print_options);
695 p = isl_printer_indent(p, -4);
696 p = isl_printer_start_line(p);
697 p = isl_printer_print_str(p, "}");
698 p = isl_printer_end_line(p);
700 return p;
703 struct print_host_user_data_opencl {
704 struct opencl_info *opencl;
705 struct gpu_prog *prog;
708 /* This function prints the i'th block size multiplied by the i'th grid size,
709 * where i (a parameter to this function) is one of the possible dimensions of
710 * grid sizes and block sizes.
711 * If the dimension of block sizes is not equal to the dimension of grid sizes
712 * the output is calculated as follows:
714 * Suppose that:
715 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
716 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
718 * The output is:
719 * If (i > dim2) then the output is block_sizes[i]
720 * If (i > dim1) then the output is grid_sizes[i]
722 static __isl_give isl_printer *opencl_print_total_number_of_work_items_for_dim(
723 __isl_take isl_printer *p, struct ppcg_kernel *kernel, int i)
725 int grid_dim, block_dim;
727 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
728 block_dim = kernel->n_block;
730 isl_pw_aff *bound_grid;
732 if (i < min(grid_dim, block_dim)) {
733 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
734 p = isl_printer_print_str(p, "(");
735 p = isl_printer_print_pw_aff(p, bound_grid);
736 p = isl_printer_print_str(p, ") * ");
737 p = isl_printer_print_int(p, kernel->block_dim[i]);
738 isl_pw_aff_free(bound_grid);
739 } else if (i >= grid_dim)
740 p = isl_printer_print_int(p, kernel->block_dim[i]);
741 else {
742 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
743 p = isl_printer_print_pw_aff(p, bound_grid);
744 isl_pw_aff_free(bound_grid);
747 return p;
750 /* Print a list that represents the total number of work items. The list is
751 * constructed by performing an element-wise multiplication of the block sizes
752 * and the grid sizes. To explain how the list is constructed, suppose that:
753 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
754 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
756 * The output of this function is constructed as follows:
757 * If (dim1 > dim2) then the output is the following list:
758 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
759 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
761 * If (dim2 > dim1) then the output is the following list:
762 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
763 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
765 * To calculate the total number of work items out of the list constructed by
766 * this function, the user should multiply the elements of the list.
768 static __isl_give isl_printer *opencl_print_total_number_of_work_items_as_list(
769 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
771 int i;
772 int grid_dim, block_dim;
774 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
775 block_dim = kernel->n_block;
777 if ((grid_dim <= 0) || (block_dim <= 0)) {
778 p = isl_printer_print_str(p, "1");
779 return p;
782 for (i = 0; i <= max(grid_dim, block_dim) - 1; i++) {
783 if (i > 0)
784 p = isl_printer_print_str(p, ", ");
786 p = opencl_print_total_number_of_work_items_for_dim(p,
787 kernel, i);
790 return p;
793 /* Print the user statement of the host code to "p".
795 * In particular, print a block of statements that defines the grid
796 * and the work group and then launches the kernel.
798 * A grid is composed of many work groups (blocks), each work group holds
799 * many work-items (threads).
801 * global_work_size[kernel->n_block] represents the total number of work
802 * items. It points to an array of kernel->n_block unsigned
803 * values that describe the total number of work-items that will execute
804 * the kernel. The total number of work-items is computed as:
805 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
807 * The size of each work group (i.e. the number of work-items in each work
808 * group) is described using block_size[kernel->n_block]. The total
809 * number of work-items in a block (work-group) is computed as:
810 * block_size[0] *... * block_size[kernel->n_block - 1].
812 * For more information check:
813 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
815 static __isl_give isl_printer *opencl_print_host_user(
816 __isl_take isl_printer *p,
817 __isl_take isl_ast_print_options *print_options,
818 __isl_keep isl_ast_node *node, void *user)
820 isl_id *id;
821 struct ppcg_kernel *kernel;
822 struct print_host_user_data_opencl *data;
823 int i;
825 id = isl_ast_node_get_annotation(node);
826 kernel = isl_id_get_user(id);
827 isl_id_free(id);
829 data = (struct print_host_user_data_opencl *) user;
831 p = isl_printer_start_line(p);
832 p = isl_printer_print_str(p, "{");
833 p = isl_printer_end_line(p);
834 p = isl_printer_indent(p, 2);
836 p = isl_printer_start_line(p);
837 p = isl_printer_print_str(p, "size_t global_work_size[");
839 if (kernel->n_block > 0)
840 p = isl_printer_print_int(p, kernel->n_block);
841 else
842 p = isl_printer_print_int(p, 1);
844 p = isl_printer_print_str(p, "] = {");
845 p = opencl_print_total_number_of_work_items_as_list(p, kernel);
846 p = isl_printer_print_str(p, "};");
847 p = isl_printer_end_line(p);
849 p = isl_printer_start_line(p);
850 p = isl_printer_print_str(p, "size_t block_size[");
852 if (kernel->n_block > 0)
853 p = isl_printer_print_int(p, kernel->n_block);
854 else
855 p = isl_printer_print_int(p, 1);
857 p = isl_printer_print_str(p, "] = {");
858 p = opencl_print_block_sizes(p, kernel);
859 p = isl_printer_print_str(p, "};");
860 p = isl_printer_end_line(p);
862 p = isl_printer_start_line(p);
863 p = isl_printer_print_str(p, "cl_kernel kernel");
864 p = isl_printer_print_int(p, kernel->id);
865 p = isl_printer_print_str(p, " = clCreateKernel(program, \"kernel");
866 p = isl_printer_print_int(p, kernel->id);
867 p = isl_printer_print_str(p, "\", &err);");
868 p = isl_printer_end_line(p);
869 p = isl_printer_start_line(p);
870 p = isl_printer_print_str(p, "openclCheckReturn(err);");
871 p = isl_printer_end_line(p);
873 opencl_set_kernel_arguments(p, data->prog, kernel);
875 p = isl_printer_start_line(p);
876 p = isl_printer_print_str(p, "openclCheckReturn(clEnqueueNDRangeKernel"
877 "(queue, kernel");
878 p = isl_printer_print_int(p, kernel->id);
879 p = isl_printer_print_str(p, ", ");
880 if (kernel->n_block > 0)
881 p = isl_printer_print_int(p, kernel->n_block);
882 else
883 p = isl_printer_print_int(p, 1);
885 p = isl_printer_print_str(p, ", NULL, global_work_size, "
886 "block_size, "
887 "0, NULL, NULL));");
888 p = isl_printer_end_line(p);
889 p = isl_printer_start_line(p);
890 p = isl_printer_print_str(p, "openclCheckReturn("
891 "clReleaseKernel(kernel");
892 p = isl_printer_print_int(p, kernel->id);
893 p = isl_printer_print_str(p, "));");
894 p = isl_printer_end_line(p);
895 p = isl_printer_start_line(p);
896 p = isl_printer_print_str(p, "clFinish(queue);");
897 p = isl_printer_end_line(p);
898 p = isl_printer_indent(p, -2);
899 p = isl_printer_start_line(p);
900 p = isl_printer_print_str(p, "}");
901 p = isl_printer_end_line(p);
903 p = isl_printer_start_line(p);
904 p = isl_printer_end_line(p);
906 data->opencl->kprinter = opencl_print_kernel(data->prog, kernel,
907 data->opencl->kprinter);
909 isl_ast_print_options_free(print_options);
911 return p;
914 static __isl_give isl_printer *opencl_print_host_code(
915 __isl_take isl_printer *p, struct gpu_prog *prog,
916 __isl_keep isl_ast_node *tree, struct opencl_info *opencl)
918 isl_ast_print_options *print_options;
919 isl_ctx *ctx = isl_ast_node_get_ctx(tree);
920 struct print_host_user_data_opencl data = { opencl, prog };
922 print_options = isl_ast_print_options_alloc(ctx);
923 print_options = isl_ast_print_options_set_print_user(print_options,
924 &opencl_print_host_user, &data);
926 p = gpu_print_macros(p, tree);
927 p = isl_ast_node_print(tree, p, print_options);
929 return p;
932 /* Copy "array" back from the GPU to the host.
934 static __isl_give isl_printer *copy_array_from_device(__isl_take isl_printer *p,
935 void *user)
937 struct gpu_array_info *array = user;
939 p = isl_printer_start_line(p);
940 p = isl_printer_print_str(p, "openclCheckReturn("
941 "clEnqueueReadBuffer(queue,"
942 " dev_");
943 p = isl_printer_print_str(p, array->name);
944 p = isl_printer_print_str(p, ", CL_TRUE, 0, ");
945 p = gpu_array_info_print_size(p, array);
947 if (gpu_array_is_scalar(array))
948 p = isl_printer_print_str(p, ", &");
949 else
950 p = isl_printer_print_str(p, ", ");
951 p = isl_printer_print_str(p, array->name);
952 p = isl_printer_print_str(p, ", 0, NULL, NULL));");
953 p = isl_printer_end_line(p);
955 return p;
958 /* Copy copy_out arrays back from the GPU to the host.
960 * Only perform the copying for arrays with strictly positive size.
962 static __isl_give isl_printer *opencl_copy_arrays_from_device(
963 __isl_take isl_printer *p, struct gpu_prog *prog)
965 int i;
966 isl_union_set *copy_out;
967 copy_out = isl_union_set_copy(prog->copy_out);
969 for (i = 0; i < prog->n_array; ++i) {
970 struct gpu_array_info *array = &prog->array[i];
971 isl_space *space;
972 isl_set *copy_out_i;
973 isl_set *guard;
974 int empty;
976 space = isl_space_copy(array->space);
977 copy_out_i = isl_union_set_extract_set(copy_out, space);
978 empty = isl_set_plain_is_empty(copy_out_i);
979 isl_set_free(copy_out_i);
980 if (empty)
981 continue;
983 guard = gpu_array_positive_size_guard(array);
984 p = ppcg_print_guarded(p, guard, isl_set_copy(prog->context),
985 &copy_array_from_device, array);
988 isl_union_set_free(copy_out);
989 p = isl_printer_start_line(p);
990 p = isl_printer_end_line(p);
991 return p;
994 /* Create an OpenCL device, context, command queue and build the kernel.
995 * input is the name of the input file provided to ppcg.
997 static __isl_give isl_printer *opencl_setup(__isl_take isl_printer *p,
998 const char *input, struct opencl_info *info)
1000 int len;
1002 p = isl_printer_start_line(p);
1003 p = isl_printer_print_str(p, "cl_device_id device;");
1004 p = isl_printer_end_line(p);
1005 p = isl_printer_start_line(p);
1006 p = isl_printer_print_str(p, "cl_context context;");
1007 p = isl_printer_end_line(p);
1008 p = isl_printer_start_line(p);
1009 p = isl_printer_print_str(p, "cl_program program;");
1010 p = isl_printer_end_line(p);
1011 p = isl_printer_start_line(p);
1012 p = isl_printer_print_str(p, "cl_command_queue queue;");
1013 p = isl_printer_end_line(p);
1014 p = isl_printer_start_line(p);
1015 p = isl_printer_print_str(p, "cl_int err;");
1016 p = isl_printer_end_line(p);
1017 p = isl_printer_start_line(p);
1018 p = isl_printer_print_str(p, "device = opencl_create_device(");
1019 p = isl_printer_print_int(p, info->options->opencl_use_gpu);
1020 p = isl_printer_print_str(p, ");");
1021 p = isl_printer_end_line(p);
1022 p = isl_printer_start_line(p);
1023 p = isl_printer_print_str(p, "context = clCreateContext(NULL, 1, "
1024 "&device, NULL, NULL, &err);");
1025 p = isl_printer_end_line(p);
1026 p = isl_printer_start_line(p);
1027 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1028 p = isl_printer_end_line(p);
1029 p = isl_printer_start_line(p);
1030 p = isl_printer_print_str(p, "queue = clCreateCommandQueue"
1031 "(context, device, 0, &err);");
1032 p = isl_printer_end_line(p);
1033 p = isl_printer_start_line(p);
1034 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1035 p = isl_printer_end_line(p);
1037 p = isl_printer_start_line(p);
1038 p = isl_printer_print_str(p, "program = opencl_build_program("
1039 "context, device, \"");
1040 p = isl_printer_print_str(p, info->kernel_c_name);
1041 p = isl_printer_print_str(p, "\", \"");
1043 if (info->options->opencl_compiler_options)
1044 p = isl_printer_print_str(p,
1045 info->options->opencl_compiler_options);
1047 p = isl_printer_print_str(p, "\");");
1048 p = isl_printer_end_line(p);
1049 p = isl_printer_start_line(p);
1050 p = isl_printer_end_line(p);
1052 return p;
1055 static __isl_give isl_printer *opencl_release_cl_objects(
1056 __isl_take isl_printer *p, struct opencl_info *info)
1058 p = isl_printer_start_line(p);
1059 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseCommandQueue"
1060 "(queue));");
1061 p = isl_printer_end_line(p);
1062 p = isl_printer_start_line(p);
1063 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseProgram"
1064 "(program));");
1065 p = isl_printer_end_line(p);
1066 p = isl_printer_start_line(p);
1067 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseContext"
1068 "(context));");
1069 p = isl_printer_end_line(p);
1071 return p;
1074 /* Free the device array corresponding to "array"
1076 static __isl_give isl_printer *release_device_array(__isl_take isl_printer *p,
1077 struct gpu_array_info *array)
1079 p = isl_printer_start_line(p);
1080 p = isl_printer_print_str(p, "openclCheckReturn("
1081 "clReleaseMemObject(dev_");
1082 p = isl_printer_print_str(p, array->name);
1083 p = isl_printer_print_str(p, "));");
1084 p = isl_printer_end_line(p);
1086 return p;
1089 /* Free the device arrays.
1091 static __isl_give isl_printer *opencl_release_device_arrays(
1092 __isl_take isl_printer *p, struct gpu_prog *prog)
1094 int i, j;
1096 for (i = 0; i < prog->n_array; ++i) {
1097 struct gpu_array_info *array = &prog->array[i];
1098 if (gpu_array_is_read_only_scalar(array))
1099 continue;
1101 p = release_device_array(p, array);
1103 return p;
1106 /* Given a gpu_prog "prog" and the corresponding transformed AST
1107 * "tree", print the entire OpenCL code to "p".
1109 static __isl_give isl_printer *print_opencl(__isl_take isl_printer *p,
1110 struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
1111 struct gpu_types *types, void *user)
1113 struct opencl_info *opencl = user;
1115 opencl->kprinter = isl_printer_set_output_format(opencl->kprinter,
1116 ISL_FORMAT_C);
1117 if (any_double_elements(prog))
1118 opencl->kprinter = opencl_enable_double_support(
1119 opencl->kprinter);
1120 opencl->kprinter = gpu_print_types(opencl->kprinter, types, prog);
1122 if (!opencl->kprinter)
1123 return isl_printer_free(p);
1125 p = ppcg_start_block(p);
1127 p = opencl_print_host_macros(p);
1129 p = opencl_declare_device_arrays(p, prog);
1130 p = opencl_setup(p, opencl->input, opencl);
1131 p = opencl_allocate_device_arrays(p, prog);
1133 p = opencl_print_host_code(p, prog, tree, opencl);
1135 p = opencl_copy_arrays_from_device(p, prog);
1136 p = opencl_release_device_arrays(p, prog);
1137 p = opencl_release_cl_objects(p, opencl);
1139 p = ppcg_end_block(p);
1141 return p;
1144 /* Transform the code in the file called "input" by replacing
1145 * all scops by corresponding OpenCL code.
1146 * The host code is written to "output" or a name derived from
1147 * "input" if "output" is NULL.
1148 * The kernel code is placed in separate files with names
1149 * derived from "output" or "input".
1151 * We let generate_gpu do all the hard work and then let it call
1152 * us back for printing the AST in print_cuda.
1154 * To prepare for this printing, we first open the output files
1155 * and we close them after generate_gpu has finished.
1157 int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
1158 const char *input, const char *output)
1160 struct opencl_info opencl = { options, input, output };
1161 int r;
1163 opencl.kprinter = isl_printer_to_str(ctx);
1164 r = opencl_open_files(&opencl);
1166 if (r >= 0)
1167 r = generate_gpu(ctx, input, opencl.host_c, options,
1168 &print_opencl, &opencl);
1170 opencl_close_files(&opencl);
1171 isl_printer_free(opencl.kprinter);
1173 return r;