program.cpp 16.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
// Copyright 2012 Francisco Jerez
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17
18
19
20
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
21
22
23
24
//

#include "api/util.hpp"
#include "core/program.hpp"
25
#include "util/u_debug.h"
26

Serge Martin's avatar
Serge Martin committed
27
28
#include <sstream>

29
30
using namespace clover;

31
namespace {
32
33
34
35
36
37
38
39
40

   std::string
   build_options(const char *p_opts, const char *p_debug) {
      auto opts = std::string(p_opts ? p_opts : "");
      std::string extra_opts = debug_get_option(p_debug, "");

      return detokenize(std::vector<std::string>{opts, extra_opts}, " ");
   }

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
   class build_notifier {
   public:
      build_notifier(cl_program prog,
                     void (*notifer)(cl_program, void *), void *data) :
                     prog_(prog), notifer(notifer), data_(data) { }

      ~build_notifier() {
         if (notifer)
            notifer(prog_, data_);
      }

   private:
      cl_program prog_;
      void (*notifer)(cl_program, void *);
      void *data_;
   };

58
59
60
61
62
63
   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) {
      if (!pfn_notify && user_data)
64
65
66
67
68
69
         throw error(CL_INVALID_VALUE);

      if (prog.kernel_ref_count())
         throw error(CL_INVALID_OPERATION);

      if (any_of([&](const device &dev) {
70
               return !count(dev, prog.devices());
71
            }, objs<allow_empty_tag>(d_devs, num_devs)))
72
73
74
75
         throw error(CL_INVALID_DEVICE);
   }
}

76
CLOVER_API cl_program
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
82
83
   std::string source;

   if (!count || !strings ||
84
       any_of(is_zero(), range(strings, count)))
85
86
87
88
89
90
91
92
93
      throw error(CL_INVALID_VALUE);

   // Concatenate all the provided fragments together
   for (unsigned i = 0; i < count; ++i)
         source += (lengths && lengths[i] ?
                    std::string(strings[i], strings[i] + lengths[i]) :
                    std::string(strings[i]));

   // ...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
100
101
   return NULL;
}

102
CLOVER_API cl_program
103
clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
104
105
106
107
                          const cl_device_id *d_devs,
                          const size_t *lengths,
                          const unsigned char **binaries,
                          cl_int *r_status, cl_int *r_errcode) try {
108
   auto &ctx = obj(d_ctx);
109
110
111
   auto devs = objs(d_devs, n);

   if (!lengths || !binaries)
112
113
      throw error(CL_INVALID_VALUE);

114
   if (any_of([&](const device &dev) {
115
            return !count(dev, ctx.devices());
116
         }, devs))
117
118
119
      throw error(CL_INVALID_DEVICE);

   // Deserialize the provided binaries,
120
   std::vector<std::pair<cl_int, module>> result = map(
121
122
123
124
125
      [](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {
         if (!p || !l)
            return { CL_INVALID_VALUE, {} };

         try {
Serge Martin's avatar
Serge Martin committed
126
127
            std::stringbuf bin( { (char*)p, l } );
            std::istream s(&bin);
128
129
130

            return { CL_SUCCESS, module::deserialize(s) };

Serge Martin's avatar
Serge Martin committed
131
         } catch (std::istream::failure &e) {
132
133
134
            return { CL_INVALID_BINARY, {} };
         }
      },
135
136
      range(binaries, n),
      range(lengths, n));
137
138

   // update the status array,
139
140
   if (r_status)
      copy(map(keys(), result), r_status);
141

142
   if (any_of(key_equals(CL_INVALID_VALUE), result))
143
144
      throw error(CL_INVALID_VALUE);

145
   if (any_of(key_equals(CL_INVALID_BINARY), result))
146
147
148
      throw error(CL_INVALID_BINARY);

   // initialize a program object with them.
149
150
   ret_error(r_errcode, CL_SUCCESS);
   return new program(ctx, devs, map(values(), result));
151
152

} catch (error &e) {
153
   ret_error(r_errcode, e);
154
155
156
   return NULL;
}

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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) try {
   auto &ctx = obj(d_ctx);
   auto devs = objs(d_devs, n);

   if (any_of([&](const device &dev) {
            return !count(dev, ctx.devices());
         }, devs))
      throw error(CL_INVALID_DEVICE);

   // No currently supported built-in kernels.
   throw error(CL_INVALID_VALUE);

} catch (error &e) {
   ret_error(r_errcode, e);
   return NULL;
}


179
CLOVER_API cl_int
180
181
clRetainProgram(cl_program d_prog) try {
   obj(d_prog).retain();
182
   return CL_SUCCESS;
183
184
185

} catch (error &e) {
   return e.get();
186
187
}

188
CLOVER_API cl_int
189
190
191
clReleaseProgram(cl_program d_prog) try {
   if (obj(d_prog).release())
      delete pobj(d_prog);
192
193

   return CL_SUCCESS;
194
195
196

} catch (error &e) {
   return e.get();
197
198
}

