• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2012 Francisco Jerez
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22 
23 #include "api/util.hpp"
24 #include "core/program.hpp"
25 #include "core/platform.hpp"
26 #include "util/u_debug.h"
27 
28 #include <limits>
29 #include <sstream>
30 
31 using namespace clover;
32 
33 namespace {
34 
35    std::string
build_options(const char * p_opts,const char * p_debug)36    build_options(const char *p_opts, const char *p_debug) {
37       auto opts = std::string(p_opts ? p_opts : "");
38       std::string extra_opts = debug_get_option(p_debug, "");
39 
40       return detokenize(std::vector<std::string>{opts, extra_opts}, " ");
41    }
42 
43    class build_notifier {
44    public:
build_notifier(cl_program prog,void (CL_CALLBACK * notifer)(cl_program,void *),void * data)45       build_notifier(cl_program prog,
46                      void (CL_CALLBACK * notifer)(cl_program, void *), void *data) :
47                      prog_(prog), notifer(notifer), data_(data) { }
48 
~build_notifier()49       ~build_notifier() {
50          if (notifer)
51             notifer(prog_, data_);
52       }
53 
54    private:
55       cl_program prog_;
56       void (CL_CALLBACK * notifer)(cl_program, void *);
57       void *data_;
58    };
59 
60    void
validate_build_common(const program & prog,cl_uint num_devs,const cl_device_id * d_devs,void (CL_CALLBACK * pfn_notify)(cl_program,void *),void * user_data)61    validate_build_common(const program &prog, cl_uint num_devs,
62                          const cl_device_id *d_devs,
63                          void (CL_CALLBACK * pfn_notify)(cl_program, void *),
64                          void *user_data) {
65       if (!pfn_notify && user_data)
66          throw error(CL_INVALID_VALUE);
67 
68       if (prog.kernel_ref_count())
69          throw error(CL_INVALID_OPERATION);
70 
71       if (any_of([&](const device &dev) {
72                return !count(dev, prog.devices());
73             }, objs<allow_empty_tag>(d_devs, num_devs)))
74          throw error(CL_INVALID_DEVICE);
75    }
76 
77    enum program::il_type
identify_and_validate_il(const std::string & il,const cl_version opencl_version,const context::notify_action & notify)78    identify_and_validate_il(const std::string &il,
79                             const cl_version opencl_version,
80                             const context::notify_action &notify) {
81 
82       return program::il_type::none;
83    }
84 }
85 
86 CLOVER_API cl_program
clCreateProgramWithSource(cl_context d_ctx,cl_uint count,const char ** strings,const size_t * lengths,cl_int * r_errcode)87 clCreateProgramWithSource(cl_context d_ctx, cl_uint count,
88                           const char **strings, const size_t *lengths,
89                           cl_int *r_errcode) try {
90    auto &ctx = obj(d_ctx);
91    std::string source;
92 
93    if (!count || !strings ||
94        any_of(is_zero(), range(strings, count)))
95       throw error(CL_INVALID_VALUE);
96 
97    // Concatenate all the provided fragments together
98    for (unsigned i = 0; i < count; ++i)
99          source += (lengths && lengths[i] ?
100                     std::string(strings[i], strings[i] + lengths[i]) :
101                     std::string(strings[i]));
102 
103    // ...and create a program object for them.
104    ret_error(r_errcode, CL_SUCCESS);
105    return new program(ctx, std::move(source), program::il_type::source);
106 
107 } catch (error &e) {
108    ret_error(r_errcode, e);
109    return NULL;
110 }
111 
112 CLOVER_API cl_program
clCreateProgramWithBinary(cl_context d_ctx,cl_uint n,const cl_device_id * d_devs,const size_t * lengths,const unsigned char ** binaries,cl_int * r_status,cl_int * r_errcode)113 clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
114                           const cl_device_id *d_devs,
115                           const size_t *lengths,
116                           const unsigned char **binaries,
117                           cl_int *r_status, cl_int *r_errcode) try {
118    auto &ctx = obj(d_ctx);
119    auto devs = objs(d_devs, n);
120 
121    if (!lengths || !binaries)
122       throw error(CL_INVALID_VALUE);
123 
124    if (any_of([&](const device &dev) {
125             return !count(dev, ctx.devices());
126          }, devs))
127       throw error(CL_INVALID_DEVICE);
128 
129    // Deserialize the provided binaries,
130    std::vector<std::pair<cl_int, binary>> result = map(
131       [](const unsigned char *p, size_t l) -> std::pair<cl_int, binary> {
132          if (!p || !l)
133             return { CL_INVALID_VALUE, {} };
134 
135          try {
136             std::stringbuf bin( std::string{ (char*)p, l } );
137             std::istream s(&bin);
138 
139             return { CL_SUCCESS, binary::deserialize(s) };
140 
141          } catch (std::istream::failure &) {
142             return { CL_INVALID_BINARY, {} };
143          }
144       },
145       range(binaries, n),
146       range(lengths, n));
147 
148    // update the status array,
149    if (r_status)
150       copy(map(keys(), result), r_status);
151 
152    if (any_of(key_equals(CL_INVALID_VALUE), result))
153       throw error(CL_INVALID_VALUE);
154 
155    if (any_of(key_equals(CL_INVALID_BINARY), result))
156       throw error(CL_INVALID_BINARY);
157 
158    // initialize a program object with them.
159    ret_error(r_errcode, CL_SUCCESS);
160    return new program(ctx, devs, map(values(), result));
161 
162 } catch (error &e) {
163    ret_error(r_errcode, e);
164    return NULL;
165 }
166 
167 cl_program
CreateProgramWithILKHR(cl_context d_ctx,const void * il,size_t length,cl_int * r_errcode)168 clover::CreateProgramWithILKHR(cl_context d_ctx, const void *il,
169                                size_t length, cl_int *r_errcode) try {
170    auto &ctx = obj(d_ctx);
171 
172    if (!il || !length)
173       throw error(CL_INVALID_VALUE);
174 
175    // Compute the highest OpenCL version supported by all devices associated to
176    // the context. That is the version used for validating the SPIR-V binary.
177    cl_version min_opencl_version = std::numeric_limits<uint32_t>::max();
178    for (const device &dev : ctx.devices()) {
179       const cl_version opencl_version = dev.device_version();
180       min_opencl_version = std::min(opencl_version, min_opencl_version);
181    }
182 
183    const char *stream = reinterpret_cast<const char *>(il);
184    std::string binary(stream, stream + length);
185    const enum program::il_type il_type = identify_and_validate_il(binary,
186                                                                   min_opencl_version,
187                                                                   ctx.notify);
188 
189    if (il_type == program::il_type::none)
190       throw error(CL_INVALID_VALUE);
191 
192    // Initialize a program object with it.
193    ret_error(r_errcode, CL_SUCCESS);
194    return new program(ctx, std::move(binary), il_type);
195 
196 } catch (error &e) {
197    ret_error(r_errcode, e);
198    return NULL;
199 }
200 
201 CLOVER_API cl_program
clCreateProgramWithIL(cl_context d_ctx,const void * il,size_t length,cl_int * r_errcode)202 clCreateProgramWithIL(cl_context d_ctx,
203                       const void *il,
204                       size_t length,
205                       cl_int *r_errcode) {
206    return CreateProgramWithILKHR(d_ctx, il, length, r_errcode);
207 }
208 
209 CLOVER_API cl_program
clCreateProgramWithBuiltInKernels(cl_context d_ctx,cl_uint n,const cl_device_id * d_devs,const char * kernel_names,cl_int * r_errcode)210 clCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
211                                   const cl_device_id *d_devs,
212                                   const char *kernel_names,
213                                   cl_int *r_errcode) try {
214    auto &ctx = obj(d_ctx);
215    auto devs = objs(d_devs, n);
216 
217    if (any_of([&](const device &dev) {
218             return !count(dev, ctx.devices());
219          }, devs))
220       throw error(CL_INVALID_DEVICE);
221 
222    // No currently supported built-in kernels.
223    throw error(CL_INVALID_VALUE);
224 
225 } catch (error &e) {
226    ret_error(r_errcode, e);
227    return NULL;
228 }
229 
230 
231 CLOVER_API cl_int
clRetainProgram(cl_program d_prog)232 clRetainProgram(cl_program d_prog) try {
233    obj(d_prog).retain();
234    return CL_SUCCESS;
235 
236 } catch (error &e) {
237    return e.get();
238 }
239 
240 CLOVER_API cl_int
clReleaseProgram(cl_program d_prog)241 clReleaseProgram(cl_program d_prog) try {
242    if (obj(d_prog).release())
243       delete pobj(d_prog);
244 
245    return CL_SUCCESS;
246 
247 } catch (error &e) {
248    return e.get();
249 }
250 
251 CLOVER_API cl_int
clBuildProgram(cl_program d_prog,cl_uint num_devs,const cl_device_id * d_devs,const char * p_opts,void (CL_CALLBACK * pfn_notify)(cl_program,void *),void * user_data)252 clBuildProgram(cl_program d_prog, cl_uint num_devs,
253                const cl_device_id *d_devs, const char *p_opts,
254                void (CL_CALLBACK * pfn_notify)(cl_program, void *),
255                void *user_data) try {
256    auto &prog = obj(d_prog);
257    auto devs =
258       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
259    const auto opts = build_options(p_opts, "CLOVER_EXTRA_BUILD_OPTIONS");
260 
261    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
262 
263    auto notifier = build_notifier(d_prog, pfn_notify, user_data);
264 
265    if (prog.il_type() != program::il_type::none) {
266       prog.compile(devs, opts);
267       prog.link(devs, opts, { prog });
268    } else if (any_of([&](const device &dev){
269          return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
270          }, devs)) {
271       // According to the OpenCL 1.2 specification, “if program is created
272       // with clCreateProgramWithBinary, then the program binary must be an
273       // executable binary (not a compiled binary or library).”
274       throw error(CL_INVALID_BINARY);
275    }
276 
277    return CL_SUCCESS;
278 
279 } catch (error &e) {
280    return e.get();
281 }
282 
283 CLOVER_API cl_int
clCompileProgram(cl_program d_prog,cl_uint num_devs,const cl_device_id * d_devs,const char * p_opts,cl_uint num_headers,const cl_program * d_header_progs,const char ** header_names,void (CL_CALLBACK * pfn_notify)(cl_program,void *),void * user_data)284 clCompileProgram(cl_program d_prog, cl_uint num_devs,
285                  const cl_device_id *d_devs, const char *p_opts,
286                  cl_uint num_headers, const cl_program *d_header_progs,
287                  const char **header_names,
288                  void (CL_CALLBACK * pfn_notify)(cl_program, void *),
289                  void *user_data) try {
290    auto &prog = obj(d_prog);
291    auto devs =
292        (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
293    const auto opts = build_options(p_opts, "CLOVER_EXTRA_COMPILE_OPTIONS");
294    header_map headers;
295 
296    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
297 
298    auto notifier = build_notifier(d_prog, pfn_notify, user_data);
299 
300    if (bool(num_headers) != bool(header_names))
301       throw error(CL_INVALID_VALUE);
302 
303    if (prog.il_type() == program::il_type::none)
304       throw error(CL_INVALID_OPERATION);
305 
306    for_each([&](const char *name, const program &header) {
307          if (header.il_type() == program::il_type::none)
308             throw error(CL_INVALID_OPERATION);
309 
310          if (!any_of(key_equals(name), headers))
311             headers.push_back(std::pair<std::string, std::string>(
312                                  name, header.source()));
313       },
314       range(header_names, num_headers),
315       objs<allow_empty_tag>(d_header_progs, num_headers));
316 
317    prog.compile(devs, opts, headers);
318    return CL_SUCCESS;
319 
320 } catch (invalid_build_options_error &) {
321    return CL_INVALID_COMPILER_OPTIONS;
322 
323 } catch (build_error &) {
324    return CL_COMPILE_PROGRAM_FAILURE;
325 
326 } catch (error &e) {
327    return e.get();
328 }
329 
330 namespace {
331    ref_vector<device>
validate_link_devices(const ref_vector<program> & progs,const ref_vector<device> & all_devs,const std::string & opts)332    validate_link_devices(const ref_vector<program> &progs,
333                          const ref_vector<device> &all_devs,
334                          const std::string &opts) {
335       std::vector<device *> devs;
336       const bool create_library =
337          opts.find("-create-library") != std::string::npos;
338       const bool enable_link_options =
339          opts.find("-enable-link-options") != std::string::npos;
340       const bool has_link_options =
341          opts.find("-cl-denorms-are-zero") != std::string::npos ||
342          opts.find("-cl-no-signed-zeroes") != std::string::npos ||
343          opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
344          opts.find("-cl-finite-math-only") != std::string::npos ||
345          opts.find("-cl-fast-relaxed-math") != std::string::npos ||
346          opts.find("-cl-no-subgroup-ifp") != std::string::npos;
347 
348       // According to the OpenCL 1.2 specification, "[the
349       // -enable-link-options] option must be specified with the
350       // create-library option".
351       if (enable_link_options && !create_library)
352          throw error(CL_INVALID_LINKER_OPTIONS);
353 
354       // According to the OpenCL 1.2 specification, "the
355       // [program linking options] can be specified when linking a program
356       // executable".
357       if (has_link_options && create_library)
358          throw error(CL_INVALID_LINKER_OPTIONS);
359 
360       for (auto &dev : all_devs) {
361          const auto has_binary = [&](const program &prog) {
362             const auto t = prog.build(dev).binary_type();
363             return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
364                    t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
365          };
366 
367          // According to the OpenCL 1.2 specification, a library is made of
368          // “compiled binaries specified in input_programs argument to
369          // clLinkProgram“; compiled binaries does not refer to libraries:
370          // “input_programs is an array of program objects that are compiled
371          // binaries or libraries that are to be linked to create the program
372          // executable”.
373          if (create_library && any_of([&](const program &prog) {
374                   const auto t = prog.build(dev).binary_type();
375                   return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
376                }, progs))
377             throw error(CL_INVALID_OPERATION);
378 
379          // According to the CL 1.2 spec, when "all programs specified [..]
380          // contain a compiled binary or library for the device [..] a link is
381          // performed",
382          else if (all_of(has_binary, progs))
383             devs.push_back(&dev);
384 
385          // otherwise if "none of the programs contain a compiled binary or
386          // library for that device [..] no link is performed.  All other
387          // cases will return a CL_INVALID_OPERATION error."
388          else if (any_of(has_binary, progs))
389             throw error(CL_INVALID_OPERATION);
390 
391          // According to the OpenCL 1.2 specification, "[t]he linker may apply
392          // [program linking options] to all compiled program objects
393          // specified to clLinkProgram. The linker may apply these options
394          // only to libraries which were created with the
395          // -enable-link-option."
396          else if (has_link_options && any_of([&](const program &prog) {
397                   const auto t = prog.build(dev).binary_type();
398                   return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
399                           (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
400                            prog.build(dev).opts.find("-enable-link-options") !=
401                               std::string::npos));
402                }, progs))
403             throw error(CL_INVALID_LINKER_OPTIONS);
404       }
405 
406       return map(derefs(), devs);
407    }
408 }
409 
410 CLOVER_API cl_program
clLinkProgram(cl_context d_ctx,cl_uint num_devs,const cl_device_id * d_devs,const char * p_opts,cl_uint num_progs,const cl_program * d_progs,void (CL_CALLBACK * pfn_notify)(cl_program,void *),void * user_data,cl_int * r_errcode)411 clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
412               const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
413               void (CL_CALLBACK * pfn_notify) (cl_program, void *), void *user_data,
414               cl_int *r_errcode) try {
415    auto &ctx = obj(d_ctx);
416    const auto opts = build_options(p_opts, "CLOVER_EXTRA_LINK_OPTIONS");
417    auto progs = objs(d_progs, num_progs);
418    auto all_devs =
419       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
420    auto prog = create<program>(ctx, all_devs);
421    auto r_prog = ret_object(prog);
422 
423    auto notifier = build_notifier(r_prog, pfn_notify, user_data);
424 
425    auto devs = validate_link_devices(progs, all_devs, opts);
426 
427    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
428 
429    try {
430       prog().link(devs, opts, progs);
431       ret_error(r_errcode, CL_SUCCESS);
432 
433    } catch (build_error &) {
434       ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
435    }
436 
437    return r_prog;
438 
439 } catch (invalid_build_options_error &) {
440    ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
441    return NULL;
442 
443 } catch (error &e) {
444    ret_error(r_errcode, e);
445    return NULL;
446 }
447 
448 CLOVER_API cl_int
clUnloadCompiler()449 clUnloadCompiler() {
450    return CL_SUCCESS;
451 }
452 
453 CLOVER_API cl_int
clUnloadPlatformCompiler(cl_platform_id d_platform)454 clUnloadPlatformCompiler(cl_platform_id d_platform) try {
455    find_platform(d_platform);
456    return CL_SUCCESS;
457 } catch (error &e) {
458    return e.get();
459 }
460 
461 CLOVER_API cl_int
clGetProgramInfo(cl_program d_prog,cl_program_info param,size_t size,void * r_buf,size_t * r_size)462 clGetProgramInfo(cl_program d_prog, cl_program_info param,
463                  size_t size, void *r_buf, size_t *r_size) try {
464    property_buffer buf { r_buf, size, r_size };
465    auto &prog = obj(d_prog);
466 
467    switch (param) {
468    case CL_PROGRAM_REFERENCE_COUNT:
469       buf.as_scalar<cl_uint>() = prog.ref_count();
470       break;
471 
472    case CL_PROGRAM_CONTEXT:
473       buf.as_scalar<cl_context>() = desc(prog.context());
474       break;
475 
476    case CL_PROGRAM_NUM_DEVICES:
477       buf.as_scalar<cl_uint>() = (prog.devices().size() ?
478                                   prog.devices().size() :
479                                   prog.context().devices().size());
480       break;
481 
482    case CL_PROGRAM_DEVICES:
483       buf.as_vector<cl_device_id>() = (prog.devices().size() ?
484                                        descs(prog.devices()) :
485                                        descs(prog.context().devices()));
486       break;
487 
488    case CL_PROGRAM_SOURCE:
489       buf.as_string() = prog.source();
490       break;
491 
492    case CL_PROGRAM_BINARY_SIZES:
493       buf.as_vector<size_t>() = map([&](const device &dev) {
494             return prog.build(dev).bin.size();
495          },
496          prog.devices());
497       break;
498 
499    case CL_PROGRAM_BINARIES:
500       buf.as_matrix<unsigned char>() = map([&](const device &dev) {
501             std::stringbuf bin;
502             std::ostream s(&bin);
503             prog.build(dev).bin.serialize(s);
504             return bin.str();
505          },
506          prog.devices());
507       break;
508 
509    case CL_PROGRAM_NUM_KERNELS:
510       buf.as_scalar<cl_uint>() = prog.symbols().size();
511       break;
512 
513    case CL_PROGRAM_KERNEL_NAMES:
514       buf.as_string() = fold([](const std::string &a, const binary::symbol &s) {
515             return ((a.empty() ? "" : a + ";") + s.name);
516          }, std::string(), prog.symbols());
517       break;
518 
519    case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT:
520    case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT:
521       buf.as_scalar<cl_bool>() = CL_FALSE;
522       break;
523 
524    case CL_PROGRAM_IL:
525       if (prog.il_type() == program::il_type::spirv)
526          buf.as_vector<char>() = prog.source();
527       else if (r_size)
528          *r_size = 0u;
529       break;
530    default:
531       throw error(CL_INVALID_VALUE);
532    }
533 
534    return CL_SUCCESS;
535 
536 } catch (error &e) {
537    return e.get();
538 }
539 
540 CLOVER_API cl_int
clGetProgramBuildInfo(cl_program d_prog,cl_device_id d_dev,cl_program_build_info param,size_t size,void * r_buf,size_t * r_size)541 clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
542                       cl_program_build_info param,
543                       size_t size, void *r_buf, size_t *r_size) try {
544    property_buffer buf { r_buf, size, r_size };
545    auto &prog = obj(d_prog);
546    auto &dev = obj(d_dev);
547 
548    if (!count(dev, prog.context().devices()))
549       return CL_INVALID_DEVICE;
550 
551    switch (param) {
552    case CL_PROGRAM_BUILD_STATUS:
553       buf.as_scalar<cl_build_status>() = prog.build(dev).status();
554       break;
555 
556    case CL_PROGRAM_BUILD_OPTIONS:
557       buf.as_string() = prog.build(dev).opts;
558       break;
559 
560    case CL_PROGRAM_BUILD_LOG:
561       buf.as_string() = prog.build(dev).log;
562       break;
563 
564    case CL_PROGRAM_BINARY_TYPE:
565       buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
566       break;
567 
568    case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:
569       buf.as_scalar<size_t>() = 0;
570       break;
571 
572    default:
573       throw error(CL_INVALID_VALUE);
574    }
575 
576    return CL_SUCCESS;
577 
578 } catch (error &e) {
579    return e.get();
580 }
581