gallium: rename 'state tracker' to 'frontend'
[mesa.git] / src / gallium / frontends / clover / api / program.cpp
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
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.devices());
45 }, objs<allow_empty_tag>(d_devs, num_devs)))
46 throw error(CL_INVALID_DEVICE);
47 }
48 }
49
50 CLOVER_API cl_program
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
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
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
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
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
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 =
180 (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.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 } else if (any_of([&](const device &dev){
190 return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
191 }, devs)) {
192 // According to the OpenCL 1.2 specification, “if program is created
193 // with clCreateProgramWithBinary, then the program binary must be an
194 // executable binary (not a compiled binary or library).”
195 throw error(CL_INVALID_BINARY);
196 }
197
198 return CL_SUCCESS;
199
200 } catch (error &e) {
201 return e.get();
202 }
203
204 CLOVER_API cl_int
205 clCompileProgram(cl_program d_prog, cl_uint num_devs,
206 const cl_device_id *d_devs, const char *p_opts,
207 cl_uint num_headers, const cl_program *d_header_progs,
208 const char **header_names,
209 void (*pfn_notify)(cl_program, void *),
210 void *user_data) try {
211 auto &prog = obj(d_prog);
212 auto devs =
213 (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
214 const auto opts = std::string(p_opts ? p_opts : "") + " " +
215 debug_get_option("CLOVER_EXTRA_COMPILE_OPTIONS", "");
216 header_map headers;
217
218 validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
219
220 if (bool(num_headers) != bool(header_names))
221 throw error(CL_INVALID_VALUE);
222
223 if (!prog.has_source)
224 throw error(CL_INVALID_OPERATION);
225
226 for_each([&](const char *name, const program &header) {
227 if (!header.has_source)
228 throw error(CL_INVALID_OPERATION);
229
230 if (!any_of(key_equals(name), headers))
231 headers.push_back(std::pair<std::string, std::string>(
232 name, header.source()));
233 },
234 range(header_names, num_headers),
235 objs<allow_empty_tag>(d_header_progs, num_headers));
236
237 prog.compile(devs, opts, headers);
238 return CL_SUCCESS;
239
240 } catch (invalid_build_options_error &e) {
241 return CL_INVALID_COMPILER_OPTIONS;
242
243 } catch (build_error &e) {
244 return CL_COMPILE_PROGRAM_FAILURE;
245
246 } catch (error &e) {
247 return e.get();
248 }
249
250 namespace {
251 ref_vector<device>
252 validate_link_devices(const ref_vector<program> &progs,
253 const ref_vector<device> &all_devs,
254 const std::string &opts) {
255 std::vector<device *> devs;
256 const bool create_library =
257 opts.find("-create-library") != std::string::npos;
258 const bool enable_link_options =
259 opts.find("-enable-link-options") != std::string::npos;
260 const bool has_link_options =
261 opts.find("-cl-denorms-are-zero") != std::string::npos ||
262 opts.find("-cl-no-signed-zeroes") != std::string::npos ||
263 opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
264 opts.find("-cl-finite-math-only") != std::string::npos ||
265 opts.find("-cl-fast-relaxed-math") != std::string::npos ||
266 opts.find("-cl-no-subgroup-ifp") != std::string::npos;
267
268 // According to the OpenCL 1.2 specification, "[the
269 // -enable-link-options] option must be specified with the
270 // create-library option".
271 if (enable_link_options && !create_library)
272 throw error(CL_INVALID_LINKER_OPTIONS);
273
274 // According to the OpenCL 1.2 specification, "the
275 // [program linking options] can be specified when linking a program
276 // executable".
277 if (has_link_options && create_library)
278 throw error(CL_INVALID_LINKER_OPTIONS);
279
280 for (auto &dev : all_devs) {
281 const auto has_binary = [&](const program &prog) {
282 const auto t = prog.build(dev).binary_type();
283 return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
284 t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
285 };
286
287 // According to the OpenCL 1.2 specification, a library is made of
288 // “compiled binaries specified in input_programs argument to
289 // clLinkProgram“; compiled binaries does not refer to libraries:
290 // “input_programs is an array of program objects that are compiled
291 // binaries or libraries that are to be linked to create the program
292 // executable”.
293 if (create_library && any_of([&](const program &prog) {
294 const auto t = prog.build(dev).binary_type();
295 return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
296 }, progs))
297 throw error(CL_INVALID_OPERATION);
298
299 // According to the CL 1.2 spec, when "all programs specified [..]
300 // contain a compiled binary or library for the device [..] a link is
301 // performed",
302 else if (all_of(has_binary, progs))
303 devs.push_back(&dev);
304
305 // otherwise if "none of the programs contain a compiled binary or
306 // library for that device [..] no link is performed. All other
307 // cases will return a CL_INVALID_OPERATION error."
308 else if (any_of(has_binary, progs))
309 throw error(CL_INVALID_OPERATION);
310
311 // According to the OpenCL 1.2 specification, "[t]he linker may apply
312 // [program linking options] to all compiled program objects
313 // specified to clLinkProgram. The linker may apply these options
314 // only to libraries which were created with the
315 // -enable-link-option."
316 else if (has_link_options && any_of([&](const program &prog) {
317 const auto t = prog.build(dev).binary_type();
318 return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
319 (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
320 prog.build(dev).opts.find("-enable-link-options") !=
321 std::string::npos));
322 }, progs))
323 throw error(CL_INVALID_LINKER_OPTIONS);
324 }
325
326 return map(derefs(), devs);
327 }
328 }
329
330 CLOVER_API cl_program
331 clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
332 const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
333 void (*pfn_notify) (cl_program, void *), void *user_data,
334 cl_int *r_errcode) try {
335 auto &ctx = obj(d_ctx);
336 const auto opts = std::string(p_opts ? p_opts : "") + " " +
337 debug_get_option("CLOVER_EXTRA_LINK_OPTIONS", "");
338 auto progs = objs(d_progs, num_progs);
339 auto all_devs =
340 (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
341 auto prog = create<program>(ctx, all_devs);
342 auto devs = validate_link_devices(progs, all_devs, opts);
343
344 validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
345
346 try {
347 prog().link(devs, opts, progs);
348 ret_error(r_errcode, CL_SUCCESS);
349
350 } catch (build_error &e) {
351 ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
352 }
353
354 return ret_object(prog);
355
356 } catch (invalid_build_options_error &e) {
357 ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
358 return NULL;
359
360 } catch (error &e) {
361 ret_error(r_errcode, e);
362 return NULL;
363 }
364
365 CLOVER_API cl_int
366 clUnloadCompiler() {
367 return CL_SUCCESS;
368 }
369
370 CLOVER_API cl_int
371 clUnloadPlatformCompiler(cl_platform_id d_platform) {
372 return CL_SUCCESS;
373 }
374
375 CLOVER_API cl_int
376 clGetProgramInfo(cl_program d_prog, cl_program_info param,
377 size_t size, void *r_buf, size_t *r_size) try {
378 property_buffer buf { r_buf, size, r_size };
379 auto &prog = obj(d_prog);
380
381 switch (param) {
382 case CL_PROGRAM_REFERENCE_COUNT:
383 buf.as_scalar<cl_uint>() = prog.ref_count();
384 break;
385
386 case CL_PROGRAM_CONTEXT:
387 buf.as_scalar<cl_context>() = desc(prog.context());
388 break;
389
390 case CL_PROGRAM_NUM_DEVICES:
391 buf.as_scalar<cl_uint>() = (prog.devices().size() ?
392 prog.devices().size() :
393 prog.context().devices().size());
394 break;
395
396 case CL_PROGRAM_DEVICES:
397 buf.as_vector<cl_device_id>() = (prog.devices().size() ?
398 descs(prog.devices()) :
399 descs(prog.context().devices()));
400 break;
401
402 case CL_PROGRAM_SOURCE:
403 buf.as_string() = prog.source();
404 break;
405
406 case CL_PROGRAM_BINARY_SIZES:
407 buf.as_vector<size_t>() = map([&](const device &dev) {
408 return prog.build(dev).binary.size();
409 },
410 prog.devices());
411 break;
412
413 case CL_PROGRAM_BINARIES:
414 buf.as_matrix<unsigned char>() = map([&](const device &dev) {
415 std::stringbuf bin;
416 std::ostream s(&bin);
417 prog.build(dev).binary.serialize(s);
418 return bin.str();
419 },
420 prog.devices());
421 break;
422
423 case CL_PROGRAM_NUM_KERNELS:
424 buf.as_scalar<cl_uint>() = prog.symbols().size();
425 break;
426
427 case CL_PROGRAM_KERNEL_NAMES:
428 buf.as_string() = fold([](const std::string &a, const module::symbol &s) {
429 return ((a.empty() ? "" : a + ";") + s.name);
430 }, std::string(), prog.symbols());
431 break;
432
433 default:
434 throw error(CL_INVALID_VALUE);
435 }
436
437 return CL_SUCCESS;
438
439 } catch (error &e) {
440 return e.get();
441 }
442
443 CLOVER_API cl_int
444 clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
445 cl_program_build_info param,
446 size_t size, void *r_buf, size_t *r_size) try {
447 property_buffer buf { r_buf, size, r_size };
448 auto &prog = obj(d_prog);
449 auto &dev = obj(d_dev);
450
451 if (!count(dev, prog.context().devices()))
452 return CL_INVALID_DEVICE;
453
454 switch (param) {
455 case CL_PROGRAM_BUILD_STATUS:
456 buf.as_scalar<cl_build_status>() = prog.build(dev).status();
457 break;
458
459 case CL_PROGRAM_BUILD_OPTIONS:
460 buf.as_string() = prog.build(dev).opts;
461 break;
462
463 case CL_PROGRAM_BUILD_LOG:
464 buf.as_string() = prog.build(dev).log;
465 break;
466
467 case CL_PROGRAM_BINARY_TYPE:
468 buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
469 break;
470
471 default:
472 throw error(CL_INVALID_VALUE);
473 }
474
475 return CL_SUCCESS;
476
477 } catch (error &e) {
478 return e.get();
479 }