program.cpp 18.7 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 "spirv/invocation.hpp"
26
#include "util/u_debug.h"
27

28
#include <limits>
Serge Martin's avatar
Serge Martin committed
29 30
#include <sstream>

31 32
using namespace clover;

33
namespace {
34 35 36 37 38 39 40 41 42

   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}, " ");
   }

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
   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_;
   };

60 61 62 63 64 65
   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)
66 67 68 69 70 71
         throw error(CL_INVALID_VALUE);

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

      if (any_of([&](const device &dev) {
72
               return !count(dev, prog.devices());
73
            }, objs<allow_empty_tag>(d_devs, num_devs)))
74 75
         throw error(CL_INVALID_DEVICE);
   }
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

   enum program::il_type
   identify_and_validate_il(const std::string &il,
                            const cl_version opencl_version,
                            const context::notify_action &notify) {

      enum program::il_type il_type = program::il_type::none;

#ifdef HAVE_CLOVER_SPIRV
      if (spirv::is_binary_spirv(il)) {
         std::string log;
         if (!spirv::is_valid_spirv(il, opencl_version, log)) {
            if (notify) {
               notify(log.c_str());
            }
            throw error(CL_INVALID_VALUE);
         }
         il_type = program::il_type::spirv;
      }
#endif

      return il_type;
   }
99 100
}

101
CLOVER_API cl_program
102
clCreateProgramWithSource(cl_context d_ctx, cl_uint count,
103
                          const char **strings, const size_t *lengths,
104
                          cl_int *r_errcode) try {
105
   auto &ctx = obj(d_ctx);
106 107 108
   std::string source;

   if (!count || !strings ||
109
       any_of(is_zero(), range(strings, count)))
110 111 112 113 114 115 116 117 118
      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.
119
   ret_error(r_errcode, CL_SUCCESS);
120
   return new program(ctx, std::move(source), program::il_type::source);
121 122

} catch (error &e) {
123
   ret_error(r_errcode, e);
124 125 126
   return NULL;
}

