exploit independences during dependence analysis
[ppcg.git] / opencl.c
bloba7edbb985b8cd65fb06c82f58d2f4fa6ac94ef32
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"
23 #include "util.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.
37 struct opencl_info {
38 struct ppcg_options *options;
39 const char *input;
40 const char *output;
41 char kernel_c_name[PATH_MAX];
43 isl_printer *kprinter;
45 FILE *host_c;
46 FILE *kernel_c;
49 /* Open the file called "name" for writing or print an error message.
51 static FILE *open_or_croak(const char *name)
53 FILE *file;
55 file = fopen(name, "w");
56 if (!file)
57 fprintf(stderr, "Failed to open \"%s\" for writing\n", name);
58 return file;
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
65 * by the user.
67 * Return 0 on success and -1 on failure.
69 static int opencl_open_files(struct opencl_info *info)
71 char name[PATH_MAX];
72 int i;
73 int len;
75 if (info->output) {
76 const char *ext;
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);
83 } else {
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)
95 return -1;
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,
107 "#include <");
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");
113 return 0;
116 /* Write text to a file and escape some special characters that would break a
117 * C string.
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);
126 prev = str + 1;
129 if (*prev)
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\"");
147 prev = str + 1;
150 if (*prev) {
151 fprintf(file, "\n\"");
152 opencl_print_escaped(prev, prev + strlen(prev), file);
153 fprintf(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);
169 if (!raw)
170 return -1;
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");
177 } else
178 fprintf(opencl->kernel_c, "%s", raw);
180 free(raw);
182 return 0;
185 /* Close all output files. Write the kernel contents to the kernel file before
186 * closing it.
188 * Return 0 on success and -1 on failure.
190 static int opencl_close_files(struct opencl_info *info)
192 int r = 0;
194 if (info->kernel_c) {
195 r = opencl_write_kernel_file(info);
196 fclose(info->kernel_c);
198 if (info->host_c)
199 fclose(info->host_c);
201 return r;
204 static __isl_give isl_printer *opencl_print_host_macros(
205 __isl_take isl_printer *p)
207 const char *macros =
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);
219 p = isl_ast_op_type_print_macro(isl_ast_op_max, p);
221 return p;
224 static __isl_give isl_printer *opencl_declare_device_arrays(
225 __isl_take isl_printer *p, struct gpu_prog *prog)
227 int i;
229 for (i = 0; i < prog->n_array; ++i) {
230 if (gpu_array_is_read_only_scalar(&prog->array[i]))
231 continue;
232 if (!prog->array[i].accessed)
233 continue;
234 p = isl_printer_start_line(p);
235 p = isl_printer_print_str(p, "cl_mem dev_");
236 p = isl_printer_print_str(p, prog->array[i].name);
237 p = isl_printer_print_str(p, ";");
238 p = isl_printer_end_line(p);
240 p = isl_printer_start_line(p);
241 p = isl_printer_end_line(p);
242 return p;
245 /* Given an array, check whether its positive size guard expression is
246 * trivial.
248 static int is_array_positive_size_guard_trivial(struct gpu_array_info *array)
250 isl_set *guard;
251 int is_trivial;
253 guard = gpu_array_positive_size_guard(array);
254 is_trivial = isl_set_plain_is_universe(guard);
255 isl_set_free(guard);
256 return is_trivial;
259 /* Allocate a device array for "array'.
261 * Emit a max-expression to ensure the device array can contain at least one
262 * element if the array's positive size guard expression is not trivial.
264 static __isl_give isl_printer *allocate_device_array(__isl_take isl_printer *p,
265 struct gpu_array_info *array)
267 int need_lower_bound;
269 p = ppcg_start_block(p);
271 p = isl_printer_start_line(p);
272 p = isl_printer_print_str(p, "dev_");
273 p = isl_printer_print_str(p, array->name);
274 p = isl_printer_print_str(p, " = clCreateBuffer(context, ");
275 p = isl_printer_print_str(p, "CL_MEM_READ_WRITE, ");
277 need_lower_bound = !is_array_positive_size_guard_trivial(array);
278 if (need_lower_bound) {
279 p = isl_printer_print_str(p, "max(sizeof(");
280 p = isl_printer_print_str(p, array->type);
281 p = isl_printer_print_str(p, "), ");
283 p = gpu_array_info_print_size(p, array);
284 if (need_lower_bound)
285 p = isl_printer_print_str(p, ")");
287 p = isl_printer_print_str(p, ", NULL, &err);");
288 p = isl_printer_end_line(p);
289 p = isl_printer_start_line(p);
290 p = isl_printer_print_str(p, "openclCheckReturn(err);");
291 p = isl_printer_end_line(p);
293 p = ppcg_end_block(p);
295 return p;
298 /* Allocate accessed device arrays.
300 static __isl_give isl_printer *opencl_allocate_device_arrays(
301 __isl_take isl_printer *p, struct gpu_prog *prog)
303 int i;
305 for (i = 0; i < prog->n_array; ++i) {
306 struct gpu_array_info *array = &prog->array[i];
308 if (gpu_array_is_read_only_scalar(array))
309 continue;
310 if (!array->accessed)
311 continue;
313 p = allocate_device_array(p, array);
315 p = isl_printer_start_line(p);
316 p = isl_printer_end_line(p);
317 return p;
320 /* Print a call to the OpenCL clSetKernelArg() function which sets
321 * the arguments of the kernel. arg_name and arg_index are the name and the
322 * index of the kernel argument. The index of the leftmost argument of
323 * the kernel is 0 whereas the index of the rightmost argument of the kernel
324 * is n - 1, where n is the total number of the kernel arguments.
325 * read_only_scalar is a boolean that indicates whether the argument is a read
326 * only scalar.
328 static __isl_give isl_printer *opencl_set_kernel_argument(
329 __isl_take isl_printer *p, int kernel_id,
330 const char *arg_name, int arg_index, int read_only_scalar)
332 p = isl_printer_start_line(p);
333 p = isl_printer_print_str(p,
334 "openclCheckReturn(clSetKernelArg(kernel");
335 p = isl_printer_print_int(p, kernel_id);
336 p = isl_printer_print_str(p, ", ");
337 p = isl_printer_print_int(p, arg_index);
338 p = isl_printer_print_str(p, ", sizeof(");
340 if (read_only_scalar) {
341 p = isl_printer_print_str(p, arg_name);
342 p = isl_printer_print_str(p, "), &");
343 } else
344 p = isl_printer_print_str(p, "cl_mem), (void *) &dev_");
346 p = isl_printer_print_str(p, arg_name);
347 p = isl_printer_print_str(p, "));");
348 p = isl_printer_end_line(p);
350 return p;
353 /* Print the block sizes as a list of the sizes in each
354 * dimension.
356 static __isl_give isl_printer *opencl_print_block_sizes(
357 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
359 int i;
361 if (kernel->n_block > 0)
362 for (i = 0; i < kernel->n_block; ++i) {
363 if (i)
364 p = isl_printer_print_str(p, ", ");
365 p = isl_printer_print_int(p, kernel->block_dim[i]);
367 else
368 p = isl_printer_print_str(p, "1");
370 return p;
373 /* Set the arguments of the OpenCL kernel by printing a call to the OpenCL
374 * clSetKernelArg() function for each kernel argument.
376 static __isl_give isl_printer *opencl_set_kernel_arguments(
377 __isl_take isl_printer *p, struct gpu_prog *prog,
378 struct ppcg_kernel *kernel)
380 int i, n, ro;
381 unsigned nparam;
382 isl_space *space;
383 int arg_index = 0;
385 for (i = 0; i < prog->n_array; ++i) {
386 isl_set *arr;
387 int empty;
389 space = isl_space_copy(prog->array[i].space);
390 arr = isl_union_set_extract_set(kernel->arrays, space);
391 empty = isl_set_plain_is_empty(arr);
392 isl_set_free(arr);
393 if (empty)
394 continue;
395 ro = gpu_array_is_read_only_scalar(&prog->array[i]);
396 opencl_set_kernel_argument(p, kernel->id, prog->array[i].name,
397 arg_index, ro);
398 arg_index++;
401 space = isl_union_set_get_space(kernel->arrays);
402 nparam = isl_space_dim(space, isl_dim_param);
403 for (i = 0; i < nparam; ++i) {
404 const char *name;
406 name = isl_space_get_dim_name(space, isl_dim_param, i);
407 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
408 arg_index++;
410 isl_space_free(space);
412 n = isl_space_dim(kernel->space, isl_dim_set);
413 for (i = 0; i < n; ++i) {
414 const char *name;
416 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
417 opencl_set_kernel_argument(p, kernel->id, name, arg_index, 1);
418 arg_index++;
421 return p;
424 /* Print the arguments to a kernel declaration or call. If "types" is set,
425 * then print a declaration (including the types of the arguments).
427 * The arguments are printed in the following order
428 * - the arrays accessed by the kernel
429 * - the parameters
430 * - the host loop iterators
432 static __isl_give isl_printer *opencl_print_kernel_arguments(
433 __isl_take isl_printer *p, struct gpu_prog *prog,
434 struct ppcg_kernel *kernel, int types)
436 int i, n;
437 int first = 1;
438 unsigned nparam;
439 isl_space *space;
440 const char *type;
442 for (i = 0; i < prog->n_array; ++i) {
443 isl_set *arr;
444 int empty;
446 space = isl_space_copy(prog->array[i].space);
447 arr = isl_union_set_extract_set(kernel->arrays, space);
448 empty = isl_set_plain_is_empty(arr);
449 isl_set_free(arr);
450 if (empty)
451 continue;
453 if (!first)
454 p = isl_printer_print_str(p, ", ");
456 if (types)
457 p = gpu_array_info_print_declaration_argument(p,
458 &prog->array[i], "__global");
459 else
460 p = gpu_array_info_print_call_argument(p,
461 &prog->array[i]);
463 first = 0;
466 space = isl_union_set_get_space(kernel->arrays);
467 nparam = isl_space_dim(space, isl_dim_param);
468 for (i = 0; i < nparam; ++i) {
469 const char *name;
471 name = isl_space_get_dim_name(space, isl_dim_param, i);
473 if (!first)
474 p = isl_printer_print_str(p, ", ");
475 if (types)
476 p = isl_printer_print_str(p, "int ");
477 p = isl_printer_print_str(p, name);
479 first = 0;
481 isl_space_free(space);
483 n = isl_space_dim(kernel->space, isl_dim_set);
484 type = isl_options_get_ast_iterator_type(prog->ctx);
485 for (i = 0; i < n; ++i) {
486 const char *name;
488 if (!first)
489 p = isl_printer_print_str(p, ", ");
490 name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
491 if (types) {
492 p = isl_printer_print_str(p, type);
493 p = isl_printer_print_str(p, " ");
495 p = isl_printer_print_str(p, name);
497 first = 0;
500 return p;
503 /* Print the header of the given kernel.
505 static __isl_give isl_printer *opencl_print_kernel_header(
506 __isl_take isl_printer *p, struct gpu_prog *prog,
507 struct ppcg_kernel *kernel)
509 p = isl_printer_start_line(p);
510 p = isl_printer_print_str(p, "__kernel void kernel");
511 p = isl_printer_print_int(p, kernel->id);
512 p = isl_printer_print_str(p, "(");
513 p = opencl_print_kernel_arguments(p, prog, kernel, 1);
514 p = isl_printer_print_str(p, ")");
515 p = isl_printer_end_line(p);
517 return p;
520 /* Print a list of iterators of type "type" with names "ids" to "p".
521 * Each iterator is assigned the corresponding opencl identifier returned
522 * by the function "opencl_id".
523 * Unlike the equivalent function in the CUDA backend which prints iterators
524 * in reverse order to promote coalescing, this function does not print
525 * iterators in reverse order. The OpenCL backend currently does not take
526 * into account any coalescing considerations.
528 static __isl_give isl_printer *print_iterators(__isl_take isl_printer *p,
529 const char *type, __isl_keep isl_id_list *ids, const char *opencl_id)
531 int i, n;
533 n = isl_id_list_n_id(ids);
534 if (n <= 0)
535 return p;
536 p = isl_printer_start_line(p);
537 p = isl_printer_print_str(p, type);
538 p = isl_printer_print_str(p, " ");
539 for (i = 0; i < n; ++i) {
540 isl_id *id;
542 if (i)
543 p = isl_printer_print_str(p, ", ");
544 id = isl_id_list_get_id(ids, i);
545 p = isl_printer_print_id(p, id);
546 isl_id_free(id);
547 p = isl_printer_print_str(p, " = ");
548 p = isl_printer_print_str(p, opencl_id);
549 p = isl_printer_print_str(p, "(");
550 p = isl_printer_print_int(p, i);
551 p = isl_printer_print_str(p, ")");
553 p = isl_printer_print_str(p, ";");
554 p = isl_printer_end_line(p);
556 return p;
559 static __isl_give isl_printer *opencl_print_kernel_iterators(
560 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
562 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
563 const char *type;
565 type = isl_options_get_ast_iterator_type(ctx);
567 p = print_iterators(p, type, kernel->block_ids, "get_group_id");
568 p = print_iterators(p, type, kernel->thread_ids, "get_local_id");
570 return p;
573 static __isl_give isl_printer *opencl_print_kernel_var(
574 __isl_take isl_printer *p, struct ppcg_kernel_var *var)
576 int j;
577 isl_val *v;
579 p = isl_printer_start_line(p);
580 if (var->type == ppcg_access_shared)
581 p = isl_printer_print_str(p, "__local ");
582 p = isl_printer_print_str(p, var->array->type);
583 p = isl_printer_print_str(p, " ");
584 p = isl_printer_print_str(p, var->name);
585 for (j = 0; j < var->array->n_index; ++j) {
586 p = isl_printer_print_str(p, "[");
587 v = isl_vec_get_element_val(var->size, j);
588 p = isl_printer_print_val(p, v);
589 p = isl_printer_print_str(p, "]");
590 isl_val_free(v);
592 p = isl_printer_print_str(p, ";");
593 p = isl_printer_end_line(p);
595 return p;
598 static __isl_give isl_printer *opencl_print_kernel_vars(
599 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
601 int i;
603 for (i = 0; i < kernel->n_var; ++i)
604 p = opencl_print_kernel_var(p, &kernel->var[i]);
606 return p;
609 /* Print a call to barrier() which is a sync statement.
610 * All work-items in a work-group executing the kernel on a processor must
611 * execute the barrier() function before any are allowed to continue execution
612 * beyond the barrier.
613 * The flag CLK_LOCAL_MEM_FENCE makes the barrier function either flush any
614 * variables stored in local memory or queue a memory fence to ensure correct
615 * ordering of memory operations to local memory.
616 * The flag CLK_GLOBAL_MEM_FENCE makes the barrier function queue a memory
617 * fence to ensure correct ordering of memory operations to global memory.
619 static __isl_give isl_printer *opencl_print_sync(__isl_take isl_printer *p,
620 struct ppcg_kernel_stmt *stmt)
622 p = isl_printer_start_line(p);
623 p = isl_printer_print_str(p,
624 "barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);");
625 p = isl_printer_end_line(p);
627 return p;
630 /* Data structure containing function names for which the calls
631 * should be changed from
633 * name(arg)
635 * to
637 * opencl_name((type) (arg))
639 static struct ppcg_opencl_fn {
640 const char *name;
641 const char *opencl_name;
642 const char *type;
643 } opencl_fn[] = {
644 { "expf", "exp", "float" },
645 { "powf", "pow", "float" },
646 { "sqrtf", "sqrt", "float" },
649 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
651 /* If the name of function called by "expr" matches any of those
652 * in ppcg_opencl_fn, then replace the call by a cast to the corresponding
653 * type in ppcg_opencl_fn and a call to corresponding OpenCL function.
655 static __isl_give pet_expr *map_opencl_call(__isl_take pet_expr *expr,
656 void *user)
658 const char *name;
659 int i;
661 name = pet_expr_call_get_name(expr);
662 for (i = 0; i < ARRAY_SIZE(opencl_fn); ++i) {
663 pet_expr *arg;
665 if (strcmp(name, opencl_fn[i].name))
666 continue;
667 expr = pet_expr_call_set_name(expr, opencl_fn[i].opencl_name);
668 arg = pet_expr_get_arg(expr, 0);
669 arg = pet_expr_new_cast(opencl_fn[i].type, arg);
670 expr = pet_expr_set_arg(expr, 0, arg);
672 return expr;
675 /* Print the body of a statement from the input program,
676 * for use in OpenCL code.
678 * Before calling ppcg_kernel_print_domain to print the actual statement body,
679 * we first modify this body to take into account that the output code
680 * is OpenCL code. In particular, if the statement calls any function
681 * with a "f" suffix, then it needs to be replaced by a call to
682 * the corresponding function without suffix after casting the argument
683 * to a float.
685 static __isl_give isl_printer *print_opencl_kernel_domain(
686 __isl_take isl_printer *p, struct ppcg_kernel_stmt *stmt)
688 struct pet_stmt *ps;
689 pet_tree *tree;
691 ps = stmt->u.d.stmt->stmt;
692 tree = pet_tree_copy(ps->body);
693 ps->body = pet_tree_map_call_expr(ps->body, &map_opencl_call, NULL);
694 p = ppcg_kernel_print_domain(p, stmt);
695 pet_tree_free(ps->body);
696 ps->body = tree;
698 return p;
701 /* This function is called for each user statement in the AST,
702 * i.e., for each kernel body statement, copy statement or sync statement.
704 static __isl_give isl_printer *opencl_print_kernel_stmt(
705 __isl_take isl_printer *p,
706 __isl_take isl_ast_print_options *print_options,
707 __isl_keep isl_ast_node *node, void *user)
709 isl_id *id;
710 struct ppcg_kernel_stmt *stmt;
712 id = isl_ast_node_get_annotation(node);
713 stmt = isl_id_get_user(id);
714 isl_id_free(id);
716 isl_ast_print_options_free(print_options);
718 switch (stmt->type) {
719 case ppcg_kernel_copy:
720 return ppcg_kernel_print_copy(p, stmt);
721 case ppcg_kernel_sync:
722 return opencl_print_sync(p, stmt);
723 case ppcg_kernel_domain:
724 return print_opencl_kernel_domain(p, stmt);
727 return p;
730 /* Return true if there is a double array in prog->array or
731 * if any of the types in prog->scop involve any doubles.
732 * To check the latter condition, we simply search for the string "double"
733 * in the type definitions, which may result in false positives.
735 static __isl_give int any_double_elements(struct gpu_prog *prog)
737 int i;
739 for (i = 0; i < prog->n_array; ++i)
740 if (strcmp(prog->array[i].type, "double") == 0)
741 return 1;
743 for (i = 0; i < prog->scop->pet->n_type; ++i) {
744 struct pet_type *type = prog->scop->pet->types[i];
746 if (strstr(type->definition, "double"))
747 return 1;
750 return 0;
753 /* Prints a #pragma to enable support for double floating-point
754 * precision. OpenCL 1.0 adds support for double precision floating-point as
755 * an optional extension. An application that wants to use double will need to
756 * include the #pragma OPENCL EXTENSION cl_khr_fp64 : enable directive before
757 * any double precision data type is declared in the kernel code.
759 static __isl_give isl_printer *opencl_enable_double_support(
760 __isl_take isl_printer *p)
762 p = isl_printer_start_line(p);
763 p = isl_printer_print_str(p, "#pragma OPENCL EXTENSION cl_khr_fp64 :"
764 " enable");
765 p = isl_printer_end_line(p);
766 p = isl_printer_start_line(p);
767 p = isl_printer_end_line(p);
769 return p;
772 static __isl_give isl_printer *opencl_print_kernel(struct gpu_prog *prog,
773 struct ppcg_kernel *kernel, __isl_take isl_printer *p)
775 isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
776 isl_ast_print_options *print_options;
778 print_options = isl_ast_print_options_alloc(ctx);
779 print_options = isl_ast_print_options_set_print_user(print_options,
780 &opencl_print_kernel_stmt, NULL);
782 p = isl_printer_set_output_format(p, ISL_FORMAT_C);
783 p = opencl_print_kernel_header(p, prog, kernel);
784 p = isl_printer_print_str(p, "{");
785 p = isl_printer_end_line(p);
786 p = isl_printer_indent(p, 4);
787 p = opencl_print_kernel_iterators(p, kernel);
788 p = opencl_print_kernel_vars(p, kernel);
789 p = isl_printer_end_line(p);
790 p = gpu_print_macros(p, kernel->tree);
791 p = isl_ast_node_print(kernel->tree, p, print_options);
792 p = isl_printer_indent(p, -4);
793 p = isl_printer_start_line(p);
794 p = isl_printer_print_str(p, "}");
795 p = isl_printer_end_line(p);
797 return p;
800 struct print_host_user_data_opencl {
801 struct opencl_info *opencl;
802 struct gpu_prog *prog;
805 /* This function prints the i'th block size multiplied by the i'th grid size,
806 * where i (a parameter to this function) is one of the possible dimensions of
807 * grid sizes and block sizes.
808 * If the dimension of block sizes is not equal to the dimension of grid sizes
809 * the output is calculated as follows:
811 * Suppose that:
812 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
813 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
815 * The output is:
816 * If (i > dim2) then the output is block_sizes[i]
817 * If (i > dim1) then the output is grid_sizes[i]
819 static __isl_give isl_printer *opencl_print_total_number_of_work_items_for_dim(
820 __isl_take isl_printer *p, struct ppcg_kernel *kernel, int i)
822 int grid_dim, block_dim;
823 isl_pw_aff *bound_grid;
825 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
826 block_dim = kernel->n_block;
828 if (i < min(grid_dim, block_dim)) {
829 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
830 p = isl_printer_print_str(p, "(");
831 p = isl_printer_print_pw_aff(p, bound_grid);
832 p = isl_printer_print_str(p, ") * ");
833 p = isl_printer_print_int(p, kernel->block_dim[i]);
834 isl_pw_aff_free(bound_grid);
835 } else if (i >= grid_dim)
836 p = isl_printer_print_int(p, kernel->block_dim[i]);
837 else {
838 bound_grid = isl_multi_pw_aff_get_pw_aff(kernel->grid_size, i);
839 p = isl_printer_print_pw_aff(p, bound_grid);
840 isl_pw_aff_free(bound_grid);
843 return p;
846 /* Print a list that represents the total number of work items. The list is
847 * constructed by performing an element-wise multiplication of the block sizes
848 * and the grid sizes. To explain how the list is constructed, suppose that:
849 * block_sizes[dim1] is the list of blocks sizes and it contains dim1 elements.
850 * grid_sizes[dim2] is the list of grid sizes and it contains dim2 elements.
852 * The output of this function is constructed as follows:
853 * If (dim1 > dim2) then the output is the following list:
854 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim2-1]*block_sizes[dim2-1],
855 * block_sizes[dim2], ..., block_sizes[dim1-2], block_sizes[dim1-1].
857 * If (dim2 > dim1) then the output is the following list:
858 * grid_sizes[0]*block_sizes[0], ..., grid_sizes[dim1-1] * block_sizes[dim1-1],
859 * grid_sizes[dim1], grid_sizes[dim2-2], grid_sizes[dim2-1].
861 * To calculate the total number of work items out of the list constructed by
862 * this function, the user should multiply the elements of the list.
864 static __isl_give isl_printer *opencl_print_total_number_of_work_items_as_list(
865 __isl_take isl_printer *p, struct ppcg_kernel *kernel)
867 int i;
868 int grid_dim, block_dim;
870 grid_dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
871 block_dim = kernel->n_block;
873 if ((grid_dim <= 0) || (block_dim <= 0)) {
874 p = isl_printer_print_str(p, "1");
875 return p;
878 for (i = 0; i <= max(grid_dim, block_dim) - 1; i++) {
879 if (i > 0)
880 p = isl_printer_print_str(p, ", ");
882 p = opencl_print_total_number_of_work_items_for_dim(p,
883 kernel, i);
886 return p;
889 /* Copy "array" from the host to the device (to_host = 0) or
890 * back from the device to the host (to_host = 1).
892 static __isl_give isl_printer *copy_array(__isl_take isl_printer *p,
893 struct gpu_array_info *array, int to_host)
895 p = isl_printer_start_line(p);
896 p = isl_printer_print_str(p, "openclCheckReturn(");
897 if (to_host)
898 p = isl_printer_print_str(p, "clEnqueueReadBuffer");
899 else
900 p = isl_printer_print_str(p, "clEnqueueWriteBuffer");
901 p = isl_printer_print_str(p, "(queue, dev_");
902 p = isl_printer_print_str(p, array->name);
903 p = isl_printer_print_str(p, ", CL_TRUE, 0, ");
904 p = gpu_array_info_print_size(p, array);
906 if (gpu_array_is_scalar(array))
907 p = isl_printer_print_str(p, ", &");
908 else
909 p = isl_printer_print_str(p, ", ");
910 p = isl_printer_print_str(p, array->name);
911 p = isl_printer_print_str(p, ", 0, NULL, NULL));");
912 p = isl_printer_end_line(p);
914 return p;
917 /* Print a statement for copying an array to or from the device.
918 * The statement identifier is called "to_device_<array name>" or
919 * "from_device_<array name>" and its user pointer points
920 * to the gpu_array_info of the array that needs to be copied.
922 * Extract the array from the identifier and call
923 * copy_array_to_device or copy_array_from_device.
925 static __isl_give isl_printer *print_to_from_device(__isl_take isl_printer *p,
926 __isl_keep isl_ast_node *node, struct gpu_prog *prog)
928 isl_ast_expr *expr, *arg;
929 isl_id *id;
930 const char *name;
931 struct gpu_array_info *array;
933 expr = isl_ast_node_user_get_expr(node);
934 arg = isl_ast_expr_get_op_arg(expr, 0);
935 id = isl_ast_expr_get_id(arg);
936 name = isl_id_get_name(id);
937 array = isl_id_get_user(id);
938 isl_id_free(id);
939 isl_ast_expr_free(arg);
940 isl_ast_expr_free(expr);
942 if (!name)
943 array = NULL;
944 if (!array)
945 return isl_printer_free(p);
947 if (!prefixcmp(name, "to_device"))
948 return copy_array(p, array, 0);
949 else
950 return copy_array(p, array, 1);
953 /* Print the user statement of the host code to "p".
955 * The host code may contain original user statements, kernel launches and
956 * statements that copy data to/from the device.
957 * The original user statements and the kernel launches have
958 * an associated annotation, while the data copy statements do not.
959 * The latter are handled by print_to_from_device.
960 * The annotation on the user statements is called "user".
962 * In case of a kernel launch, print a block of statements that
963 * defines the grid and the work group and then launches the kernel.
965 * A grid is composed of many work groups (blocks), each work group holds
966 * many work-items (threads).
968 * global_work_size[kernel->n_block] represents the total number of work
969 * items. It points to an array of kernel->n_block unsigned
970 * values that describe the total number of work-items that will execute
971 * the kernel. The total number of work-items is computed as:
972 * global_work_size[0] *...* global_work_size[kernel->n_block - 1].
974 * The size of each work group (i.e. the number of work-items in each work
975 * group) is described using block_size[kernel->n_block]. The total
976 * number of work-items in a block (work-group) is computed as:
977 * block_size[0] *... * block_size[kernel->n_block - 1].
979 * For more information check:
980 * http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueNDRangeKernel.html
982 static __isl_give isl_printer *opencl_print_host_user(
983 __isl_take isl_printer *p,
984 __isl_take isl_ast_print_options *print_options,
985 __isl_keep isl_ast_node *node, void *user)
987 isl_id *id;
988 int is_user;
989 struct ppcg_kernel *kernel;
990 struct ppcg_kernel_stmt *stmt;
991 struct print_host_user_data_opencl *data;
993 isl_ast_print_options_free(print_options);
995 data = (struct print_host_user_data_opencl *) user;
997 id = isl_ast_node_get_annotation(node);
998 if (!id)
999 return print_to_from_device(p, node, data->prog);
1001 is_user = !strcmp(isl_id_get_name(id), "user");
1002 kernel = is_user ? NULL : isl_id_get_user(id);
1003 stmt = is_user ? isl_id_get_user(id) : NULL;
1004 isl_id_free(id);
1006 if (is_user)
1007 return ppcg_kernel_print_domain(p, stmt);
1009 p = isl_printer_start_line(p);
1010 p = isl_printer_print_str(p, "{");
1011 p = isl_printer_end_line(p);
1012 p = isl_printer_indent(p, 2);
1014 p = isl_printer_start_line(p);
1015 p = isl_printer_print_str(p, "size_t global_work_size[");
1017 if (kernel->n_block > 0)
1018 p = isl_printer_print_int(p, kernel->n_block);
1019 else
1020 p = isl_printer_print_int(p, 1);
1022 p = isl_printer_print_str(p, "] = {");
1023 p = opencl_print_total_number_of_work_items_as_list(p, kernel);
1024 p = isl_printer_print_str(p, "};");
1025 p = isl_printer_end_line(p);
1027 p = isl_printer_start_line(p);
1028 p = isl_printer_print_str(p, "size_t block_size[");
1030 if (kernel->n_block > 0)
1031 p = isl_printer_print_int(p, kernel->n_block);
1032 else
1033 p = isl_printer_print_int(p, 1);
1035 p = isl_printer_print_str(p, "] = {");
1036 p = opencl_print_block_sizes(p, kernel);
1037 p = isl_printer_print_str(p, "};");
1038 p = isl_printer_end_line(p);
1040 p = isl_printer_start_line(p);
1041 p = isl_printer_print_str(p, "cl_kernel kernel");
1042 p = isl_printer_print_int(p, kernel->id);
1043 p = isl_printer_print_str(p, " = clCreateKernel(program, \"kernel");
1044 p = isl_printer_print_int(p, kernel->id);
1045 p = isl_printer_print_str(p, "\", &err);");
1046 p = isl_printer_end_line(p);
1047 p = isl_printer_start_line(p);
1048 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1049 p = isl_printer_end_line(p);
1051 opencl_set_kernel_arguments(p, data->prog, kernel);
1053 p = isl_printer_start_line(p);
1054 p = isl_printer_print_str(p, "openclCheckReturn(clEnqueueNDRangeKernel"
1055 "(queue, kernel");
1056 p = isl_printer_print_int(p, kernel->id);
1057 p = isl_printer_print_str(p, ", ");
1058 if (kernel->n_block > 0)
1059 p = isl_printer_print_int(p, kernel->n_block);
1060 else
1061 p = isl_printer_print_int(p, 1);
1063 p = isl_printer_print_str(p, ", NULL, global_work_size, "
1064 "block_size, "
1065 "0, NULL, NULL));");
1066 p = isl_printer_end_line(p);
1067 p = isl_printer_start_line(p);
1068 p = isl_printer_print_str(p, "openclCheckReturn("
1069 "clReleaseKernel(kernel");
1070 p = isl_printer_print_int(p, kernel->id);
1071 p = isl_printer_print_str(p, "));");
1072 p = isl_printer_end_line(p);
1073 p = isl_printer_start_line(p);
1074 p = isl_printer_print_str(p, "clFinish(queue);");
1075 p = isl_printer_end_line(p);
1076 p = isl_printer_indent(p, -2);
1077 p = isl_printer_start_line(p);
1078 p = isl_printer_print_str(p, "}");
1079 p = isl_printer_end_line(p);
1081 p = isl_printer_start_line(p);
1082 p = isl_printer_end_line(p);
1084 data->opencl->kprinter = opencl_print_kernel(data->prog, kernel,
1085 data->opencl->kprinter);
1087 return p;
1090 static __isl_give isl_printer *opencl_print_host_code(
1091 __isl_take isl_printer *p, struct gpu_prog *prog,
1092 __isl_keep isl_ast_node *tree, struct opencl_info *opencl)
1094 isl_ast_print_options *print_options;
1095 isl_ctx *ctx = isl_ast_node_get_ctx(tree);
1096 struct print_host_user_data_opencl data = { opencl, prog };
1098 print_options = isl_ast_print_options_alloc(ctx);
1099 print_options = isl_ast_print_options_set_print_user(print_options,
1100 &opencl_print_host_user, &data);
1102 p = gpu_print_macros(p, tree);
1103 p = isl_ast_node_print(tree, p, print_options);
1105 return p;
1108 /* Create an OpenCL device, context, command queue and build the kernel.
1109 * input is the name of the input file provided to ppcg.
1111 static __isl_give isl_printer *opencl_setup(__isl_take isl_printer *p,
1112 const char *input, struct opencl_info *info)
1114 p = isl_printer_start_line(p);
1115 p = isl_printer_print_str(p, "cl_device_id device;");
1116 p = isl_printer_end_line(p);
1117 p = isl_printer_start_line(p);
1118 p = isl_printer_print_str(p, "cl_context context;");
1119 p = isl_printer_end_line(p);
1120 p = isl_printer_start_line(p);
1121 p = isl_printer_print_str(p, "cl_program program;");
1122 p = isl_printer_end_line(p);
1123 p = isl_printer_start_line(p);
1124 p = isl_printer_print_str(p, "cl_command_queue queue;");
1125 p = isl_printer_end_line(p);
1126 p = isl_printer_start_line(p);
1127 p = isl_printer_print_str(p, "cl_int err;");
1128 p = isl_printer_end_line(p);
1129 p = isl_printer_start_line(p);
1130 p = isl_printer_print_str(p, "device = opencl_create_device(");
1131 p = isl_printer_print_int(p, info->options->opencl_use_gpu);
1132 p = isl_printer_print_str(p, ");");
1133 p = isl_printer_end_line(p);
1134 p = isl_printer_start_line(p);
1135 p = isl_printer_print_str(p, "context = clCreateContext(NULL, 1, "
1136 "&device, NULL, NULL, &err);");
1137 p = isl_printer_end_line(p);
1138 p = isl_printer_start_line(p);
1139 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1140 p = isl_printer_end_line(p);
1141 p = isl_printer_start_line(p);
1142 p = isl_printer_print_str(p, "queue = clCreateCommandQueue"
1143 "(context, device, 0, &err);");
1144 p = isl_printer_end_line(p);
1145 p = isl_printer_start_line(p);
1146 p = isl_printer_print_str(p, "openclCheckReturn(err);");
1147 p = isl_printer_end_line(p);
1149 p = isl_printer_start_line(p);
1150 p = isl_printer_print_str(p, "program = ");
1152 if (info->options->opencl_embed_kernel_code) {
1153 p = isl_printer_print_str(p, "opencl_build_program_from_string("
1154 "context, device, kernel_code, "
1155 "sizeof(kernel_code), \"");
1156 } else {
1157 p = isl_printer_print_str(p, "opencl_build_program_from_file("
1158 "context, device, \"");
1159 p = isl_printer_print_str(p, info->kernel_c_name);
1160 p = isl_printer_print_str(p, "\", \"");
1163 if (info->options->opencl_compiler_options)
1164 p = isl_printer_print_str(p,
1165 info->options->opencl_compiler_options);
1167 p = isl_printer_print_str(p, "\");");
1168 p = isl_printer_end_line(p);
1169 p = isl_printer_start_line(p);
1170 p = isl_printer_end_line(p);
1172 return p;
1175 static __isl_give isl_printer *opencl_release_cl_objects(
1176 __isl_take isl_printer *p, struct opencl_info *info)
1178 p = isl_printer_start_line(p);
1179 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseCommandQueue"
1180 "(queue));");
1181 p = isl_printer_end_line(p);
1182 p = isl_printer_start_line(p);
1183 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseProgram"
1184 "(program));");
1185 p = isl_printer_end_line(p);
1186 p = isl_printer_start_line(p);
1187 p = isl_printer_print_str(p, "openclCheckReturn(clReleaseContext"
1188 "(context));");
1189 p = isl_printer_end_line(p);
1191 return p;
1194 /* Free the device array corresponding to "array"
1196 static __isl_give isl_printer *release_device_array(__isl_take isl_printer *p,
1197 struct gpu_array_info *array)
1199 p = isl_printer_start_line(p);
1200 p = isl_printer_print_str(p, "openclCheckReturn("
1201 "clReleaseMemObject(dev_");
1202 p = isl_printer_print_str(p, array->name);
1203 p = isl_printer_print_str(p, "));");
1204 p = isl_printer_end_line(p);
1206 return p;
1209 /* Free the accessed device arrays.
1211 static __isl_give isl_printer *opencl_release_device_arrays(
1212 __isl_take isl_printer *p, struct gpu_prog *prog)
1214 int i;
1216 for (i = 0; i < prog->n_array; ++i) {
1217 struct gpu_array_info *array = &prog->array[i];
1218 if (gpu_array_is_read_only_scalar(array))
1219 continue;
1220 if (!array->accessed)
1221 continue;
1223 p = release_device_array(p, array);
1225 return p;
1228 /* Given a gpu_prog "prog" and the corresponding transformed AST
1229 * "tree", print the entire OpenCL code to "p".
1231 static __isl_give isl_printer *print_opencl(__isl_take isl_printer *p,
1232 struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
1233 struct gpu_types *types, void *user)
1235 struct opencl_info *opencl = user;
1237 opencl->kprinter = isl_printer_set_output_format(opencl->kprinter,
1238 ISL_FORMAT_C);
1239 if (any_double_elements(prog))
1240 opencl->kprinter = opencl_enable_double_support(
1241 opencl->kprinter);
1242 if (opencl->options->opencl_print_kernel_types)
1243 opencl->kprinter = gpu_print_types(opencl->kprinter, types,
1244 prog);
1246 if (!opencl->kprinter)
1247 return isl_printer_free(p);
1249 p = ppcg_start_block(p);
1251 p = opencl_print_host_macros(p);
1253 p = gpu_print_local_declarations(p, prog);
1254 p = opencl_declare_device_arrays(p, prog);
1255 p = opencl_setup(p, opencl->input, opencl);
1256 p = opencl_allocate_device_arrays(p, prog);
1258 p = opencl_print_host_code(p, prog, tree, opencl);
1260 p = opencl_release_device_arrays(p, prog);
1261 p = opencl_release_cl_objects(p, opencl);
1263 p = ppcg_end_block(p);
1265 return p;
1268 /* Transform the code in the file called "input" by replacing
1269 * all scops by corresponding OpenCL code.
1270 * The host code is written to "output" or a name derived from
1271 * "input" if "output" is NULL.
1272 * The kernel code is placed in separate files with names
1273 * derived from "output" or "input".
1275 * We let generate_gpu do all the hard work and then let it call
1276 * us back for printing the AST in print_opencl.
1278 * To prepare for this printing, we first open the output files
1279 * and we close them after generate_gpu has finished.
1281 int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
1282 const char *input, const char *output)
1284 struct opencl_info opencl = { options, input, output };
1285 int r;
1287 opencl.kprinter = isl_printer_to_str(ctx);
1288 r = opencl_open_files(&opencl);
1290 if (r >= 0)
1291 r = generate_gpu(ctx, input, opencl.host_c, options,
1292 &print_opencl, &opencl);
1294 if (opencl_close_files(&opencl) < 0)
1295 r = -1;
1296 isl_printer_free(opencl.kprinter);
1298 return r;