199
CLOVER_API cl_int
200
201
202
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 *),
203
204
               void *user_data) try {
   auto &prog = obj(d_prog);
205
206
   auto devs =
      (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
212
   auto notifier = build_notifier(d_prog, pfn_notify, user_data);

213
214
215
   if (prog.has_source) {
      prog.compile(devs, opts);
      prog.link(devs, opts, { prog });
216
217
218
219
220
221
222
   } else if (any_of([&](const device &dev){
         return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
         }, devs)) {
      // According to the OpenCL 1.2 specification, “if program is created
      // with clCreateProgramWithBinary, then the program binary must be an
      // executable binary (not a compiled binary or library).”
      throw error(CL_INVALID_BINARY);
223
224
   }

225
   return CL_SUCCESS;
226

227
228
} catch (error &e) {
   return e.get();
229
230
231
232
233
234
235
236
237
}

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) try {
238
   auto &prog = obj(d_prog);
239
240
   auto devs =
       (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
247
   auto notifier = build_notifier(d_prog, pfn_notify, user_data);

248
249
   if (bool(num_headers) != bool(header_names))
      throw error(CL_INVALID_VALUE);
250

251
   if (!prog.has_source)
252
253
      throw error(CL_INVALID_OPERATION);

254
255
256
257
258
   for_each([&](const char *name, const program &header) {
         if (!header.has_source)
            throw error(CL_INVALID_OPERATION);

         if (!any_of(key_equals(name), headers))
Serge Martin's avatar
Serge Martin committed
259
            headers.push_back(std::pair<std::string, std::string>(
260
261
262
                                 name, header.source()));
      },
      range(header_names, num_headers),
263
      objs<allow_empty_tag>(d_header_progs, num_headers));
264

265
   prog.compile(devs, opts, headers);
266
267
   return CL_SUCCESS;

268
269
270
} catch (invalid_build_options_error &e) {
   return CL_INVALID_COMPILER_OPTIONS;

271
272
273
} catch (build_error &e) {
   return CL_COMPILE_PROGRAM_FAILURE;

274
275
276
277
} catch (error &e) {
   return e.get();
}

278
279
280
namespace {
   ref_vector<device>
   validate_link_devices(const ref_vector<program> &progs,
281
282
                         const ref_vector<device> &all_devs,
                         const std::string &opts) {
283
      std::vector<device *> devs;
284
285
      const bool create_library =
         opts.find("-create-library") != std::string::npos;
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
      const bool enable_link_options =
         opts.find("-enable-link-options") != std::string::npos;
      const bool has_link_options =
         opts.find("-cl-denorms-are-zero") != std::string::npos ||
         opts.find("-cl-no-signed-zeroes") != std::string::npos ||
         opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
         opts.find("-cl-finite-math-only") != std::string::npos ||
         opts.find("-cl-fast-relaxed-math") != std::string::npos ||
         opts.find("-cl-no-subgroup-ifp") != std::string::npos;

      // According to the OpenCL 1.2 specification, "[the
      // -enable-link-options] option must be specified with the
      // create-library option".
      if (enable_link_options && !create_library)
         throw error(CL_INVALID_LINKER_OPTIONS);

      // According to the OpenCL 1.2 specification, "the
      // [program linking options] can be specified when linking a program
      // executable".
      if (has_link_options && create_library)
         throw error(CL_INVALID_LINKER_OPTIONS);
307
308
309

      for (auto &dev : all_devs) {
         const auto has_binary = [&](const program &prog) {
310
311
312
            const auto t = prog.build(dev).binary_type();
            return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
                   t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
313
314
         };

315
316
317
318
319
320
321
322
323
324
325
326
         // According to the OpenCL 1.2 specification, a library is made of
         // “compiled binaries specified in input_programs argument to
         // clLinkProgram“; compiled binaries does not refer to libraries:
         // “input_programs is an array of program objects that are compiled
         // binaries or libraries that are to be linked to create the program
         // executable”.
         if (create_library && any_of([&](const program &prog) {
                  const auto t = prog.build(dev).binary_type();
                  return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
               }, progs))
            throw error(CL_INVALID_OPERATION);

327
328
329
         // According to the CL 1.2 spec, when "all programs specified [..]
         // contain a compiled binary or library for the device [..] a link is
         // performed",
330
         else if (all_of(has_binary, progs))
331
332
333
334
335
336
337
            devs.push_back(&dev);

         // otherwise if "none of the programs contain a compiled binary or
         // library for that device [..] no link is performed.  All other
         // cases will return a CL_INVALID_OPERATION error."
         else if (any_of(has_binary, progs))
            throw error(CL_INVALID_OPERATION);
338
339
340
341
342
343
344
345
346
347
348
349
350
351

         // According to the OpenCL 1.2 specification, "[t]he linker may apply
         // [program linking options] to all compiled program objects
         // specified to clLinkProgram. The linker may apply these options
         // only to libraries which were created with the
         // -enable-link-option."
         else if (has_link_options && any_of([&](const program &prog) {
                  const auto t = prog.build(dev).binary_type();
                  return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
                          (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
                           prog.build(dev).opts.find("-enable-link-options") !=
                              std::string::npos));
               }, progs))
            throw error(CL_INVALID_LINKER_OPTIONS);
352
353
354
355
356
357
      }

      return map(derefs(), devs);
   }
}

