opencl.c: move up opencl_copy_arrays_from_device
[ppcg.git] / opencl.c
blobdc5bdb5e93c0932cf34d1e3208725183041f874e
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 if (info->options->opencl_embed_kernel_code) {
105 fprintf(info->host_c, "#include \"%s\"\n\n",
106 info->kernel_c_name);
109 fprintf(info->kernel_h, "#if defined(__APPLE__)\n");
110 fprintf(info->kernel_h, "#include <OpenCL/opencl.h>\n");
111 fprintf(info->kernel_h, "#else\n");
112 fprintf(info->kernel_h, "#include <CL/opencl.h>\n");
113 fprintf(info->kernel_h, "#endif\n\n");
114 fprintf(info->kernel_h, "cl_device_id opencl_create_device("
115 "int use_gpu);\n");
116 fprintf(info->kernel_h, "cl_program opencl_build_program_from_string("
117 "cl_context ctx, "
118 "cl_device_id dev, const char *program_source, "
119 "size_t program_size, "
120 "const char *opencl_options);\n");
121 fprintf(info->kernel_h, "cl_program opencl_build_program_from_file("
122 "cl_context ctx, "
123 "cl_device_id dev, const char *filename, "
124 "const char *opencl_options);\n");
125 fprintf(info->kernel_h,
126 "const char *opencl_error_string(cl_int error);\n");
127 for (i = 0; i < info->options->opencl_n_include_file; ++i) {
128 info->kprinter = isl_printer_print_str(info->kprinter,
129 "#include <");
130 info->kprinter = isl_printer_print_str(info->kprinter,
131 info->options->opencl_include_files[i]);
132 info->kprinter = isl_printer_print_str(info->kprinter, ">\n");
135 return 0;
138 /* Write text to a file and escape some special characters that would break a
139 * C string.
141 static void opencl_print_escaped(const char *str, const char *end, FILE *file)
143 const char *prev = str;
145 while ((str = strpbrk(prev, "\"\\")) && str < end) {
146 fwrite(prev, 1, str - prev, file);
147 fprintf(file, "\\%c", *str);
148 prev = str + 1;
151 if (*prev)
152 fwrite(prev, 1, end - prev, file);
155 /* Write text to a file as a C string literal.
157 * This function also prints any characters after the last newline, although
158 * normally the input string should end with a newline.
160 static void opencl_print_as_c_string(const char *str, FILE *file)
162 const char *prev = str;
164 while ((str = strchr(prev, '\n'))) {
165 fprintf(file, "\n\"");
166 opencl_print_escaped(prev, str, file);
167 fprintf(file, "\\n\"");
169 prev = str + 1;
172 if (*prev) {
173 fprintf(file, "\n\"");
174 opencl_print_escaped(prev, prev + strlen(prev), file);
175 fprintf(file, "\"");
179 /* Write the code that we have accumulated in the kernel isl_printer to the
180 * kernel.cl file. If the opencl_embed_kernel_code option has been set, print
181 * the code as a C string literal. Start that string literal with an empty
182 * line, such that line numbers reported by the OpenCL C compiler match those
183 * of the kernel file.
185 static void opencl_write_kernel_file(struct opencl_info *opencl)
187 char *raw = isl_printer_get_str(opencl->kprinter);
189 if (opencl->options->opencl_embed_kernel_code) {
190 fprintf(opencl->kernel_c,
191 "static const char kernel_code[] = \"\\n\"");
192 opencl_print_as_c_string(raw, opencl->kernel_c);
193 fprintf(opencl->kernel_c, ";\n");
194 } else
195 fprintf(opencl->kernel_c, "%s", raw);
197 free(raw);
200 /* Close all output files. Write the kernel contents to the kernel file before
201 * closing it.
203 static void opencl_close_files(struct opencl_info *info)
205 if (info->kernel_c) {
206 opencl_write_kernel_file(info);
207 fclose(info->kernel_c);
209 if (info->kernel_h)
210 fclose(info->kernel_h);
211 if (info->host_c)
212 fclose(info->host_c);
215 static __isl_give isl_printer *opencl_print_host_macros(
216 __isl_take isl_printer *p)
218 const char *macros =
219 "#define openclCheckReturn(ret) \\\n"
220 " if (ret != CL_SUCCESS) {\\\n"
221 " fprintf(stderr, \"OpenCL error: %s\\n\", "
222 "opencl_error_string(ret)); \\\n"
223 " fflush(stderr); \\\n"
224 " assert(ret == CL_SUCCESS);\\\n }\n";
226 p = isl_printer_start_line(p);
227 p = isl_printer_print_str(p, macros);
228 p = isl_printer_end_line(p);
230 p = isl_ast_op_type_print_macro(isl_ast_op_max, p);
232 return p;
235 static __isl_give isl_printer *opencl_declare_device_arrays(
236 __isl_take isl_printer *p, struct gpu_prog *prog)
238 int i;
240 for (i = 0; i < prog->n_array; ++i) {
241 if (gpu_array_is_read_only_scalar(&prog->array[i]))
242 continue;
243 p = isl_printer_start_line(p);
244 p = isl_printer_print_str(p, "cl_mem dev_");
245 p = isl_printer_print_str(p, prog->array[i].name);
246 p = isl_printer_print_str(p, ";");
247 p = isl_printer_end_line(p);
249 p = isl_printer_start_line(p);
250 p = isl_printer_end_line(p);
251 return p;
254 /* Given an array, check whether its positive size guard expression is
255 * trivial.
257 static int is_array_positive_size_guard_trivial(struct gpu_array_info *array)
259 isl_set *guard;
260 int is_trivial;
262 guard = gpu_array_positive_size_guard(array);
263 is_trivial = isl_set_plain_is_universe(guard);
264 isl_set_free(guard);
265 return is_trivial;
268 /* Allocate a device array for array and copy the contents to the device
269 * if copy is set.
271 * Emit a max-expression to ensure the device array can contain at least one
272 * element if the array's positive size guard expression is not trivial.
274 static __isl_give isl_printer *allocate_device_array(__isl_take isl_printer *p,
275 struct gpu_array_info *array, int copy)
277 int need_lower_bound;
279 p = ppcg_start_block(p);
281 p = isl_printer_start_line(p);
282 p = isl_printer_print_str(p, "dev_");
283 p = isl_printer_print_str(p, array->name);
284 p = isl_printer_print_str(p, " = clCreateBuffer(context, ");
285 p = isl_printer_print_str(p, "CL_MEM_READ_WRITE");
287 if (!copy)
288 p = isl_printer_print_str(p, ", ");
289 else
290 p = isl_printer_print_str(p, " | CL_MEM_COPY_HOST_PTR, ");
292 need_lower_bound = !is_array_positive_size_guard_trivial(array);
293 if (need_lower_bound) {
294 p = isl_printer_print_str(p, "max(sizeof(");
295 p = isl_printer_print_str(p, array->type);
296 p = isl_printer_print_str(p, "), ");
298 p = gpu_array_info_print_size(p, array);
299 if (need_lower_bound)
300 p = isl_printer_print_str(p, ")");
302 if (!copy)
303 p = isl_printer_print_str(p, ", NULL");
304 else if (gpu_array_is_scalar(array)) {
305 p = isl_printer_print_str(p, ", &");
306 p = isl_printer_print_str(p, array->name);
307 } else {
308 p = isl_printer_print_str(p, ", ");
309 p = isl_printer_print_str(p, array->name);
312 p = isl_printer_print_str(p, ", &err);");
313 p = isl_printer_end_line(p);
314 p = isl_printer_start_line(p);
315 p = isl_printer_print_str(p, "openclCheckReturn(err);");
316 p = isl_printer_end_line(p);
318 p = ppcg_end_block(p);
320 return p;
323 /* Allocate device arrays and copy the contents of copy_in arrays into device.
325 static __isl_give isl_printer *opencl_allocate_device_arrays(
326 __isl_take isl_printer *p, struct gpu_prog *prog)
328 int i;
330 for (i = 0; i < prog->n_array; ++i) {
331 struct gpu_array_info *array = &prog->array[i];
332 isl_space *space;
333 isl_set *read_i;
334 int empty;
336 if (gpu_array_is_read_only_scalar(array))
337 continue;
339 space = isl_space_copy(array->space);
340 read_i = isl_union_set_extract_set(prog->copy_in, space);
341 empty = isl_set_plain_is_empty(read_i);
342 isl_set_free(read_i);
344 p = allocate_device_array(p, array, !empty);
346 p = isl_printer_start_line(p);
347 p = isl_printer_end_line(p);
348 return p;
351 /* Print a call to the OpenCL clSetKernelArg() function which sets
352 * the arguments of the kernel. arg_name and arg_index are the name and the
353 * index of the kernel argument. The index of the leftmost argument of
354 * the kernel is 0 whereas the index of the rightmost argument of the kernel
355 * is n - 1, where n is the total number of the kernel arguments.
356 * read_only_scalar is a boolean that indicates whether the argument is a read
357 * only scalar.
359 static __isl_give isl_printer *opencl_set_kernel_argument(
360 __isl_take isl_printer *p, int kernel_id,
361 const char *arg_name, int arg_index, int read_only_scalar)
363 p = isl_printer_start_line(p);
364 p = isl_printer_print_str(p,
365 "openclCheckReturn(clSetKernelArg(kernel");
366 p = isl_printer_print_int(p, kernel_id);
367 p = isl_printer_print_str(p, ", ");
368 p = isl_printer_print_int(p, arg_index);
369 p = isl_printer_print_str(p, ", sizeof(");
371 if (read_only_scalar) {
372 p = isl_printer_print_str(p, arg_name);
373 p = isl_printer_print_str(p, "), &");
374 } else
375 p = isl_printer_print_str(p, "cl_mem), (void *) &dev_");
377 p = isl_printer_print_str(p, arg_name);
378 p = isl_printer_print_str(p, "));");
379 p = isl_printer_end_line(p);
381 return p;
384 /* Print the block sizes as a list of the sizes in each
385 * dimension.
387 static __isl_give isl_printer *opencl_print_block_sizes(
388 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
390 int i;
392 if (kernel->n_block > 0)
393 for (i = 0; i < kernel->n_block; ++i) {
394 if (i)
395 p = isl_printer_print_str(p, ", ");
396 p = isl_printer_print_int(p, kernel->block_dim[i]);
398 else
399 p = isl_printer_print_str(p, "1");
401 return p;
404 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
405 * clSetKernelArg() function for each kernel argument.
407 static __isl_give isl_printer *opencl_set_kernel_arguments(
408 __isl_take isl_printer *p, struct gpu_prog *prog,
409 struct ppcg_kernel *kernel)
411 int i, n, ro;
412 unsigned nparam;
413 isl_space *space;
414 const char *type;
415 int arg_index = 0;
417 for (i = 0; i < prog->n_array; ++i) {
418 isl_set *arr;
419 int empty;
421 space = isl_space_copy(prog->array[i].space);
422 arr = isl_union_set_extract_set(kernel->arrays, space);
423 empty = isl_set_plain_is_empty(arr);
424 isl_set_free(arr);
425 if (empty)
426 continue;
427 ro = gpu_array_is_read_only_scalar(&prog->array[i]);
428 opencl_set_kernel_argument(p, kernel->id, prog->array[i].name,
429 arg_index, ro);
430 arg_index++;
433 space = isl_union_set_get_space(kernel->arrays);
434 nparam = isl_space_dim(space, isl_dim_param);
435 for (i = 0; i < nparam; ++i) {
436 const char *name;
438 name = isl_space_get_dim_name(space, isl_dim_param, i);
439 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
440 arg_index++;
442 isl_space_free(space);
444 n = isl_space_dim(kernel->space, isl_dim_set);
445 for (i = 0; i < n; ++i) {
446 const char *name;
447 isl_id *id;
449 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
450 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
451 arg_index++;
454 return p;
457 /* Print the arguments to a kernel declaration or call. If "types" is set,
458 * then print a declaration (including the types of the arguments).
460 * The arguments are printed in the following order
461 * - the arrays accessed by the kernel
462 * - the parameters
463 * - the host loop iterators
465 static __isl_give isl_printer *opencl_print_kernel_arguments(
466 __isl_take isl_printer *p, struct gpu_prog *prog,
467 struct ppcg_kernel *kernel, int types)
469 int i, n;
470 int first = 1;
471 unsigned nparam;
472 isl_space *space;
473 const char *type;
475 for (i = 0; i < prog->n_array; ++i) {
476 isl_set *arr;
477 int empty;
479 space = isl_space_copy(prog->array[i].space);
480 arr = isl_union_set_extract_set(kernel->arrays, space);
481 empty = isl_set_plain_is_empty(arr);
482 isl_set_free(arr);
483 if (empty)
484 continue;
486 if (!first)
487 p = isl_printer_print_str(p, ", ");
489 if (types)
490 p = gpu_array_info_print_declaration_argument(p,
491 &prog->array[i], "__global");
492 else
493 p = gpu_array_info_print_call_argument(p,
494 &prog->array[i]);
496 first = 0;
499 space = isl_union_set_get_space(kernel->arrays);
500 nparam = isl_space_dim(space, isl_dim_param);
501 for (i = 0; i < nparam; ++i) {
502 const char *name;
504 name = isl_space_get_dim_name(space, isl_dim_param, i);
506 if (!first)
507 p = isl_printer_print_str(p, ", ");
508 if (types)
509 p = isl_printer_print_str(p, "int ");
510 p = isl_printer_print_str(p, name);
512 first = 0;
514 isl_space_free(space);
516 n = isl_space_dim(kernel->space, isl_dim_set);
517 type = isl_options_get_ast_iterator_type(prog->ctx);
518 for (i = 0; i < n; ++i) {
519 const char *name;
520 isl_id *id;
522 if (!first)
523 p = isl_printer_print_str(p, ", ");
524 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
525 if (types) {
526 p = isl_printer_print_str(p, type);
527 p = isl_printer_print_str(p, " ");
529 p = isl_printer_print_str(p, name);
531 first = 0;
534 return p;
537 /* Print the header of the given kernel.
539 static __isl_give isl_printer *opencl_print_kernel_header(
540 __isl_take isl_printer *p, struct gpu_prog *prog,
541 struct ppcg_kernel *kernel)
543 p = isl_printer_start_line(p);
544 p = isl_printer_print_str(p, "__kernel void kernel");
545 p = isl_printer_print_int(p, kernel->id);
546 p = isl_printer_print_str(p, "(");
547 p = opencl_print_kernel_arguments(p, prog, kernel, 1);
548 p = isl_printer_print_str(p, ")");
549 p = isl_printer_end_line(p);
551 return p;
554 /* Unlike the equivalent function in the CUDA backend which prints iterators
555 * in reverse order to promote coalescing, this function does not print
556 * iterators in reverse order. The OpenCL backend currently does not take
557 * into account any coalescing considerations.
559 static __isl_give isl_printer *opencl_print_kernel_iterators(
560 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
562 int i, n_grid;
563 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
564 const char *type;
566 type = isl_options_get_ast_iterator_type(ctx);
568 n_grid = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
569 if (n_grid > 0) {
570 p = isl_printer_start_line(p);
571 p = isl_printer_print_str(p, type);
572 p = isl_printer_print_str(p, " ");
573 for (i = 0; i < n_grid; ++i) {
574 if (i)
575 p = isl_printer_print_str(p, ", ");
576 p = isl_printer_print_str(p, "b");
577 p = isl_printer_print_int(p, i);
578 p = isl_printer_print_str(p, " = get_group_id(");
579 p = isl_printer_print_int(p, i);
580 p = isl_printer_print_str(p, ")");
582 p = isl_printer_print_str(p, ";");
583 p = isl_printer_end_line(p);
586 if (kernel->n_block > 0) {
587 p = isl_printer_start_line(p);
588 p = isl_printer_print_str(p, type);
589 p = isl_printer_print_str(p, " ");
590 for (i = 0; i < kernel->n_block; ++i) {
591 if (i)
592 p = isl_printer_print_str(p, ", ");
593 p = isl_printer_print_str(p, "t");
594 p = isl_printer_print_int(p, i);
595 p = isl_printer_print_str(p, " = get_local_id(");
596 p = isl_printer_print_int(p, i);
597 p = isl_printer_print_str(p, ")");
599 p = isl_printer_print_str(p, ";");
600 p = isl_printer_end_line(p);
603 return p;
606 static __isl_give isl_printer *opencl_print_kernel_var(
607 __isl_take isl_printer *p, struct ppcg_kernel_var *var)
609 int j;
610 isl_val *v;
612 p = isl_printer_start_line(p);
613 if (var->type == ppcg_access_shared)
614 p = isl_printer_print_str(p, "__local ");
615 p = isl_printer_print_str(p, var->array->type);
616 p = isl_printer_print_str(p, " ");
617 p = isl_printer_print_str(p, var->name);
618 for (j = 0; j < var->array->n_index; ++j) {
619 p = isl_printer_print_str(p, "[");
620 v = isl_vec_get_element_val(var->size, j);
621 p = isl_printer_print_val(p, v);
622 p = isl_printer_print_str(p, "]");
623 isl_val_free(v);
625 p = isl_printer_print_str(p, ";");
626 p = isl_printer_end_line(p);
628 return p;
631 static __isl_give isl_printer *opencl_print_kernel_vars(
632 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
634 int i;
636 for (i = 0; i < kernel->n_var; ++i)
637 p = opencl_print_kernel_var(p, &kernel->var[i]);
639 return p;
642 /* Print a call to barrier() which is a sync statement.
643 * All work-items in a work-group executing the kernel on a processor must
644 * execute the barrier() function before any are allowed to continue execution
645 * beyond the barrier.
646 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
647 * variables stored in local memory or queue a memory fence to ensure correct
648 * ordering of memory operations to local memory.
649 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
650 * fence to ensure correct ordering of memory operations to global memory.
652 static __isl_give isl_printer *opencl_print_sync(__isl_take isl_printer *p,
653 struct ppcg_kernel_stmt *stmt)
655 p = isl_printer_start_line(p);
656 p = isl_printer_print_str(p,
657 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
658 p = isl_printer_end_line(p);
660 return p;
663 /* This function is called for each user statement in the AST,
664 * i.e., for each kernel body statement, copy statement or sync statement.
666 static __isl_give isl_printer *opencl_print_kernel_stmt(
667 __isl_take isl_printer *p,
668 __isl_take isl_ast_print_options *print_options,
669 __isl_keep isl_ast_node *node, void *user)
671 isl_id *id;
672 struct ppcg_kernel_stmt *stmt;
674 id = isl_ast_node_get_annotation(node);
675 stmt = isl_id_get_user(id);
676 isl_id_free(id);
678 isl_ast_print_options_free(print_options);
680 switch (stmt->type) {
681 case ppcg_kernel_copy:
682 return ppcg_kernel_print_copy(p, stmt);
683 case ppcg_kernel_sync:
684 return opencl_print_sync(p, stmt);
685 case ppcg_kernel_domain:
686 return ppcg_kernel_print_domain(p, stmt);
689 return p;
692 /* Return true if there is a double array in prog->array or
693 * if any of the types in prog->scop involve any doubles.
694 * To check the latter condition, we simply search for the string "double"
695 * in the type definitions, which may result in false positives.
697 static __isl_give int any_double_elements(struct gpu_prog *prog)
699 int i;
701 for (i = 0; i < prog->n_array; ++i)
702 if (strcmp(prog->array[i].type, "double") == 0)
703 return 1;
705 for (i = 0; i < prog->scop->pet->n_type; ++i) {
706 struct pet_type *type = prog->scop->pet->types[i];
708 if (strstr(type->definition, "double"))
709 return 1;
712 return 0;
715 /* Prints a #pragma to enable support for double floating-point
716 * precision. OpenCL 1.0 adds support for double precision floating-point as
717 * an optional extension. An application that wants to use double will need to
718 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
719 * any double precision data type is declared in the kernel code.
721 static __isl_give isl_printer *opencl_enable_double_support(
722 __isl_take isl_printer *p)
724 int i;
726 p = isl_printer_start_line(p);
727 p = isl_printer_print_str(p, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
728 " enable");
729 p = isl_printer_end_line(p);
730 p = isl_printer_start_line(p);
731 p = isl_printer_end_line(p);
733 return p;
736 static __isl_give isl_printer *opencl_print_kernel(struct gpu_prog *prog,
737 struct ppcg_kernel *kernel, __isl_take isl_printer *p)
739 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
740 isl_ast_print_options *print_options;
742 print_options = isl_ast_print_options_alloc(ctx);
743 print_options = isl_ast_print_options_set_print_user(print_options,
744 &opencl_print_kernel_stmt, NULL);
746 p = isl_printer_set_output_format(p, ISL_FORMAT_C);
747 p = opencl_print_kernel_header(p, prog, kernel);
748 p = isl_printer_print_str(p, "{");
749 p = isl_printer_end_line(p);
750 p = isl_printer_indent(p, 4);
751 p = opencl_print_kernel_iterators(p, kernel);
752 p = opencl_print_kernel_vars(p, kernel);
753 p = isl_printer_end_line(p);
754 p = gpu_print_macros(p, kernel->tree);
755 p = isl_ast_node_print(kernel->tree, p, print_options);
756 p = isl_printer_indent(p, -4);
757 p = isl_printer_start_line(p);
758 p = isl_printer_print_str(p, "}");
759 p = isl_printer_end_line(p);
761 return p;
764 struct print_host_user_data_opencl {
765 struct opencl_info *opencl;
766 struct gpu_prog *prog;
769 /* This function prints the i'th block size multiplied by the i'th grid size,
770 * where i (a parameter to this function) is one of the possible dimensions of
771 * grid sizes and block sizes.
772 * If the dimension of block sizes is not equal to the dimension of grid sizes
773 * the output is calculated as follows:
775 * Suppose that:
776 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
777 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
779 * The output is:
780 * If (i > dim2) then the output is block_sizes[i]
781 * If (i > dim1) then the output is grid_sizes[i]
783 static __isl_give isl_printer *opencl_print_total_number_of_work_items_for_dim(
784 __isl_take isl_printer *p, struct ppcg_kernel *kernel, int i)
786 int grid_dim, block_dim;
787 isl_pw_aff *bound_grid;
789 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
790 block_dim = kernel->n_block;
792 if (i < min(grid_dim, block_dim)) {
793 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
794 p = isl_printer_print_str(p, "(");
795 p = isl_printer_print_pw_aff(p, bound_grid);
796 p = isl_printer_print_str(p, ") * ");
797 p = isl_printer_print_int(p, kernel->block_dim[i]);
798 isl_pw_aff_free(bound_grid);
799 } else if (i >= grid_dim)
800 p = isl_printer_print_int(p, kernel->block_dim[i]);
801 else {
802 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
803 p = isl_printer_print_pw_aff(p, bound_grid);
804 isl_pw_aff_free(bound_grid);
807 return p;
810 /* Print a list that represents the total number of work items. The list is
811 * constructed by performing an element-wise multiplication of the block sizes
812 * and the grid sizes. To explain how the list is constructed, suppose that:
813 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
814 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
816 * The output of this function is constructed as follows:
817 * If (dim1 > dim2) then the output is the following list:
818 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
819 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
821 * If (dim2 > dim1) then the output is the following list:
822 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
823 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
825 * To calculate the total number of work items out of the list constructed by
826 * this function, the user should multiply the elements of the list.
828 static __isl_give isl_printer *opencl_print_total_number_of_work_items_as_list(
829 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
831 int i;
832 int grid_dim, block_dim;
834 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
835 block_dim = kernel->n_block;
837 if ((grid_dim <= 0) || (block_dim <= 0)) {
838 p = isl_printer_print_str(p, "1");
839 return p;
842 for (i = 0; i <= max(grid_dim, block_dim) - 1; i++) {
843 if (i > 0)
844 p = isl_printer_print_str(p, ", ");
846 p = opencl_print_total_number_of_work_items_for_dim(p,
847 kernel, i);
850 return p;
853 /* Copy "array" back from the GPU to the host.
855 static __isl_give isl_printer *copy_array_from_device(__isl_take isl_printer *p,
856 void *user)
858 struct gpu_array_info *array = user;
860 p = isl_printer_start_line(p);
861 p = isl_printer_print_str(p, "openclCheckReturn("
862 "clEnqueueReadBuffer(queue,"
863 " dev_");
864 p = isl_printer_print_str(p, array->name);
865 p = isl_printer_print_str(p, ", CL_TRUE, 0, ");
866 p = gpu_array_info_print_size(p, array);
868 if (gpu_array_is_scalar(array))
869 p = isl_printer_print_str(p, ", &");
870 else
871 p = isl_printer_print_str(p, ", ");
872 p = isl_printer_print_str(p, array->name);
873 p = isl_printer_print_str(p, ", 0, NULL, NULL));");
874 p = isl_printer_end_line(p);
876 return p;
879 /* Copy copy_out arrays back from the GPU to the host.
881 * Only perform the copying for arrays with strictly positive size.
883 static __isl_give isl_printer *opencl_copy_arrays_from_device(
884 __isl_take isl_printer *p, struct gpu_prog *prog)
886 int i;
887 isl_union_set *copy_out;
888 copy_out = isl_union_set_copy(prog->copy_out);
890 for (i = 0; i < prog->n_array; ++i) {
891 struct gpu_array_info *array = &prog->array[i];
892 isl_space *space;
893 isl_set *copy_out_i;
894 isl_set *guard;
895 int empty;
897 space = isl_space_copy(array->space);
898 copy_out_i = isl_union_set_extract_set(copy_out, space);
899 empty = isl_set_plain_is_empty(copy_out_i);
900 isl_set_free(copy_out_i);
901 if (empty)
902 continue;
904 guard = gpu_array_positive_size_guard(array);
905 p = ppcg_print_guarded(p, guard, isl_set_copy(prog->context),
906 &copy_array_from_device, array);
909 isl_union_set_free(copy_out);
910 p = isl_printer_start_line(p);
911 p = isl_printer_end_line(p);
912 return p;
915 /* Print the user statement of the host code to "p".
917 * In particular, print a block of statements that defines the grid
918 * and the work group and then launches the kernel.
920 * A grid is composed of many work groups (blocks), each work group holds
921 * many work-items (threads).
923 * global_work_size[kernel->n_block] represents the total number of work
924 * items. It points to an array of kernel->n_block unsigned
925 * values that describe the total number of work-items that will execute
926 * the kernel. The total number of work-items is computed as:
927 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
929 * The size of each work group (i.e. the number of work-items in each work
930 * group) is described using block_size[kernel->n_block]. The total
931 * number of work-items in a block (work-group) is computed as:
932 * block_size[0] *... * block_size[kernel->n_block - 1].
934 * For more information check:
935 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
937 static __isl_give isl_printer *opencl_print_host_user(
938 __isl_take isl_printer *p,
939 __isl_take isl_ast_print_options *print_options,
940 __isl_keep isl_ast_node *node, void *user)
942 isl_id *id;
943 struct ppcg_kernel *kernel;
944 struct print_host_user_data_opencl *data;
945 int i;
947 id = isl_ast_node_get_annotation(node);
948 kernel = isl_id_get_user(id);
949 isl_id_free(id);
951 data = (struct print_host_user_data_opencl *) user;
953 p = isl_printer_start_line(p);
954 p = isl_printer_print_str(p, "{");
955 p = isl_printer_end_line(p);
956 p = isl_printer_indent(p, 2);
958 p = isl_printer_start_line(p);
959 p = isl_printer_print_str(p, "size_t global_work_size[");
961 if (kernel->n_block > 0)
962 p = isl_printer_print_int(p, kernel->n_block);
963 else
964 p = isl_printer_print_int(p, 1);
966 p = isl_printer_print_str(p, "] = {");
967 p = opencl_print_total_number_of_work_items_as_list(p, kernel);
968 p = isl_printer_print_str(p, "};");
969 p = isl_printer_end_line(p);
971 p = isl_printer_start_line(p);
972 p = isl_printer_print_str(p, "size_t block_size[");
974 if (kernel->n_block > 0)
975 p = isl_printer_print_int(p, kernel->n_block);
976 else
977 p = isl_printer_print_int(p, 1);
979 p = isl_printer_print_str(p, "] = {");
980 p = opencl_print_block_sizes(p, kernel);
981 p = isl_printer_print_str(p, "};");
982 p = isl_printer_end_line(p);
984 p = isl_printer_start_line(p);
985 p = isl_printer_print_str(p, "cl_kernel kernel");
986 p = isl_printer_print_int(p, kernel->id);
987 p = isl_printer_print_str(p, " = clCreateKernel(program, \"kernel");
988 p = isl_printer_print_int(p, kernel->id);
989 p = isl_printer_print_str(p, "\", &err);");
990 p = isl_printer_end_line(p);
991 p = isl_printer_start_line(p);
992 p = isl_printer_print_str(p, "openclCheckReturn(err);");
993 p = isl_printer_end_line(p);
995 opencl_set_kernel_arguments(p, data->prog, kernel);
997 p = isl_printer_start_line(p);
998 p = isl_printer_print_str(p, "openclCheckReturn(clEnqueueNDRangeKernel"
999 "(queue, kernel");
1000 p = isl_printer_print_int(p, kernel->id);
1001 p = isl_printer_print_str(p, ", ");
1002 if (kernel->n_block > 0)
1003 p = isl_printer_print_int(p, kernel->n_block);
1004 else
1005 p = isl_printer_print_int(p, 1);
1007 p = isl_printer_print_str(p, ", NULL, global_work_size, "
1008 "block_size, "
1009 "0, NULL, NULL));");
1010 p = isl_printer_end_line(p);
1011 p = isl_printer_start_line(p);
1012 p = isl_printer_print_str(p, "openclCheckReturn("
1013 "clReleaseKernel(kernel");
1014 p = isl_printer_print_int(p, kernel->id);
1015 p = isl_printer_print_str(p, "));");
1016 p = isl_printer_end_line(p);
1017 p = isl_printer_start_line(p);
1018 p = isl_printer_print_str(p, "clFinish(queue);");
1019 p = isl_printer_end_line(p);
1020 p = isl_printer_indent(p, -2);
1021 p = isl_printer_start_line(p);
1022 p = isl_printer_print_str(p, "}");
1023 p = isl_printer_end_line(p);
1025 p = isl_printer_start_line(p);
1026 p = isl_printer_end_line(p);
1028 data->opencl->kprinter = opencl_print_kernel(data->prog, kernel,
1029 data->opencl->kprinter);
1031 isl_ast_print_options_free(print_options);
1033 return p;
1036 static __isl_give isl_printer *opencl_print_host_code(
1037 __isl_take isl_printer *p, struct gpu_prog *prog,
1038 __isl_keep isl_ast_node *tree, struct opencl_info *opencl)
1040 isl_ast_print_options *print_options;
1041 isl_ctx *ctx = isl_ast_node_get_ctx(tree);
1042 struct print_host_user_data_opencl data = { opencl, prog };
1044 print_options = isl_ast_print_options_alloc(ctx);
1045 print_options = isl_ast_print_options_set_print_user(print_options,
1046 &opencl_print_host_user, &data);
1048 p = gpu_print_macros(p, tree);
1049 p = isl_ast_node_print(tree, p, print_options);
1051 return p;
1054 /* Create an OpenCL device, context, command queue and build the kernel.
1055 * input is the name of the input file provided to ppcg.
1057 static __isl_give isl_printer *opencl_setup(__isl_take isl_printer *p,
1058 const char *input, struct opencl_info *info)
1060 int len;
1062 p = isl_printer_start_line(p);
1063 p = isl_printer_print_str(p, "cl_device_id device;");
1064 p = isl_printer_end_line(p);
1065 p = isl_printer_start_line(p);
1066 p = isl_printer_print_str(p, "cl_context context;");
1067 p = isl_printer_end_line(p);
1068 p = isl_printer_start_line(p);
1069 p = isl_printer_print_str(p, "cl_program program;");
1070 p = isl_printer_end_line(p);
1071 p = isl_printer_start_line(p);
1072 p = isl_printer_print_str(p, "cl_command_queue queue;");
1073 p = isl_printer_end_line(p);
1074 p = isl_printer_start_line(p);
1075 p = isl_printer_print_str(p, "cl_int err;");
1076 p = isl_printer_end_line(p);
1077 p = isl_printer_start_line(p);
1078 p = isl_printer_print_str(p, "device = opencl_create_device(");
1079 p = isl_printer_print_int(p, info->options->opencl_use_gpu);
1080 p = isl_printer_print_str(p, ");");
1081 p = isl_printer_end_line(p);
1082 p = isl_printer_start_line(p);
1083 p = isl_printer_print_str(p, "context = clCreateContext(NULL, 1, "
1084 "&device, NULL, NULL, &err);");
1085 p = isl_printer_end_line(p);
1086 p = isl_printer_start_line(p);
1087 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1088 p = isl_printer_end_line(p);
1089 p = isl_printer_start_line(p);
1090 p = isl_printer_print_str(p, "queue = clCreateCommandQueue"
1091 "(context, device, 0, &err);");
1092 p = isl_printer_end_line(p);
1093 p = isl_printer_start_line(p);
1094 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1095 p = isl_printer_end_line(p);
1097 p = isl_printer_start_line(p);
1098 p = isl_printer_print_str(p, "program = ");
1100 if (info->options->opencl_embed_kernel_code) {
1101 p = isl_printer_print_str(p, "opencl_build_program_from_string("
1102 "context, device, kernel_code, "
1103 "sizeof(kernel_code), \"");
1104 } else {
1105 p = isl_printer_print_str(p, "opencl_build_program_from_file("
1106 "context, device, \"");
1107 p = isl_printer_print_str(p, info->kernel_c_name);
1108 p = isl_printer_print_str(p, "\", \"");
1111 if (info->options->opencl_compiler_options)
1112 p = isl_printer_print_str(p,
1113 info->options->opencl_compiler_options);
1115 p = isl_printer_print_str(p, "\");");
1116 p = isl_printer_end_line(p);
1117 p = isl_printer_start_line(p);
1118 p = isl_printer_end_line(p);
1120 return p;
1123 static __isl_give isl_printer *opencl_release_cl_objects(
1124 __isl_take isl_printer *p, struct opencl_info *info)
1126 p = isl_printer_start_line(p);
1127 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseCommandQueue"
1128 "(queue));");
1129 p = isl_printer_end_line(p);
1130 p = isl_printer_start_line(p);
1131 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseProgram"
1132 "(program));");
1133 p = isl_printer_end_line(p);
1134 p = isl_printer_start_line(p);
1135 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseContext"
1136 "(context));");
1137 p = isl_printer_end_line(p);
1139 return p;
1142 /* Free the device array corresponding to "array"
1144 static __isl_give isl_printer *release_device_array(__isl_take isl_printer *p,
1145 struct gpu_array_info *array)
1147 p = isl_printer_start_line(p);
1148 p = isl_printer_print_str(p, "openclCheckReturn("
1149 "clReleaseMemObject(dev_");
1150 p = isl_printer_print_str(p, array->name);
1151 p = isl_printer_print_str(p, "));");
1152 p = isl_printer_end_line(p);
1154 return p;
1157 /* Free the device arrays.
1159 static __isl_give isl_printer *opencl_release_device_arrays(
1160 __isl_take isl_printer *p, struct gpu_prog *prog)
1162 int i;
1164 for (i = 0; i < prog->n_array; ++i) {
1165 struct gpu_array_info *array = &prog->array[i];
1166 if (gpu_array_is_read_only_scalar(array))
1167 continue;
1169 p = release_device_array(p, array);
1171 return p;
1174 /* Given a gpu_prog "prog" and the corresponding transformed AST
1175 * "tree", print the entire OpenCL code to "p".
1177 static __isl_give isl_printer *print_opencl(__isl_take isl_printer *p,
1178 struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
1179 struct gpu_types *types, void *user)
1181 struct opencl_info *opencl = user;
1183 opencl->kprinter = isl_printer_set_output_format(opencl->kprinter,
1184 ISL_FORMAT_C);
1185 if (any_double_elements(prog))
1186 opencl->kprinter = opencl_enable_double_support(
1187 opencl->kprinter);
1188 opencl->kprinter = gpu_print_types(opencl->kprinter, types, prog);
1190 if (!opencl->kprinter)
1191 return isl_printer_free(p);
1193 p = ppcg_start_block(p);
1195 p = opencl_print_host_macros(p);
1197 p = opencl_declare_device_arrays(p, prog);
1198 p = opencl_setup(p, opencl->input, opencl);
1199 p = opencl_allocate_device_arrays(p, prog);
1201 p = opencl_print_host_code(p, prog, tree, opencl);
1203 p = opencl_copy_arrays_from_device(p, prog);
1204 p = opencl_release_device_arrays(p, prog);
1205 p = opencl_release_cl_objects(p, opencl);
1207 p = ppcg_end_block(p);
1209 return p;
1212 /* Transform the code in the file called "input" by replacing
1213 * all scops by corresponding OpenCL code.
1214 * The host code is written to "output" or a name derived from
1215 * "input" if "output" is NULL.
1216 * The kernel code is placed in separate files with names
1217 * derived from "output" or "input".
1219 * We let generate_gpu do all the hard work and then let it call
1220 * us back for printing the AST in print_cuda.
1222 * To prepare for this printing, we first open the output files
1223 * and we close them after generate_gpu has finished.
1225 int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
1226 const char *input, const char *output)
1228 struct opencl_info opencl = { options, input, output };
1229 int r;
1231 opencl.kprinter = isl_printer_to_str(ctx);
1232 r = opencl_open_files(&opencl);
1234 if (r >= 0)
1235 r = generate_gpu(ctx, input, opencl.host_c, options,
1236 &print_opencl, &opencl);
1238 opencl_close_files(&opencl);
1239 isl_printer_free(opencl.kprinter);
1241 return r;