127
CLOVER_API cl_program
128
clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
129 130 131 132
                          const cl_device_id *d_devs,
                          const size_t *lengths,
                          const unsigned char **binaries,
                          cl_int *r_status, cl_int *r_errcode) try {
133
   auto &ctx = obj(d_ctx);
134 135 136
   auto devs = objs(d_devs, n);

   if (!lengths || !binaries)
137 138
      throw error(CL_INVALID_VALUE);

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

   // Deserialize the provided binaries,
145
   std::vector<std::pair<cl_int, module>> result = map(
146 147 148 149 150
      [](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {
         if (!p || !l)
            return { CL_INVALID_VALUE, {} };

         try {
Jesse Natalie's avatar
Jesse Natalie committed
151
            std::stringbuf bin( std::string{ (char*)p, l } );
Serge Martin's avatar
Serge Martin committed
152
            std::istream s(&bin);
153 154 155

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

Serge Martin's avatar
Serge Martin committed
156
         } catch (std::istream::failure &e) {
157 158 159
            return { CL_INVALID_BINARY, {} };
         }
      },
160 161
      range(binaries, n),
      range(lengths, n));
162 163

   // update the status array,
164 165
   if (r_status)
      copy(map(keys(), result), r_status);
166

167
   if (any_of(key_equals(CL_INVALID_VALUE), result))
168 169
      throw error(CL_INVALID_VALUE);

170
   if (any_of(key_equals(CL_INVALID_BINARY), result))
171 172 173
      throw error(CL_INVALID_BINARY);

   // initialize a program object with them.
174 175
   ret_error(r_errcode, CL_SUCCESS);
   return new program(ctx, devs, map(values(), result));
176 177

} catch (error &e) {
178
   ret_error(r_errcode, e);
179 180 181
   return NULL;
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
cl_program
clover::CreateProgramWithILKHR(cl_context d_ctx, const void *il,
                               size_t length, cl_int *r_errcode) try {
   auto &ctx = obj(d_ctx);

   if (!il || !length)
      throw error(CL_INVALID_VALUE);

   // Compute the highest OpenCL version supported by all devices associated to
   // the context. That is the version used for validating the SPIR-V binary.
   cl_version min_opencl_version = std::numeric_limits<uint32_t>::max();
   for (const device &dev : ctx.devices()) {
      const cl_version opencl_version = dev.device_version();
      min_opencl_version = std::min(opencl_version, min_opencl_version);
   }

   const char *stream = reinterpret_cast<const char *>(il);
   std::string binary(stream, stream + length);
   const enum program::il_type il_type = identify_and_validate_il(binary,
                                                                  min_opencl_version,
                                                                  ctx.notify);

   if (il_type == program::il_type::none)
      throw error(CL_INVALID_VALUE);

   // Initialize a program object with it.
   ret_error(r_errcode, CL_SUCCESS);
   return new program(ctx, std::move(binary), il_type);

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

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
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;
}


238
CLOVER_API cl_int
239 240
clRetainProgram(cl_program d_prog) try {
   obj(d_prog).retain();
241
   return CL_SUCCESS;
242 243 244

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

247
CLOVER_API cl_int
248 249 250
clReleaseProgram(cl_program d_prog) try {
   if (obj(d_prog).release())
      delete pobj(d_prog);
251 252

   return CL_SUCCESS;
253 254 255

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

258
CLOVER_API cl_int
259 260 261
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 *),
262 263
               void *user_data) try {
   auto &prog = obj(d_prog);
264 265
   auto devs =
      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
266
   const auto opts = build_options(p_opts, "CLOVER_EXTRA_BUILD_OPTIONS");
267

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

270 271
   auto notifier = build_notifier(d_prog, pfn_notify, user_data);

272
   if (prog.il_type() != program::il_type::none) {
273 274
      prog.compile(devs, opts);
      prog.link(devs, opts, { prog });
275 276 277 278 279 280 281
   } 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);
282 283
   }

284
   return CL_SUCCESS;
285

286 287
} catch (error &e) {
   return e.get();
288 289 290 291 292 293 294 295 296
}

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 {
297
   auto &prog = obj(d_prog);
298 299
   auto devs =
       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
300
   const auto opts = build_options(p_opts, "CLOVER_EXTRA_COMPILE_OPTIONS");
301
   header_map headers;
302

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

305 306
   auto notifier = build_notifier(d_prog, pfn_notify, user_data);

307 308
   if (bool(num_headers) != bool(header_names))
      throw error(CL_INVALID_VALUE);
309

310
   if (prog.il_type() == program::il_type::none)
311 312
      throw error(CL_INVALID_OPERATION);

313
   for_each([&](const char *name, const program &header) {
314
         if (header.il_type() == program::il_type::none)
315 316 317
            throw error(CL_INVALID_OPERATION);

         if (!any_of(key_equals(name), headers))
Serge Martin's avatar
Serge Martin committed
318
            headers.push_back(std::pair<std::string, std::string>(
319 320 321
                                 name, header.source()));
      },
      range(header_names, num_headers),
322
      objs<allow_empty_tag>(d_header_progs, num_headers));
323

324
   prog.compile(devs, opts, headers);
325 326
   return CL_SUCCESS;

327 328 329
} catch (invalid_build_options_error &e) {
   return CL_INVALID_COMPILER_OPTIONS;

330 331 332
} catch (build_error &e) {
   return CL_COMPILE_PROGRAM_FAILURE;

333 334 335 336
} catch (error &e) {
   return e.get();
}

337 338 339
namespace {
   ref_vector<device>
   validate_link_devices(const ref_vector<program> &progs,
340 341
                         const ref_vector<device> &all_devs,
                         const std::string &opts) {
342
      std::vector<device *> devs;
343 344
      const bool create_library =
         opts.find("-create-library") != std::string::npos;
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
      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);
366 367 368

      for (auto &dev : all_devs) {
         const auto has_binary = [&](const program &prog) {
369 370 371
            const auto t = prog.build(dev).binary_type();
            return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
                   t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
372 373
         };

374 375 376 377 378 379 380 381 382 383 384 385
         // 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);

386 387 388
         // According to the CL 1.2 spec, when "all programs specified [..]
         // contain a compiled binary or library for the device [..] a link is
         // performed",
389
         else if (all_of(has_binary, progs))
390 391 392 393 394 395 396
            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);
397 398 399 400 401 402 403 404 405 406 407 408 409 410

         // 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);
411 412 413 414 415 416
      }

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

