• 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    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)33    validate_build_common(const program &prog, cl_uint num_devs,
34                          const cl_device_id *d_devs,
35                          void (*pfn_notify)(cl_program, void *),
36                          void *user_data) {
37       if (!pfn_notify && user_data)
38          throw error(CL_INVALID_VALUE);
39 
40       if (prog.kernel_ref_count())
41          throw error(CL_INVALID_OPERATION);
42 
43       if (any_of([&](const device &dev) {
44                return !count(dev, prog.context().devices());
45             }, objs<allow_empty_tag>(d_devs, num_devs)))
46          throw error(CL_INVALID_DEVICE);
47    }
48 }
49 
50 CLOVER_API cl_program
clCreateProgramWithSource(cl_context d_ctx,cl_uint count,const char ** strings,const size_t * lengths,cl_int * r_errcode)51 clCreateProgramWithSource(cl_context d_ctx, cl_uint count,
52                           const char **strings, const size_t *lengths,
53                           cl_int *r_errcode) try {
54    auto &ctx = obj(d_ctx);
55    std::string source;
56 
57    if (!count || !strings ||
58        any_of(is_zero(), range(strings, count)))
59       throw error(CL_INVALID_VALUE);
60 
61    // Concatenate all the provided fragments together
62    for (unsigned i = 0; i < count; ++i)
63          source += (lengths && lengths[i] ?
64                     std::string(strings[i], strings[i] + lengths[i]) :
65                     std::string(strings[i]));
66 
67    // ...and create a program object for them.
68    ret_error(r_errcode, CL_SUCCESS);
69    return new program(ctx, source);
70 
71 } catch (error &e) {
72    ret_error(r_errcode, e);
73    return NULL;
74 }
75 
76 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)77 clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
78                           const cl_device_id *d_devs,
79                           const size_t *lengths,
80                           const unsigned char **binaries,
81                           cl_int *r_status, cl_int *r_errcode) try {
82    auto &ctx = obj(d_ctx);
83    auto devs = objs(d_devs, n);
84 
85    if (!lengths || !binaries)
86       throw error(CL_INVALID_VALUE);
87 
88    if (any_of([&](const device &dev) {
89             return !count(dev, ctx.devices());
90          }, devs))
91       throw error(CL_INVALID_DEVICE);
92 
93    // Deserialize the provided binaries,
94    std::vector<std::pair<cl_int, module>> result = map(
95       [](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {
96          if (!p || !l)
97             return { CL_INVALID_VALUE, {} };
98 
99          try {
100             std::stringbuf bin( { (char*)p, l } );
101             std::istream s(&bin);
102 
103             return { CL_SUCCESS, module::deserialize(s) };
104 
105          } catch (std::istream::failure &e) {
106             return { CL_INVALID_BINARY, {} };
107          }
108       },
109       range(binaries, n),
110       range(lengths, n));
111 
112    // update the status array,
113    if (r_status)
114       copy(map(keys(), result), r_status);
115 
116    if (any_of(key_equals(CL_INVALID_VALUE), result))
117       throw error(CL_INVALID_VALUE);
118 
119    if (any_of(key_equals(CL_INVALID_BINARY), result))
120       throw error(CL_INVALID_BINARY);
121 
122    // initialize a program object with them.
123    ret_error(r_errcode, CL_SUCCESS);
124    return new program(ctx, devs, map(values(), result));
125 
126 } catch (error &e) {
127    ret_error(r_errcode, e);
128    return NULL;
129 }
130 
131 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)132 clCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
133                                   const cl_device_id *d_devs,
134                                   const char *kernel_names,
135                                   cl_int *r_errcode) try {
136    auto &ctx = obj(d_ctx);
137    auto devs = objs(d_devs, n);
138 
139    if (any_of([&](const device &dev) {
140             return !count(dev, ctx.devices());
141          }, devs))
142       throw error(CL_INVALID_DEVICE);
143 
144    // No currently supported built-in kernels.
145    throw error(CL_INVALID_VALUE);
146 
147 } catch (error &e) {
148    ret_error(r_errcode, e);
149    return NULL;
150 }
151 
152 
153 CLOVER_API cl_int
clRetainProgram(cl_program d_prog)154 clRetainProgram(cl_program d_prog) try {
155    obj(d_prog).retain();
156    return CL_SUCCESS;
157 
158 } catch (error &e) {
159    return e.get();
160 }
161 
162 CLOVER_API cl_int
clReleaseProgram(cl_program d_prog)163 clReleaseProgram(cl_program d_prog) try {
164    if (obj(d_prog).release())
165       delete pobj(d_prog);
166 
167    return CL_SUCCESS;
168 
169 } catch (error &e) {
170    return e.get();
171 }
172 
173 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)174 clBuildProgram(cl_program d_prog, cl_uint num_devs,
175                const cl_device_id *d_devs, const char *p_opts,
176                void (*pfn_notify)(cl_program, void *),
177                void *user_data) try {
178    auto &prog = obj(d_prog);
179    auto devs = (d_devs ? objs(d_devs, num_devs) :
180                 ref_vector<device>(prog.context().devices()));
181    const auto opts = std::string(p_opts ? p_opts : "") + " " +
182                      debug_get_option("CLOVER_EXTRA_BUILD_OPTIONS", "");
183 
184    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
185 
186    if (prog.has_source) {
187       prog.compile(devs, opts);
188       prog.link(devs, opts, { prog });
189    }
190 
191    return CL_SUCCESS;
192 
193 } catch (error &e) {
194    return e.get();
195 }
196 
197 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)198 clCompileProgram(cl_program d_prog, cl_uint num_devs,
199                  const cl_device_id *d_devs, const char *p_opts,
200                  cl_uint num_headers, const cl_program *d_header_progs,
201                  const char **header_names,
202                  void (*pfn_notify)(cl_program, void *),
203                  void *user_data) try {
204    auto &prog = obj(d_prog);
205    auto devs = (d_devs ? objs(d_devs, num_devs) :
206                 ref_vector<device>(prog.context().devices()));
207    const auto opts = std::string(p_opts ? p_opts : "") + " " +
208                      debug_get_option("CLOVER_EXTRA_COMPILE_OPTIONS", "");
209    header_map headers;
210 
211    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
212 
213    if (bool(num_headers) != bool(header_names))
214       throw error(CL_INVALID_VALUE);
215 
216    if (!prog.has_source)
217       throw error(CL_INVALID_OPERATION);
218 
219    for_each([&](const char *name, const program &header) {
220          if (!header.has_source)
221             throw error(CL_INVALID_OPERATION);
222 
223          if (!any_of(key_equals(name), headers))
224             headers.push_back(std::pair<std::string, std::string>(
225                                  name, header.source()));
226       },
227       range(header_names, num_headers),
228       objs<allow_empty_tag>(d_header_progs, num_headers));
229 
230    prog.compile(devs, opts, headers);
231    return CL_SUCCESS;
232 
233 } catch (invalid_build_options_error &e) {
234    return CL_INVALID_COMPILER_OPTIONS;
235 
236 } catch (build_error &e) {
237    return CL_COMPILE_PROGRAM_FAILURE;
238 
239 } catch (error &e) {
240    return e.get();
241 }
242 
243 namespace {
244    ref_vector<device>
validate_link_devices(const ref_vector<program> & progs,const ref_vector<device> & all_devs)245    validate_link_devices(const ref_vector<program> &progs,
246                          const ref_vector<device> &all_devs) {
247       std::vector<device *> devs;
248 
249       for (auto &dev : all_devs) {
250          const auto has_binary = [&](const program &prog) {
251             const auto t = prog.build(dev).binary_type();
252             return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
253                    t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
254          };
255 
256          // According to the CL 1.2 spec, when "all programs specified [..]
257          // contain a compiled binary or library for the device [..] a link is
258          // performed",
259          if (all_of(has_binary, progs))
260             devs.push_back(&dev);
261 
262          // otherwise if "none of the programs contain a compiled binary or
263          // library for that device [..] no link is performed.  All other
264          // cases will return a CL_INVALID_OPERATION error."
265          else if (any_of(has_binary, progs))
266             throw error(CL_INVALID_OPERATION);
267       }
268 
269       return map(derefs(), devs);
270    }
271 }
272 
273 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)274 clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
275               const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
276               void (*pfn_notify) (cl_program, void *), void *user_data,
277               cl_int *r_errcode) try {
278    auto &ctx = obj(d_ctx);
279    const auto opts = std::string(p_opts ? p_opts : "") + " " +
280                      debug_get_option("CLOVER_EXTRA_LINK_OPTIONS", "");
281    auto progs = objs(d_progs, num_progs);
282    auto prog = create<program>(ctx);
283    auto devs = validate_link_devices(progs,
284                                      (d_devs ? objs(d_devs, num_devs) :
285                                       ref_vector<device>(ctx.devices())));
286 
287    validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
288 
289    try {
290       prog().link(devs, opts, progs);
291       ret_error(r_errcode, CL_SUCCESS);
292 
293    } catch (build_error &e) {
294       ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
295    }
296 
297    return ret_object(prog);
298 
299 } catch (invalid_build_options_error &e) {
300    ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
301    return NULL;
302 
303 } catch (error &e) {
304    ret_error(r_errcode, e);
305    return NULL;
306 }
307 
308 CLOVER_API cl_int
clUnloadCompiler()309 clUnloadCompiler() {
310    return CL_SUCCESS;
311 }
312 
313 CLOVER_API cl_int
clUnloadPlatformCompiler(cl_platform_id d_platform)314 clUnloadPlatformCompiler(cl_platform_id d_platform) {
315    return CL_SUCCESS;
316 }
317 
318 CLOVER_API cl_int
clGetProgramInfo(cl_program d_prog,cl_program_info param,size_t size,void * r_buf,size_t * r_size)319 clGetProgramInfo(cl_program d_prog, cl_program_info param,
320                  size_t size, void *r_buf, size_t *r_size) try {
321    property_buffer buf { r_buf, size, r_size };
322    auto &prog = obj(d_prog);
323 
324    switch (param) {
325    case CL_PROGRAM_REFERENCE_COUNT:
326       buf.as_scalar<cl_uint>() = prog.ref_count();
327       break;
328 
329    case CL_PROGRAM_CONTEXT:
330       buf.as_scalar<cl_context>() = desc(prog.context());
331       break;
332 
333    case CL_PROGRAM_NUM_DEVICES:
334       buf.as_scalar<cl_uint>() = (prog.devices().size() ?
335                                   prog.devices().size() :
336                                   prog.context().devices().size());
337       break;
338 
339    case CL_PROGRAM_DEVICES:
340       buf.as_vector<cl_device_id>() = (prog.devices().size() ?
341                                        descs(prog.devices()) :
342                                        descs(prog.context().devices()));
343       break;
344 
345    case CL_PROGRAM_SOURCE:
346       buf.as_string() = prog.source();
347       break;
348 
349    case CL_PROGRAM_BINARY_SIZES:
350       buf.as_vector<size_t>() = map([&](const device &dev) {
351             return prog.build(dev).binary.size();
352          },
353          prog.devices());
354       break;
355 
356    case CL_PROGRAM_BINARIES:
357       buf.as_matrix<unsigned char>() = map([&](const device &dev) {
358             std::stringbuf bin;
359             std::ostream s(&bin);
360             prog.build(dev).binary.serialize(s);
361             return bin.str();
362          },
363          prog.devices());
364       break;
365 
366    case CL_PROGRAM_NUM_KERNELS:
367       buf.as_scalar<cl_uint>() = prog.symbols().size();
368       break;
369 
370    case CL_PROGRAM_KERNEL_NAMES:
371       buf.as_string() = fold([](const std::string &a, const module::symbol &s) {
372             return ((a.empty() ? "" : a + ";") + s.name);
373          }, std::string(), prog.symbols());
374       break;
375 
376    default:
377       throw error(CL_INVALID_VALUE);
378    }
379 
380    return CL_SUCCESS;
381 
382 } catch (error &e) {
383    return e.get();
384 }
385 
386 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)387 clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
388                       cl_program_build_info param,
389                       size_t size, void *r_buf, size_t *r_size) try {
390    property_buffer buf { r_buf, size, r_size };
391    auto &prog = obj(d_prog);
392    auto &dev = obj(d_dev);
393 
394    if (!count(dev, prog.context().devices()))
395       return CL_INVALID_DEVICE;
396 
397    switch (param) {
398    case CL_PROGRAM_BUILD_STATUS:
399       buf.as_scalar<cl_build_status>() = prog.build(dev).status();
400       break;
401 
402    case CL_PROGRAM_BUILD_OPTIONS:
403       buf.as_string() = prog.build(dev).opts;
404       break;
405 
406    case CL_PROGRAM_BUILD_LOG:
407       buf.as_string() = prog.build(dev).log;
408       break;
409 
410    case CL_PROGRAM_BINARY_TYPE:
411       buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
412       break;
413 
414    default:
415       throw error(CL_INVALID_VALUE);
416    }
417 
418    return CL_SUCCESS;
419 
420 } catch (error &e) {
421    return e.get();
422 }
423