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
18 #include "gpu_print.h"
25 #define min(a, b) (((a) < (b)) ? (a) : (b))
26 #define max(a, b) (((a) > (b)) ? (a) : (b))
28 /* options are the global options passed to generate_opencl.
29 * input is the name of the input file.
30 * output is the user-specified output file name and may be NULL
31 * if not specified by the user.
32 * kernel_c_name is the name of the kernel_c file.
33 * kprinter is an isl_printer for the kernel file.
34 * host_c is the generated source file for the host code. kernel_c is
35 * the generated source file for the kernel.
38 struct ppcg_options
*options
;
41 char kernel_c_name
[PATH_MAX
];
43 isl_printer
*kprinter
;
49 /* Open the file called "name" for writing or print an error message.
51 static FILE *open_or_croak(const char *name
)
55 file
= fopen(name
, "w");
57 fprintf(stderr
, "Failed to open \"%s\" for writing\n", name
);
61 /* Open the host .c file and the kernel .h and .cl files for writing.
62 * Their names are derived from info->output (or info->input if
63 * the user did not specify an output file name).
64 * Add the necessary includes to these files, including those specified
67 * Return 0 on success and -1 on failure.
69 static int opencl_open_files(struct opencl_info
*info
)
78 ext
= strrchr(info
->output
, '.');
79 len
= ext
? ext
- info
->output
: strlen(info
->output
);
80 memcpy(name
, info
->output
, len
);
82 info
->host_c
= open_or_croak(info
->output
);
84 len
= ppcg_extract_base_name(name
, info
->input
);
86 strcpy(name
+ len
, "_host.c");
87 info
->host_c
= open_or_croak(name
);
90 memcpy(info
->kernel_c_name
, name
, len
);
91 strcpy(info
->kernel_c_name
+ len
, "_kernel.cl");
92 info
->kernel_c
= open_or_croak(info
->kernel_c_name
);
94 if (!info
->host_c
|| !info
->kernel_c
)
97 fprintf(info
->host_c
, "#include <assert.h>\n");
98 fprintf(info
->host_c
, "#include <stdio.h>\n");
99 fprintf(info
->host_c
, "#include \"ocl_utilities.h\"\n");
100 if (info
->options
->opencl_embed_kernel_code
) {
101 fprintf(info
->host_c
, "#include \"%s\"\n\n",
102 info
->kernel_c_name
);
105 for (i
= 0; i
< info
->options
->opencl_n_include_file
; ++i
) {
106 info
->kprinter
= isl_printer_print_str(info
->kprinter
,
108 info
->kprinter
= isl_printer_print_str(info
->kprinter
,
109 info
->options
->opencl_include_files
[i
]);
110 info
->kprinter
= isl_printer_print_str(info
->kprinter
, ">\n");
116 /* Write text to a file and escape some special characters that would break a
119 static void opencl_print_escaped(const char *str
, const char *end
, FILE *file
)
121 const char *prev
= str
;
123 while ((str
= strpbrk(prev
, "\"\\")) && str
< end
) {
124 fwrite(prev
, 1, str
- prev
, file
);
125 fprintf(file
, "\\%c", *str
);
130 fwrite(prev
, 1, end
- prev
, file
);
133 /* Write text to a file as a C string literal.
135 * This function also prints any characters after the last newline, although
136 * normally the input string should end with a newline.
138 static void opencl_print_as_c_string(const char *str
, FILE *file
)
140 const char *prev
= str
;
142 while ((str
= strchr(prev
, '\n'))) {
143 fprintf(file
, "\n\"");
144 opencl_print_escaped(prev
, str
, file
);
145 fprintf(file
, "\\n\"");
151 fprintf(file
, "\n\"");
152 opencl_print_escaped(prev
, prev
+ strlen(prev
), file
);
157 /* Write the code that we have accumulated in the kernel isl_printer to the
158 * kernel.cl file. If the opencl_embed_kernel_code option has been set, print
159 * the code as a C string literal. Start that string literal with an empty
160 * line, such that line numbers reported by the OpenCL C compiler match those
161 * of the kernel file.
163 * Return 0 on success and -1 on failure.
165 static int opencl_write_kernel_file(struct opencl_info
*opencl
)
167 char *raw
= isl_printer_get_str(opencl
->kprinter
);
172 if (opencl
->options
->opencl_embed_kernel_code
) {
173 fprintf(opencl
->kernel_c
,
174 "static const char kernel_code[] = \"\\n\"");
175 opencl_print_as_c_string(raw
, opencl
->kernel_c
);
176 fprintf(opencl
->kernel_c
, ";\n");
178 fprintf(opencl
->kernel_c
, "%s", raw
);
185 /* Close all output files. Write the kernel contents to the kernel file before
188 * Return 0 on success and -1 on failure.
190 static int opencl_close_files(struct opencl_info
*info
)
194 if (info
->kernel_c
) {
195 r
= opencl_write_kernel_file(info
);
196 fclose(info
->kernel_c
);
199 fclose(info
->host_c
);
204 static __isl_give isl_printer
*opencl_print_host_macros(
205 __isl_take isl_printer
*p
)
208 "#define openclCheckReturn(ret) \\\n"
209 " if (ret != CL_SUCCESS) {\\\n"
210 " fprintf(stderr, \"OpenCL error: %s\\n\", "
211 "opencl_error_string(ret)); \\\n"
212 " fflush(stderr); \\\n"
213 " assert(ret == CL_SUCCESS);\\\n }\n";
215 p
= isl_printer_start_line(p
);
216 p
= isl_printer_print_str(p
, macros
);
217 p
= isl_printer_end_line(p
);
222 static __isl_give isl_printer
*opencl_declare_device_arrays(
223 __isl_take isl_printer
*p
, struct gpu_prog
*prog
)
227 for (i
= 0; i
< prog
->n_array
; ++i
) {
228 if (!gpu_array_requires_device_allocation(&prog
->array
[i
]))
230 p
= isl_printer_start_line(p
);
231 p
= isl_printer_print_str(p
, "cl_mem dev_");
232 p
= isl_printer_print_str(p
, prog
->array
[i
].name
);
233 p
= isl_printer_print_str(p
, ";");
234 p
= isl_printer_end_line(p
);
236 p
= isl_printer_start_line(p
);
237 p
= isl_printer_end_line(p
);
241 /* Given an array, check whether its positive size guard expression is
244 static int is_array_positive_size_guard_trivial(struct gpu_array_info
*array
)
249 guard
= gpu_array_positive_size_guard(array
);
250 is_trivial
= isl_set_plain_is_universe(guard
);
255 /* Allocate a device array for "array'.
257 * Emit a max-expression to ensure the device array can contain at least one
258 * element if the array's positive size guard expression is not trivial.
260 static __isl_give isl_printer
*allocate_device_array(__isl_take isl_printer
*p
,
261 struct gpu_array_info
*array
)
263 int need_lower_bound
;
265 need_lower_bound
= !is_array_positive_size_guard_trivial(array
);
266 if (need_lower_bound
)
267 p
= ppcg_print_macro(isl_ast_op_max
, p
);
269 p
= ppcg_ast_expr_print_macros(array
->bound_expr
, p
);
270 p
= ppcg_start_block(p
);
272 p
= isl_printer_start_line(p
);
273 p
= isl_printer_print_str(p
, "dev_");
274 p
= isl_printer_print_str(p
, array
->name
);
275 p
= isl_printer_print_str(p
, " = clCreateBuffer(context, ");
276 p
= isl_printer_print_str(p
, "CL_MEM_READ_WRITE, ");
278 if (need_lower_bound
) {
279 p
= isl_printer_print_str(p
, ppcg_max
);
280 p
= isl_printer_print_str(p
, "(sizeof(");
281 p
= isl_printer_print_str(p
, array
->type
);
282 p
= isl_printer_print_str(p
, "), ");
284 p
= gpu_array_info_print_size(p
, array
);
285 if (need_lower_bound
)
286 p
= isl_printer_print_str(p
, ")");
288 p
= isl_printer_print_str(p
, ", NULL, &err);");
289 p
= isl_printer_end_line(p
);
290 p
= isl_printer_start_line(p
);
291 p
= isl_printer_print_str(p
, "openclCheckReturn(err);");
292 p
= isl_printer_end_line(p
);
294 p
= ppcg_end_block(p
);
299 /* Allocate accessed device arrays.
301 static __isl_give isl_printer
*opencl_allocate_device_arrays(
302 __isl_take isl_printer
*p
, struct gpu_prog
*prog
)
306 for (i
= 0; i
< prog
->n_array
; ++i
) {
307 struct gpu_array_info
*array
= &prog
->array
[i
];
309 if (!gpu_array_requires_device_allocation(array
))
312 p
= allocate_device_array(p
, array
);
314 p
= isl_printer_start_line(p
);
315 p
= isl_printer_end_line(p
);
319 /* Free the device array corresponding to "array"
321 static __isl_give isl_printer
*release_device_array(__isl_take isl_printer
*p
,
322 struct gpu_array_info
*array
)
324 p
= isl_printer_start_line(p
);
325 p
= isl_printer_print_str(p
, "openclCheckReturn("
326 "clReleaseMemObject(dev_");
327 p
= isl_printer_print_str(p
, array
->name
);
328 p
= isl_printer_print_str(p
, "));");
329 p
= isl_printer_end_line(p
);
334 /* Free the accessed device arrays.
336 static __isl_give isl_printer
*opencl_release_device_arrays(
337 __isl_take isl_printer
*p
, struct gpu_prog
*prog
)
341 for (i
= 0; i
< prog
->n_array
; ++i
) {
342 struct gpu_array_info
*array
= &prog
->array
[i
];
343 if (!gpu_array_requires_device_allocation(array
))
346 p
= release_device_array(p
, array
);
351 /* Create an OpenCL device, context, command queue and build the kernel.
352 * input is the name of the input file provided to ppcg.
354 static __isl_give isl_printer
*opencl_setup(__isl_take isl_printer
*p
,
355 const char *input
, struct opencl_info
*info
)
357 p
= isl_printer_start_line(p
);
358 p
= isl_printer_print_str(p
, "cl_device_id device;");
359 p
= isl_printer_end_line(p
);
360 p
= isl_printer_start_line(p
);
361 p
= isl_printer_print_str(p
, "cl_context context;");
362 p
= isl_printer_end_line(p
);
363 p
= isl_printer_start_line(p
);
364 p
= isl_printer_print_str(p
, "cl_program program;");
365 p
= isl_printer_end_line(p
);
366 p
= isl_printer_start_line(p
);
367 p
= isl_printer_print_str(p
, "cl_command_queue queue;");
368 p
= isl_printer_end_line(p
);
369 p
= isl_printer_start_line(p
);
370 p
= isl_printer_print_str(p
, "cl_int err;");
371 p
= isl_printer_end_line(p
);
372 p
= isl_printer_start_line(p
);
373 p
= isl_printer_print_str(p
, "device = opencl_create_device(");
374 p
= isl_printer_print_int(p
, info
->options
->opencl_use_gpu
);
375 p
= isl_printer_print_str(p
, ");");
376 p
= isl_printer_end_line(p
);
377 p
= isl_printer_start_line(p
);
378 p
= isl_printer_print_str(p
, "context = clCreateContext(NULL, 1, "
379 "&device, NULL, NULL, &err);");
380 p
= isl_printer_end_line(p
);
381 p
= isl_printer_start_line(p
);
382 p
= isl_printer_print_str(p
, "openclCheckReturn(err);");
383 p
= isl_printer_end_line(p
);
384 p
= isl_printer_start_line(p
);
385 p
= isl_printer_print_str(p
, "queue = clCreateCommandQueue"
386 "(context, device, 0, &err);");
387 p
= isl_printer_end_line(p
);
388 p
= isl_printer_start_line(p
);
389 p
= isl_printer_print_str(p
, "openclCheckReturn(err);");
390 p
= isl_printer_end_line(p
);
392 p
= isl_printer_start_line(p
);
393 p
= isl_printer_print_str(p
, "program = ");
395 if (info
->options
->opencl_embed_kernel_code
) {
396 p
= isl_printer_print_str(p
, "opencl_build_program_from_string("
397 "context, device, kernel_code, "
398 "sizeof(kernel_code), \"");
400 p
= isl_printer_print_str(p
, "opencl_build_program_from_file("
401 "context, device, \"");
402 p
= isl_printer_print_str(p
, info
->kernel_c_name
);
403 p
= isl_printer_print_str(p
, "\", \"");
406 if (info
->options
->opencl_compiler_options
)
407 p
= isl_printer_print_str(p
,
408 info
->options
->opencl_compiler_options
);
410 p
= isl_printer_print_str(p
, "\");");
411 p
= isl_printer_end_line(p
);
412 p
= isl_printer_start_line(p
);
413 p
= isl_printer_end_line(p
);
418 static __isl_give isl_printer
*opencl_release_cl_objects(
419 __isl_take isl_printer
*p
, struct opencl_info
*info
)
421 p
= isl_printer_start_line(p
);
422 p
= isl_printer_print_str(p
, "openclCheckReturn(clReleaseCommandQueue"
424 p
= isl_printer_end_line(p
);
425 p
= isl_printer_start_line(p
);
426 p
= isl_printer_print_str(p
, "openclCheckReturn(clReleaseProgram"
428 p
= isl_printer_end_line(p
);
429 p
= isl_printer_start_line(p
);
430 p
= isl_printer_print_str(p
, "openclCheckReturn(clReleaseContext"
432 p
= isl_printer_end_line(p
);
437 /* Print a call to the OpenCL clSetKernelArg() function which sets
438 * the arguments of the kernel. arg_name and arg_index are the name and the
439 * index of the kernel argument. The index of the leftmost argument of
440 * the kernel is 0 whereas the index of the rightmost argument of the kernel
441 * is n - 1, where n is the total number of the kernel arguments.
442 * read_only_scalar is a boolean that indicates whether the argument is a read
445 static __isl_give isl_printer
*opencl_set_kernel_argument(
446 __isl_take isl_printer
*p
, int kernel_id
,
447 const char *arg_name
, int arg_index
, int read_only_scalar
)
449 p
= isl_printer_start_line(p
);
450 p
= isl_printer_print_str(p
,
451 "openclCheckReturn(clSetKernelArg(kernel");
452 p
= isl_printer_print_int(p
, kernel_id
);
453 p
= isl_printer_print_str(p
, ", ");
454 p
= isl_printer_print_int(p
, arg_index
);
455 p
= isl_printer_print_str(p
, ", sizeof(");
457 if (read_only_scalar
) {
458 p
= isl_printer_print_str(p
, arg_name
);
459 p
= isl_printer_print_str(p
, "), &");
461 p
= isl_printer_print_str(p
, "cl_mem), (void *) &dev_");
463 p
= isl_printer_print_str(p
, arg_name
);
464 p
= isl_printer_print_str(p
, "));");
465 p
= isl_printer_end_line(p
);
470 /* Print the block sizes as a list of the sizes in each
473 static __isl_give isl_printer
*opencl_print_block_sizes(
474 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
)
478 if (kernel
->n_block
> 0)
479 for (i
= 0; i
< kernel
->n_block
; ++i
) {
481 p
= isl_printer_print_str(p
, ", ");
482 p
= isl_printer_print_int(p
, kernel
->block_dim
[i
]);
485 p
= isl_printer_print_str(p
, "1");
490 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
491 * clSetKernelArg() function for each kernel argument.
493 static __isl_give isl_printer
*opencl_set_kernel_arguments(
494 __isl_take isl_printer
*p
, struct gpu_prog
*prog
,
495 struct ppcg_kernel
*kernel
)
502 for (i
= 0; i
< prog
->n_array
; ++i
) {
505 required
= ppcg_kernel_requires_array_argument(kernel
, i
);
507 return isl_printer_free(p
);
510 ro
= gpu_array_is_read_only_scalar(&prog
->array
[i
]);
511 opencl_set_kernel_argument(p
, kernel
->id
, prog
->array
[i
].name
,
516 space
= isl_union_set_get_space(kernel
->arrays
);
517 nparam
= isl_space_dim(space
, isl_dim_param
);
518 for (i
= 0; i
< nparam
; ++i
) {
521 name
= isl_space_get_dim_name(space
, isl_dim_param
, i
);
522 opencl_set_kernel_argument(p
, kernel
->id
, name
, arg_index
, 1);
525 isl_space_free(space
);
527 n
= isl_space_dim(kernel
->space
, isl_dim_set
);
528 for (i
= 0; i
< n
; ++i
) {
531 name
= isl_space_get_dim_name(kernel
->space
, isl_dim_set
, i
);
532 opencl_set_kernel_argument(p
, kernel
->id
, name
, arg_index
, 1);
539 /* Print the arguments to a kernel declaration or call. If "types" is set,
540 * then print a declaration (including the types of the arguments).
542 * The arguments are printed in the following order
543 * - the arrays accessed by the kernel
545 * - the host loop iterators
547 static __isl_give isl_printer
*opencl_print_kernel_arguments(
548 __isl_take isl_printer
*p
, struct gpu_prog
*prog
,
549 struct ppcg_kernel
*kernel
, int types
)
557 for (i
= 0; i
< prog
->n_array
; ++i
) {
560 required
= ppcg_kernel_requires_array_argument(kernel
, i
);
562 return isl_printer_free(p
);
567 p
= isl_printer_print_str(p
, ", ");
570 p
= gpu_array_info_print_declaration_argument(p
,
571 &prog
->array
[i
], "__global");
573 p
= gpu_array_info_print_call_argument(p
,
579 space
= isl_union_set_get_space(kernel
->arrays
);
580 nparam
= isl_space_dim(space
, isl_dim_param
);
581 for (i
= 0; i
< nparam
; ++i
) {
584 name
= isl_space_get_dim_name(space
, isl_dim_param
, i
);
587 p
= isl_printer_print_str(p
, ", ");
589 p
= isl_printer_print_str(p
, "int ");
590 p
= isl_printer_print_str(p
, name
);
594 isl_space_free(space
);
596 n
= isl_space_dim(kernel
->space
, isl_dim_set
);
597 type
= isl_options_get_ast_iterator_type(prog
->ctx
);
598 for (i
= 0; i
< n
; ++i
) {
602 p
= isl_printer_print_str(p
, ", ");
603 name
= isl_space_get_dim_name(kernel
->space
, isl_dim_set
, i
);
605 p
= isl_printer_print_str(p
, type
);
606 p
= isl_printer_print_str(p
, " ");
608 p
= isl_printer_print_str(p
, name
);
616 /* Print the header of the given kernel.
618 static __isl_give isl_printer
*opencl_print_kernel_header(
619 __isl_take isl_printer
*p
, struct gpu_prog
*prog
,
620 struct ppcg_kernel
*kernel
)
622 p
= isl_printer_start_line(p
);
623 p
= isl_printer_print_str(p
, "__kernel void kernel");
624 p
= isl_printer_print_int(p
, kernel
->id
);
625 p
= isl_printer_print_str(p
, "(");
626 p
= opencl_print_kernel_arguments(p
, prog
, kernel
, 1);
627 p
= isl_printer_print_str(p
, ")");
628 p
= isl_printer_end_line(p
);
633 /* Print a list of iterators of type "type" with names "ids" to "p".
634 * Each iterator is assigned the corresponding opencl identifier returned
635 * by the function "opencl_id".
636 * Unlike the equivalent function in the CUDA backend which prints iterators
637 * in reverse order to promote coalescing, this function does not print
638 * iterators in reverse order. The OpenCL backend currently does not take
639 * into account any coalescing considerations.
641 static __isl_give isl_printer
*print_iterators(__isl_take isl_printer
*p
,
642 const char *type
, __isl_keep isl_id_list
*ids
, const char *opencl_id
)
646 n
= isl_id_list_n_id(ids
);
649 p
= isl_printer_start_line(p
);
650 p
= isl_printer_print_str(p
, type
);
651 p
= isl_printer_print_str(p
, " ");
652 for (i
= 0; i
< n
; ++i
) {
656 p
= isl_printer_print_str(p
, ", ");
657 id
= isl_id_list_get_id(ids
, i
);
658 p
= isl_printer_print_id(p
, id
);
660 p
= isl_printer_print_str(p
, " = ");
661 p
= isl_printer_print_str(p
, opencl_id
);
662 p
= isl_printer_print_str(p
, "(");
663 p
= isl_printer_print_int(p
, i
);
664 p
= isl_printer_print_str(p
, ")");
666 p
= isl_printer_print_str(p
, ";");
667 p
= isl_printer_end_line(p
);
672 static __isl_give isl_printer
*opencl_print_kernel_iterators(
673 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
)
675 isl_ctx
*ctx
= isl_ast_node_get_ctx(kernel
->tree
);
678 type
= isl_options_get_ast_iterator_type(ctx
);
680 p
= print_iterators(p
, type
, kernel
->block_ids
, "get_group_id");
681 p
= print_iterators(p
, type
, kernel
->thread_ids
, "get_local_id");
686 static __isl_give isl_printer
*opencl_print_kernel_var(
687 __isl_take isl_printer
*p
, struct ppcg_kernel_var
*var
)
692 p
= isl_printer_start_line(p
);
693 if (var
->type
== ppcg_access_shared
)
694 p
= isl_printer_print_str(p
, "__local ");
695 p
= isl_printer_print_str(p
, var
->array
->type
);
696 p
= isl_printer_print_str(p
, " ");
697 p
= isl_printer_print_str(p
, var
->name
);
698 for (j
= 0; j
< var
->array
->n_index
; ++j
) {
699 p
= isl_printer_print_str(p
, "[");
700 v
= isl_vec_get_element_val(var
->size
, j
);
701 p
= isl_printer_print_val(p
, v
);
702 p
= isl_printer_print_str(p
, "]");
705 p
= isl_printer_print_str(p
, ";");
706 p
= isl_printer_end_line(p
);
711 static __isl_give isl_printer
*opencl_print_kernel_vars(
712 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
)
716 for (i
= 0; i
< kernel
->n_var
; ++i
)
717 p
= opencl_print_kernel_var(p
, &kernel
->var
[i
]);
722 /* Print a call to barrier() which is a sync statement.
723 * All work-items in a work-group executing the kernel on a processor must
724 * execute the barrier() function before any are allowed to continue execution
725 * beyond the barrier.
726 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
727 * variables stored in local memory or queue a memory fence to ensure correct
728 * ordering of memory operations to local memory.
729 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
730 * fence to ensure correct ordering of memory operations to global memory.
732 static __isl_give isl_printer
*opencl_print_sync(__isl_take isl_printer
*p
,
733 struct ppcg_kernel_stmt
*stmt
)
735 p
= isl_printer_start_line(p
);
736 p
= isl_printer_print_str(p
,
737 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
738 p
= isl_printer_end_line(p
);
743 /* Data structure containing function names for which the calls
744 * should be changed from
750 * opencl_name((type) (arg))
752 static struct ppcg_opencl_fn
{
754 const char *opencl_name
;
757 { "expf", "exp", "float" },
758 { "powf", "pow", "float" },
759 { "sqrtf", "sqrt", "float" },
762 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
764 /* If the name of function called by "expr" matches any of those
765 * in ppcg_opencl_fn, then replace the call by a cast to the corresponding
766 * type in ppcg_opencl_fn and a call to corresponding OpenCL function.
768 static __isl_give pet_expr
*map_opencl_call(__isl_take pet_expr
*expr
,
774 name
= pet_expr_call_get_name(expr
);
775 for (i
= 0; i
< ARRAY_SIZE(opencl_fn
); ++i
) {
778 if (strcmp(name
, opencl_fn
[i
].name
))
780 expr
= pet_expr_call_set_name(expr
, opencl_fn
[i
].opencl_name
);
781 arg
= pet_expr_get_arg(expr
, 0);
782 arg
= pet_expr_new_cast(opencl_fn
[i
].type
, arg
);
783 expr
= pet_expr_set_arg(expr
, 0, arg
);
788 /* Print the body of a statement from the input program,
789 * for use in OpenCL code.
791 * Before calling ppcg_kernel_print_domain to print the actual statement body,
792 * we first modify this body to take into account that the output code
793 * is OpenCL code. In particular, if the statement calls any function
794 * with a "f" suffix, then it needs to be replaced by a call to
795 * the corresponding function without suffix after casting the argument
798 static __isl_give isl_printer
*print_opencl_kernel_domain(
799 __isl_take isl_printer
*p
, struct ppcg_kernel_stmt
*stmt
)
804 ps
= stmt
->u
.d
.stmt
->stmt
;
805 tree
= pet_tree_copy(ps
->body
);
806 ps
->body
= pet_tree_map_call_expr(ps
->body
, &map_opencl_call
, NULL
);
807 p
= ppcg_kernel_print_domain(p
, stmt
);
808 pet_tree_free(ps
->body
);
814 /* This function is called for each user statement in the AST,
815 * i.e., for each kernel body statement, copy statement or sync statement.
817 static __isl_give isl_printer
*opencl_print_kernel_stmt(
818 __isl_take isl_printer
*p
,
819 __isl_take isl_ast_print_options
*print_options
,
820 __isl_keep isl_ast_node
*node
, void *user
)
823 struct ppcg_kernel_stmt
*stmt
;
825 id
= isl_ast_node_get_annotation(node
);
826 stmt
= isl_id_get_user(id
);
829 isl_ast_print_options_free(print_options
);
831 switch (stmt
->type
) {
832 case ppcg_kernel_copy
:
833 return ppcg_kernel_print_copy(p
, stmt
);
834 case ppcg_kernel_sync
:
835 return opencl_print_sync(p
, stmt
);
836 case ppcg_kernel_domain
:
837 return print_opencl_kernel_domain(p
, stmt
);
843 /* Return true if there is a double array in prog->array or
844 * if any of the types in prog->scop involve any doubles.
845 * To check the latter condition, we simply search for the string "double"
846 * in the type definitions, which may result in false positives.
848 static __isl_give
int any_double_elements(struct gpu_prog
*prog
)
852 for (i
= 0; i
< prog
->n_array
; ++i
)
853 if (strcmp(prog
->array
[i
].type
, "double") == 0)
856 for (i
= 0; i
< prog
->scop
->pet
->n_type
; ++i
) {
857 struct pet_type
*type
= prog
->scop
->pet
->types
[i
];
859 if (strstr(type
->definition
, "double"))
866 /* Prints a #pragma to enable support for double floating-point
867 * precision. OpenCL 1.0 adds support for double precision floating-point as
868 * an optional extension. An application that wants to use double will need to
869 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
870 * any double precision data type is declared in the kernel code.
872 static __isl_give isl_printer
*opencl_enable_double_support(
873 __isl_take isl_printer
*p
)
875 p
= isl_printer_start_line(p
);
876 p
= isl_printer_print_str(p
, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
878 p
= isl_printer_end_line(p
);
879 p
= isl_printer_start_line(p
);
880 p
= isl_printer_end_line(p
);
885 /* Macro definitions for ppcg_min and ppcg_max for use
886 * in OpenCL kernel code.
887 * These macro definitions essentially call the corresponding
888 * OpenCL macros/functions, but first ensure that the two arguments
889 * have the same type, since the OpenCL versions are only defined
890 * in case those arguments have the same type.
892 static const char *opencl_min
=
893 "(x,y) min((__typeof__(x + y)) x, (__typeof__(x + y)) y)";
894 static const char *opencl_max
=
895 "(x,y) max((__typeof__(x + y)) x, (__typeof__(x + y)) y)";
897 /* Set the macro definitions for ppcg_min and ppcg_max to
898 * OpenCL specific versions.
900 static __isl_give isl_printer
*set_opencl_macros(__isl_take isl_printer
*p
)
902 return ppcg_set_macros(p
, opencl_min
, opencl_max
);
905 static __isl_give isl_printer
*opencl_print_kernel(struct gpu_prog
*prog
,
906 struct ppcg_kernel
*kernel
, __isl_take isl_printer
*p
)
908 isl_ctx
*ctx
= isl_ast_node_get_ctx(kernel
->tree
);
909 isl_ast_print_options
*print_options
;
911 print_options
= isl_ast_print_options_alloc(ctx
);
912 print_options
= isl_ast_print_options_set_print_user(print_options
,
913 &opencl_print_kernel_stmt
, NULL
);
915 p
= isl_printer_set_output_format(p
, ISL_FORMAT_C
);
916 p
= opencl_print_kernel_header(p
, prog
, kernel
);
917 p
= isl_printer_print_str(p
, "{");
918 p
= isl_printer_end_line(p
);
919 p
= isl_printer_indent(p
, 4);
920 p
= opencl_print_kernel_iterators(p
, kernel
);
921 p
= opencl_print_kernel_vars(p
, kernel
);
922 p
= isl_printer_end_line(p
);
923 p
= ppcg_set_macro_names(p
);
924 p
= set_opencl_macros(p
);
925 p
= gpu_print_macros(p
, kernel
->tree
);
926 p
= isl_ast_node_print(kernel
->tree
, p
, print_options
);
927 p
= isl_printer_indent(p
, -4);
928 p
= isl_printer_start_line(p
);
929 p
= isl_printer_print_str(p
, "}");
930 p
= isl_printer_end_line(p
);
935 struct print_host_user_data_opencl
{
936 struct opencl_info
*opencl
;
937 struct gpu_prog
*prog
;
940 /* This function prints the i'th block size multiplied by the i'th grid size,
941 * where i (a parameter to this function) is one of the possible dimensions of
942 * grid sizes and block sizes.
943 * If the dimension of block sizes is not equal to the dimension of grid sizes
944 * the output is calculated as follows:
947 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
948 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
951 * If (i > dim2) then the output is block_sizes[i]
952 * If (i > dim1) then the output is grid_sizes[i]
954 static __isl_give isl_printer
*opencl_print_total_number_of_work_items_for_dim(
955 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
, int i
)
957 int grid_dim
, block_dim
;
958 isl_ast_expr
*grid_size_expr
;
959 isl_ast_expr
*bound_grid
;
961 grid_dim
= isl_multi_pw_aff_dim(kernel
->grid_size
, isl_dim_set
);
962 block_dim
= kernel
->n_block
;
964 if (i
< min(grid_dim
, block_dim
)) {
965 grid_size_expr
= kernel
->grid_size_expr
;
966 bound_grid
= isl_ast_expr_get_op_arg(grid_size_expr
, 1 + i
);
967 p
= isl_printer_print_str(p
, "(");
968 p
= isl_printer_print_ast_expr(p
, bound_grid
);
969 p
= isl_printer_print_str(p
, ") * ");
970 p
= isl_printer_print_int(p
, kernel
->block_dim
[i
]);
971 isl_ast_expr_free(bound_grid
);
972 } else if (i
>= grid_dim
) {
973 p
= isl_printer_print_int(p
, kernel
->block_dim
[i
]);
975 grid_size_expr
= kernel
->grid_size_expr
;
976 bound_grid
= isl_ast_expr_get_op_arg(grid_size_expr
, 1 + i
);
977 p
= isl_printer_print_ast_expr(p
, bound_grid
);
978 isl_ast_expr_free(bound_grid
);
984 /* Print a list that represents the total number of work items. The list is
985 * constructed by performing an element-wise multiplication of the block sizes
986 * and the grid sizes. To explain how the list is constructed, suppose that:
987 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
988 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
990 * The output of this function is constructed as follows:
991 * If (dim1 > dim2) then the output is the following list:
992 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
993 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
995 * If (dim2 > dim1) then the output is the following list:
996 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
997 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
999 * To calculate the total number of work items out of the list constructed by
1000 * this function, the user should multiply the elements of the list.
1002 static __isl_give isl_printer
*opencl_print_total_number_of_work_items_as_list(
1003 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
)
1006 int grid_dim
, block_dim
;
1008 grid_dim
= isl_multi_pw_aff_dim(kernel
->grid_size
, isl_dim_set
);
1009 block_dim
= kernel
->n_block
;
1011 if ((grid_dim
<= 0) || (block_dim
<= 0)) {
1012 p
= isl_printer_print_str(p
, "1");
1016 for (i
= 0; i
<= max(grid_dim
, block_dim
) - 1; i
++) {
1018 p
= isl_printer_print_str(p
, ", ");
1020 p
= opencl_print_total_number_of_work_items_for_dim(p
,
1027 /* Copy "array" from the host to the device (to_host = 0) or
1028 * back from the device to the host (to_host = 1).
1030 static __isl_give isl_printer
*copy_array(__isl_take isl_printer
*p
,
1031 struct gpu_array_info
*array
, int to_host
)
1033 p
= isl_printer_start_line(p
);
1034 p
= isl_printer_print_str(p
, "openclCheckReturn(");
1036 p
= isl_printer_print_str(p
, "clEnqueueReadBuffer");
1038 p
= isl_printer_print_str(p
, "clEnqueueWriteBuffer");
1039 p
= isl_printer_print_str(p
, "(queue, dev_");
1040 p
= isl_printer_print_str(p
, array
->name
);
1041 p
= isl_printer_print_str(p
, ", CL_TRUE, 0, ");
1042 p
= gpu_array_info_print_size(p
, array
);
1044 if (gpu_array_is_scalar(array
))
1045 p
= isl_printer_print_str(p
, ", &");
1047 p
= isl_printer_print_str(p
, ", ");
1048 p
= isl_printer_print_str(p
, array
->name
);
1049 p
= isl_printer_print_str(p
, ", 0, NULL, NULL));");
1050 p
= isl_printer_end_line(p
);
1055 /* Print code for initializing the device for execution of the transformed
1056 * code. This includes declaring locally defined variables as well as
1057 * declaring and allocating the required copies of arrays on the device.
1059 static __isl_give isl_printer
*init_device(__isl_take isl_printer
*p
,
1060 struct gpu_prog
*prog
, struct opencl_info
*opencl
)
1062 p
= opencl_print_host_macros(p
);
1064 p
= gpu_print_local_declarations(p
, prog
);
1065 p
= opencl_declare_device_arrays(p
, prog
);
1066 p
= opencl_setup(p
, opencl
->input
, opencl
);
1067 p
= opencl_allocate_device_arrays(p
, prog
);
1072 /* Print code for clearing the device after execution of the transformed code.
1073 * In particular, free the memory that was allocated on the device.
1075 static __isl_give isl_printer
*clear_device(__isl_take isl_printer
*p
,
1076 struct gpu_prog
*prog
, struct opencl_info
*opencl
)
1078 p
= opencl_release_device_arrays(p
, prog
);
1079 p
= opencl_release_cl_objects(p
, opencl
);
1084 /* Print a statement for copying an array to or from the device,
1085 * or for initializing or clearing the device.
1086 * The statement identifier of a copying node is called
1087 * "to_device_<array name>" or "from_device_<array name>" and
1088 * its user pointer points to the gpu_array_info of the array
1089 * that needs to be copied.
1090 * The node for initializing the device is called "init_device".
1091 * The node for clearing the device is called "clear_device".
1093 * Extract the array (if any) from the identifier and call
1094 * init_device, clear_device, copy_array_to_device or copy_array_from_device.
1096 static __isl_give isl_printer
*print_device_node(__isl_take isl_printer
*p
,
1097 __isl_keep isl_ast_node
*node
, struct gpu_prog
*prog
,
1098 struct opencl_info
*opencl
)
1100 isl_ast_expr
*expr
, *arg
;
1103 struct gpu_array_info
*array
;
1105 expr
= isl_ast_node_user_get_expr(node
);
1106 arg
= isl_ast_expr_get_op_arg(expr
, 0);
1107 id
= isl_ast_expr_get_id(arg
);
1108 name
= isl_id_get_name(id
);
1109 array
= isl_id_get_user(id
);
1111 isl_ast_expr_free(arg
);
1112 isl_ast_expr_free(expr
);
1115 return isl_printer_free(p
);
1116 if (!strcmp(name
, "init_device"))
1117 return init_device(p
, prog
, opencl
);
1118 if (!strcmp(name
, "clear_device"))
1119 return clear_device(p
, prog
, opencl
);
1121 return isl_printer_free(p
);
1123 if (!prefixcmp(name
, "to_device"))
1124 return copy_array(p
, array
, 0);
1126 return copy_array(p
, array
, 1);
1129 /* Print the user statement of the host code to "p".
1131 * The host code may contain original user statements, kernel launches,
1132 * statements that copy data to/from the device and statements
1133 * the initialize or clear the device.
1134 * The original user statements and the kernel launches have
1135 * an associated annotation, while the other statements do not.
1136 * The latter are handled by print_device_node.
1137 * The annotation on the user statements is called "user".
1139 * In case of a kernel launch, print a block of statements that
1140 * defines the grid and the work group and then launches the kernel.
1142 * A grid is composed of many work groups (blocks), each work group holds
1143 * many work-items (threads).
1145 * global_work_size[kernel->n_block] represents the total number of work
1146 * items. It points to an array of kernel->n_block unsigned
1147 * values that describe the total number of work-items that will execute
1148 * the kernel. The total number of work-items is computed as:
1149 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
1151 * The size of each work group (i.e. the number of work-items in each work
1152 * group) is described using block_size[kernel->n_block]. The total
1153 * number of work-items in a block (work-group) is computed as:
1154 * block_size[0] *... * block_size[kernel->n_block - 1].
1156 * For more information check:
1157 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
1159 static __isl_give isl_printer
*opencl_print_host_user(
1160 __isl_take isl_printer
*p
,
1161 __isl_take isl_ast_print_options
*print_options
,
1162 __isl_keep isl_ast_node
*node
, void *user
)
1166 struct ppcg_kernel
*kernel
;
1167 struct ppcg_kernel_stmt
*stmt
;
1168 struct print_host_user_data_opencl
*data
;
1170 isl_ast_print_options_free(print_options
);
1172 data
= (struct print_host_user_data_opencl
*) user
;
1174 id
= isl_ast_node_get_annotation(node
);
1176 return print_device_node(p
, node
, data
->prog
, data
->opencl
);
1178 is_user
= !strcmp(isl_id_get_name(id
), "user");
1179 kernel
= is_user
? NULL
: isl_id_get_user(id
);
1180 stmt
= is_user
? isl_id_get_user(id
) : NULL
;
1184 return ppcg_kernel_print_domain(p
, stmt
);
1186 p
= isl_printer_start_line(p
);
1187 p
= isl_printer_print_str(p
, "{");
1188 p
= isl_printer_end_line(p
);
1189 p
= isl_printer_indent(p
, 2);
1191 p
= isl_printer_start_line(p
);
1192 p
= isl_printer_print_str(p
, "size_t global_work_size[");
1194 if (kernel
->n_block
> 0)
1195 p
= isl_printer_print_int(p
, kernel
->n_block
);
1197 p
= isl_printer_print_int(p
, 1);
1199 p
= isl_printer_print_str(p
, "] = {");
1200 p
= opencl_print_total_number_of_work_items_as_list(p
, kernel
);
1201 p
= isl_printer_print_str(p
, "};");
1202 p
= isl_printer_end_line(p
);
1204 p
= isl_printer_start_line(p
);
1205 p
= isl_printer_print_str(p
, "size_t block_size[");
1207 if (kernel
->n_block
> 0)
1208 p
= isl_printer_print_int(p
, kernel
->n_block
);
1210 p
= isl_printer_print_int(p
, 1);
1212 p
= isl_printer_print_str(p
, "] = {");
1213 p
= opencl_print_block_sizes(p
, kernel
);
1214 p
= isl_printer_print_str(p
, "};");
1215 p
= isl_printer_end_line(p
);
1217 p
= isl_printer_start_line(p
);
1218 p
= isl_printer_print_str(p
, "cl_kernel kernel");
1219 p
= isl_printer_print_int(p
, kernel
->id
);
1220 p
= isl_printer_print_str(p
, " = clCreateKernel(program, \"kernel");
1221 p
= isl_printer_print_int(p
, kernel
->id
);
1222 p
= isl_printer_print_str(p
, "\", &err);");
1223 p
= isl_printer_end_line(p
);
1224 p
= isl_printer_start_line(p
);
1225 p
= isl_printer_print_str(p
, "openclCheckReturn(err);");
1226 p
= isl_printer_end_line(p
);
1228 opencl_set_kernel_arguments(p
, data
->prog
, kernel
);
1230 p
= isl_printer_start_line(p
);
1231 p
= isl_printer_print_str(p
, "openclCheckReturn(clEnqueueNDRangeKernel"
1233 p
= isl_printer_print_int(p
, kernel
->id
);
1234 p
= isl_printer_print_str(p
, ", ");
1235 if (kernel
->n_block
> 0)
1236 p
= isl_printer_print_int(p
, kernel
->n_block
);
1238 p
= isl_printer_print_int(p
, 1);
1240 p
= isl_printer_print_str(p
, ", NULL, global_work_size, "
1242 "0, NULL, NULL));");
1243 p
= isl_printer_end_line(p
);
1244 p
= isl_printer_start_line(p
);
1245 p
= isl_printer_print_str(p
, "openclCheckReturn("
1246 "clReleaseKernel(kernel");
1247 p
= isl_printer_print_int(p
, kernel
->id
);
1248 p
= isl_printer_print_str(p
, "));");
1249 p
= isl_printer_end_line(p
);
1250 p
= isl_printer_start_line(p
);
1251 p
= isl_printer_print_str(p
, "clFinish(queue);");
1252 p
= isl_printer_end_line(p
);
1253 p
= isl_printer_indent(p
, -2);
1254 p
= isl_printer_start_line(p
);
1255 p
= isl_printer_print_str(p
, "}");
1256 p
= isl_printer_end_line(p
);
1258 p
= isl_printer_start_line(p
);
1259 p
= isl_printer_end_line(p
);
1261 data
->opencl
->kprinter
= opencl_print_kernel(data
->prog
, kernel
,
1262 data
->opencl
->kprinter
);
1267 static __isl_give isl_printer
*opencl_print_host_code(
1268 __isl_take isl_printer
*p
, struct gpu_prog
*prog
,
1269 __isl_keep isl_ast_node
*tree
, struct opencl_info
*opencl
)
1271 isl_ast_print_options
*print_options
;
1272 isl_ctx
*ctx
= isl_ast_node_get_ctx(tree
);
1273 struct print_host_user_data_opencl data
= { opencl
, prog
};
1275 print_options
= isl_ast_print_options_alloc(ctx
);
1276 print_options
= isl_ast_print_options_set_print_user(print_options
,
1277 &opencl_print_host_user
, &data
);
1279 p
= gpu_print_macros(p
, tree
);
1280 p
= isl_ast_node_print(tree
, p
, print_options
);
1285 /* Given a gpu_prog "prog" and the corresponding transformed AST
1286 * "tree", print the entire OpenCL code to "p".
1288 static __isl_give isl_printer
*print_opencl(__isl_take isl_printer
*p
,
1289 struct gpu_prog
*prog
, __isl_keep isl_ast_node
*tree
,
1290 struct gpu_types
*types
, void *user
)
1292 struct opencl_info
*opencl
= user
;
1294 opencl
->kprinter
= isl_printer_set_output_format(opencl
->kprinter
,
1296 if (any_double_elements(prog
))
1297 opencl
->kprinter
= opencl_enable_double_support(
1299 if (opencl
->options
->opencl_print_kernel_types
)
1300 opencl
->kprinter
= gpu_print_types(opencl
->kprinter
, types
,
1303 if (!opencl
->kprinter
)
1304 return isl_printer_free(p
);
1306 p
= opencl_print_host_code(p
, prog
, tree
, opencl
);
1311 /* Transform the code in the file called "input" by replacing
1312 * all scops by corresponding OpenCL code.
1313 * The host code is written to "output" or a name derived from
1314 * "input" if "output" is NULL.
1315 * The kernel code is placed in separate files with names
1316 * derived from "output" or "input".
1318 * We let generate_gpu do all the hard work and then let it call
1319 * us back for printing the AST in print_opencl.
1321 * To prepare for this printing, we first open the output files
1322 * and we close them after generate_gpu has finished.
1324 int generate_opencl(isl_ctx
*ctx
, struct ppcg_options
*options
,
1325 const char *input
, const char *output
)
1327 struct opencl_info opencl
= { options
, input
, output
};
1330 opencl
.kprinter
= isl_printer_to_str(ctx
);
1331 r
= opencl_open_files(&opencl
);
1334 r
= generate_gpu(ctx
, input
, opencl
.host_c
, options
,
1335 &print_opencl
, &opencl
);
1337 if (opencl_close_files(&opencl
) < 0)
1339 isl_printer_free(opencl
.kprinter
);