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"
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.
37 struct ppcg_options
*options
;
40 char kernel_c_name
[PATH_MAX
];
42 isl_printer
*kprinter
;
48 /* Open the file called "name" for writing or print an error message.
50 static FILE *open_or_croak(const char *name
)
54 file
= fopen(name
, "w");
56 fprintf(stderr
, "Failed to open \"%s\" for writing\n", name
);
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
66 * Return 0 on success and -1 on failure.
68 static int opencl_open_files(struct opencl_info
*info
)
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
);
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
)
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
,
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");
115 /* Write text to a file and escape some special characters that would break a
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
);
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\"");
150 fprintf(file
, "\n\"");
151 opencl_print_escaped(prev
, prev
+ strlen(prev
), 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
);
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");
177 fprintf(opencl
->kernel_c
, "%s", raw
);
184 /* Close all output files. Write the kernel contents to the kernel file before
187 * Return 0 on success and -1 on failure.
189 static int opencl_close_files(struct opencl_info
*info
)
193 if (info
->kernel_c
) {
194 r
= opencl_write_kernel_file(info
);
195 fclose(info
->kernel_c
);
198 fclose(info
->host_c
);
203 static __isl_give isl_printer
*opencl_print_host_macros(
204 __isl_take isl_printer
*p
)
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
);
223 static __isl_give isl_printer
*opencl_declare_device_arrays(
224 __isl_take isl_printer
*p
, struct gpu_prog
*prog
)
228 for (i
= 0; i
< prog
->n_array
; ++i
) {
229 if (gpu_array_is_read_only_scalar(&prog
->array
[i
]))
231 if (!prog
->array
[i
].accessed
)
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
);
244 /* Given an array, check whether its positive size guard expression is
247 static int is_array_positive_size_guard_trivial(struct gpu_array_info
*array
)
252 guard
= gpu_array_positive_size_guard(array
);
253 is_trivial
= isl_set_plain_is_universe(guard
);
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
);
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
)
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
))
309 if (!array
->accessed
)
312 p
= allocate_device_array(p
, array
);
314 p
= isl_printer_start_line(p
);
315 p
= isl_printer_end_line(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
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
, "), &");
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
);
352 /* Print the block sizes as a list of the sizes in each
355 static __isl_give isl_printer
*opencl_print_block_sizes(
356 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
)
360 if (kernel
->n_block
> 0)
361 for (i
= 0; i
< kernel
->n_block
; ++i
) {
363 p
= isl_printer_print_str(p
, ", ");
364 p
= isl_printer_print_int(p
, kernel
->block_dim
[i
]);
367 p
= isl_printer_print_str(p
, "1");
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
)
384 for (i
= 0; i
< prog
->n_array
; ++i
) {
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
);
394 ro
= gpu_array_is_read_only_scalar(&prog
->array
[i
]);
395 opencl_set_kernel_argument(p
, kernel
->id
, prog
->array
[i
].name
,
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
) {
405 name
= isl_space_get_dim_name(space
, isl_dim_param
, i
);
406 opencl_set_kernel_argument(p
, kernel
->id
, name
, arg_index
, 1);
409 isl_space_free(space
);
411 n
= isl_space_dim(kernel
->space
, isl_dim_set
);
412 for (i
= 0; i
< n
; ++i
) {
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);
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
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
)
441 for (i
= 0; i
< prog
->n_array
; ++i
) {
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
);
453 p
= isl_printer_print_str(p
, ", ");
456 p
= gpu_array_info_print_declaration_argument(p
,
457 &prog
->array
[i
], "__global");
459 p
= gpu_array_info_print_call_argument(p
,
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
) {
470 name
= isl_space_get_dim_name(space
, isl_dim_param
, i
);
473 p
= isl_printer_print_str(p
, ", ");
475 p
= isl_printer_print_str(p
, "int ");
476 p
= isl_printer_print_str(p
, name
);
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
) {
488 p
= isl_printer_print_str(p
, ", ");
489 name
= isl_space_get_dim_name(kernel
->space
, isl_dim_set
, i
);
491 p
= isl_printer_print_str(p
, type
);
492 p
= isl_printer_print_str(p
, " ");
494 p
= isl_printer_print_str(p
, name
);
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
);
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
)
532 n
= isl_id_list_n_id(ids
);
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
) {
542 p
= isl_printer_print_str(p
, ", ");
543 id
= isl_id_list_get_id(ids
, i
);
544 p
= isl_printer_print_id(p
, 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
);
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
);
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");
572 static __isl_give isl_printer
*opencl_print_kernel_var(
573 __isl_take isl_printer
*p
, struct ppcg_kernel_var
*var
)
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
, "]");
591 p
= isl_printer_print_str(p
, ";");
592 p
= isl_printer_end_line(p
);
597 static __isl_give isl_printer
*opencl_print_kernel_vars(
598 __isl_take isl_printer
*p
, struct ppcg_kernel
*kernel
)
602 for (i
= 0; i
< kernel
->n_var
; ++i
)
603 p
= opencl_print_kernel_var(p
, &kernel
->var
[i
]);
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
);
629 /* Data structure containing function names for which the calls
630 * should be changed from
636 * opencl_name((type) (arg))
638 static struct ppcg_opencl_fn
{
640 const char *opencl_name
;
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
,
660 name
= pet_expr_call_get_name(expr
);
661 for (i
= 0; i
< ARRAY_SIZE(opencl_fn
); ++i
) {
664 if (strcmp(name
, opencl_fn
[i
].name
))
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
);
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
684 static __isl_give isl_printer
*print_opencl_kernel_domain(
685 __isl_take isl_printer
*p
, struct ppcg_kernel_stmt
*stmt
)
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
);
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
)
709 struct ppcg_kernel_stmt
*stmt
;
711 id
= isl_ast_node_get_annotation(node
);
712 stmt
= isl_id_get_user(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
);
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
)
738 for (i
= 0; i
< prog
->n_array
; ++i
)
739 if (strcmp(prog
->array
[i
].type
, "double") == 0)
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"))
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 :"
764 p
= isl_printer_end_line(p
);
765 p
= isl_printer_start_line(p
);
766 p
= isl_printer_end_line(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
);
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:
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.
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
]);
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
);
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
)
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");
877 for (i
= 0; i
<= max(grid_dim
, block_dim
) - 1; i
++) {
879 p
= isl_printer_print_str(p
, ", ");
881 p
= opencl_print_total_number_of_work_items_for_dim(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(");
897 p
= isl_printer_print_str(p
, "clEnqueueReadBuffer");
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
, ", &");
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
);
916 /* Copy "array" from the host to the device.
918 static __isl_give isl_printer
*copy_array_to_device(__isl_take isl_printer
*p
,
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
,
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
)
946 for (i
= 0; i
< prog
->n_array
; ++i
) {
947 struct gpu_array_info
*array
= &prog
->array
[i
];
953 if (gpu_array_is_read_only_scalar(array
))
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
);
963 guard
= gpu_array_positive_size_guard(array
);
964 p
= ppcg_print_guarded(p
, guard
, isl_set_copy(prog
->context
),
965 to_host
? ©_array_from_device
:
966 ©_array_to_device
, array
);
969 p
= isl_printer_start_line(p
);
970 p
= isl_printer_end_line(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
)
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
);
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
);
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
);
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"
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
);
1079 p
= isl_printer_print_int(p
, 1);
1081 p
= isl_printer_print_str(p
, ", NULL, global_work_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
);
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
);
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), \"");
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
);
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"
1201 p
= isl_printer_end_line(p
);
1202 p
= isl_printer_start_line(p
);
1203 p
= isl_printer_print_str(p
, "openclCheckReturn(clReleaseProgram"
1205 p
= isl_printer_end_line(p
);
1206 p
= isl_printer_start_line(p
);
1207 p
= isl_printer_print_str(p
, "openclCheckReturn(clReleaseContext"
1209 p
= isl_printer_end_line(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
);
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
)
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
))
1240 if (!array
->accessed
)
1243 p
= release_device_array(p
, array
);
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
,
1259 if (any_double_elements(prog
))
1260 opencl
->kprinter
= opencl_enable_double_support(
1262 if (opencl
->options
->opencl_print_kernel_types
)
1263 opencl
->kprinter
= gpu_print_types(opencl
->kprinter
, types
,
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
);
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
};
1308 opencl
.kprinter
= isl_printer_to_str(ctx
);
1309 r
= opencl_open_files(&opencl
);
1312 r
= generate_gpu(ctx
, input
, opencl
.host_c
, options
,
1313 &print_opencl
, &opencl
);
1315 if (opencl_close_files(&opencl
) < 0)
1317 isl_printer_free(opencl
.kprinter
);