417 418 419
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,
420 421 422
              void (*pfn_notify) (cl_program, void *), void *user_data,
              cl_int *r_errcode) try {
   auto &ctx = obj(d_ctx);
423
   const auto opts = build_options(p_opts, "CLOVER_EXTRA_LINK_OPTIONS");
424
   auto progs = objs(d_progs, num_progs);
425 426 427
   auto all_devs =
      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
   auto prog = create<program>(ctx, all_devs);
428 429 430 431
   auto r_prog = ret_object(prog);

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

432
   auto devs = validate_link_devices(progs, all_devs, opts);
433 434 435 436 437 438 439 440 441 442 443

   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);
   }

444
   return r_prog;
445 446 447 448 449 450 451

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

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

455
CLOVER_API cl_int
456 457 458 459
clUnloadCompiler() {
   return CL_SUCCESS;
}

460 461 462 463 464
CLOVER_API cl_int
clUnloadPlatformCompiler(cl_platform_id d_platform) {
   return CL_SUCCESS;
}

465
CLOVER_API cl_int
466
clGetProgramInfo(cl_program d_prog, cl_program_info param,
467 468
                 size_t size, void *r_buf, size_t *r_size) try {
   property_buffer buf { r_buf, size, r_size };
469
   auto &prog = obj(d_prog);
470 471 472

   switch (param) {
   case CL_PROGRAM_REFERENCE_COUNT:
473
      buf.as_scalar<cl_uint>() = prog.ref_count();
474
      break;
475 476

   case CL_PROGRAM_CONTEXT:
477
      buf.as_scalar<cl_context>() = desc(prog.context());
478
      break;
479 480

   case CL_PROGRAM_NUM_DEVICES:
481 482
      buf.as_scalar<cl_uint>() = (prog.devices().size() ?
                                  prog.devices().size() :
483
                                  prog.context().devices().size());
484
      break;
485 486

   case CL_PROGRAM_DEVICES:
487 488
      buf.as_vector<cl_device_id>() = (prog.devices().size() ?
                                       descs(prog.devices()) :
489
                                       descs(prog.context().devices()));
490
      break;
491 492

   case CL_PROGRAM_SOURCE:
493
      buf.as_string() = prog.source();
494
      break;
495 496

   case CL_PROGRAM_BINARY_SIZES:
497
      buf.as_vector<size_t>() = map([&](const device &dev) {
498
            return prog.build(dev).binary.size();
499 500
         },
         prog.devices());
501
      break;
502 503

   case CL_PROGRAM_BINARIES:
504
      buf.as_matrix<unsigned char>() = map([&](const device &dev) {
Serge Martin's avatar
Serge Martin committed
505 506
            std::stringbuf bin;
            std::ostream s(&bin);
507
            prog.build(dev).binary.serialize(s);
Serge Martin's avatar
Serge Martin committed
508
            return bin.str();
509 510
         },
         prog.devices());
511
      break;
512

513 514 515 516 517 518
   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) {
519
            return ((a.empty() ? "" : a + ";") + s.name);
520 521 522
         }, std::string(), prog.symbols());
      break;

523 524 525 526 527 528
   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:
529 530 531 532
      if (prog.il_type() != program::il_type::none)
         buf.as_string() = prog.source();
      else if (r_size)
         *r_size = 0u;
533
      break;
534
   default:
535
      throw error(CL_INVALID_VALUE);
536
   }
537 538 539 540 541

   return CL_SUCCESS;

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

544
CLOVER_API cl_int
545
clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
546
                      cl_program_build_info param,
547 548
                      size_t size, void *r_buf, size_t *r_size) try {
   property_buffer buf { r_buf, size, r_size };
549 550
   auto &prog = obj(d_prog);
   auto &dev = obj(d_dev);
551

552
   if (!count(dev, prog.context().devices()))
553 554 555 556
      return CL_INVALID_DEVICE;

   switch (param) {
   case CL_PROGRAM_BUILD_STATUS:
557
      buf.as_scalar<cl_build_status>() = prog.build(dev).status();
558
      break;
559 560

   case CL_PROGRAM_BUILD_OPTIONS:
561
      buf.as_string() = prog.build(dev).opts;
562
      break;
563 564

   case CL_PROGRAM_BUILD_LOG:
565
      buf.as_string() = prog.build(dev).log;
566
      break;
567

568 569 570 571
   case CL_PROGRAM_BINARY_TYPE:
      buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
      break;

572 573 574 575
   case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:
      buf.as_scalar<size_t>() = 0;
      break;

576
   default:
577
      throw error(CL_INVALID_VALUE);
578
   }
579 580 581 582 583

   return CL_SUCCESS;

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