• 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 "util/u_debug.h"
26 
27 #include <sstream>
28 
29 using namespace clover;
30 
31 namespace {
32 
33    std::string
build_options(const char * p_opts,const char * p_debug)34    build_options(const char *p_opts, const char *p_debug) {
35       auto opts = std::string(p_opts ? p_opts : "");
36       std::string extra_opts = debug_get_option(p_debug, "");
37 
38       return detokenize(std::vector<std::string>{opts, extra_opts}, " ");
39    }
40 
41    class build_notifier {
42    public:
build_notifier(cl_program prog,void (* notifer)(cl_program,void *),void * data)43       build_notifier(cl_program prog,
44                      void (*notifer)(cl_program, void *), void *data) :
45                      prog_(prog), notifer(notifer), data_(data) { }
46 
~build_notifier()47       ~build_notifier() {
48          if (notifer)
49             notifer(prog_, data_);
50       }
51 
52    private:
53       cl_program prog_;
54       void (*notifer)(cl_program, void *);
55       void *data_;
56    };
57 
58    void
validate_build_common(const program & prog,cl_uint num_devs,const cl_device_id * d_devs,void (* pfn_notify)(cl_program,void *),void * user_data)59    validate_build_common(const program &prog, cl_uint num_devs,
60                          const cl_device_id *d_devs,
61                          void (*pfn_notify)(cl_program, void *),
62                          void *user_data) {
63       if (!pfn_notify && user_data)
64          throw error(CL_INVALID_VALUE);
65 
66       if (prog.kernel_ref_count())
67          throw error(CL_INVALID_OPERATION);
68 
69       if (any_of([&](const device &dev) {
70                return !count(dev, prog.devices());
71             }, objs<allow_empty_tag>(d_devs, num_devs)))
72          throw error(CL_INVALID_DEVICE);
73    }
74 }
75 
76 CLOVER_API cl_program
clCreateProgramWithSource(cl_context d_ctx,cl_uint count,const char ** strings,const size_t * lengths,cl_int * r_errcode)77 clCreateProgramWithSource(cl_context d_ctx, cl_uint count,
78                           const char **strings, const size_t *lengths,
79                           cl_int *r_errcode) try {
80    auto &ctx = obj(d_ctx);
81    std::string source;
82 
83    if (!count || !strings ||
84        any_of(is_zero(), range(strings, count)))
85       throw error(CL_INVALID_VALUE);
86 
87    // Concatenate all the provided fragments together
88    for (unsigned i = 0; i < count; ++i)
89          source += (lengths && lengths[i] ?
90                     std::string(strings[i], strings[i] + lengths[i]) :
91                     std::string(strings[i]));
92 
93    // ...and create a program object for them.
94    ret_error(r_errcode, CL_SUCCESS);
95    return new program(ctx, source);
96 
97 } catch (error &e) {
98    ret_error(r_errcode, e);
99    return NULL;
100 }
101 
102 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)103 clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
104                           const cl_device_id *d_devs,
105                           const size_t *lengths,
106                           const unsigned char **binaries,
107                           cl_int *r_status, cl_int *r_errcode) try {
108    auto &ctx = obj(d_ctx);
109    auto devs = objs(d_devs, n);
110 
111    if (!lengths || !binaries)
112       throw error(CL_INVALID_VALUE);
113 
114    if (any_of([&](const device &dev) {
115             return !count(dev, ctx.devices());
116          }, devs))
117       throw error(CL_INVALID_DEVICE);
118 
119    // Deserialize the provided binaries,
120    std::vector<std::pair<cl_int, module>> result = map(
121       [](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {
122          if (!p || !l)
123             return { CL_INVALID_VALUE, {} };
124 
125          try {
126             std::stringbuf bin( { (char*)p, l } );
127             std::istream s(&bin);
128 
129             return { CL_SUCCESS, module::deserialize(s) };
130 
131          } catch (std::istream::failure &e) {
132             return { CL_INVALID_BINARY, {} };
133          }
134       },
135       range(binaries, n),
136       range(lengths, n));
137 
138    // update the status array,
139    if (r_status)
140       copy(map(keys(), result), r_status);
141 
142    if (any_of(key_equals(CL_INVALID_VALUE), result))
143       throw error(CL_INVALID_VALUE);
144 
145    if (any_of(key_equals(CL_INVALID_BINARY), result))
146       throw error(CL_INVALID_BINARY);
147 
148    // initialize a program object with them.
149    ret_error(r_errcode, CL_SUCCESS);
150    return new program(ctx, devs, map(values(), result));
151 
152 } catch (error &e) {
153    ret_error(r_errcode, e);
154    return NULL;
155 }
156 
157 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)158 clCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
159                                   const cl_device_id *d_devs,
160                                   const char *kernel_names,
161                                   cl_int *r_errcode) try {
162    auto &ctx = obj(d_ctx);
163    auto devs = objs(d_devs, n);
164 
165    if (any_of([&](const device &dev) {
166             return !count(dev, ctx.devices());
167          }, devs))
168       throw error(CL_INVALID_DEVICE);
169 
170    // No currently supported built-in kernels.
171    throw error(CL_INVALID_VALUE);
172 
173 } catch (error &e) {
174    ret_error(r_errcode, e);
175    return NULL;
176 }
177 
178 
179 CLOVER_API cl_int
clRetainProgram(cl_program d_prog)180 clRetainProgram(cl_program d_prog) try {
181    obj(d_prog).retain();
182    return CL_SUCCESS;
183 
184 } catch (error &e) {
185    return e.get();
186 }
187 
188 CLOVER_API cl_int
clReleaseProgram(cl_program d_prog)189 clReleaseProgram(cl_program d_prog) try {
190    if (obj(d_prog).release())
191       delete pobj(d_prog);
192 
193    return CL_SUCCESS;
194 
195 } catch (error &e) {
196    return e.get();
197 }
198 
199 CLOVER_API cl_int
clBuildProgram(cl_program d_prog,cl_uint num_devs,const cl_device_id * d_devs,const char * p_opts,void (* pfn_notify)(cl_program,void *),void * user_data)200 clBuildProgram(cl_program d_prog, cl_uint num_devs,
201                const cl_device_id *d_devs, const char *p_opts,
202                void (*pfn_notify)(cl_program, void *),
203                void *user_data) try {
204    auto &prog = obj(d_prog);
205    auto devs =
206       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
207    const auto opts = build_options(p_opts, "CLOVER_EXTRA_BUILD_OPTIONS");
208 
209    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
210 
211    auto notifier = build_notifier(d_prog, pfn_notify, user_data);
212 
213    if (prog.has_source) {
214       prog.compile(devs, opts);
215       prog.link(devs, opts, { prog });
216    } else if (any_of([&](const device &dev){
217          return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
218          }, devs)) {
219       // According to the OpenCL 1.2 specification, “if program is created
220       // with clCreateProgramWithBinary, then the program binary must be an
221       // executable binary (not a compiled binary or library).”
222       throw error(CL_INVALID_BINARY);
223    }
224 
225    return CL_SUCCESS;
226 
227 } catch (error &e) {
228    return e.get();
229 }
230 
231 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 (* pfn_notify)(cl_program,void *),void * user_data)232 clCompileProgram(cl_program d_prog, cl_uint num_devs,
233                  const cl_device_id *d_devs, const char *p_opts,
234                  cl_uint num_headers, const cl_program *d_header_progs,
235                  const char **header_names,
236                  void (*pfn_notify)(cl_program, void *),
237                  void *user_data) try {
238    auto &prog = obj(d_prog);
239    auto devs =
240        (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
241    const auto opts = build_options(p_opts, "CLOVER_EXTRA_COMPILE_OPTIONS");
242    header_map headers;
243 
244    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
245 
246    auto notifier = build_notifier(d_prog, pfn_notify, user_data);
247 
248    if (bool(num_headers) != bool(header_names))
249       throw error(CL_INVALID_VALUE);
250 
251    if (!prog.has_source)
252       throw error(CL_INVALID_OPERATION);
253 
254    for_each([&](const char *name, const program &header) {
255          if (!header.has_source)
256             throw error(CL_INVALID_OPERATION);
257 
258          if (!any_of(key_equals(name), headers))
259             headers.push_back(std::pair<std::string, std::string>(
260                                  name, header.source()));
261       },
262       range(header_names, num_headers),
263       objs<allow_empty_tag>(d_header_progs, num_headers));
264 
265    prog.compile(devs, opts, headers);
266    return CL_SUCCESS;
267 
268 } catch (invalid_build_options_error &e) {
269    return CL_INVALID_COMPILER_OPTIONS;
270 
271 } catch (build_error &e) {
272    return CL_COMPILE_PROGRAM_FAILURE;
273 
274 } catch (error &e) {
275    return e.get();
276 }
277 
278 namespace {
279    ref_vector<device>
validate_link_devices(const ref_vector<program> & progs,const ref_vector<device> & all_devs,const std::string & opts)280    validate_link_devices(const ref_vector<program> &progs,
281                          const ref_vector<device> &all_devs,
282                          const std::string &opts) {
283       std::vector<device *> devs;
284       const bool create_library =
285          opts.find("-create-library") != std::string::npos;
286       const bool enable_link_options =
287          opts.find("-enable-link-options") != std::string::npos;
288       const bool has_link_options =
289          opts.find("-cl-denorms-are-zero") != std::string::npos ||
290          opts.find("-cl-no-signed-zeroes") != std::string::npos ||
291          opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
292          opts.find("-cl-finite-math-only") != std::string::npos ||
293          opts.find("-cl-fast-relaxed-math") != std::string::npos ||
294          opts.find("-cl-no-subgroup-ifp") != std::string::npos;
295 
296       // According to the OpenCL 1.2 specification, "[the
297       // -enable-link-options] option must be specified with the
298       // create-library option".
299       if (enable_link_options && !create_library)
300          throw error(CL_INVALID_LINKER_OPTIONS);
301 
302       // According to the OpenCL 1.2 specification, "the
303       // [program linking options] can be specified when linking a program
304       // executable".
305       if (has_link_options && create_library)
306          throw error(CL_INVALID_LINKER_OPTIONS);
307 
308       for (auto &dev : all_devs) {
309          const auto has_binary = [&](const program &prog) {
310             const auto t = prog.build(dev).binary_type();
311             return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
312                    t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
313          };
314 
315          // According to the OpenCL 1.2 specification, a library is made of
316          // “compiled binaries specified in input_programs argument to
317          // clLinkProgram“; compiled binaries does not refer to libraries:
318          // “input_programs is an array of program objects that are compiled
319          // binaries or libraries that are to be linked to create the program
320          // executable”.
321          if (create_library && any_of([&](const program &prog) {
322                   const auto t = prog.build(dev).binary_type();
323                   return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
324                }, progs))
325             throw error(CL_INVALID_OPERATION);
326 
327          // According to the CL 1.2 spec, when "all programs specified [..]
328          // contain a compiled binary or library for the device [..] a link is
329          // performed",
330          else if (all_of(has_binary, progs))
331             devs.push_back(&dev);
332 
333          // otherwise if "none of the programs contain a compiled binary or
334          // library for that device [..] no link is performed.  All other
335          // cases will return a CL_INVALID_OPERATION error."
336          else if (any_of(has_binary, progs))
337             throw error(CL_INVALID_OPERATION);
338 
339          // According to the OpenCL 1.2 specification, "[t]he linker may apply
340          // [program linking options] to all compiled program objects
341          // specified to clLinkProgram. The linker may apply these options
342          // only to libraries which were created with the
343          // -enable-link-option."
344          else if (has_link_options && any_of([&](const program &prog) {
345                   const auto t = prog.build(dev).binary_type();
346                   return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
347                           (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
348                            prog.build(dev).opts.find("-enable-link-options") !=
349                               std::string::npos));
350                }, progs))
351             throw error(CL_INVALID_LINKER_OPTIONS);
352       }
353 
354       return map(derefs(), devs);
355    }
356 }
357 
358 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 (* pfn_notify)(cl_program,void *),void * user_data,cl_int * r_errcode)359 clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
360               const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
361               void (*pfn_notify) (cl_program, void *), void *user_data,
362               cl_int *r_errcode) try {
363    auto &ctx = obj(d_ctx);
364    const auto opts = build_options(p_opts, "CLOVER_EXTRA_LINK_OPTIONS");
365    auto progs = objs(d_progs, num_progs);
366    auto all_devs =
367       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
368    auto prog = create<program>(ctx, all_devs);
369    auto r_prog = ret_object(prog);
370 
371    auto notifier = build_notifier(r_prog, pfn_notify, user_data);
372 
373    auto devs = validate_link_devices(progs, all_devs, opts);
374 
375    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
376 
377    try {
378       prog().link(devs, opts, progs);
379       ret_error(r_errcode, CL_SUCCESS);
380 
381    } catch (build_error &e) {
382       ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
383    }
384 
385    return r_prog;
386 
387 } catch (invalid_build_options_error &e) {
388    ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
389    return NULL;
390 
391 } catch (error &e) {
392    ret_error(r_errcode, e);
393    return NULL;
394 }
395 
396 CLOVER_API cl_int
clUnloadCompiler()397 clUnloadCompiler() {
398    return CL_SUCCESS;
399 }
400 
401 CLOVER_API cl_int
clUnloadPlatformCompiler(cl_platform_id d_platform)402 clUnloadPlatformCompiler(cl_platform_id d_platform) {
403    return CL_SUCCESS;
404 }
405 
406 CLOVER_API cl_int
clGetProgramInfo(cl_program d_prog,cl_program_info param,size_t size,void * r_buf,size_t * r_size)407 clGetProgramInfo(cl_program d_prog, cl_program_info param,
408                  size_t size, void *r_buf, size_t *r_size) try {
409    property_buffer buf { r_buf, size, r_size };
410    auto &prog = obj(d_prog);
411 
412    switch (param) {
413    case CL_PROGRAM_REFERENCE_COUNT:
414       buf.as_scalar<cl_uint>() = prog.ref_count();
415       break;
416 
417    case CL_PROGRAM_CONTEXT:
418       buf.as_scalar<cl_context>() = desc(prog.context());
419       break;
420 
421    case CL_PROGRAM_NUM_DEVICES:
422       buf.as_scalar<cl_uint>() = (prog.devices().size() ?
423                                   prog.devices().size() :
424                                   prog.context().devices().size());
425       break;
426 
427    case CL_PROGRAM_DEVICES:
428       buf.as_vector<cl_device_id>() = (prog.devices().size() ?
429                                        descs(prog.devices()) :
430                                        descs(prog.context().devices()));
431       break;
432 
433    case CL_PROGRAM_SOURCE:
434       buf.as_string() = prog.source();
435       break;
436 
437    case CL_PROGRAM_BINARY_SIZES:
438       buf.as_vector<size_t>() = map([&](const device &dev) {
439             return prog.build(dev).binary.size();
440          },
441          prog.devices());
442       break;
443 
444    case CL_PROGRAM_BINARIES:
445       buf.as_matrix<unsigned char>() = map([&](const device &dev) {
446             std::stringbuf bin;
447             std::ostream s(&bin);
448             prog.build(dev).binary.serialize(s);
449             return bin.str();
450          },
451          prog.devices());
452       break;
453 
454    case CL_PROGRAM_NUM_KERNELS:
455       buf.as_scalar<cl_uint>() = prog.symbols().size();
456       break;
457 
458    case CL_PROGRAM_KERNEL_NAMES:
459       buf.as_string() = fold([](const std::string &a, const module::symbol &s) {
460             return ((a.empty() ? "" : a + ";") + s.name);
461          }, std::string(), prog.symbols());
462       break;
463 
464    default:
465       throw error(CL_INVALID_VALUE);
466    }
467 
468    return CL_SUCCESS;
469 
470 } catch (error &e) {
471    return e.get();
472 }
473 
474 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)475 clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
476                       cl_program_build_info param,
477                       size_t size, void *r_buf, size_t *r_size) try {
478    property_buffer buf { r_buf, size, r_size };
479    auto &prog = obj(d_prog);
480    auto &dev = obj(d_dev);
481 
482    if (!count(dev, prog.context().devices()))
483       return CL_INVALID_DEVICE;
484 
485    switch (param) {
486    case CL_PROGRAM_BUILD_STATUS:
487       buf.as_scalar<cl_build_status>() = prog.build(dev).status();
488       break;
489 
490    case CL_PROGRAM_BUILD_OPTIONS:
491       buf.as_string() = prog.build(dev).opts;
492       break;
493 
494    case CL_PROGRAM_BUILD_LOG:
495       buf.as_string() = prog.build(dev).log;
496       break;
497 
498    case CL_PROGRAM_BINARY_TYPE:
499       buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
500       break;
501 
502    default:
503       throw error(CL_INVALID_VALUE);
504    }
505 
506    return CL_SUCCESS;
507 
508 } catch (error &e) {
509    return e.get();
510 }
511