opencl: replace calls to mathf functions by overloaded function and cast
[ppcg.git] / opencl.c
blob3af7990278a8b5d54f3b4eea0bdae59d7dc37c17
1 /*
2 * Copyright 2013 Ecole Normale Superieure
4 * Use of this software is governed by the MIT 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->kernel_h)
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 * Return 0 on success and -1 on failure.
187 static int opencl_write_kernel_file(struct opencl_info *opencl)
189 char *raw = isl_printer_get_str(opencl->kprinter);
191 if (!raw)
192 return -1;
194 if (opencl->options->opencl_embed_kernel_code) {
195 fprintf(opencl->kernel_c,
196 "static const char kernel_code[] = \"\\n\"");
197 opencl_print_as_c_string(raw, opencl->kernel_c);
198 fprintf(opencl->kernel_c, ";\n");
199 } else
200 fprintf(opencl->kernel_c, "%s", raw);
202 free(raw);
204 return 0;
207 /* Close all output files. Write the kernel contents to the kernel file before
208 * closing it.
210 * Return 0 on success and -1 on failure.
212 static int opencl_close_files(struct opencl_info *info)
214 int r = 0;
216 if (info->kernel_c) {
217 r = opencl_write_kernel_file(info);
218 fclose(info->kernel_c);
220 if (info->kernel_h)
221 fclose(info->kernel_h);
222 if (info->host_c)
223 fclose(info->host_c);
225 return r;
228 static __isl_give isl_printer *opencl_print_host_macros(
229 __isl_take isl_printer *p)
231 const char *macros =
232 "#define openclCheckReturn(ret) \\\n"
233 " if (ret != CL_SUCCESS) {\\\n"
234 " fprintf(stderr, \"OpenCL error: %s\\n\", "
235 "opencl_error_string(ret)); \\\n"
236 " fflush(stderr); \\\n"
237 " assert(ret == CL_SUCCESS);\\\n }\n";
239 p = isl_printer_start_line(p);
240 p = isl_printer_print_str(p, macros);
241 p = isl_printer_end_line(p);
243 p = isl_ast_op_type_print_macro(isl_ast_op_max, p);
245 return p;
248 static __isl_give isl_printer *opencl_declare_device_arrays(
249 __isl_take isl_printer *p, struct gpu_prog *prog)
251 int i;
253 for (i = 0; i < prog->n_array; ++i) {
254 if (gpu_array_is_read_only_scalar(&prog->array[i]))
255 continue;
256 p = isl_printer_start_line(p);
257 p = isl_printer_print_str(p, "cl_mem dev_");
258 p = isl_printer_print_str(p, prog->array[i].name);
259 p = isl_printer_print_str(p, ";");
260 p = isl_printer_end_line(p);
262 p = isl_printer_start_line(p);
263 p = isl_printer_end_line(p);
264 return p;
267 /* Given an array, check whether its positive size guard expression is
268 * trivial.
270 static int is_array_positive_size_guard_trivial(struct gpu_array_info *array)
272 isl_set *guard;
273 int is_trivial;
275 guard = gpu_array_positive_size_guard(array);
276 is_trivial = isl_set_plain_is_universe(guard);
277 isl_set_free(guard);
278 return is_trivial;
281 /* Allocate a device array for "array'.
283 * Emit a max-expression to ensure the device array can contain at least one
284 * element if the array's positive size guard expression is not trivial.
286 static __isl_give isl_printer *allocate_device_array(__isl_take isl_printer *p,
287 struct gpu_array_info *array)
289 int need_lower_bound;
291 p = ppcg_start_block(p);
293 p = isl_printer_start_line(p);
294 p = isl_printer_print_str(p, "dev_");
295 p = isl_printer_print_str(p, array->name);
296 p = isl_printer_print_str(p, " = clCreateBuffer(context, ");
297 p = isl_printer_print_str(p, "CL_MEM_READ_WRITE, ");
299 need_lower_bound = !is_array_positive_size_guard_trivial(array);
300 if (need_lower_bound) {
301 p = isl_printer_print_str(p, "max(sizeof(");
302 p = isl_printer_print_str(p, array->type);
303 p = isl_printer_print_str(p, "), ");
305 p = gpu_array_info_print_size(p, array);
306 if (need_lower_bound)
307 p = isl_printer_print_str(p, ")");
309 p = isl_printer_print_str(p, ", NULL, &err);");
310 p = isl_printer_end_line(p);
311 p = isl_printer_start_line(p);
312 p = isl_printer_print_str(p, "openclCheckReturn(err);");
313 p = isl_printer_end_line(p);
315 p = ppcg_end_block(p);
317 return p;
320 /* Allocate device arrays.
322 static __isl_give isl_printer *opencl_allocate_device_arrays(
323 __isl_take isl_printer *p, struct gpu_prog *prog)
325 int i;
327 for (i = 0; i < prog->n_array; ++i) {
328 struct gpu_array_info *array = &prog->array[i];
330 if (gpu_array_is_read_only_scalar(array))
331 continue;
333 p = allocate_device_array(p, array);
335 p = isl_printer_start_line(p);
336 p = isl_printer_end_line(p);
337 return p;
340 /* Print a call to the OpenCL clSetKernelArg() function which sets
341 * the arguments of the kernel. arg_name and arg_index are the name and the
342 * index of the kernel argument. The index of the leftmost argument of
343 * the kernel is 0 whereas the index of the rightmost argument of the kernel
344 * is n - 1, where n is the total number of the kernel arguments.
345 * read_only_scalar is a boolean that indicates whether the argument is a read
346 * only scalar.
348 static __isl_give isl_printer *opencl_set_kernel_argument(
349 __isl_take isl_printer *p, int kernel_id,
350 const char *arg_name, int arg_index, int read_only_scalar)
352 p = isl_printer_start_line(p);
353 p = isl_printer_print_str(p,
354 "openclCheckReturn(clSetKernelArg(kernel");
355 p = isl_printer_print_int(p, kernel_id);
356 p = isl_printer_print_str(p, ", ");
357 p = isl_printer_print_int(p, arg_index);
358 p = isl_printer_print_str(p, ", sizeof(");
360 if (read_only_scalar) {
361 p = isl_printer_print_str(p, arg_name);
362 p = isl_printer_print_str(p, "), &");
363 } else
364 p = isl_printer_print_str(p, "cl_mem), (void *) &dev_");
366 p = isl_printer_print_str(p, arg_name);
367 p = isl_printer_print_str(p, "));");
368 p = isl_printer_end_line(p);
370 return p;
373 /* Print the block sizes as a list of the sizes in each
374 * dimension.
376 static __isl_give isl_printer *opencl_print_block_sizes(
377 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
379 int i;
381 if (kernel->n_block > 0)
382 for (i = 0; i < kernel->n_block; ++i) {
383 if (i)
384 p = isl_printer_print_str(p, ", ");
385 p = isl_printer_print_int(p, kernel->block_dim[i]);
387 else
388 p = isl_printer_print_str(p, "1");
390 return p;
393 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
394 * clSetKernelArg() function for each kernel argument.
396 static __isl_give isl_printer *opencl_set_kernel_arguments(
397 __isl_take isl_printer *p, struct gpu_prog *prog,
398 struct ppcg_kernel *kernel)
400 int i, n, ro;
401 unsigned nparam;
402 isl_space *space;
403 int arg_index = 0;
405 for (i = 0; i < prog->n_array; ++i) {
406 isl_set *arr;
407 int empty;
409 space = isl_space_copy(prog->array[i].space);
410 arr = isl_union_set_extract_set(kernel->arrays, space);
411 empty = isl_set_plain_is_empty(arr);
412 isl_set_free(arr);
413 if (empty)
414 continue;
415 ro = gpu_array_is_read_only_scalar(&prog->array[i]);
416 opencl_set_kernel_argument(p, kernel->id, prog->array[i].name,
417 arg_index, ro);
418 arg_index++;
421 space = isl_union_set_get_space(kernel->arrays);
422 nparam = isl_space_dim(space, isl_dim_param);
423 for (i = 0; i < nparam; ++i) {
424 const char *name;
426 name = isl_space_get_dim_name(space, isl_dim_param, i);
427 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
428 arg_index++;
430 isl_space_free(space);
432 n = isl_space_dim(kernel->space, isl_dim_set);
433 for (i = 0; i < n; ++i) {
434 const char *name;
436 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
437 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
438 arg_index++;
441 return p;
444 /* Print the arguments to a kernel declaration or call. If "types" is set,
445 * then print a declaration (including the types of the arguments).
447 * The arguments are printed in the following order
448 * - the arrays accessed by the kernel
449 * - the parameters
450 * - the host loop iterators
452 static __isl_give isl_printer *opencl_print_kernel_arguments(
453 __isl_take isl_printer *p, struct gpu_prog *prog,
454 struct ppcg_kernel *kernel, int types)
456 int i, n;
457 int first = 1;
458 unsigned nparam;
459 isl_space *space;
460 const char *type;
462 for (i = 0; i < prog->n_array; ++i) {
463 isl_set *arr;
464 int empty;
466 space = isl_space_copy(prog->array[i].space);
467 arr = isl_union_set_extract_set(kernel->arrays, space);
468 empty = isl_set_plain_is_empty(arr);
469 isl_set_free(arr);
470 if (empty)
471 continue;
473 if (!first)
474 p = isl_printer_print_str(p, ", ");
476 if (types)
477 p = gpu_array_info_print_declaration_argument(p,
478 &prog->array[i], "__global");
479 else
480 p = gpu_array_info_print_call_argument(p,
481 &prog->array[i]);
483 first = 0;
486 space = isl_union_set_get_space(kernel->arrays);
487 nparam = isl_space_dim(space, isl_dim_param);
488 for (i = 0; i < nparam; ++i) {
489 const char *name;
491 name = isl_space_get_dim_name(space, isl_dim_param, i);
493 if (!first)
494 p = isl_printer_print_str(p, ", ");
495 if (types)
496 p = isl_printer_print_str(p, "int ");
497 p = isl_printer_print_str(p, name);
499 first = 0;
501 isl_space_free(space);
503 n = isl_space_dim(kernel->space, isl_dim_set);
504 type = isl_options_get_ast_iterator_type(prog->ctx);
505 for (i = 0; i < n; ++i) {
506 const char *name;
508 if (!first)
509 p = isl_printer_print_str(p, ", ");
510 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
511 if (types) {
512 p = isl_printer_print_str(p, type);
513 p = isl_printer_print_str(p, " ");
515 p = isl_printer_print_str(p, name);
517 first = 0;
520 return p;
523 /* Print the header of the given kernel.
525 static __isl_give isl_printer *opencl_print_kernel_header(
526 __isl_take isl_printer *p, struct gpu_prog *prog,
527 struct ppcg_kernel *kernel)
529 p = isl_printer_start_line(p);
530 p = isl_printer_print_str(p, "__kernel void kernel");
531 p = isl_printer_print_int(p, kernel->id);
532 p = isl_printer_print_str(p, "(");
533 p = opencl_print_kernel_arguments(p, prog, kernel, 1);
534 p = isl_printer_print_str(p, ")");
535 p = isl_printer_end_line(p);
537 return p;
540 /* Print a list of iterators of type "type" with names "ids" to "p".
541 * Each iterator is assigned the corresponding opencl identifier returned
542 * by the function "opencl_id".
543 * Unlike the equivalent function in the CUDA backend which prints iterators
544 * in reverse order to promote coalescing, this function does not print
545 * iterators in reverse order. The OpenCL backend currently does not take
546 * into account any coalescing considerations.
548 static __isl_give isl_printer *print_iterators(__isl_take isl_printer *p,
549 const char *type, __isl_keep isl_id_list *ids, const char *opencl_id)
551 int i, n;
553 n = isl_id_list_n_id(ids);
554 if (n <= 0)
555 return p;
556 p = isl_printer_start_line(p);
557 p = isl_printer_print_str(p, type);
558 p = isl_printer_print_str(p, " ");
559 for (i = 0; i < n; ++i) {
560 isl_id *id;
562 if (i)
563 p = isl_printer_print_str(p, ", ");
564 id = isl_id_list_get_id(ids, i);
565 p = isl_printer_print_id(p, id);
566 isl_id_free(id);
567 p = isl_printer_print_str(p, " = ");
568 p = isl_printer_print_str(p, opencl_id);
569 p = isl_printer_print_str(p, "(");
570 p = isl_printer_print_int(p, i);
571 p = isl_printer_print_str(p, ")");
573 p = isl_printer_print_str(p, ";");
574 p = isl_printer_end_line(p);
576 return p;
579 static __isl_give isl_printer *opencl_print_kernel_iterators(
580 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
582 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
583 const char *type;
585 type = isl_options_get_ast_iterator_type(ctx);
587 p = print_iterators(p, type, kernel->block_ids, "get_group_id");
588 p = print_iterators(p, type, kernel->thread_ids, "get_local_id");
590 return p;
593 static __isl_give isl_printer *opencl_print_kernel_var(
594 __isl_take isl_printer *p, struct ppcg_kernel_var *var)
596 int j;
597 isl_val *v;
599 p = isl_printer_start_line(p);
600 if (var->type == ppcg_access_shared)
601 p = isl_printer_print_str(p, "__local ");
602 p = isl_printer_print_str(p, var->array->type);
603 p = isl_printer_print_str(p, " ");
604 p = isl_printer_print_str(p, var->name);
605 for (j = 0; j < var->array->n_index; ++j) {
606 p = isl_printer_print_str(p, "[");
607 v = isl_vec_get_element_val(var->size, j);
608 p = isl_printer_print_val(p, v);
609 p = isl_printer_print_str(p, "]");
610 isl_val_free(v);
612 p = isl_printer_print_str(p, ";");
613 p = isl_printer_end_line(p);
615 return p;
618 static __isl_give isl_printer *opencl_print_kernel_vars(
619 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
621 int i;
623 for (i = 0; i < kernel->n_var; ++i)
624 p = opencl_print_kernel_var(p, &kernel->var[i]);
626 return p;
629 /* Print a call to barrier() which is a sync statement.
630 * All work-items in a work-group executing the kernel on a processor must
631 * execute the barrier() function before any are allowed to continue execution
632 * beyond the barrier.
633 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
634 * variables stored in local memory or queue a memory fence to ensure correct
635 * ordering of memory operations to local memory.
636 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
637 * fence to ensure correct ordering of memory operations to global memory.
639 static __isl_give isl_printer *opencl_print_sync(__isl_take isl_printer *p,
640 struct ppcg_kernel_stmt *stmt)
642 p = isl_printer_start_line(p);
643 p = isl_printer_print_str(p,
644 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
645 p = isl_printer_end_line(p);
647 return p;
650 /* Data structure containing function names for which the calls
651 * should be changed from
653 * name(arg)
655 * to
657 * opencl_name((type) (arg))
659 static struct ppcg_opencl_fn {
660 const char *name;
661 const char *opencl_name;
662 const char *type;
663 } opencl_fn[] = {
664 { "expf", "exp", "float" },
665 { "powf", "pow", "float" },
666 { "sqrtf", "sqrt", "float" },
669 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
671 /* If the name of function called by "expr" matches any of those
672 * in ppcg_opencl_fn, then replace the call by a cast to the corresponding
673 * type in ppcg_opencl_fn and a call to corresponding OpenCL function.
675 static __isl_give pet_expr *map_opencl_call(__isl_take pet_expr *expr,
676 void *user)
678 const char *name;
679 int i;
681 name = pet_expr_call_get_name(expr);
682 for (i = 0; i < ARRAY_SIZE(opencl_fn); ++i) {
683 pet_expr *arg;
685 if (strcmp(name, opencl_fn[i].name))
686 continue;
687 expr = pet_expr_call_set_name(expr, opencl_fn[i].opencl_name);
688 arg = pet_expr_get_arg(expr, 0);
689 arg = pet_expr_new_cast(opencl_fn[i].type, arg);
690 expr = pet_expr_set_arg(expr, 0, arg);
692 return expr;
695 /* Print the body of a statement from the input program,
696 * for use in OpenCL code.
698 * Before calling ppcg_kernel_print_domain to print the actual statement body,
699 * we first modify this body to take into account that the output code
700 * is OpenCL code. In particular, if the statement calls any function
701 * with a "f" suffix, then it needs to be replaced by a call to
702 * the corresponding function without suffix after casting the argument
703 * to a float.
705 static __isl_give isl_printer *print_opencl_kernel_domain(
706 __isl_take isl_printer *p, struct ppcg_kernel_stmt *stmt)
708 struct pet_stmt *ps;
709 pet_tree *tree;
711 ps = stmt->u.d.stmt->stmt;
712 tree = pet_tree_copy(ps->body);
713 ps->body = pet_tree_map_call_expr(ps->body, &map_opencl_call, NULL);
714 p = ppcg_kernel_print_domain(p, stmt);
715 pet_tree_free(ps->body);
716 ps->body = tree;
718 return p;
721 /* This function is called for each user statement in the AST,
722 * i.e., for each kernel body statement, copy statement or sync statement.
724 static __isl_give isl_printer *opencl_print_kernel_stmt(
725 __isl_take isl_printer *p,
726 __isl_take isl_ast_print_options *print_options,
727 __isl_keep isl_ast_node *node, void *user)
729 isl_id *id;
730 struct ppcg_kernel_stmt *stmt;
732 id = isl_ast_node_get_annotation(node);
733 stmt = isl_id_get_user(id);
734 isl_id_free(id);
736 isl_ast_print_options_free(print_options);
738 switch (stmt->type) {
739 case ppcg_kernel_copy:
740 return ppcg_kernel_print_copy(p, stmt);
741 case ppcg_kernel_sync:
742 return opencl_print_sync(p, stmt);
743 case ppcg_kernel_domain:
744 return print_opencl_kernel_domain(p, stmt);
747 return p;
750 /* Return true if there is a double array in prog->array or
751 * if any of the types in prog->scop involve any doubles.
752 * To check the latter condition, we simply search for the string "double"
753 * in the type definitions, which may result in false positives.
755 static __isl_give int any_double_elements(struct gpu_prog *prog)
757 int i;
759 for (i = 0; i < prog->n_array; ++i)
760 if (strcmp(prog->array[i].type, "double") == 0)
761 return 1;
763 for (i = 0; i < prog->scop->pet->n_type; ++i) {
764 struct pet_type *type = prog->scop->pet->types[i];
766 if (strstr(type->definition, "double"))
767 return 1;
770 return 0;
773 /* Prints a #pragma to enable support for double floating-point
774 * precision. OpenCL 1.0 adds support for double precision floating-point as
775 * an optional extension. An application that wants to use double will need to
776 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
777 * any double precision data type is declared in the kernel code.
779 static __isl_give isl_printer *opencl_enable_double_support(
780 __isl_take isl_printer *p)
782 p = isl_printer_start_line(p);
783 p = isl_printer_print_str(p, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
784 " enable");
785 p = isl_printer_end_line(p);
786 p = isl_printer_start_line(p);
787 p = isl_printer_end_line(p);
789 return p;
792 static __isl_give isl_printer *opencl_print_kernel(struct gpu_prog *prog,
793 struct ppcg_kernel *kernel, __isl_take isl_printer *p)
795 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
796 isl_ast_print_options *print_options;
798 print_options = isl_ast_print_options_alloc(ctx);
799 print_options = isl_ast_print_options_set_print_user(print_options,
800 &opencl_print_kernel_stmt, NULL);
802 p = isl_printer_set_output_format(p, ISL_FORMAT_C);
803 p = opencl_print_kernel_header(p, prog, kernel);
804 p = isl_printer_print_str(p, "{");
805 p = isl_printer_end_line(p);
806 p = isl_printer_indent(p, 4);
807 p = opencl_print_kernel_iterators(p, kernel);
808 p = opencl_print_kernel_vars(p, kernel);
809 p = isl_printer_end_line(p);
810 p = gpu_print_macros(p, kernel->tree);
811 p = isl_ast_node_print(kernel->tree, p, print_options);
812 p = isl_printer_indent(p, -4);
813 p = isl_printer_start_line(p);
814 p = isl_printer_print_str(p, "}");
815 p = isl_printer_end_line(p);
817 return p;
820 struct print_host_user_data_opencl {
821 struct opencl_info *opencl;
822 struct gpu_prog *prog;
825 /* This function prints the i'th block size multiplied by the i'th grid size,
826 * where i (a parameter to this function) is one of the possible dimensions of
827 * grid sizes and block sizes.
828 * If the dimension of block sizes is not equal to the dimension of grid sizes
829 * the output is calculated as follows:
831 * Suppose that:
832 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
833 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
835 * The output is:
836 * If (i > dim2) then the output is block_sizes[i]
837 * If (i > dim1) then the output is grid_sizes[i]
839 static __isl_give isl_printer *opencl_print_total_number_of_work_items_for_dim(
840 __isl_take isl_printer *p, struct ppcg_kernel *kernel, int i)
842 int grid_dim, block_dim;
843 isl_pw_aff *bound_grid;
845 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
846 block_dim = kernel->n_block;
848 if (i < min(grid_dim, block_dim)) {
849 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
850 p = isl_printer_print_str(p, "(");
851 p = isl_printer_print_pw_aff(p, bound_grid);
852 p = isl_printer_print_str(p, ") * ");
853 p = isl_printer_print_int(p, kernel->block_dim[i]);
854 isl_pw_aff_free(bound_grid);
855 } else if (i >= grid_dim)
856 p = isl_printer_print_int(p, kernel->block_dim[i]);
857 else {
858 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
859 p = isl_printer_print_pw_aff(p, bound_grid);
860 isl_pw_aff_free(bound_grid);
863 return p;
866 /* Print a list that represents the total number of work items. The list is
867 * constructed by performing an element-wise multiplication of the block sizes
868 * and the grid sizes. To explain how the list is constructed, suppose that:
869 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
870 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
872 * The output of this function is constructed as follows:
873 * If (dim1 > dim2) then the output is the following list:
874 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
875 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
877 * If (dim2 > dim1) then the output is the following list:
878 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
879 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
881 * To calculate the total number of work items out of the list constructed by
882 * this function, the user should multiply the elements of the list.
884 static __isl_give isl_printer *opencl_print_total_number_of_work_items_as_list(
885 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
887 int i;
888 int grid_dim, block_dim;
890 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
891 block_dim = kernel->n_block;
893 if ((grid_dim <= 0) || (block_dim <= 0)) {
894 p = isl_printer_print_str(p, "1");
895 return p;
898 for (i = 0; i <= max(grid_dim, block_dim) - 1; i++) {
899 if (i > 0)
900 p = isl_printer_print_str(p, ", ");
902 p = opencl_print_total_number_of_work_items_for_dim(p,
903 kernel, i);
906 return p;
909 /* Copy "array" from the host to the device (to_host = 0) or
910 * back from the device to the host (to_host = 1).
912 static __isl_give isl_printer *copy_array(__isl_take isl_printer *p,
913 struct gpu_array_info *array, int to_host)
915 p = isl_printer_start_line(p);
916 p = isl_printer_print_str(p, "openclCheckReturn(");
917 if (to_host)
918 p = isl_printer_print_str(p, "clEnqueueReadBuffer");
919 else
920 p = isl_printer_print_str(p, "clEnqueueWriteBuffer");
921 p = isl_printer_print_str(p, "(queue, dev_");
922 p = isl_printer_print_str(p, array->name);
923 p = isl_printer_print_str(p, ", CL_TRUE, 0, ");
924 p = gpu_array_info_print_size(p, array);
926 if (gpu_array_is_scalar(array))
927 p = isl_printer_print_str(p, ", &");
928 else
929 p = isl_printer_print_str(p, ", ");
930 p = isl_printer_print_str(p, array->name);
931 p = isl_printer_print_str(p, ", 0, NULL, NULL));");
932 p = isl_printer_end_line(p);
934 return p;
937 /* Copy "array" from the host to the device.
939 static __isl_give isl_printer *copy_array_to_device(__isl_take isl_printer *p,
940 void *user)
942 struct gpu_array_info *array = user;
944 return copy_array(p, array, 0);
947 /* Copy "array" back from the device to the host.
949 static __isl_give isl_printer *copy_array_from_device(__isl_take isl_printer *p,
950 void *user)
952 struct gpu_array_info *array = user;
954 return copy_array(p, array, 1);
957 /* Copy the "copy" arrays from the host to the device (to_host = 0) or
958 * back from the device to the host (to_host = 1).
960 * Only perform the copying for arrays with strictly positive size.
962 static __isl_give isl_printer *opencl_copy_arrays(__isl_take isl_printer *p,
963 struct gpu_prog *prog, __isl_keep isl_union_set *copy, int to_host)
965 int i;
967 for (i = 0; i < prog->n_array; ++i) {
968 struct gpu_array_info *array = &prog->array[i];
969 isl_space *space;
970 isl_set *copy_i;
971 isl_set *guard;
972 int empty;
974 if (gpu_array_is_read_only_scalar(array))
975 continue;
977 space = isl_space_copy(array->space);
978 copy_i = isl_union_set_extract_set(copy, space);
979 empty = isl_set_plain_is_empty(copy_i);
980 isl_set_free(copy_i);
981 if (empty)
982 continue;
984 guard = gpu_array_positive_size_guard(array);
985 p = ppcg_print_guarded(p, guard, isl_set_copy(prog->context),
986 to_host ? &copy_array_from_device :
987 &copy_array_to_device, array);
990 p = isl_printer_start_line(p);
991 p = isl_printer_end_line(p);
992 return p;
995 /* Copy the prog->copy_in arrays from the host to the device.
997 static __isl_give isl_printer *opencl_copy_arrays_to_device(
998 __isl_take isl_printer *p, struct gpu_prog *prog)
1000 return opencl_copy_arrays(p, prog, prog->copy_in, 0);
1003 /* Copy the prog->copy_out arrays back from the device to the host.
1005 static __isl_give isl_printer *opencl_copy_arrays_from_device(
1006 __isl_take isl_printer *p, struct gpu_prog *prog)
1008 return opencl_copy_arrays(p, prog, prog->copy_out, 1);
1011 /* Print the user statement of the host code to "p".
1013 * In particular, print a block of statements that defines the grid
1014 * and the work group and then launches the kernel.
1016 * A grid is composed of many work groups (blocks), each work group holds
1017 * many work-items (threads).
1019 * global_work_size[kernel->n_block] represents the total number of work
1020 * items. It points to an array of kernel->n_block unsigned
1021 * values that describe the total number of work-items that will execute
1022 * the kernel. The total number of work-items is computed as:
1023 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
1025 * The size of each work group (i.e. the number of work-items in each work
1026 * group) is described using block_size[kernel->n_block]. The total
1027 * number of work-items in a block (work-group) is computed as:
1028 * block_size[0] *... * block_size[kernel->n_block - 1].
1030 * For more information check:
1031 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
1033 static __isl_give isl_printer *opencl_print_host_user(
1034 __isl_take isl_printer *p,
1035 __isl_take isl_ast_print_options *print_options,
1036 __isl_keep isl_ast_node *node, void *user)
1038 isl_id *id;
1039 struct ppcg_kernel *kernel;
1040 struct print_host_user_data_opencl *data;
1042 id = isl_ast_node_get_annotation(node);
1043 kernel = isl_id_get_user(id);
1044 isl_id_free(id);
1046 data = (struct print_host_user_data_opencl *) user;
1048 p = isl_printer_start_line(p);
1049 p = isl_printer_print_str(p, "{");
1050 p = isl_printer_end_line(p);
1051 p = isl_printer_indent(p, 2);
1053 p = isl_printer_start_line(p);
1054 p = isl_printer_print_str(p, "size_t global_work_size[");
1056 if (kernel->n_block > 0)
1057 p = isl_printer_print_int(p, kernel->n_block);
1058 else
1059 p = isl_printer_print_int(p, 1);
1061 p = isl_printer_print_str(p, "] = {");
1062 p = opencl_print_total_number_of_work_items_as_list(p, kernel);
1063 p = isl_printer_print_str(p, "};");
1064 p = isl_printer_end_line(p);
1066 p = isl_printer_start_line(p);
1067 p = isl_printer_print_str(p, "size_t block_size[");
1069 if (kernel->n_block > 0)
1070 p = isl_printer_print_int(p, kernel->n_block);
1071 else
1072 p = isl_printer_print_int(p, 1);
1074 p = isl_printer_print_str(p, "] = {");
1075 p = opencl_print_block_sizes(p, kernel);
1076 p = isl_printer_print_str(p, "};");
1077 p = isl_printer_end_line(p);
1079 p = isl_printer_start_line(p);
1080 p = isl_printer_print_str(p, "cl_kernel kernel");
1081 p = isl_printer_print_int(p, kernel->id);
1082 p = isl_printer_print_str(p, " = clCreateKernel(program, \"kernel");
1083 p = isl_printer_print_int(p, kernel->id);
1084 p = isl_printer_print_str(p, "\", &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);
1090 opencl_set_kernel_arguments(p, data->prog, kernel);
1092 p = isl_printer_start_line(p);
1093 p = isl_printer_print_str(p, "openclCheckReturn(clEnqueueNDRangeKernel"
1094 "(queue, kernel");
1095 p = isl_printer_print_int(p, kernel->id);
1096 p = isl_printer_print_str(p, ", ");
1097 if (kernel->n_block > 0)
1098 p = isl_printer_print_int(p, kernel->n_block);
1099 else
1100 p = isl_printer_print_int(p, 1);
1102 p = isl_printer_print_str(p, ", NULL, global_work_size, "
1103 "block_size, "
1104 "0, NULL, NULL));");
1105 p = isl_printer_end_line(p);
1106 p = isl_printer_start_line(p);
1107 p = isl_printer_print_str(p, "openclCheckReturn("
1108 "clReleaseKernel(kernel");
1109 p = isl_printer_print_int(p, kernel->id);
1110 p = isl_printer_print_str(p, "));");
1111 p = isl_printer_end_line(p);
1112 p = isl_printer_start_line(p);
1113 p = isl_printer_print_str(p, "clFinish(queue);");
1114 p = isl_printer_end_line(p);
1115 p = isl_printer_indent(p, -2);
1116 p = isl_printer_start_line(p);
1117 p = isl_printer_print_str(p, "}");
1118 p = isl_printer_end_line(p);
1120 p = isl_printer_start_line(p);
1121 p = isl_printer_end_line(p);
1123 data->opencl->kprinter = opencl_print_kernel(data->prog, kernel,
1124 data->opencl->kprinter);
1126 isl_ast_print_options_free(print_options);
1128 return p;
1131 static __isl_give isl_printer *opencl_print_host_code(
1132 __isl_take isl_printer *p, struct gpu_prog *prog,
1133 __isl_keep isl_ast_node *tree, struct opencl_info *opencl)
1135 isl_ast_print_options *print_options;
1136 isl_ctx *ctx = isl_ast_node_get_ctx(tree);
1137 struct print_host_user_data_opencl data = { opencl, prog };
1139 print_options = isl_ast_print_options_alloc(ctx);
1140 print_options = isl_ast_print_options_set_print_user(print_options,
1141 &opencl_print_host_user, &data);
1143 p = gpu_print_macros(p, tree);
1144 p = isl_ast_node_print(tree, p, print_options);
1146 return p;
1149 /* Create an OpenCL device, context, command queue and build the kernel.
1150 * input is the name of the input file provided to ppcg.
1152 static __isl_give isl_printer *opencl_setup(__isl_take isl_printer *p,
1153 const char *input, struct opencl_info *info)
1155 p = isl_printer_start_line(p);
1156 p = isl_printer_print_str(p, "cl_device_id device;");
1157 p = isl_printer_end_line(p);
1158 p = isl_printer_start_line(p);
1159 p = isl_printer_print_str(p, "cl_context context;");
1160 p = isl_printer_end_line(p);
1161 p = isl_printer_start_line(p);
1162 p = isl_printer_print_str(p, "cl_program program;");
1163 p = isl_printer_end_line(p);
1164 p = isl_printer_start_line(p);
1165 p = isl_printer_print_str(p, "cl_command_queue queue;");
1166 p = isl_printer_end_line(p);
1167 p = isl_printer_start_line(p);
1168 p = isl_printer_print_str(p, "cl_int err;");
1169 p = isl_printer_end_line(p);
1170 p = isl_printer_start_line(p);
1171 p = isl_printer_print_str(p, "device = opencl_create_device(");
1172 p = isl_printer_print_int(p, info->options->opencl_use_gpu);
1173 p = isl_printer_print_str(p, ");");
1174 p = isl_printer_end_line(p);
1175 p = isl_printer_start_line(p);
1176 p = isl_printer_print_str(p, "context = clCreateContext(NULL, 1, "
1177 "&device, NULL, NULL, &err);");
1178 p = isl_printer_end_line(p);
1179 p = isl_printer_start_line(p);
1180 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1181 p = isl_printer_end_line(p);
1182 p = isl_printer_start_line(p);
1183 p = isl_printer_print_str(p, "queue = clCreateCommandQueue"
1184 "(context, device, 0, &err);");
1185 p = isl_printer_end_line(p);
1186 p = isl_printer_start_line(p);
1187 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1188 p = isl_printer_end_line(p);
1190 p = isl_printer_start_line(p);
1191 p = isl_printer_print_str(p, "program = ");
1193 if (info->options->opencl_embed_kernel_code) {
1194 p = isl_printer_print_str(p, "opencl_build_program_from_string("
1195 "context, device, kernel_code, "
1196 "sizeof(kernel_code), \"");
1197 } else {
1198 p = isl_printer_print_str(p, "opencl_build_program_from_file("
1199 "context, device, \"");
1200 p = isl_printer_print_str(p, info->kernel_c_name);
1201 p = isl_printer_print_str(p, "\", \"");
1204 if (info->options->opencl_compiler_options)
1205 p = isl_printer_print_str(p,
1206 info->options->opencl_compiler_options);
1208 p = isl_printer_print_str(p, "\");");
1209 p = isl_printer_end_line(p);
1210 p = isl_printer_start_line(p);
1211 p = isl_printer_end_line(p);
1213 return p;
1216 static __isl_give isl_printer *opencl_release_cl_objects(
1217 __isl_take isl_printer *p, struct opencl_info *info)
1219 p = isl_printer_start_line(p);
1220 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseCommandQueue"
1221 "(queue));");
1222 p = isl_printer_end_line(p);
1223 p = isl_printer_start_line(p);
1224 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseProgram"
1225 "(program));");
1226 p = isl_printer_end_line(p);
1227 p = isl_printer_start_line(p);
1228 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseContext"
1229 "(context));");
1230 p = isl_printer_end_line(p);
1232 return p;
1235 /* Free the device array corresponding to "array"
1237 static __isl_give isl_printer *release_device_array(__isl_take isl_printer *p,
1238 struct gpu_array_info *array)
1240 p = isl_printer_start_line(p);
1241 p = isl_printer_print_str(p, "openclCheckReturn("
1242 "clReleaseMemObject(dev_");
1243 p = isl_printer_print_str(p, array->name);
1244 p = isl_printer_print_str(p, "));");
1245 p = isl_printer_end_line(p);
1247 return p;
1250 /* Free the device arrays.
1252 static __isl_give isl_printer *opencl_release_device_arrays(
1253 __isl_take isl_printer *p, struct gpu_prog *prog)
1255 int i;
1257 for (i = 0; i < prog->n_array; ++i) {
1258 struct gpu_array_info *array = &prog->array[i];
1259 if (gpu_array_is_read_only_scalar(array))
1260 continue;
1262 p = release_device_array(p, array);
1264 return p;
1267 /* Given a gpu_prog "prog" and the corresponding transformed AST
1268 * "tree", print the entire OpenCL code to "p".
1270 static __isl_give isl_printer *print_opencl(__isl_take isl_printer *p,
1271 struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
1272 struct gpu_types *types, void *user)
1274 struct opencl_info *opencl = user;
1276 opencl->kprinter = isl_printer_set_output_format(opencl->kprinter,
1277 ISL_FORMAT_C);
1278 if (any_double_elements(prog))
1279 opencl->kprinter = opencl_enable_double_support(
1280 opencl->kprinter);
1281 if (opencl->options->opencl_print_kernel_types)
1282 opencl->kprinter = gpu_print_types(opencl->kprinter, types,
1283 prog);
1285 if (!opencl->kprinter)
1286 return isl_printer_free(p);
1288 p = ppcg_start_block(p);
1290 p = opencl_print_host_macros(p);
1292 p = opencl_declare_device_arrays(p, prog);
1293 p = opencl_setup(p, opencl->input, opencl);
1294 p = opencl_allocate_device_arrays(p, prog);
1295 p = opencl_copy_arrays_to_device(p, prog);
1297 p = opencl_print_host_code(p, prog, tree, opencl);
1299 p = opencl_copy_arrays_from_device(p, prog);
1300 p = opencl_release_device_arrays(p, prog);
1301 p = opencl_release_cl_objects(p, opencl);
1303 p = ppcg_end_block(p);
1305 return p;
1308 /* Transform the code in the file called "input" by replacing
1309 * all scops by corresponding OpenCL code.
1310 * The host code is written to "output" or a name derived from
1311 * "input" if "output" is NULL.
1312 * The kernel code is placed in separate files with names
1313 * derived from "output" or "input".
1315 * We let generate_gpu do all the hard work and then let it call
1316 * us back for printing the AST in print_opencl.
1318 * To prepare for this printing, we first open the output files
1319 * and we close them after generate_gpu has finished.
1321 int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
1322 const char *input, const char *output)
1324 struct opencl_info opencl = { options, input, output };
1325 int r;
1327 opencl.kprinter = isl_printer_to_str(ctx);
1328 r = opencl_open_files(&opencl);
1330 if (r >= 0)
1331 r = generate_gpu(ctx, input, opencl.host_c, options,
1332 &print_opencl, &opencl);
1334 if (opencl_close_files(&opencl) < 0)
1335 r = -1;
1336 isl_printer_free(opencl.kprinter);
1338 return r;