17ec681f3Smrg//
27ec681f3Smrg// Copyright 2012 Francisco Jerez
37ec681f3Smrg//
47ec681f3Smrg// Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg// copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg// to deal in the Software without restriction, including without limitation
77ec681f3Smrg// the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg// and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg// Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg//
117ec681f3Smrg// The above copyright notice and this permission notice shall be included in
127ec681f3Smrg// all copies or substantial portions of the Software.
137ec681f3Smrg//
147ec681f3Smrg// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
157ec681f3Smrg// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167ec681f3Smrg// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
177ec681f3Smrg// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
187ec681f3Smrg// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
197ec681f3Smrg// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
207ec681f3Smrg// OTHER DEALINGS IN THE SOFTWARE.
217ec681f3Smrg//
227ec681f3Smrg
237ec681f3Smrg#include "api/util.hpp"
247ec681f3Smrg#include "core/program.hpp"
257ec681f3Smrg#include "core/platform.hpp"
267ec681f3Smrg#include "spirv/invocation.hpp"
277ec681f3Smrg#include "util/u_debug.h"
287ec681f3Smrg
297ec681f3Smrg#include <limits>
307ec681f3Smrg#include <sstream>
317ec681f3Smrg
327ec681f3Smrgusing namespace clover;
337ec681f3Smrg
347ec681f3Smrgnamespace {
357ec681f3Smrg
367ec681f3Smrg   std::string
377ec681f3Smrg   build_options(const char *p_opts, const char *p_debug) {
387ec681f3Smrg      auto opts = std::string(p_opts ? p_opts : "");
397ec681f3Smrg      std::string extra_opts = debug_get_option(p_debug, "");
407ec681f3Smrg
417ec681f3Smrg      return detokenize(std::vector<std::string>{opts, extra_opts}, " ");
427ec681f3Smrg   }
437ec681f3Smrg
447ec681f3Smrg   class build_notifier {
457ec681f3Smrg   public:
467ec681f3Smrg      build_notifier(cl_program prog,
477ec681f3Smrg                     void (*notifer)(cl_program, void *), void *data) :
487ec681f3Smrg                     prog_(prog), notifer(notifer), data_(data) { }
497ec681f3Smrg
507ec681f3Smrg      ~build_notifier() {
517ec681f3Smrg         if (notifer)
527ec681f3Smrg            notifer(prog_, data_);
537ec681f3Smrg      }
547ec681f3Smrg
557ec681f3Smrg   private:
567ec681f3Smrg      cl_program prog_;
577ec681f3Smrg      void (*notifer)(cl_program, void *);
587ec681f3Smrg      void *data_;
597ec681f3Smrg   };
607ec681f3Smrg
617ec681f3Smrg   void
627ec681f3Smrg   validate_build_common(const program &prog, cl_uint num_devs,
637ec681f3Smrg                         const cl_device_id *d_devs,
647ec681f3Smrg                         void (*pfn_notify)(cl_program, void *),
657ec681f3Smrg                         void *user_data) {
667ec681f3Smrg      if (!pfn_notify && user_data)
677ec681f3Smrg         throw error(CL_INVALID_VALUE);
687ec681f3Smrg
697ec681f3Smrg      if (prog.kernel_ref_count())
707ec681f3Smrg         throw error(CL_INVALID_OPERATION);
717ec681f3Smrg
727ec681f3Smrg      if (any_of([&](const device &dev) {
737ec681f3Smrg               return !count(dev, prog.devices());
747ec681f3Smrg            }, objs<allow_empty_tag>(d_devs, num_devs)))
757ec681f3Smrg         throw error(CL_INVALID_DEVICE);
767ec681f3Smrg   }
777ec681f3Smrg
787ec681f3Smrg   enum program::il_type
797ec681f3Smrg   identify_and_validate_il(const std::string &il,
807ec681f3Smrg                            const cl_version opencl_version,
817ec681f3Smrg                            const context::notify_action &notify) {
827ec681f3Smrg
837ec681f3Smrg      enum program::il_type il_type = program::il_type::none;
847ec681f3Smrg
857ec681f3Smrg#ifdef HAVE_CLOVER_SPIRV
867ec681f3Smrg      if (spirv::is_binary_spirv(il)) {
877ec681f3Smrg         std::string log;
887ec681f3Smrg         if (!spirv::is_valid_spirv(il, opencl_version, log)) {
897ec681f3Smrg            if (notify) {
907ec681f3Smrg               notify(log.c_str());
917ec681f3Smrg            }
927ec681f3Smrg            throw error(CL_INVALID_VALUE);
937ec681f3Smrg         }
947ec681f3Smrg         il_type = program::il_type::spirv;
957ec681f3Smrg      }
967ec681f3Smrg#endif
977ec681f3Smrg
987ec681f3Smrg      return il_type;
997ec681f3Smrg   }
1007ec681f3Smrg}
1017ec681f3Smrg
1027ec681f3SmrgCLOVER_API cl_program
1037ec681f3SmrgclCreateProgramWithSource(cl_context d_ctx, cl_uint count,
1047ec681f3Smrg                          const char **strings, const size_t *lengths,
1057ec681f3Smrg                          cl_int *r_errcode) try {
1067ec681f3Smrg   auto &ctx = obj(d_ctx);
1077ec681f3Smrg   std::string source;
1087ec681f3Smrg
1097ec681f3Smrg   if (!count || !strings ||
1107ec681f3Smrg       any_of(is_zero(), range(strings, count)))
1117ec681f3Smrg      throw error(CL_INVALID_VALUE);
1127ec681f3Smrg
1137ec681f3Smrg   // Concatenate all the provided fragments together
1147ec681f3Smrg   for (unsigned i = 0; i < count; ++i)
1157ec681f3Smrg         source += (lengths && lengths[i] ?
1167ec681f3Smrg                    std::string(strings[i], strings[i] + lengths[i]) :
1177ec681f3Smrg                    std::string(strings[i]));
1187ec681f3Smrg
1197ec681f3Smrg   // ...and create a program object for them.
1207ec681f3Smrg   ret_error(r_errcode, CL_SUCCESS);
1217ec681f3Smrg   return new program(ctx, std::move(source), program::il_type::source);
1227ec681f3Smrg
1237ec681f3Smrg} catch (error &e) {
1247ec681f3Smrg   ret_error(r_errcode, e);
1257ec681f3Smrg   return NULL;
1267ec681f3Smrg}
1277ec681f3Smrg
1287ec681f3SmrgCLOVER_API cl_program
1297ec681f3SmrgclCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
1307ec681f3Smrg                          const cl_device_id *d_devs,
1317ec681f3Smrg                          const size_t *lengths,
1327ec681f3Smrg                          const unsigned char **binaries,
1337ec681f3Smrg                          cl_int *r_status, cl_int *r_errcode) try {
1347ec681f3Smrg   auto &ctx = obj(d_ctx);
1357ec681f3Smrg   auto devs = objs(d_devs, n);
1367ec681f3Smrg
1377ec681f3Smrg   if (!lengths || !binaries)
1387ec681f3Smrg      throw error(CL_INVALID_VALUE);
1397ec681f3Smrg
1407ec681f3Smrg   if (any_of([&](const device &dev) {
1417ec681f3Smrg            return !count(dev, ctx.devices());
1427ec681f3Smrg         }, devs))
1437ec681f3Smrg      throw error(CL_INVALID_DEVICE);
1447ec681f3Smrg
1457ec681f3Smrg   // Deserialize the provided binaries,
1467ec681f3Smrg   std::vector<std::pair<cl_int, binary>> result = map(
1477ec681f3Smrg      [](const unsigned char *p, size_t l) -> std::pair<cl_int, binary> {
1487ec681f3Smrg         if (!p || !l)
1497ec681f3Smrg            return { CL_INVALID_VALUE, {} };
1507ec681f3Smrg
1517ec681f3Smrg         try {
1527ec681f3Smrg            std::stringbuf bin( std::string{ (char*)p, l } );
1537ec681f3Smrg            std::istream s(&bin);
1547ec681f3Smrg
1557ec681f3Smrg            return { CL_SUCCESS, binary::deserialize(s) };
1567ec681f3Smrg
1577ec681f3Smrg         } catch (std::istream::failure &) {
1587ec681f3Smrg            return { CL_INVALID_BINARY, {} };
1597ec681f3Smrg         }
1607ec681f3Smrg      },
1617ec681f3Smrg      range(binaries, n),
1627ec681f3Smrg      range(lengths, n));
1637ec681f3Smrg
1647ec681f3Smrg   // update the status array,
1657ec681f3Smrg   if (r_status)
1667ec681f3Smrg      copy(map(keys(), result), r_status);
1677ec681f3Smrg
1687ec681f3Smrg   if (any_of(key_equals(CL_INVALID_VALUE), result))
1697ec681f3Smrg      throw error(CL_INVALID_VALUE);
1707ec681f3Smrg
1717ec681f3Smrg   if (any_of(key_equals(CL_INVALID_BINARY), result))
1727ec681f3Smrg      throw error(CL_INVALID_BINARY);
1737ec681f3Smrg
1747ec681f3Smrg   // initialize a program object with them.
1757ec681f3Smrg   ret_error(r_errcode, CL_SUCCESS);
1767ec681f3Smrg   return new program(ctx, devs, map(values(), result));
1777ec681f3Smrg
1787ec681f3Smrg} catch (error &e) {
1797ec681f3Smrg   ret_error(r_errcode, e);
1807ec681f3Smrg   return NULL;
1817ec681f3Smrg}
1827ec681f3Smrg
1837ec681f3Smrgcl_program
1847ec681f3Smrgclover::CreateProgramWithILKHR(cl_context d_ctx, const void *il,
1857ec681f3Smrg                               size_t length, cl_int *r_errcode) try {
1867ec681f3Smrg   auto &ctx = obj(d_ctx);
1877ec681f3Smrg
1887ec681f3Smrg   if (!il || !length)
1897ec681f3Smrg      throw error(CL_INVALID_VALUE);
1907ec681f3Smrg
1917ec681f3Smrg   // Compute the highest OpenCL version supported by all devices associated to
1927ec681f3Smrg   // the context. That is the version used for validating the SPIR-V binary.
1937ec681f3Smrg   cl_version min_opencl_version = std::numeric_limits<uint32_t>::max();
1947ec681f3Smrg   for (const device &dev : ctx.devices()) {
1957ec681f3Smrg      const cl_version opencl_version = dev.device_version();
1967ec681f3Smrg      min_opencl_version = std::min(opencl_version, min_opencl_version);
1977ec681f3Smrg   }
1987ec681f3Smrg
1997ec681f3Smrg   const char *stream = reinterpret_cast<const char *>(il);
2007ec681f3Smrg   std::string binary(stream, stream + length);
2017ec681f3Smrg   const enum program::il_type il_type = identify_and_validate_il(binary,
2027ec681f3Smrg                                                                  min_opencl_version,
2037ec681f3Smrg                                                                  ctx.notify);
2047ec681f3Smrg
2057ec681f3Smrg   if (il_type == program::il_type::none)
2067ec681f3Smrg      throw error(CL_INVALID_VALUE);
2077ec681f3Smrg
2087ec681f3Smrg   // Initialize a program object with it.
2097ec681f3Smrg   ret_error(r_errcode, CL_SUCCESS);
2107ec681f3Smrg   return new program(ctx, std::move(binary), il_type);
2117ec681f3Smrg
2127ec681f3Smrg} catch (error &e) {
2137ec681f3Smrg   ret_error(r_errcode, e);
2147ec681f3Smrg   return NULL;
2157ec681f3Smrg}
2167ec681f3Smrg
2177ec681f3SmrgCLOVER_API cl_program
2187ec681f3SmrgclCreateProgramWithIL(cl_context d_ctx,
2197ec681f3Smrg                      const void *il,
2207ec681f3Smrg                      size_t length,
2217ec681f3Smrg                      cl_int *r_errcode) {
2227ec681f3Smrg   return CreateProgramWithILKHR(d_ctx, il, length, r_errcode);
2237ec681f3Smrg}
2247ec681f3Smrg
2257ec681f3SmrgCLOVER_API cl_program
2267ec681f3SmrgclCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
2277ec681f3Smrg                                  const cl_device_id *d_devs,
2287ec681f3Smrg                                  const char *kernel_names,
2297ec681f3Smrg                                  cl_int *r_errcode) try {
2307ec681f3Smrg   auto &ctx = obj(d_ctx);
2317ec681f3Smrg   auto devs = objs(d_devs, n);
2327ec681f3Smrg
2337ec681f3Smrg   if (any_of([&](const device &dev) {
2347ec681f3Smrg            return !count(dev, ctx.devices());
2357ec681f3Smrg         }, devs))
2367ec681f3Smrg      throw error(CL_INVALID_DEVICE);
2377ec681f3Smrg
2387ec681f3Smrg   // No currently supported built-in kernels.
2397ec681f3Smrg   throw error(CL_INVALID_VALUE);
2407ec681f3Smrg
2417ec681f3Smrg} catch (error &e) {
2427ec681f3Smrg   ret_error(r_errcode, e);
2437ec681f3Smrg   return NULL;
2447ec681f3Smrg}
2457ec681f3Smrg
2467ec681f3Smrg
2477ec681f3SmrgCLOVER_API cl_int
2487ec681f3SmrgclRetainProgram(cl_program d_prog) try {
2497ec681f3Smrg   obj(d_prog).retain();
2507ec681f3Smrg   return CL_SUCCESS;
2517ec681f3Smrg
2527ec681f3Smrg} catch (error &e) {
2537ec681f3Smrg   return e.get();
2547ec681f3Smrg}
2557ec681f3Smrg
2567ec681f3SmrgCLOVER_API cl_int
2577ec681f3SmrgclReleaseProgram(cl_program d_prog) try {
2587ec681f3Smrg   if (obj(d_prog).release())
2597ec681f3Smrg      delete pobj(d_prog);
2607ec681f3Smrg
2617ec681f3Smrg   return CL_SUCCESS;
2627ec681f3Smrg
2637ec681f3Smrg} catch (error &e) {
2647ec681f3Smrg   return e.get();
2657ec681f3Smrg}
2667ec681f3Smrg
2677ec681f3SmrgCLOVER_API cl_int
2687ec681f3SmrgclBuildProgram(cl_program d_prog, cl_uint num_devs,
2697ec681f3Smrg               const cl_device_id *d_devs, const char *p_opts,
2707ec681f3Smrg               void (*pfn_notify)(cl_program, void *),
2717ec681f3Smrg               void *user_data) try {
2727ec681f3Smrg   auto &prog = obj(d_prog);
2737ec681f3Smrg   auto devs =
2747ec681f3Smrg      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
2757ec681f3Smrg   const auto opts = build_options(p_opts, "CLOVER_EXTRA_BUILD_OPTIONS");
2767ec681f3Smrg
2777ec681f3Smrg   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
2787ec681f3Smrg
2797ec681f3Smrg   auto notifier = build_notifier(d_prog, pfn_notify, user_data);
2807ec681f3Smrg
2817ec681f3Smrg   if (prog.il_type() != program::il_type::none) {
2827ec681f3Smrg      prog.compile(devs, opts);
2837ec681f3Smrg      prog.link(devs, opts, { prog });
2847ec681f3Smrg   } else if (any_of([&](const device &dev){
2857ec681f3Smrg         return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
2867ec681f3Smrg         }, devs)) {
2877ec681f3Smrg      // According to the OpenCL 1.2 specification, “if program is created
2887ec681f3Smrg      // with clCreateProgramWithBinary, then the program binary must be an
2897ec681f3Smrg      // executable binary (not a compiled binary or library).”
2907ec681f3Smrg      throw error(CL_INVALID_BINARY);
2917ec681f3Smrg   }
2927ec681f3Smrg
2937ec681f3Smrg   return CL_SUCCESS;
2947ec681f3Smrg
2957ec681f3Smrg} catch (error &e) {
2967ec681f3Smrg   return e.get();
2977ec681f3Smrg}
2987ec681f3Smrg
2997ec681f3SmrgCLOVER_API cl_int
3007ec681f3SmrgclCompileProgram(cl_program d_prog, cl_uint num_devs,
3017ec681f3Smrg                 const cl_device_id *d_devs, const char *p_opts,
3027ec681f3Smrg                 cl_uint num_headers, const cl_program *d_header_progs,
3037ec681f3Smrg                 const char **header_names,
3047ec681f3Smrg                 void (*pfn_notify)(cl_program, void *),
3057ec681f3Smrg                 void *user_data) try {
3067ec681f3Smrg   auto &prog = obj(d_prog);
3077ec681f3Smrg   auto devs =
3087ec681f3Smrg       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
3097ec681f3Smrg   const auto opts = build_options(p_opts, "CLOVER_EXTRA_COMPILE_OPTIONS");
3107ec681f3Smrg   header_map headers;
3117ec681f3Smrg
3127ec681f3Smrg   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
3137ec681f3Smrg
3147ec681f3Smrg   auto notifier = build_notifier(d_prog, pfn_notify, user_data);
3157ec681f3Smrg
3167ec681f3Smrg   if (bool(num_headers) != bool(header_names))
3177ec681f3Smrg      throw error(CL_INVALID_VALUE);
3187ec681f3Smrg
3197ec681f3Smrg   if (prog.il_type() == program::il_type::none)
3207ec681f3Smrg      throw error(CL_INVALID_OPERATION);
3217ec681f3Smrg
3227ec681f3Smrg   for_each([&](const char *name, const program &header) {
3237ec681f3Smrg         if (header.il_type() == program::il_type::none)
3247ec681f3Smrg            throw error(CL_INVALID_OPERATION);
3257ec681f3Smrg
3267ec681f3Smrg         if (!any_of(key_equals(name), headers))
3277ec681f3Smrg            headers.push_back(std::pair<std::string, std::string>(
3287ec681f3Smrg                                 name, header.source()));
3297ec681f3Smrg      },
3307ec681f3Smrg      range(header_names, num_headers),
3317ec681f3Smrg      objs<allow_empty_tag>(d_header_progs, num_headers));
3327ec681f3Smrg
3337ec681f3Smrg   prog.compile(devs, opts, headers);
3347ec681f3Smrg   return CL_SUCCESS;
3357ec681f3Smrg
3367ec681f3Smrg} catch (invalid_build_options_error &) {
3377ec681f3Smrg   return CL_INVALID_COMPILER_OPTIONS;
3387ec681f3Smrg
3397ec681f3Smrg} catch (build_error &) {
3407ec681f3Smrg   return CL_COMPILE_PROGRAM_FAILURE;
3417ec681f3Smrg
3427ec681f3Smrg} catch (error &e) {
3437ec681f3Smrg   return e.get();
3447ec681f3Smrg}
3457ec681f3Smrg
3467ec681f3Smrgnamespace {
3477ec681f3Smrg   ref_vector<device>
3487ec681f3Smrg   validate_link_devices(const ref_vector<program> &progs,
3497ec681f3Smrg                         const ref_vector<device> &all_devs,
3507ec681f3Smrg                         const std::string &opts) {
3517ec681f3Smrg      std::vector<device *> devs;
3527ec681f3Smrg      const bool create_library =
3537ec681f3Smrg         opts.find("-create-library") != std::string::npos;
3547ec681f3Smrg      const bool enable_link_options =
3557ec681f3Smrg         opts.find("-enable-link-options") != std::string::npos;
3567ec681f3Smrg      const bool has_link_options =
3577ec681f3Smrg         opts.find("-cl-denorms-are-zero") != std::string::npos ||
3587ec681f3Smrg         opts.find("-cl-no-signed-zeroes") != std::string::npos ||
3597ec681f3Smrg         opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
3607ec681f3Smrg         opts.find("-cl-finite-math-only") != std::string::npos ||
3617ec681f3Smrg         opts.find("-cl-fast-relaxed-math") != std::string::npos ||
3627ec681f3Smrg         opts.find("-cl-no-subgroup-ifp") != std::string::npos;
3637ec681f3Smrg
3647ec681f3Smrg      // According to the OpenCL 1.2 specification, "[the
3657ec681f3Smrg      // -enable-link-options] option must be specified with the
3667ec681f3Smrg      // create-library option".
3677ec681f3Smrg      if (enable_link_options && !create_library)
3687ec681f3Smrg         throw error(CL_INVALID_LINKER_OPTIONS);
3697ec681f3Smrg
3707ec681f3Smrg      // According to the OpenCL 1.2 specification, "the
3717ec681f3Smrg      // [program linking options] can be specified when linking a program
3727ec681f3Smrg      // executable".
3737ec681f3Smrg      if (has_link_options && create_library)
3747ec681f3Smrg         throw error(CL_INVALID_LINKER_OPTIONS);
3757ec681f3Smrg
3767ec681f3Smrg      for (auto &dev : all_devs) {
3777ec681f3Smrg         const auto has_binary = [&](const program &prog) {
3787ec681f3Smrg            const auto t = prog.build(dev).binary_type();
3797ec681f3Smrg            return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
3807ec681f3Smrg                   t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
3817ec681f3Smrg         };
3827ec681f3Smrg
3837ec681f3Smrg         // According to the OpenCL 1.2 specification, a library is made of
3847ec681f3Smrg         // “compiled binaries specified in input_programs argument to
3857ec681f3Smrg         // clLinkProgram“; compiled binaries does not refer to libraries:
3867ec681f3Smrg         // “input_programs is an array of program objects that are compiled
3877ec681f3Smrg         // binaries or libraries that are to be linked to create the program
3887ec681f3Smrg         // executable”.
3897ec681f3Smrg         if (create_library && any_of([&](const program &prog) {
3907ec681f3Smrg                  const auto t = prog.build(dev).binary_type();
3917ec681f3Smrg                  return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
3927ec681f3Smrg               }, progs))
3937ec681f3Smrg            throw error(CL_INVALID_OPERATION);
3947ec681f3Smrg
3957ec681f3Smrg         // According to the CL 1.2 spec, when "all programs specified [..]
3967ec681f3Smrg         // contain a compiled binary or library for the device [..] a link is
3977ec681f3Smrg         // performed",
3987ec681f3Smrg         else if (all_of(has_binary, progs))
3997ec681f3Smrg            devs.push_back(&dev);
4007ec681f3Smrg
4017ec681f3Smrg         // otherwise if "none of the programs contain a compiled binary or
4027ec681f3Smrg         // library for that device [..] no link is performed.  All other
4037ec681f3Smrg         // cases will return a CL_INVALID_OPERATION error."
4047ec681f3Smrg         else if (any_of(has_binary, progs))
4057ec681f3Smrg            throw error(CL_INVALID_OPERATION);
4067ec681f3Smrg
4077ec681f3Smrg         // According to the OpenCL 1.2 specification, "[t]he linker may apply
4087ec681f3Smrg         // [program linking options] to all compiled program objects
4097ec681f3Smrg         // specified to clLinkProgram. The linker may apply these options
4107ec681f3Smrg         // only to libraries which were created with the
4117ec681f3Smrg         // -enable-link-option."
4127ec681f3Smrg         else if (has_link_options && any_of([&](const program &prog) {
4137ec681f3Smrg                  const auto t = prog.build(dev).binary_type();
4147ec681f3Smrg                  return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
4157ec681f3Smrg                          (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
4167ec681f3Smrg                           prog.build(dev).opts.find("-enable-link-options") !=
4177ec681f3Smrg                              std::string::npos));
4187ec681f3Smrg               }, progs))
4197ec681f3Smrg            throw error(CL_INVALID_LINKER_OPTIONS);
4207ec681f3Smrg      }
4217ec681f3Smrg
4227ec681f3Smrg      return map(derefs(), devs);
4237ec681f3Smrg   }
4247ec681f3Smrg}
4257ec681f3Smrg
4267ec681f3SmrgCLOVER_API cl_program
4277ec681f3SmrgclLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
4287ec681f3Smrg              const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
4297ec681f3Smrg              void (*pfn_notify) (cl_program, void *), void *user_data,
4307ec681f3Smrg              cl_int *r_errcode) try {
4317ec681f3Smrg   auto &ctx = obj(d_ctx);
4327ec681f3Smrg   const auto opts = build_options(p_opts, "CLOVER_EXTRA_LINK_OPTIONS");
4337ec681f3Smrg   auto progs = objs(d_progs, num_progs);
4347ec681f3Smrg   auto all_devs =
4357ec681f3Smrg      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
4367ec681f3Smrg   auto prog = create<program>(ctx, all_devs);
4377ec681f3Smrg   auto r_prog = ret_object(prog);
4387ec681f3Smrg
4397ec681f3Smrg   auto notifier = build_notifier(r_prog, pfn_notify, user_data);
4407ec681f3Smrg
4417ec681f3Smrg   auto devs = validate_link_devices(progs, all_devs, opts);
4427ec681f3Smrg
4437ec681f3Smrg   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
4447ec681f3Smrg
4457ec681f3Smrg   try {
4467ec681f3Smrg      prog().link(devs, opts, progs);
4477ec681f3Smrg      ret_error(r_errcode, CL_SUCCESS);
4487ec681f3Smrg
4497ec681f3Smrg   } catch (build_error &) {
4507ec681f3Smrg      ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
4517ec681f3Smrg   }
4527ec681f3Smrg
4537ec681f3Smrg   return r_prog;
4547ec681f3Smrg
4557ec681f3Smrg} catch (invalid_build_options_error &) {
4567ec681f3Smrg   ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
4577ec681f3Smrg   return NULL;
4587ec681f3Smrg
4597ec681f3Smrg} catch (error &e) {
4607ec681f3Smrg   ret_error(r_errcode, e);
4617ec681f3Smrg   return NULL;
4627ec681f3Smrg}
4637ec681f3Smrg
4647ec681f3SmrgCLOVER_API cl_int
4657ec681f3SmrgclUnloadCompiler() {
4667ec681f3Smrg   return CL_SUCCESS;
4677ec681f3Smrg}
4687ec681f3Smrg
4697ec681f3SmrgCLOVER_API cl_int
4707ec681f3SmrgclUnloadPlatformCompiler(cl_platform_id d_platform) try {
4717ec681f3Smrg   find_platform(d_platform);
4727ec681f3Smrg   return CL_SUCCESS;
4737ec681f3Smrg} catch (error &e) {
4747ec681f3Smrg   return e.get();
4757ec681f3Smrg}
4767ec681f3Smrg
4777ec681f3SmrgCLOVER_API cl_int
4787ec681f3SmrgclGetProgramInfo(cl_program d_prog, cl_program_info param,
4797ec681f3Smrg                 size_t size, void *r_buf, size_t *r_size) try {
4807ec681f3Smrg   property_buffer buf { r_buf, size, r_size };
4817ec681f3Smrg   auto &prog = obj(d_prog);
4827ec681f3Smrg
4837ec681f3Smrg   switch (param) {
4847ec681f3Smrg   case CL_PROGRAM_REFERENCE_COUNT:
4857ec681f3Smrg      buf.as_scalar<cl_uint>() = prog.ref_count();
4867ec681f3Smrg      break;
4877ec681f3Smrg
4887ec681f3Smrg   case CL_PROGRAM_CONTEXT:
4897ec681f3Smrg      buf.as_scalar<cl_context>() = desc(prog.context());
4907ec681f3Smrg      break;
4917ec681f3Smrg
4927ec681f3Smrg   case CL_PROGRAM_NUM_DEVICES:
4937ec681f3Smrg      buf.as_scalar<cl_uint>() = (prog.devices().size() ?
4947ec681f3Smrg                                  prog.devices().size() :
4957ec681f3Smrg                                  prog.context().devices().size());
4967ec681f3Smrg      break;
4977ec681f3Smrg
4987ec681f3Smrg   case CL_PROGRAM_DEVICES:
4997ec681f3Smrg      buf.as_vector<cl_device_id>() = (prog.devices().size() ?
5007ec681f3Smrg                                       descs(prog.devices()) :
5017ec681f3Smrg                                       descs(prog.context().devices()));
5027ec681f3Smrg      break;
5037ec681f3Smrg
5047ec681f3Smrg   case CL_PROGRAM_SOURCE:
5057ec681f3Smrg      buf.as_string() = prog.source();
5067ec681f3Smrg      break;
5077ec681f3Smrg
5087ec681f3Smrg   case CL_PROGRAM_BINARY_SIZES:
5097ec681f3Smrg      buf.as_vector<size_t>() = map([&](const device &dev) {
5107ec681f3Smrg            return prog.build(dev).bin.size();
5117ec681f3Smrg         },
5127ec681f3Smrg         prog.devices());
5137ec681f3Smrg      break;
5147ec681f3Smrg
5157ec681f3Smrg   case CL_PROGRAM_BINARIES:
5167ec681f3Smrg      buf.as_matrix<unsigned char>() = map([&](const device &dev) {
5177ec681f3Smrg            std::stringbuf bin;
5187ec681f3Smrg            std::ostream s(&bin);
5197ec681f3Smrg            prog.build(dev).bin.serialize(s);
5207ec681f3Smrg            return bin.str();
5217ec681f3Smrg         },
5227ec681f3Smrg         prog.devices());
5237ec681f3Smrg      break;
5247ec681f3Smrg
5257ec681f3Smrg   case CL_PROGRAM_NUM_KERNELS:
5267ec681f3Smrg      buf.as_scalar<cl_uint>() = prog.symbols().size();
5277ec681f3Smrg      break;
5287ec681f3Smrg
5297ec681f3Smrg   case CL_PROGRAM_KERNEL_NAMES:
5307ec681f3Smrg      buf.as_string() = fold([](const std::string &a, const binary::symbol &s) {
5317ec681f3Smrg            return ((a.empty() ? "" : a + ";") + s.name);
5327ec681f3Smrg         }, std::string(), prog.symbols());
5337ec681f3Smrg      break;
5347ec681f3Smrg
5357ec681f3Smrg   case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT:
5367ec681f3Smrg   case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT:
5377ec681f3Smrg      buf.as_scalar<cl_bool>() = CL_FALSE;
5387ec681f3Smrg      break;
5397ec681f3Smrg
5407ec681f3Smrg   case CL_PROGRAM_IL:
5417ec681f3Smrg      if (prog.il_type() == program::il_type::spirv)
5427ec681f3Smrg         buf.as_vector<char>() = prog.source();
5437ec681f3Smrg      else if (r_size)
5447ec681f3Smrg         *r_size = 0u;
5457ec681f3Smrg      break;
5467ec681f3Smrg   default:
5477ec681f3Smrg      throw error(CL_INVALID_VALUE);
5487ec681f3Smrg   }
5497ec681f3Smrg
5507ec681f3Smrg   return CL_SUCCESS;
5517ec681f3Smrg
5527ec681f3Smrg} catch (error &e) {
5537ec681f3Smrg   return e.get();
5547ec681f3Smrg}
5557ec681f3Smrg
5567ec681f3SmrgCLOVER_API cl_int
5577ec681f3SmrgclGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
5587ec681f3Smrg                      cl_program_build_info param,
5597ec681f3Smrg                      size_t size, void *r_buf, size_t *r_size) try {
5607ec681f3Smrg   property_buffer buf { r_buf, size, r_size };
5617ec681f3Smrg   auto &prog = obj(d_prog);
5627ec681f3Smrg   auto &dev = obj(d_dev);
5637ec681f3Smrg
5647ec681f3Smrg   if (!count(dev, prog.context().devices()))
5657ec681f3Smrg      return CL_INVALID_DEVICE;
5667ec681f3Smrg
5677ec681f3Smrg   switch (param) {
5687ec681f3Smrg   case CL_PROGRAM_BUILD_STATUS:
5697ec681f3Smrg      buf.as_scalar<cl_build_status>() = prog.build(dev).status();
5707ec681f3Smrg      break;
5717ec681f3Smrg
5727ec681f3Smrg   case CL_PROGRAM_BUILD_OPTIONS:
5737ec681f3Smrg      buf.as_string() = prog.build(dev).opts;
5747ec681f3Smrg      break;
5757ec681f3Smrg
5767ec681f3Smrg   case CL_PROGRAM_BUILD_LOG:
5777ec681f3Smrg      buf.as_string() = prog.build(dev).log;
5787ec681f3Smrg      break;
5797ec681f3Smrg
5807ec681f3Smrg   case CL_PROGRAM_BINARY_TYPE:
5817ec681f3Smrg      buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
5827ec681f3Smrg      break;
5837ec681f3Smrg
5847ec681f3Smrg   case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:
5857ec681f3Smrg      buf.as_scalar<size_t>() = 0;
5867ec681f3Smrg      break;
5877ec681f3Smrg
5887ec681f3Smrg   default:
5897ec681f3Smrg      throw error(CL_INVALID_VALUE);
5907ec681f3Smrg   }
5917ec681f3Smrg
5927ec681f3Smrg   return CL_SUCCESS;
5937ec681f3Smrg
5947ec681f3Smrg} catch (error &e) {
5957ec681f3Smrg   return e.get();
5967ec681f3Smrg}
597