ppcg_kernel: keep track of isl_ctx
[ppcg.git] / opencl.c
blob6391ab1a5525c649c0cb707778472cdc55845626
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.
36 struct opencl_info {
37 struct ppcg_options *options;
38 const char *input;
39 const char *output;
40 char kernel_c_name[PATH_MAX];
42 isl_printer *kprinter;
44 FILE *host_c;
45 FILE *kernel_c;
48 /* Open the file called "name" for writing or print an error message.
50 static FILE *open_or_croak(const char *name)
52 FILE *file;
54 file = fopen(name, "w");
55 if (!file)
56 fprintf(stderr, "Failed to open \"%s\" for writing\n", name);
57 return file;
60 /* Open the host .c file and the kernel .h and .cl files for writing.
61 * Their names are derived from info->output (or info->input if
62 * the user did not specify an output file name).
63 * Add the necessary includes to these files, including those specified
64 * by the user.
66 * Return 0 on success and -1 on failure.
68 static int opencl_open_files(struct opencl_info *info)
70 char name[PATH_MAX];
71 int i;
72 int len;
74 if (info->output) {
75 const char *ext;
77 ext = strrchr(info->output, '.');
78 len = ext ? ext - info->output : strlen(info->output);
79 memcpy(name, info->output, len);
81 info->host_c = open_or_croak(info->output);
82 } else {
83 len = ppcg_extract_base_name(name, info->input);
85 strcpy(name + len, "_host.c");
86 info->host_c = open_or_croak(name);
89 memcpy(info->kernel_c_name, name, len);
90 strcpy(info->kernel_c_name + len, "_kernel.cl");
91 info->kernel_c = open_or_croak(info->kernel_c_name);
93 if (!info->host_c || !info->kernel_c)
94 return -1;
96 fprintf(info->host_c, "#include <assert.h>\n");
97 fprintf(info->host_c, "#include <stdio.h>\n");
98 fprintf(info->host_c, "#include \"ocl_utilities.h\"\n");
99 if (info->options->opencl_embed_kernel_code) {
100 fprintf(info->host_c, "#include \"%s\"\n\n",
101 info->kernel_c_name);
104 for (i = 0; i < info->options->opencl_n_include_file; ++i) {
105 info->kprinter = isl_printer_print_str(info->kprinter,
106 "#include <");
107 info->kprinter = isl_printer_print_str(info->kprinter,
108 info->options->opencl_include_files[i]);
109 info->kprinter = isl_printer_print_str(info->kprinter, ">\n");
112 return 0;
115 /* Write text to a file and escape some special characters that would break a
116 * C string.
118 static void opencl_print_escaped(const char *str, const char *end, FILE *file)
120 const char *prev = str;
122 while ((str = strpbrk(prev, "\"\\")) && str < end) {
123 fwrite(prev, 1, str - prev, file);
124 fprintf(file, "\\%c", *str);
125 prev = str + 1;
128 if (*prev)
129 fwrite(prev, 1, end - prev, file);
132 /* Write text to a file as a C string literal.
134 * This function also prints any characters after the last newline, although
135 * normally the input string should end with a newline.
137 static void opencl_print_as_c_string(const char *str, FILE *file)
139 const char *prev = str;
141 while ((str = strchr(prev, '\n'))) {
142 fprintf(file, "\n\"");
143 opencl_print_escaped(prev, str, file);
144 fprintf(file, "\\n\"");
146 prev = str + 1;
149 if (*prev) {
150 fprintf(file, "\n\"");
151 opencl_print_escaped(prev, prev + strlen(prev), file);
152 fprintf(file, "\"");
156 /* Write the code that we have accumulated in the kernel isl_printer to the
157 * kernel.cl file. If the opencl_embed_kernel_code option has been set, print
158 * the code as a C string literal. Start that string literal with an empty
159 * line, such that line numbers reported by the OpenCL C compiler match those
160 * of the kernel file.
162 * Return 0 on success and -1 on failure.
164 static int opencl_write_kernel_file(struct opencl_info *opencl)
166 char *raw = isl_printer_get_str(opencl->kprinter);
168 if (!raw)
169 return -1;
171 if (opencl->options->opencl_embed_kernel_code) {
172 fprintf(opencl->kernel_c,
173 "static const char kernel_code[] = \"\\n\"");
174 opencl_print_as_c_string(raw, opencl->kernel_c);
175 fprintf(opencl->kernel_c, ";\n");
176 } else
177 fprintf(opencl->kernel_c, "%s", raw);
179 free(raw);
181 return 0;
184 /* Close all output files. Write the kernel contents to the kernel file before
185 * closing it.
187 * Return 0 on success and -1 on failure.
189 static int opencl_close_files(struct opencl_info *info)
191 int r = 0;
193 if (info->kernel_c) {
194 r = opencl_write_kernel_file(info);
195 fclose(info->kernel_c);
197 if (info->host_c)
198 fclose(info->host_c);
200 return r;
203 static __isl_give isl_printer *opencl_print_host_macros(
204 __isl_take isl_printer *p)
206 const char *macros =
207 "#define openclCheckReturn(ret) \\\n"
208 " if (ret != CL_SUCCESS) {\\\n"
209 " fprintf(stderr, \"OpenCL error: %s\\n\", "
210 "opencl_error_string(ret)); \\\n"
211 " fflush(stderr); \\\n"
212 " assert(ret == CL_SUCCESS);\\\n }\n";
214 p = isl_printer_start_line(p);
215 p = isl_printer_print_str(p, macros);
216 p = isl_printer_end_line(p);
218 p = isl_ast_op_type_print_macro(isl_ast_op_max, p);
220 return p;
223 static __isl_give isl_printer *opencl_declare_device_arrays(
224 __isl_take isl_printer *p, struct gpu_prog *prog)
226 int i;
228 for (i = 0; i < prog->n_array; ++i) {
229 if (gpu_array_is_read_only_scalar(&prog->array[i]))
230 continue;
231 if (!prog->array[i].accessed)
232 continue;
233 p = isl_printer_start_line(p);
234 p = isl_printer_print_str(p, "cl_mem dev_");
235 p = isl_printer_print_str(p, prog->array[i].name);
236 p = isl_printer_print_str(p, ";");
237 p = isl_printer_end_line(p);
239 p = isl_printer_start_line(p);
240 p = isl_printer_end_line(p);
241 return p;
244 /* Given an array, check whether its positive size guard expression is
245 * trivial.
247 static int is_array_positive_size_guard_trivial(struct gpu_array_info *array)
249 isl_set *guard;
250 int is_trivial;
252 guard = gpu_array_positive_size_guard(array);
253 is_trivial = isl_set_plain_is_universe(guard);
254 isl_set_free(guard);
255 return is_trivial;
258 /* Allocate a device array for "array'.
260 * Emit a max-expression to ensure the device array can contain at least one
261 * element if the array's positive size guard expression is not trivial.
263 static __isl_give isl_printer *allocate_device_array(__isl_take isl_printer *p,
264 struct gpu_array_info *array)
266 int need_lower_bound;
268 p = ppcg_start_block(p);
270 p = isl_printer_start_line(p);
271 p = isl_printer_print_str(p, "dev_");
272 p = isl_printer_print_str(p, array->name);
273 p = isl_printer_print_str(p, " = clCreateBuffer(context, ");
274 p = isl_printer_print_str(p, "CL_MEM_READ_WRITE, ");
276 need_lower_bound = !is_array_positive_size_guard_trivial(array);
277 if (need_lower_bound) {
278 p = isl_printer_print_str(p, "max(sizeof(");
279 p = isl_printer_print_str(p, array->type);
280 p = isl_printer_print_str(p, "), ");
282 p = gpu_array_info_print_size(p, array);
283 if (need_lower_bound)
284 p = isl_printer_print_str(p, ")");
286 p = isl_printer_print_str(p, ", NULL, &err);");
287 p = isl_printer_end_line(p);
288 p = isl_printer_start_line(p);
289 p = isl_printer_print_str(p, "openclCheckReturn(err);");
290 p = isl_printer_end_line(p);
292 p = ppcg_end_block(p);
294 return p;
297 /* Allocate accessed device arrays.
299 static __isl_give isl_printer *opencl_allocate_device_arrays(
300 __isl_take isl_printer *p, struct gpu_prog *prog)
302 int i;
304 for (i = 0; i < prog->n_array; ++i) {
305 struct gpu_array_info *array = &prog->array[i];
307 if (gpu_array_is_read_only_scalar(array))
308 continue;
309 if (!array->accessed)
310 continue;
312 p = allocate_device_array(p, array);
314 p = isl_printer_start_line(p);
315 p = isl_printer_end_line(p);
316 return p;
319 /* Print a call to the OpenCL clSetKernelArg() function which sets
320 * the arguments of the kernel. arg_name and arg_index are the name and the
321 * index of the kernel argument. The index of the leftmost argument of
322 * the kernel is 0 whereas the index of the rightmost argument of the kernel
323 * is n - 1, where n is the total number of the kernel arguments.
324 * read_only_scalar is a boolean that indicates whether the argument is a read
325 * only scalar.
327 static __isl_give isl_printer *opencl_set_kernel_argument(
328 __isl_take isl_printer *p, int kernel_id,
329 const char *arg_name, int arg_index, int read_only_scalar)
331 p = isl_printer_start_line(p);
332 p = isl_printer_print_str(p,
333 "openclCheckReturn(clSetKernelArg(kernel");
334 p = isl_printer_print_int(p, kernel_id);
335 p = isl_printer_print_str(p, ", ");
336 p = isl_printer_print_int(p, arg_index);
337 p = isl_printer_print_str(p, ", sizeof(");
339 if (read_only_scalar) {
340 p = isl_printer_print_str(p, arg_name);
341 p = isl_printer_print_str(p, "), &");
342 } else
343 p = isl_printer_print_str(p, "cl_mem), (void *) &dev_");
345 p = isl_printer_print_str(p, arg_name);
346 p = isl_printer_print_str(p, "));");
347 p = isl_printer_end_line(p);
349 return p;
352 /* Print the block sizes as a list of the sizes in each
353 * dimension.
355 static __isl_give isl_printer *opencl_print_block_sizes(
356 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
358 int i;
360 if (kernel->n_block > 0)
361 for (i = 0; i < kernel->n_block; ++i) {
362 if (i)
363 p = isl_printer_print_str(p, ", ");
364 p = isl_printer_print_int(p, kernel->block_dim[i]);
366 else
367 p = isl_printer_print_str(p, "1");
369 return p;
372 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
373 * clSetKernelArg() function for each kernel argument.
375 static __isl_give isl_printer *opencl_set_kernel_arguments(
376 __isl_take isl_printer *p, struct gpu_prog *prog,
377 struct ppcg_kernel *kernel)
379 int i, n, ro;
380 unsigned nparam;
381 isl_space *space;
382 int arg_index = 0;
384 for (i = 0; i < prog->n_array; ++i) {
385 isl_set *arr;
386 int empty;
388 space = isl_space_copy(prog->array[i].space);
389 arr = isl_union_set_extract_set(kernel->arrays, space);
390 empty = isl_set_plain_is_empty(arr);
391 isl_set_free(arr);
392 if (empty)
393 continue;
394 ro = gpu_array_is_read_only_scalar(&prog->array[i]);
395 opencl_set_kernel_argument(p, kernel->id, prog->array[i].name,
396 arg_index, ro);
397 arg_index++;
400 space = isl_union_set_get_space(kernel->arrays);
401 nparam = isl_space_dim(space, isl_dim_param);
402 for (i = 0; i < nparam; ++i) {
403 const char *name;
405 name = isl_space_get_dim_name(space, isl_dim_param, i);
406 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
407 arg_index++;
409 isl_space_free(space);
411 n = isl_space_dim(kernel->space, isl_dim_set);
412 for (i = 0; i < n; ++i) {
413 const char *name;
415 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
416 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
417 arg_index++;
420 return p;
423 /* Print the arguments to a kernel declaration or call. If "types" is set,
424 * then print a declaration (including the types of the arguments).
426 * The arguments are printed in the following order
427 * - the arrays accessed by the kernel
428 * - the parameters
429 * - the host loop iterators
431 static __isl_give isl_printer *opencl_print_kernel_arguments(
432 __isl_take isl_printer *p, struct gpu_prog *prog,
433 struct ppcg_kernel *kernel, int types)
435 int i, n;
436 int first = 1;
437 unsigned nparam;
438 isl_space *space;
439 const char *type;
441 for (i = 0; i < prog->n_array; ++i) {
442 isl_set *arr;
443 int empty;
445 space = isl_space_copy(prog->array[i].space);
446 arr = isl_union_set_extract_set(kernel->arrays, space);
447 empty = isl_set_plain_is_empty(arr);
448 isl_set_free(arr);
449 if (empty)
450 continue;
452 if (!first)
453 p = isl_printer_print_str(p, ", ");
455 if (types)
456 p = gpu_array_info_print_declaration_argument(p,
457 &prog->array[i], "__global");
458 else
459 p = gpu_array_info_print_call_argument(p,
460 &prog->array[i]);
462 first = 0;
465 space = isl_union_set_get_space(kernel->arrays);
466 nparam = isl_space_dim(space, isl_dim_param);
467 for (i = 0; i < nparam; ++i) {
468 const char *name;
470 name = isl_space_get_dim_name(space, isl_dim_param, i);
472 if (!first)
473 p = isl_printer_print_str(p, ", ");
474 if (types)
475 p = isl_printer_print_str(p, "int ");
476 p = isl_printer_print_str(p, name);
478 first = 0;
480 isl_space_free(space);
482 n = isl_space_dim(kernel->space, isl_dim_set);
483 type = isl_options_get_ast_iterator_type(prog->ctx);
484 for (i = 0; i < n; ++i) {
485 const char *name;
487 if (!first)
488 p = isl_printer_print_str(p, ", ");
489 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
490 if (types) {
491 p = isl_printer_print_str(p, type);
492 p = isl_printer_print_str(p, " ");
494 p = isl_printer_print_str(p, name);
496 first = 0;
499 return p;
502 /* Print the header of the given kernel.
504 static __isl_give isl_printer *opencl_print_kernel_header(
505 __isl_take isl_printer *p, struct gpu_prog *prog,
506 struct ppcg_kernel *kernel)
508 p = isl_printer_start_line(p);
509 p = isl_printer_print_str(p, "__kernel void kernel");
510 p = isl_printer_print_int(p, kernel->id);
511 p = isl_printer_print_str(p, "(");
512 p = opencl_print_kernel_arguments(p, prog, kernel, 1);
513 p = isl_printer_print_str(p, ")");
514 p = isl_printer_end_line(p);
516 return p;
519 /* Print a list of iterators of type "type" with names "ids" to "p".
520 * Each iterator is assigned the corresponding opencl identifier returned
521 * by the function "opencl_id".
522 * Unlike the equivalent function in the CUDA backend which prints iterators
523 * in reverse order to promote coalescing, this function does not print
524 * iterators in reverse order. The OpenCL backend currently does not take
525 * into account any coalescing considerations.
527 static __isl_give isl_printer *print_iterators(__isl_take isl_printer *p,
528 const char *type, __isl_keep isl_id_list *ids, const char *opencl_id)
530 int i, n;
532 n = isl_id_list_n_id(ids);
533 if (n <= 0)
534 return p;
535 p = isl_printer_start_line(p);
536 p = isl_printer_print_str(p, type);
537 p = isl_printer_print_str(p, " ");
538 for (i = 0; i < n; ++i) {
539 isl_id *id;
541 if (i)
542 p = isl_printer_print_str(p, ", ");
543 id = isl_id_list_get_id(ids, i);
544 p = isl_printer_print_id(p, id);
545 isl_id_free(id);
546 p = isl_printer_print_str(p, " = ");
547 p = isl_printer_print_str(p, opencl_id);
548 p = isl_printer_print_str(p, "(");
549 p = isl_printer_print_int(p, i);
550 p = isl_printer_print_str(p, ")");
552 p = isl_printer_print_str(p, ";");
553 p = isl_printer_end_line(p);
555 return p;
558 static __isl_give isl_printer *opencl_print_kernel_iterators(
559 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
561 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
562 const char *type;
564 type = isl_options_get_ast_iterator_type(ctx);
566 p = print_iterators(p, type, kernel->block_ids, "get_group_id");
567 p = print_iterators(p, type, kernel->thread_ids, "get_local_id");
569 return p;
572 static __isl_give isl_printer *opencl_print_kernel_var(
573 __isl_take isl_printer *p, struct ppcg_kernel_var *var)
575 int j;
576 isl_val *v;
578 p = isl_printer_start_line(p);
579 if (var->type == ppcg_access_shared)
580 p = isl_printer_print_str(p, "__local ");
581 p = isl_printer_print_str(p, var->array->type);
582 p = isl_printer_print_str(p, " ");
583 p = isl_printer_print_str(p, var->name);
584 for (j = 0; j < var->array->n_index; ++j) {
585 p = isl_printer_print_str(p, "[");
586 v = isl_vec_get_element_val(var->size, j);
587 p = isl_printer_print_val(p, v);
588 p = isl_printer_print_str(p, "]");
589 isl_val_free(v);
591 p = isl_printer_print_str(p, ";");
592 p = isl_printer_end_line(p);
594 return p;
597 static __isl_give isl_printer *opencl_print_kernel_vars(
598 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
600 int i;
602 for (i = 0; i < kernel->n_var; ++i)
603 p = opencl_print_kernel_var(p, &kernel->var[i]);
605 return p;
608 /* Print a call to barrier() which is a sync statement.
609 * All work-items in a work-group executing the kernel on a processor must
610 * execute the barrier() function before any are allowed to continue execution
611 * beyond the barrier.
612 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
613 * variables stored in local memory or queue a memory fence to ensure correct
614 * ordering of memory operations to local memory.
615 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
616 * fence to ensure correct ordering of memory operations to global memory.
618 static __isl_give isl_printer *opencl_print_sync(__isl_take isl_printer *p,
619 struct ppcg_kernel_stmt *stmt)
621 p = isl_printer_start_line(p);
622 p = isl_printer_print_str(p,
623 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
624 p = isl_printer_end_line(p);
626 return p;
629 /* Data structure containing function names for which the calls
630 * should be changed from
632 * name(arg)
634 * to
636 * opencl_name((type) (arg))
638 static struct ppcg_opencl_fn {
639 const char *name;
640 const char *opencl_name;
641 const char *type;
642 } opencl_fn[] = {
643 { "expf", "exp", "float" },
644 { "powf", "pow", "float" },
645 { "sqrtf", "sqrt", "float" },
648 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
650 /* If the name of function called by "expr" matches any of those
651 * in ppcg_opencl_fn, then replace the call by a cast to the corresponding
652 * type in ppcg_opencl_fn and a call to corresponding OpenCL function.
654 static __isl_give pet_expr *map_opencl_call(__isl_take pet_expr *expr,
655 void *user)
657 const char *name;
658 int i;
660 name = pet_expr_call_get_name(expr);
661 for (i = 0; i < ARRAY_SIZE(opencl_fn); ++i) {
662 pet_expr *arg;
664 if (strcmp(name, opencl_fn[i].name))
665 continue;
666 expr = pet_expr_call_set_name(expr, opencl_fn[i].opencl_name);
667 arg = pet_expr_get_arg(expr, 0);
668 arg = pet_expr_new_cast(opencl_fn[i].type, arg);
669 expr = pet_expr_set_arg(expr, 0, arg);
671 return expr;
674 /* Print the body of a statement from the input program,
675 * for use in OpenCL code.
677 * Before calling ppcg_kernel_print_domain to print the actual statement body,
678 * we first modify this body to take into account that the output code
679 * is OpenCL code. In particular, if the statement calls any function
680 * with a "f" suffix, then it needs to be replaced by a call to
681 * the corresponding function without suffix after casting the argument
682 * to a float.
684 static __isl_give isl_printer *print_opencl_kernel_domain(
685 __isl_take isl_printer *p, struct ppcg_kernel_stmt *stmt)
687 struct pet_stmt *ps;
688 pet_tree *tree;
690 ps = stmt->u.d.stmt->stmt;
691 tree = pet_tree_copy(ps->body);
692 ps->body = pet_tree_map_call_expr(ps->body, &map_opencl_call, NULL);
693 p = ppcg_kernel_print_domain(p, stmt);
694 pet_tree_free(ps->body);
695 ps->body = tree;
697 return p;
700 /* This function is called for each user statement in the AST,
701 * i.e., for each kernel body statement, copy statement or sync statement.
703 static __isl_give isl_printer *opencl_print_kernel_stmt(
704 __isl_take isl_printer *p,
705 __isl_take isl_ast_print_options *print_options,
706 __isl_keep isl_ast_node *node, void *user)
708 isl_id *id;
709 struct ppcg_kernel_stmt *stmt;
711 id = isl_ast_node_get_annotation(node);
712 stmt = isl_id_get_user(id);
713 isl_id_free(id);
715 isl_ast_print_options_free(print_options);
717 switch (stmt->type) {
718 case ppcg_kernel_copy:
719 return ppcg_kernel_print_copy(p, stmt);
720 case ppcg_kernel_sync:
721 return opencl_print_sync(p, stmt);
722 case ppcg_kernel_domain:
723 return print_opencl_kernel_domain(p, stmt);
726 return p;
729 /* Return true if there is a double array in prog->array or
730 * if any of the types in prog->scop involve any doubles.
731 * To check the latter condition, we simply search for the string "double"
732 * in the type definitions, which may result in false positives.
734 static __isl_give int any_double_elements(struct gpu_prog *prog)
736 int i;
738 for (i = 0; i < prog->n_array; ++i)
739 if (strcmp(prog->array[i].type, "double") == 0)
740 return 1;
742 for (i = 0; i < prog->scop->pet->n_type; ++i) {
743 struct pet_type *type = prog->scop->pet->types[i];
745 if (strstr(type->definition, "double"))
746 return 1;
749 return 0;
752 /* Prints a #pragma to enable support for double floating-point
753 * precision. OpenCL 1.0 adds support for double precision floating-point as
754 * an optional extension. An application that wants to use double will need to
755 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
756 * any double precision data type is declared in the kernel code.
758 static __isl_give isl_printer *opencl_enable_double_support(
759 __isl_take isl_printer *p)
761 p = isl_printer_start_line(p);
762 p = isl_printer_print_str(p, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
763 " enable");
764 p = isl_printer_end_line(p);
765 p = isl_printer_start_line(p);
766 p = isl_printer_end_line(p);
768 return p;
771 static __isl_give isl_printer *opencl_print_kernel(struct gpu_prog *prog,
772 struct ppcg_kernel *kernel, __isl_take isl_printer *p)
774 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
775 isl_ast_print_options *print_options;
777 print_options = isl_ast_print_options_alloc(ctx);
778 print_options = isl_ast_print_options_set_print_user(print_options,
779 &opencl_print_kernel_stmt, NULL);
781 p = isl_printer_set_output_format(p, ISL_FORMAT_C);
782 p = opencl_print_kernel_header(p, prog, kernel);
783 p = isl_printer_print_str(p, "{");
784 p = isl_printer_end_line(p);
785 p = isl_printer_indent(p, 4);
786 p = opencl_print_kernel_iterators(p, kernel);
787 p = opencl_print_kernel_vars(p, kernel);
788 p = isl_printer_end_line(p);
789 p = gpu_print_macros(p, kernel->tree);
790 p = isl_ast_node_print(kernel->tree, p, print_options);
791 p = isl_printer_indent(p, -4);
792 p = isl_printer_start_line(p);
793 p = isl_printer_print_str(p, "}");
794 p = isl_printer_end_line(p);
796 return p;
799 struct print_host_user_data_opencl {
800 struct opencl_info *opencl;
801 struct gpu_prog *prog;
804 /* This function prints the i'th block size multiplied by the i'th grid size,
805 * where i (a parameter to this function) is one of the possible dimensions of
806 * grid sizes and block sizes.
807 * If the dimension of block sizes is not equal to the dimension of grid sizes
808 * the output is calculated as follows:
810 * Suppose that:
811 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
812 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
814 * The output is:
815 * If (i > dim2) then the output is block_sizes[i]
816 * If (i > dim1) then the output is grid_sizes[i]
818 static __isl_give isl_printer *opencl_print_total_number_of_work_items_for_dim(
819 __isl_take isl_printer *p, struct ppcg_kernel *kernel, int i)
821 int grid_dim, block_dim;
822 isl_pw_aff *bound_grid;
824 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
825 block_dim = kernel->n_block;
827 if (i < min(grid_dim, block_dim)) {
828 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
829 p = isl_printer_print_str(p, "(");
830 p = isl_printer_print_pw_aff(p, bound_grid);
831 p = isl_printer_print_str(p, ") * ");
832 p = isl_printer_print_int(p, kernel->block_dim[i]);
833 isl_pw_aff_free(bound_grid);
834 } else if (i >= grid_dim)
835 p = isl_printer_print_int(p, kernel->block_dim[i]);
836 else {
837 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
838 p = isl_printer_print_pw_aff(p, bound_grid);
839 isl_pw_aff_free(bound_grid);
842 return p;
845 /* Print a list that represents the total number of work items. The list is
846 * constructed by performing an element-wise multiplication of the block sizes
847 * and the grid sizes. To explain how the list is constructed, suppose that:
848 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
849 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
851 * The output of this function is constructed as follows:
852 * If (dim1 > dim2) then the output is the following list:
853 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
854 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
856 * If (dim2 > dim1) then the output is the following list:
857 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
858 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
860 * To calculate the total number of work items out of the list constructed by
861 * this function, the user should multiply the elements of the list.
863 static __isl_give isl_printer *opencl_print_total_number_of_work_items_as_list(
864 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
866 int i;
867 int grid_dim, block_dim;
869 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
870 block_dim = kernel->n_block;
872 if ((grid_dim <= 0) || (block_dim <= 0)) {
873 p = isl_printer_print_str(p, "1");
874 return p;
877 for (i = 0; i <= max(grid_dim, block_dim) - 1; i++) {
878 if (i > 0)
879 p = isl_printer_print_str(p, ", ");
881 p = opencl_print_total_number_of_work_items_for_dim(p,
882 kernel, i);
885 return p;
888 /* Copy "array" from the host to the device (to_host = 0) or
889 * back from the device to the host (to_host = 1).
891 static __isl_give isl_printer *copy_array(__isl_take isl_printer *p,
892 struct gpu_array_info *array, int to_host)
894 p = isl_printer_start_line(p);
895 p = isl_printer_print_str(p, "openclCheckReturn(");
896 if (to_host)
897 p = isl_printer_print_str(p, "clEnqueueReadBuffer");
898 else
899 p = isl_printer_print_str(p, "clEnqueueWriteBuffer");
900 p = isl_printer_print_str(p, "(queue, dev_");
901 p = isl_printer_print_str(p, array->name);
902 p = isl_printer_print_str(p, ", CL_TRUE, 0, ");
903 p = gpu_array_info_print_size(p, array);
905 if (gpu_array_is_scalar(array))
906 p = isl_printer_print_str(p, ", &");
907 else
908 p = isl_printer_print_str(p, ", ");
909 p = isl_printer_print_str(p, array->name);
910 p = isl_printer_print_str(p, ", 0, NULL, NULL));");
911 p = isl_printer_end_line(p);
913 return p;
916 /* Copy "array" from the host to the device.
918 static __isl_give isl_printer *copy_array_to_device(__isl_take isl_printer *p,
919 void *user)
921 struct gpu_array_info *array = user;
923 return copy_array(p, array, 0);
926 /* Copy "array" back from the device to the host.
928 static __isl_give isl_printer *copy_array_from_device(__isl_take isl_printer *p,
929 void *user)
931 struct gpu_array_info *array = user;
933 return copy_array(p, array, 1);
936 /* Copy the "copy" arrays from the host to the device (to_host = 0) or
937 * back from the device to the host (to_host = 1).
939 * Only perform the copying for arrays with strictly positive size.
941 static __isl_give isl_printer *opencl_copy_arrays(__isl_take isl_printer *p,
942 struct gpu_prog *prog, __isl_keep isl_union_set *copy, int to_host)
944 int i;
946 for (i = 0; i < prog->n_array; ++i) {
947 struct gpu_array_info *array = &prog->array[i];
948 isl_space *space;
949 isl_set *copy_i;
950 isl_set *guard;
951 int empty;
953 if (gpu_array_is_read_only_scalar(array))
954 continue;
956 space = isl_space_copy(array->space);
957 copy_i = isl_union_set_extract_set(copy, space);
958 empty = isl_set_plain_is_empty(copy_i);
959 isl_set_free(copy_i);
960 if (empty)
961 continue;
963 guard = gpu_array_positive_size_guard(array);
964 p = ppcg_print_guarded(p, guard, isl_set_copy(prog->context),
965 to_host ? &copy_array_from_device :
966 &copy_array_to_device, array);
969 p = isl_printer_start_line(p);
970 p = isl_printer_end_line(p);
971 return p;
974 /* Copy the prog->copy_in arrays from the host to the device.
976 static __isl_give isl_printer *opencl_copy_arrays_to_device(
977 __isl_take isl_printer *p, struct gpu_prog *prog)
979 return opencl_copy_arrays(p, prog, prog->copy_in, 0);
982 /* Copy the prog->copy_out arrays back from the device to the host.
984 static __isl_give isl_printer *opencl_copy_arrays_from_device(
985 __isl_take isl_printer *p, struct gpu_prog *prog)
987 return opencl_copy_arrays(p, prog, prog->copy_out, 1);
990 /* Print the user statement of the host code to "p".
992 * In particular, print a block of statements that defines the grid
993 * and the work group and then launches the kernel.
995 * A grid is composed of many work groups (blocks), each work group holds
996 * many work-items (threads).
998 * global_work_size[kernel->n_block] represents the total number of work
999 * items. It points to an array of kernel->n_block unsigned
1000 * values that describe the total number of work-items that will execute
1001 * the kernel. The total number of work-items is computed as:
1002 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
1004 * The size of each work group (i.e. the number of work-items in each work
1005 * group) is described using block_size[kernel->n_block]. The total
1006 * number of work-items in a block (work-group) is computed as:
1007 * block_size[0] *... * block_size[kernel->n_block - 1].
1009 * For more information check:
1010 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
1012 static __isl_give isl_printer *opencl_print_host_user(
1013 __isl_take isl_printer *p,
1014 __isl_take isl_ast_print_options *print_options,
1015 __isl_keep isl_ast_node *node, void *user)
1017 isl_id *id;
1018 struct ppcg_kernel *kernel;
1019 struct print_host_user_data_opencl *data;
1021 id = isl_ast_node_get_annotation(node);
1022 kernel = isl_id_get_user(id);
1023 isl_id_free(id);
1025 data = (struct print_host_user_data_opencl *) user;
1027 p = isl_printer_start_line(p);
1028 p = isl_printer_print_str(p, "{");
1029 p = isl_printer_end_line(p);
1030 p = isl_printer_indent(p, 2);
1032 p = isl_printer_start_line(p);
1033 p = isl_printer_print_str(p, "size_t global_work_size[");
1035 if (kernel->n_block > 0)
1036 p = isl_printer_print_int(p, kernel->n_block);
1037 else
1038 p = isl_printer_print_int(p, 1);
1040 p = isl_printer_print_str(p, "] = {");
1041 p = opencl_print_total_number_of_work_items_as_list(p, kernel);
1042 p = isl_printer_print_str(p, "};");
1043 p = isl_printer_end_line(p);
1045 p = isl_printer_start_line(p);
1046 p = isl_printer_print_str(p, "size_t block_size[");
1048 if (kernel->n_block > 0)
1049 p = isl_printer_print_int(p, kernel->n_block);
1050 else
1051 p = isl_printer_print_int(p, 1);
1053 p = isl_printer_print_str(p, "] = {");
1054 p = opencl_print_block_sizes(p, kernel);
1055 p = isl_printer_print_str(p, "};");
1056 p = isl_printer_end_line(p);
1058 p = isl_printer_start_line(p);
1059 p = isl_printer_print_str(p, "cl_kernel kernel");
1060 p = isl_printer_print_int(p, kernel->id);
1061 p = isl_printer_print_str(p, " = clCreateKernel(program, \"kernel");
1062 p = isl_printer_print_int(p, kernel->id);
1063 p = isl_printer_print_str(p, "\", &err);");
1064 p = isl_printer_end_line(p);
1065 p = isl_printer_start_line(p);
1066 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1067 p = isl_printer_end_line(p);
1069 opencl_set_kernel_arguments(p, data->prog, kernel);
1071 p = isl_printer_start_line(p);
1072 p = isl_printer_print_str(p, "openclCheckReturn(clEnqueueNDRangeKernel"
1073 "(queue, kernel");
1074 p = isl_printer_print_int(p, kernel->id);
1075 p = isl_printer_print_str(p, ", ");
1076 if (kernel->n_block > 0)
1077 p = isl_printer_print_int(p, kernel->n_block);
1078 else
1079 p = isl_printer_print_int(p, 1);
1081 p = isl_printer_print_str(p, ", NULL, global_work_size, "
1082 "block_size, "
1083 "0, NULL, NULL));");
1084 p = isl_printer_end_line(p);
1085 p = isl_printer_start_line(p);
1086 p = isl_printer_print_str(p, "openclCheckReturn("
1087 "clReleaseKernel(kernel");
1088 p = isl_printer_print_int(p, kernel->id);
1089 p = isl_printer_print_str(p, "));");
1090 p = isl_printer_end_line(p);
1091 p = isl_printer_start_line(p);
1092 p = isl_printer_print_str(p, "clFinish(queue);");
1093 p = isl_printer_end_line(p);
1094 p = isl_printer_indent(p, -2);
1095 p = isl_printer_start_line(p);
1096 p = isl_printer_print_str(p, "}");
1097 p = isl_printer_end_line(p);
1099 p = isl_printer_start_line(p);
1100 p = isl_printer_end_line(p);
1102 data->opencl->kprinter = opencl_print_kernel(data->prog, kernel,
1103 data->opencl->kprinter);
1105 isl_ast_print_options_free(print_options);
1107 return p;
1110 static __isl_give isl_printer *opencl_print_host_code(
1111 __isl_take isl_printer *p, struct gpu_prog *prog,
1112 __isl_keep isl_ast_node *tree, struct opencl_info *opencl)
1114 isl_ast_print_options *print_options;
1115 isl_ctx *ctx = isl_ast_node_get_ctx(tree);
1116 struct print_host_user_data_opencl data = { opencl, prog };
1118 print_options = isl_ast_print_options_alloc(ctx);
1119 print_options = isl_ast_print_options_set_print_user(print_options,
1120 &opencl_print_host_user, &data);
1122 p = gpu_print_macros(p, tree);
1123 p = isl_ast_node_print(tree, p, print_options);
1125 return p;
1128 /* Create an OpenCL device, context, command queue and build the kernel.
1129 * input is the name of the input file provided to ppcg.
1131 static __isl_give isl_printer *opencl_setup(__isl_take isl_printer *p,
1132 const char *input, struct opencl_info *info)
1134 p = isl_printer_start_line(p);
1135 p = isl_printer_print_str(p, "cl_device_id device;");
1136 p = isl_printer_end_line(p);
1137 p = isl_printer_start_line(p);
1138 p = isl_printer_print_str(p, "cl_context context;");
1139 p = isl_printer_end_line(p);
1140 p = isl_printer_start_line(p);
1141 p = isl_printer_print_str(p, "cl_program program;");
1142 p = isl_printer_end_line(p);
1143 p = isl_printer_start_line(p);
1144 p = isl_printer_print_str(p, "cl_command_queue queue;");
1145 p = isl_printer_end_line(p);
1146 p = isl_printer_start_line(p);
1147 p = isl_printer_print_str(p, "cl_int err;");
1148 p = isl_printer_end_line(p);
1149 p = isl_printer_start_line(p);
1150 p = isl_printer_print_str(p, "device = opencl_create_device(");
1151 p = isl_printer_print_int(p, info->options->opencl_use_gpu);
1152 p = isl_printer_print_str(p, ");");
1153 p = isl_printer_end_line(p);
1154 p = isl_printer_start_line(p);
1155 p = isl_printer_print_str(p, "context = clCreateContext(NULL, 1, "
1156 "&device, NULL, NULL, &err);");
1157 p = isl_printer_end_line(p);
1158 p = isl_printer_start_line(p);
1159 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1160 p = isl_printer_end_line(p);
1161 p = isl_printer_start_line(p);
1162 p = isl_printer_print_str(p, "queue = clCreateCommandQueue"
1163 "(context, device, 0, &err);");
1164 p = isl_printer_end_line(p);
1165 p = isl_printer_start_line(p);
1166 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1167 p = isl_printer_end_line(p);
1169 p = isl_printer_start_line(p);
1170 p = isl_printer_print_str(p, "program = ");
1172 if (info->options->opencl_embed_kernel_code) {
1173 p = isl_printer_print_str(p, "opencl_build_program_from_string("
1174 "context, device, kernel_code, "
1175 "sizeof(kernel_code), \"");
1176 } else {
1177 p = isl_printer_print_str(p, "opencl_build_program_from_file("
1178 "context, device, \"");
1179 p = isl_printer_print_str(p, info->kernel_c_name);
1180 p = isl_printer_print_str(p, "\", \"");
1183 if (info->options->opencl_compiler_options)
1184 p = isl_printer_print_str(p,
1185 info->options->opencl_compiler_options);
1187 p = isl_printer_print_str(p, "\");");
1188 p = isl_printer_end_line(p);
1189 p = isl_printer_start_line(p);
1190 p = isl_printer_end_line(p);
1192 return p;
1195 static __isl_give isl_printer *opencl_release_cl_objects(
1196 __isl_take isl_printer *p, struct opencl_info *info)
1198 p = isl_printer_start_line(p);
1199 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseCommandQueue"
1200 "(queue));");
1201 p = isl_printer_end_line(p);
1202 p = isl_printer_start_line(p);
1203 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseProgram"
1204 "(program));");
1205 p = isl_printer_end_line(p);
1206 p = isl_printer_start_line(p);
1207 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseContext"
1208 "(context));");
1209 p = isl_printer_end_line(p);
1211 return p;
1214 /* Free the device array corresponding to "array"
1216 static __isl_give isl_printer *release_device_array(__isl_take isl_printer *p,
1217 struct gpu_array_info *array)
1219 p = isl_printer_start_line(p);
1220 p = isl_printer_print_str(p, "openclCheckReturn("
1221 "clReleaseMemObject(dev_");
1222 p = isl_printer_print_str(p, array->name);
1223 p = isl_printer_print_str(p, "));");
1224 p = isl_printer_end_line(p);
1226 return p;
1229 /* Free the accessed device arrays.
1231 static __isl_give isl_printer *opencl_release_device_arrays(
1232 __isl_take isl_printer *p, struct gpu_prog *prog)
1234 int i;
1236 for (i = 0; i < prog->n_array; ++i) {
1237 struct gpu_array_info *array = &prog->array[i];
1238 if (gpu_array_is_read_only_scalar(array))
1239 continue;
1240 if (!array->accessed)
1241 continue;
1243 p = release_device_array(p, array);
1245 return p;
1248 /* Given a gpu_prog "prog" and the corresponding transformed AST
1249 * "tree", print the entire OpenCL code to "p".
1251 static __isl_give isl_printer *print_opencl(__isl_take isl_printer *p,
1252 struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
1253 struct gpu_types *types, void *user)
1255 struct opencl_info *opencl = user;
1257 opencl->kprinter = isl_printer_set_output_format(opencl->kprinter,
1258 ISL_FORMAT_C);
1259 if (any_double_elements(prog))
1260 opencl->kprinter = opencl_enable_double_support(
1261 opencl->kprinter);
1262 if (opencl->options->opencl_print_kernel_types)
1263 opencl->kprinter = gpu_print_types(opencl->kprinter, types,
1264 prog);
1266 if (!opencl->kprinter)
1267 return isl_printer_free(p);
1269 p = ppcg_start_block(p);
1271 p = opencl_print_host_macros(p);
1273 p = opencl_declare_device_arrays(p, prog);
1274 p = opencl_setup(p, opencl->input, opencl);
1275 p = opencl_allocate_device_arrays(p, prog);
1276 p = opencl_copy_arrays_to_device(p, prog);
1278 p = opencl_print_host_code(p, prog, tree, opencl);
1280 p = opencl_copy_arrays_from_device(p, prog);
1281 p = opencl_release_device_arrays(p, prog);
1282 p = opencl_release_cl_objects(p, opencl);
1284 p = ppcg_end_block(p);
1286 return p;
1289 /* Transform the code in the file called "input" by replacing
1290 * all scops by corresponding OpenCL code.
1291 * The host code is written to "output" or a name derived from
1292 * "input" if "output" is NULL.
1293 * The kernel code is placed in separate files with names
1294 * derived from "output" or "input".
1296 * We let generate_gpu do all the hard work and then let it call
1297 * us back for printing the AST in print_opencl.
1299 * To prepare for this printing, we first open the output files
1300 * and we close them after generate_gpu has finished.
1302 int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
1303 const char *input, const char *output)
1305 struct opencl_info opencl = { options, input, output };
1306 int r;
1308 opencl.kprinter = isl_printer_to_str(ctx);
1309 r = opencl_open_files(&opencl);
1311 if (r >= 0)
1312 r = generate_gpu(ctx, input, opencl.host_c, options,
1313 &print_opencl, &opencl);
1315 if (opencl_close_files(&opencl) < 0)
1316 r = -1;
1317 isl_printer_free(opencl.kprinter);
1319 return r;