358
359
360
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,
361
362
363
              void (*pfn_notify) (cl_program, void *), void *user_data,
              cl_int *r_errcode) try {
   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
367
368
   auto all_devs =
      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
   auto prog = create<program>(ctx, all_devs);
369
370
371
372
   auto r_prog = ret_object(prog);

   auto notifier = build_notifier(r_prog, pfn_notify, user_data);

373
   auto devs = validate_link_devices(progs, all_devs, opts);
374
375
376
377
378
379
380
381
382
383
384

   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);

   try {
      prog().link(devs, opts, progs);
      ret_error(r_errcode, CL_SUCCESS);

   } catch (build_error &e) {
      ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
   }

385
   return r_prog;
386
387
388
389
390
391
392

} catch (invalid_build_options_error &e) {
   ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
   return NULL;

} catch (error &e) {
   ret_error(r_errcode, e);
393
394
395
   return NULL;
}

396
CLOVER_API cl_int
397
398
399
400
clUnloadCompiler() {
   return CL_SUCCESS;
}

401
402
403
404
405
CLOVER_API cl_int
clUnloadPlatformCompiler(cl_platform_id d_platform) {
   return CL_SUCCESS;
}

406
CLOVER_API cl_int
407
clGetProgramInfo(cl_program d_prog, cl_program_info param,
408
409
                 size_t size, void *r_buf, size_t *r_size) try {
   property_buffer buf { r_buf, size, r_size };
410
   auto &prog = obj(d_prog);
411
412
413

   switch (param) {
   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
423
      buf.as_scalar<cl_uint>() = (prog.devices().size() ?
                                  prog.devices().size() :
424
                                  prog.context().devices().size());
425
      break;
426
427

   case CL_PROGRAM_DEVICES:
428
429
      buf.as_vector<cl_device_id>() = (prog.devices().size() ?
                                       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) {
Serge Martin's avatar
Serge Martin committed
446
447
            std::stringbuf bin;
            std::ostream s(&bin);
448
            prog.build(dev).binary.serialize(s);
Serge Martin's avatar
Serge Martin committed
449
            return bin.str();
450
451
         },
         prog.devices());
452
      break;
453

454
455
456
457
458
459
   case CL_PROGRAM_NUM_KERNELS:
      buf.as_scalar<cl_uint>() = prog.symbols().size();
      break;

   case CL_PROGRAM_KERNEL_NAMES:
      buf.as_string() = fold([](const std::string &a, const module::symbol &s) {
460
            return ((a.empty() ? "" : a + ";") + s.name);
461
462
463
         }, std::string(), prog.symbols());
      break;

464
465
466
467
468
469
470
471
472
   case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT:
   case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT:
      buf.as_scalar<cl_bool>() = CL_FALSE;
      break;

   case CL_PROGRAM_IL:
      if (r_size)
         *r_size = 0;
      break;
473
   default:
474
      throw error(CL_INVALID_VALUE);
475
   }
476
477
478
479
480

   return CL_SUCCESS;

} catch (error &e) {
   return e.get();
481
482
}

483
CLOVER_API cl_int
484
clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
485
                      cl_program_build_info param,
486
487
                      size_t size, void *r_buf, size_t *r_size) try {
   property_buffer buf { r_buf, size, r_size };
488
489
   auto &prog = obj(d_prog);
   auto &dev = obj(d_dev);
490

491
   if (!count(dev, prog.context().devices()))
492
493
494
495
      return CL_INVALID_DEVICE;

   switch (param) {
   case CL_PROGRAM_BUILD_STATUS:
496
      buf.as_scalar<cl_build_status>() = prog.build(dev).status();
497
      break;
498
499

   case CL_PROGRAM_BUILD_OPTIONS:
500
      buf.as_string() = prog.build(dev).opts;
501
      break;
502
503

   case CL_PROGRAM_BUILD_LOG:
504
      buf.as_string() = prog.build(dev).log;
505
      break;
506

507
508
509
510
   case CL_PROGRAM_BINARY_TYPE:
      buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
      break;

511
512
513
514
   case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:
      buf.as_scalar<size_t>() = 0;
      break;

515
   default:
516
      throw error(CL_INVALID_VALUE);
517
   }
518
519
520
521
522

   return CL_SUCCESS;

} catch (error &e) {
   return e.get();
523
}