1bf215546Sopenharmony_ci//
2bf215546Sopenharmony_ci// Copyright 2012 Francisco Jerez
3bf215546Sopenharmony_ci//
4bf215546Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci// copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci// to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci// and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci// Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci//
11bf215546Sopenharmony_ci// The above copyright notice and this permission notice shall be included in
12bf215546Sopenharmony_ci// all copies or substantial portions of the Software.
13bf215546Sopenharmony_ci//
14bf215546Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15bf215546Sopenharmony_ci// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16bf215546Sopenharmony_ci// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17bf215546Sopenharmony_ci// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18bf215546Sopenharmony_ci// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19bf215546Sopenharmony_ci// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20bf215546Sopenharmony_ci// OTHER DEALINGS IN THE SOFTWARE.
21bf215546Sopenharmony_ci//
22bf215546Sopenharmony_ci
23bf215546Sopenharmony_ci#include "api/util.hpp"
24bf215546Sopenharmony_ci#include "core/program.hpp"
25bf215546Sopenharmony_ci#include "core/platform.hpp"
26bf215546Sopenharmony_ci#include "spirv/invocation.hpp"
27bf215546Sopenharmony_ci#include "util/u_debug.h"
28bf215546Sopenharmony_ci
29bf215546Sopenharmony_ci#include <limits>
30bf215546Sopenharmony_ci#include <sstream>
31bf215546Sopenharmony_ci
32bf215546Sopenharmony_ciusing namespace clover;
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_cinamespace {
35bf215546Sopenharmony_ci
36bf215546Sopenharmony_ci   std::string
37bf215546Sopenharmony_ci   build_options(const char *p_opts, const char *p_debug) {
38bf215546Sopenharmony_ci      auto opts = std::string(p_opts ? p_opts : "");
39bf215546Sopenharmony_ci      std::string extra_opts = debug_get_option(p_debug, "");
40bf215546Sopenharmony_ci
41bf215546Sopenharmony_ci      return detokenize(std::vector<std::string>{opts, extra_opts}, " ");
42bf215546Sopenharmony_ci   }
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_ci   class build_notifier {
45bf215546Sopenharmony_ci   public:
46bf215546Sopenharmony_ci      build_notifier(cl_program prog,
47bf215546Sopenharmony_ci                     void (*notifer)(cl_program, void *), void *data) :
48bf215546Sopenharmony_ci                     prog_(prog), notifer(notifer), data_(data) { }
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci      ~build_notifier() {
51bf215546Sopenharmony_ci         if (notifer)
52bf215546Sopenharmony_ci            notifer(prog_, data_);
53bf215546Sopenharmony_ci      }
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   private:
56bf215546Sopenharmony_ci      cl_program prog_;
57bf215546Sopenharmony_ci      void (*notifer)(cl_program, void *);
58bf215546Sopenharmony_ci      void *data_;
59bf215546Sopenharmony_ci   };
60bf215546Sopenharmony_ci
61bf215546Sopenharmony_ci   void
62bf215546Sopenharmony_ci   validate_build_common(const program &prog, cl_uint num_devs,
63bf215546Sopenharmony_ci                         const cl_device_id *d_devs,
64bf215546Sopenharmony_ci                         void (*pfn_notify)(cl_program, void *),
65bf215546Sopenharmony_ci                         void *user_data) {
66bf215546Sopenharmony_ci      if (!pfn_notify && user_data)
67bf215546Sopenharmony_ci         throw error(CL_INVALID_VALUE);
68bf215546Sopenharmony_ci
69bf215546Sopenharmony_ci      if (prog.kernel_ref_count())
70bf215546Sopenharmony_ci         throw error(CL_INVALID_OPERATION);
71bf215546Sopenharmony_ci
72bf215546Sopenharmony_ci      if (any_of([&](const device &dev) {
73bf215546Sopenharmony_ci               return !count(dev, prog.devices());
74bf215546Sopenharmony_ci            }, objs<allow_empty_tag>(d_devs, num_devs)))
75bf215546Sopenharmony_ci         throw error(CL_INVALID_DEVICE);
76bf215546Sopenharmony_ci   }
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_ci   enum program::il_type
79bf215546Sopenharmony_ci   identify_and_validate_il(const std::string &il,
80bf215546Sopenharmony_ci                            const cl_version opencl_version,
81bf215546Sopenharmony_ci                            const context::notify_action &notify) {
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci      enum program::il_type il_type = program::il_type::none;
84bf215546Sopenharmony_ci
85bf215546Sopenharmony_ci#ifdef HAVE_CLOVER_SPIRV
86bf215546Sopenharmony_ci      if (spirv::is_binary_spirv(il)) {
87bf215546Sopenharmony_ci         std::string log;
88bf215546Sopenharmony_ci         if (!spirv::is_valid_spirv(il, opencl_version, log)) {
89bf215546Sopenharmony_ci            if (notify) {
90bf215546Sopenharmony_ci               notify(log.c_str());
91bf215546Sopenharmony_ci            }
92bf215546Sopenharmony_ci            throw error(CL_INVALID_VALUE);
93bf215546Sopenharmony_ci         }
94bf215546Sopenharmony_ci         il_type = program::il_type::spirv;
95bf215546Sopenharmony_ci      }
96bf215546Sopenharmony_ci#endif
97bf215546Sopenharmony_ci
98bf215546Sopenharmony_ci      return il_type;
99bf215546Sopenharmony_ci   }
100bf215546Sopenharmony_ci}
101bf215546Sopenharmony_ci
102bf215546Sopenharmony_ciCLOVER_API cl_program
103bf215546Sopenharmony_ciclCreateProgramWithSource(cl_context d_ctx, cl_uint count,
104bf215546Sopenharmony_ci                          const char **strings, const size_t *lengths,
105bf215546Sopenharmony_ci                          cl_int *r_errcode) try {
106bf215546Sopenharmony_ci   auto &ctx = obj(d_ctx);
107bf215546Sopenharmony_ci   std::string source;
108bf215546Sopenharmony_ci
109bf215546Sopenharmony_ci   if (!count || !strings ||
110bf215546Sopenharmony_ci       any_of(is_zero(), range(strings, count)))
111bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
112bf215546Sopenharmony_ci
113bf215546Sopenharmony_ci   // Concatenate all the provided fragments together
114bf215546Sopenharmony_ci   for (unsigned i = 0; i < count; ++i)
115bf215546Sopenharmony_ci         source += (lengths && lengths[i] ?
116bf215546Sopenharmony_ci                    std::string(strings[i], strings[i] + lengths[i]) :
117bf215546Sopenharmony_ci                    std::string(strings[i]));
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci   // ...and create a program object for them.
120bf215546Sopenharmony_ci   ret_error(r_errcode, CL_SUCCESS);
121bf215546Sopenharmony_ci   return new program(ctx, std::move(source), program::il_type::source);
122bf215546Sopenharmony_ci
123bf215546Sopenharmony_ci} catch (error &e) {
124bf215546Sopenharmony_ci   ret_error(r_errcode, e);
125bf215546Sopenharmony_ci   return NULL;
126bf215546Sopenharmony_ci}
127bf215546Sopenharmony_ci
128bf215546Sopenharmony_ciCLOVER_API cl_program
129bf215546Sopenharmony_ciclCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
130bf215546Sopenharmony_ci                          const cl_device_id *d_devs,
131bf215546Sopenharmony_ci                          const size_t *lengths,
132bf215546Sopenharmony_ci                          const unsigned char **binaries,
133bf215546Sopenharmony_ci                          cl_int *r_status, cl_int *r_errcode) try {
134bf215546Sopenharmony_ci   auto &ctx = obj(d_ctx);
135bf215546Sopenharmony_ci   auto devs = objs(d_devs, n);
136bf215546Sopenharmony_ci
137bf215546Sopenharmony_ci   if (!lengths || !binaries)
138bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
139bf215546Sopenharmony_ci
140bf215546Sopenharmony_ci   if (any_of([&](const device &dev) {
141bf215546Sopenharmony_ci            return !count(dev, ctx.devices());
142bf215546Sopenharmony_ci         }, devs))
143bf215546Sopenharmony_ci      throw error(CL_INVALID_DEVICE);
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_ci   // Deserialize the provided binaries,
146bf215546Sopenharmony_ci   std::vector<std::pair<cl_int, binary>> result = map(
147bf215546Sopenharmony_ci      [](const unsigned char *p, size_t l) -> std::pair<cl_int, binary> {
148bf215546Sopenharmony_ci         if (!p || !l)
149bf215546Sopenharmony_ci            return { CL_INVALID_VALUE, {} };
150bf215546Sopenharmony_ci
151bf215546Sopenharmony_ci         try {
152bf215546Sopenharmony_ci            std::stringbuf bin( std::string{ (char*)p, l } );
153bf215546Sopenharmony_ci            std::istream s(&bin);
154bf215546Sopenharmony_ci
155bf215546Sopenharmony_ci            return { CL_SUCCESS, binary::deserialize(s) };
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci         } catch (std::istream::failure &) {
158bf215546Sopenharmony_ci            return { CL_INVALID_BINARY, {} };
159bf215546Sopenharmony_ci         }
160bf215546Sopenharmony_ci      },
161bf215546Sopenharmony_ci      range(binaries, n),
162bf215546Sopenharmony_ci      range(lengths, n));
163bf215546Sopenharmony_ci
164bf215546Sopenharmony_ci   // update the status array,
165bf215546Sopenharmony_ci   if (r_status)
166bf215546Sopenharmony_ci      copy(map(keys(), result), r_status);
167bf215546Sopenharmony_ci
168bf215546Sopenharmony_ci   if (any_of(key_equals(CL_INVALID_VALUE), result))
169bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
170bf215546Sopenharmony_ci
171bf215546Sopenharmony_ci   if (any_of(key_equals(CL_INVALID_BINARY), result))
172bf215546Sopenharmony_ci      throw error(CL_INVALID_BINARY);
173bf215546Sopenharmony_ci
174bf215546Sopenharmony_ci   // initialize a program object with them.
175bf215546Sopenharmony_ci   ret_error(r_errcode, CL_SUCCESS);
176bf215546Sopenharmony_ci   return new program(ctx, devs, map(values(), result));
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_ci} catch (error &e) {
179bf215546Sopenharmony_ci   ret_error(r_errcode, e);
180bf215546Sopenharmony_ci   return NULL;
181bf215546Sopenharmony_ci}
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_cicl_program
184bf215546Sopenharmony_ciclover::CreateProgramWithILKHR(cl_context d_ctx, const void *il,
185bf215546Sopenharmony_ci                               size_t length, cl_int *r_errcode) try {
186bf215546Sopenharmony_ci   auto &ctx = obj(d_ctx);
187bf215546Sopenharmony_ci
188bf215546Sopenharmony_ci   if (!il || !length)
189bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
190bf215546Sopenharmony_ci
191bf215546Sopenharmony_ci   // Compute the highest OpenCL version supported by all devices associated to
192bf215546Sopenharmony_ci   // the context. That is the version used for validating the SPIR-V binary.
193bf215546Sopenharmony_ci   cl_version min_opencl_version = std::numeric_limits<uint32_t>::max();
194bf215546Sopenharmony_ci   for (const device &dev : ctx.devices()) {
195bf215546Sopenharmony_ci      const cl_version opencl_version = dev.device_version();
196bf215546Sopenharmony_ci      min_opencl_version = std::min(opencl_version, min_opencl_version);
197bf215546Sopenharmony_ci   }
198bf215546Sopenharmony_ci
199bf215546Sopenharmony_ci   const char *stream = reinterpret_cast<const char *>(il);
200bf215546Sopenharmony_ci   std::string binary(stream, stream + length);
201bf215546Sopenharmony_ci   const enum program::il_type il_type = identify_and_validate_il(binary,
202bf215546Sopenharmony_ci                                                                  min_opencl_version,
203bf215546Sopenharmony_ci                                                                  ctx.notify);
204bf215546Sopenharmony_ci
205bf215546Sopenharmony_ci   if (il_type == program::il_type::none)
206bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
207bf215546Sopenharmony_ci
208bf215546Sopenharmony_ci   // Initialize a program object with it.
209bf215546Sopenharmony_ci   ret_error(r_errcode, CL_SUCCESS);
210bf215546Sopenharmony_ci   return new program(ctx, std::move(binary), il_type);
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_ci} catch (error &e) {
213bf215546Sopenharmony_ci   ret_error(r_errcode, e);
214bf215546Sopenharmony_ci   return NULL;
215bf215546Sopenharmony_ci}
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ciCLOVER_API cl_program
218bf215546Sopenharmony_ciclCreateProgramWithIL(cl_context d_ctx,
219bf215546Sopenharmony_ci                      const void *il,
220bf215546Sopenharmony_ci                      size_t length,
221bf215546Sopenharmony_ci                      cl_int *r_errcode) {
222bf215546Sopenharmony_ci   return CreateProgramWithILKHR(d_ctx, il, length, r_errcode);
223bf215546Sopenharmony_ci}
224bf215546Sopenharmony_ci
225bf215546Sopenharmony_ciCLOVER_API cl_program
226bf215546Sopenharmony_ciclCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
227bf215546Sopenharmony_ci                                  const cl_device_id *d_devs,
228bf215546Sopenharmony_ci                                  const char *kernel_names,
229bf215546Sopenharmony_ci                                  cl_int *r_errcode) try {
230bf215546Sopenharmony_ci   auto &ctx = obj(d_ctx);
231bf215546Sopenharmony_ci   auto devs = objs(d_devs, n);
232bf215546Sopenharmony_ci
233bf215546Sopenharmony_ci   if (any_of([&](const device &dev) {
234bf215546Sopenharmony_ci            return !count(dev, ctx.devices());
235bf215546Sopenharmony_ci         }, devs))
236bf215546Sopenharmony_ci      throw error(CL_INVALID_DEVICE);
237bf215546Sopenharmony_ci
238bf215546Sopenharmony_ci   // No currently supported built-in kernels.
239bf215546Sopenharmony_ci   throw error(CL_INVALID_VALUE);
240bf215546Sopenharmony_ci
241bf215546Sopenharmony_ci} catch (error &e) {
242bf215546Sopenharmony_ci   ret_error(r_errcode, e);
243bf215546Sopenharmony_ci   return NULL;
244bf215546Sopenharmony_ci}
245bf215546Sopenharmony_ci
246bf215546Sopenharmony_ci
247bf215546Sopenharmony_ciCLOVER_API cl_int
248bf215546Sopenharmony_ciclRetainProgram(cl_program d_prog) try {
249bf215546Sopenharmony_ci   obj(d_prog).retain();
250bf215546Sopenharmony_ci   return CL_SUCCESS;
251bf215546Sopenharmony_ci
252bf215546Sopenharmony_ci} catch (error &e) {
253bf215546Sopenharmony_ci   return e.get();
254bf215546Sopenharmony_ci}
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ciCLOVER_API cl_int
257bf215546Sopenharmony_ciclReleaseProgram(cl_program d_prog) try {
258bf215546Sopenharmony_ci   if (obj(d_prog).release())
259bf215546Sopenharmony_ci      delete pobj(d_prog);
260bf215546Sopenharmony_ci
261bf215546Sopenharmony_ci   return CL_SUCCESS;
262bf215546Sopenharmony_ci
263bf215546Sopenharmony_ci} catch (error &e) {
264bf215546Sopenharmony_ci   return e.get();
265bf215546Sopenharmony_ci}
266bf215546Sopenharmony_ci
267bf215546Sopenharmony_ciCLOVER_API cl_int
268bf215546Sopenharmony_ciclBuildProgram(cl_program d_prog, cl_uint num_devs,
269bf215546Sopenharmony_ci               const cl_device_id *d_devs, const char *p_opts,
270bf215546Sopenharmony_ci               void (*pfn_notify)(cl_program, void *),
271bf215546Sopenharmony_ci               void *user_data) try {
272bf215546Sopenharmony_ci   auto &prog = obj(d_prog);
273bf215546Sopenharmony_ci   auto devs =
274bf215546Sopenharmony_ci      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
275bf215546Sopenharmony_ci   const auto opts = build_options(p_opts, "CLOVER_EXTRA_BUILD_OPTIONS");
276bf215546Sopenharmony_ci
277bf215546Sopenharmony_ci   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
278bf215546Sopenharmony_ci
279bf215546Sopenharmony_ci   auto notifier = build_notifier(d_prog, pfn_notify, user_data);
280bf215546Sopenharmony_ci
281bf215546Sopenharmony_ci   if (prog.il_type() != program::il_type::none) {
282bf215546Sopenharmony_ci      prog.compile(devs, opts);
283bf215546Sopenharmony_ci      prog.link(devs, opts, { prog });
284bf215546Sopenharmony_ci   } else if (any_of([&](const device &dev){
285bf215546Sopenharmony_ci         return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
286bf215546Sopenharmony_ci         }, devs)) {
287bf215546Sopenharmony_ci      // According to the OpenCL 1.2 specification, “if program is created
288bf215546Sopenharmony_ci      // with clCreateProgramWithBinary, then the program binary must be an
289bf215546Sopenharmony_ci      // executable binary (not a compiled binary or library).”
290bf215546Sopenharmony_ci      throw error(CL_INVALID_BINARY);
291bf215546Sopenharmony_ci   }
292bf215546Sopenharmony_ci
293bf215546Sopenharmony_ci   return CL_SUCCESS;
294bf215546Sopenharmony_ci
295bf215546Sopenharmony_ci} catch (error &e) {
296bf215546Sopenharmony_ci   return e.get();
297bf215546Sopenharmony_ci}
298bf215546Sopenharmony_ci
299bf215546Sopenharmony_ciCLOVER_API cl_int
300bf215546Sopenharmony_ciclCompileProgram(cl_program d_prog, cl_uint num_devs,
301bf215546Sopenharmony_ci                 const cl_device_id *d_devs, const char *p_opts,
302bf215546Sopenharmony_ci                 cl_uint num_headers, const cl_program *d_header_progs,
303bf215546Sopenharmony_ci                 const char **header_names,
304bf215546Sopenharmony_ci                 void (*pfn_notify)(cl_program, void *),
305bf215546Sopenharmony_ci                 void *user_data) try {
306bf215546Sopenharmony_ci   auto &prog = obj(d_prog);
307bf215546Sopenharmony_ci   auto devs =
308bf215546Sopenharmony_ci       (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
309bf215546Sopenharmony_ci   const auto opts = build_options(p_opts, "CLOVER_EXTRA_COMPILE_OPTIONS");
310bf215546Sopenharmony_ci   header_map headers;
311bf215546Sopenharmony_ci
312bf215546Sopenharmony_ci   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
313bf215546Sopenharmony_ci
314bf215546Sopenharmony_ci   auto notifier = build_notifier(d_prog, pfn_notify, user_data);
315bf215546Sopenharmony_ci
316bf215546Sopenharmony_ci   if (bool(num_headers) != bool(header_names))
317bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
318bf215546Sopenharmony_ci
319bf215546Sopenharmony_ci   if (prog.il_type() == program::il_type::none)
320bf215546Sopenharmony_ci      throw error(CL_INVALID_OPERATION);
321bf215546Sopenharmony_ci
322bf215546Sopenharmony_ci   for_each([&](const char *name, const program &header) {
323bf215546Sopenharmony_ci         if (header.il_type() == program::il_type::none)
324bf215546Sopenharmony_ci            throw error(CL_INVALID_OPERATION);
325bf215546Sopenharmony_ci
326bf215546Sopenharmony_ci         if (!any_of(key_equals(name), headers))
327bf215546Sopenharmony_ci            headers.push_back(std::pair<std::string, std::string>(
328bf215546Sopenharmony_ci                                 name, header.source()));
329bf215546Sopenharmony_ci      },
330bf215546Sopenharmony_ci      range(header_names, num_headers),
331bf215546Sopenharmony_ci      objs<allow_empty_tag>(d_header_progs, num_headers));
332bf215546Sopenharmony_ci
333bf215546Sopenharmony_ci   prog.compile(devs, opts, headers);
334bf215546Sopenharmony_ci   return CL_SUCCESS;
335bf215546Sopenharmony_ci
336bf215546Sopenharmony_ci} catch (invalid_build_options_error &) {
337bf215546Sopenharmony_ci   return CL_INVALID_COMPILER_OPTIONS;
338bf215546Sopenharmony_ci
339bf215546Sopenharmony_ci} catch (build_error &) {
340bf215546Sopenharmony_ci   return CL_COMPILE_PROGRAM_FAILURE;
341bf215546Sopenharmony_ci
342bf215546Sopenharmony_ci} catch (error &e) {
343bf215546Sopenharmony_ci   return e.get();
344bf215546Sopenharmony_ci}
345bf215546Sopenharmony_ci
346bf215546Sopenharmony_cinamespace {
347bf215546Sopenharmony_ci   ref_vector<device>
348bf215546Sopenharmony_ci   validate_link_devices(const ref_vector<program> &progs,
349bf215546Sopenharmony_ci                         const ref_vector<device> &all_devs,
350bf215546Sopenharmony_ci                         const std::string &opts) {
351bf215546Sopenharmony_ci      std::vector<device *> devs;
352bf215546Sopenharmony_ci      const bool create_library =
353bf215546Sopenharmony_ci         opts.find("-create-library") != std::string::npos;
354bf215546Sopenharmony_ci      const bool enable_link_options =
355bf215546Sopenharmony_ci         opts.find("-enable-link-options") != std::string::npos;
356bf215546Sopenharmony_ci      const bool has_link_options =
357bf215546Sopenharmony_ci         opts.find("-cl-denorms-are-zero") != std::string::npos ||
358bf215546Sopenharmony_ci         opts.find("-cl-no-signed-zeroes") != std::string::npos ||
359bf215546Sopenharmony_ci         opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
360bf215546Sopenharmony_ci         opts.find("-cl-finite-math-only") != std::string::npos ||
361bf215546Sopenharmony_ci         opts.find("-cl-fast-relaxed-math") != std::string::npos ||
362bf215546Sopenharmony_ci         opts.find("-cl-no-subgroup-ifp") != std::string::npos;
363bf215546Sopenharmony_ci
364bf215546Sopenharmony_ci      // According to the OpenCL 1.2 specification, "[the
365bf215546Sopenharmony_ci      // -enable-link-options] option must be specified with the
366bf215546Sopenharmony_ci      // create-library option".
367bf215546Sopenharmony_ci      if (enable_link_options && !create_library)
368bf215546Sopenharmony_ci         throw error(CL_INVALID_LINKER_OPTIONS);
369bf215546Sopenharmony_ci
370bf215546Sopenharmony_ci      // According to the OpenCL 1.2 specification, "the
371bf215546Sopenharmony_ci      // [program linking options] can be specified when linking a program
372bf215546Sopenharmony_ci      // executable".
373bf215546Sopenharmony_ci      if (has_link_options && create_library)
374bf215546Sopenharmony_ci         throw error(CL_INVALID_LINKER_OPTIONS);
375bf215546Sopenharmony_ci
376bf215546Sopenharmony_ci      for (auto &dev : all_devs) {
377bf215546Sopenharmony_ci         const auto has_binary = [&](const program &prog) {
378bf215546Sopenharmony_ci            const auto t = prog.build(dev).binary_type();
379bf215546Sopenharmony_ci            return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
380bf215546Sopenharmony_ci                   t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
381bf215546Sopenharmony_ci         };
382bf215546Sopenharmony_ci
383bf215546Sopenharmony_ci         // According to the OpenCL 1.2 specification, a library is made of
384bf215546Sopenharmony_ci         // “compiled binaries specified in input_programs argument to
385bf215546Sopenharmony_ci         // clLinkProgram“; compiled binaries does not refer to libraries:
386bf215546Sopenharmony_ci         // “input_programs is an array of program objects that are compiled
387bf215546Sopenharmony_ci         // binaries or libraries that are to be linked to create the program
388bf215546Sopenharmony_ci         // executable”.
389bf215546Sopenharmony_ci         if (create_library && any_of([&](const program &prog) {
390bf215546Sopenharmony_ci                  const auto t = prog.build(dev).binary_type();
391bf215546Sopenharmony_ci                  return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
392bf215546Sopenharmony_ci               }, progs))
393bf215546Sopenharmony_ci            throw error(CL_INVALID_OPERATION);
394bf215546Sopenharmony_ci
395bf215546Sopenharmony_ci         // According to the CL 1.2 spec, when "all programs specified [..]
396bf215546Sopenharmony_ci         // contain a compiled binary or library for the device [..] a link is
397bf215546Sopenharmony_ci         // performed",
398bf215546Sopenharmony_ci         else if (all_of(has_binary, progs))
399bf215546Sopenharmony_ci            devs.push_back(&dev);
400bf215546Sopenharmony_ci
401bf215546Sopenharmony_ci         // otherwise if "none of the programs contain a compiled binary or
402bf215546Sopenharmony_ci         // library for that device [..] no link is performed.  All other
403bf215546Sopenharmony_ci         // cases will return a CL_INVALID_OPERATION error."
404bf215546Sopenharmony_ci         else if (any_of(has_binary, progs))
405bf215546Sopenharmony_ci            throw error(CL_INVALID_OPERATION);
406bf215546Sopenharmony_ci
407bf215546Sopenharmony_ci         // According to the OpenCL 1.2 specification, "[t]he linker may apply
408bf215546Sopenharmony_ci         // [program linking options] to all compiled program objects
409bf215546Sopenharmony_ci         // specified to clLinkProgram. The linker may apply these options
410bf215546Sopenharmony_ci         // only to libraries which were created with the
411bf215546Sopenharmony_ci         // -enable-link-option."
412bf215546Sopenharmony_ci         else if (has_link_options && any_of([&](const program &prog) {
413bf215546Sopenharmony_ci                  const auto t = prog.build(dev).binary_type();
414bf215546Sopenharmony_ci                  return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
415bf215546Sopenharmony_ci                          (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
416bf215546Sopenharmony_ci                           prog.build(dev).opts.find("-enable-link-options") !=
417bf215546Sopenharmony_ci                              std::string::npos));
418bf215546Sopenharmony_ci               }, progs))
419bf215546Sopenharmony_ci            throw error(CL_INVALID_LINKER_OPTIONS);
420bf215546Sopenharmony_ci      }
421bf215546Sopenharmony_ci
422bf215546Sopenharmony_ci      return map(derefs(), devs);
423bf215546Sopenharmony_ci   }
424bf215546Sopenharmony_ci}
425bf215546Sopenharmony_ci
426bf215546Sopenharmony_ciCLOVER_API cl_program
427bf215546Sopenharmony_ciclLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
428bf215546Sopenharmony_ci              const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
429bf215546Sopenharmony_ci              void (*pfn_notify) (cl_program, void *), void *user_data,
430bf215546Sopenharmony_ci              cl_int *r_errcode) try {
431bf215546Sopenharmony_ci   auto &ctx = obj(d_ctx);
432bf215546Sopenharmony_ci   const auto opts = build_options(p_opts, "CLOVER_EXTRA_LINK_OPTIONS");
433bf215546Sopenharmony_ci   auto progs = objs(d_progs, num_progs);
434bf215546Sopenharmony_ci   auto all_devs =
435bf215546Sopenharmony_ci      (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
436bf215546Sopenharmony_ci   auto prog = create<program>(ctx, all_devs);
437bf215546Sopenharmony_ci   auto r_prog = ret_object(prog);
438bf215546Sopenharmony_ci
439bf215546Sopenharmony_ci   auto notifier = build_notifier(r_prog, pfn_notify, user_data);
440bf215546Sopenharmony_ci
441bf215546Sopenharmony_ci   auto devs = validate_link_devices(progs, all_devs, opts);
442bf215546Sopenharmony_ci
443bf215546Sopenharmony_ci   validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
444bf215546Sopenharmony_ci
445bf215546Sopenharmony_ci   try {
446bf215546Sopenharmony_ci      prog().link(devs, opts, progs);
447bf215546Sopenharmony_ci      ret_error(r_errcode, CL_SUCCESS);
448bf215546Sopenharmony_ci
449bf215546Sopenharmony_ci   } catch (build_error &) {
450bf215546Sopenharmony_ci      ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
451bf215546Sopenharmony_ci   }
452bf215546Sopenharmony_ci
453bf215546Sopenharmony_ci   return r_prog;
454bf215546Sopenharmony_ci
455bf215546Sopenharmony_ci} catch (invalid_build_options_error &) {
456bf215546Sopenharmony_ci   ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
457bf215546Sopenharmony_ci   return NULL;
458bf215546Sopenharmony_ci
459bf215546Sopenharmony_ci} catch (error &e) {
460bf215546Sopenharmony_ci   ret_error(r_errcode, e);
461bf215546Sopenharmony_ci   return NULL;
462bf215546Sopenharmony_ci}
463bf215546Sopenharmony_ci
464bf215546Sopenharmony_ciCLOVER_API cl_int
465bf215546Sopenharmony_ciclUnloadCompiler() {
466bf215546Sopenharmony_ci   return CL_SUCCESS;
467bf215546Sopenharmony_ci}
468bf215546Sopenharmony_ci
469bf215546Sopenharmony_ciCLOVER_API cl_int
470bf215546Sopenharmony_ciclUnloadPlatformCompiler(cl_platform_id d_platform) try {
471bf215546Sopenharmony_ci   find_platform(d_platform);
472bf215546Sopenharmony_ci   return CL_SUCCESS;
473bf215546Sopenharmony_ci} catch (error &e) {
474bf215546Sopenharmony_ci   return e.get();
475bf215546Sopenharmony_ci}
476bf215546Sopenharmony_ci
477bf215546Sopenharmony_ciCLOVER_API cl_int
478bf215546Sopenharmony_ciclGetProgramInfo(cl_program d_prog, cl_program_info param,
479bf215546Sopenharmony_ci                 size_t size, void *r_buf, size_t *r_size) try {
480bf215546Sopenharmony_ci   property_buffer buf { r_buf, size, r_size };
481bf215546Sopenharmony_ci   auto &prog = obj(d_prog);
482bf215546Sopenharmony_ci
483bf215546Sopenharmony_ci   switch (param) {
484bf215546Sopenharmony_ci   case CL_PROGRAM_REFERENCE_COUNT:
485bf215546Sopenharmony_ci      buf.as_scalar<cl_uint>() = prog.ref_count();
486bf215546Sopenharmony_ci      break;
487bf215546Sopenharmony_ci
488bf215546Sopenharmony_ci   case CL_PROGRAM_CONTEXT:
489bf215546Sopenharmony_ci      buf.as_scalar<cl_context>() = desc(prog.context());
490bf215546Sopenharmony_ci      break;
491bf215546Sopenharmony_ci
492bf215546Sopenharmony_ci   case CL_PROGRAM_NUM_DEVICES:
493bf215546Sopenharmony_ci      buf.as_scalar<cl_uint>() = (prog.devices().size() ?
494bf215546Sopenharmony_ci                                  prog.devices().size() :
495bf215546Sopenharmony_ci                                  prog.context().devices().size());
496bf215546Sopenharmony_ci      break;
497bf215546Sopenharmony_ci
498bf215546Sopenharmony_ci   case CL_PROGRAM_DEVICES:
499bf215546Sopenharmony_ci      buf.as_vector<cl_device_id>() = (prog.devices().size() ?
500bf215546Sopenharmony_ci                                       descs(prog.devices()) :
501bf215546Sopenharmony_ci                                       descs(prog.context().devices()));
502bf215546Sopenharmony_ci      break;
503bf215546Sopenharmony_ci
504bf215546Sopenharmony_ci   case CL_PROGRAM_SOURCE:
505bf215546Sopenharmony_ci      buf.as_string() = prog.source();
506bf215546Sopenharmony_ci      break;
507bf215546Sopenharmony_ci
508bf215546Sopenharmony_ci   case CL_PROGRAM_BINARY_SIZES:
509bf215546Sopenharmony_ci      buf.as_vector<size_t>() = map([&](const device &dev) {
510bf215546Sopenharmony_ci            return prog.build(dev).bin.size();
511bf215546Sopenharmony_ci         },
512bf215546Sopenharmony_ci         prog.devices());
513bf215546Sopenharmony_ci      break;
514bf215546Sopenharmony_ci
515bf215546Sopenharmony_ci   case CL_PROGRAM_BINARIES:
516bf215546Sopenharmony_ci      buf.as_matrix<unsigned char>() = map([&](const device &dev) {
517bf215546Sopenharmony_ci            std::stringbuf bin;
518bf215546Sopenharmony_ci            std::ostream s(&bin);
519bf215546Sopenharmony_ci            prog.build(dev).bin.serialize(s);
520bf215546Sopenharmony_ci            return bin.str();
521bf215546Sopenharmony_ci         },
522bf215546Sopenharmony_ci         prog.devices());
523bf215546Sopenharmony_ci      break;
524bf215546Sopenharmony_ci
525bf215546Sopenharmony_ci   case CL_PROGRAM_NUM_KERNELS:
526bf215546Sopenharmony_ci      buf.as_scalar<cl_uint>() = prog.symbols().size();
527bf215546Sopenharmony_ci      break;
528bf215546Sopenharmony_ci
529bf215546Sopenharmony_ci   case CL_PROGRAM_KERNEL_NAMES:
530bf215546Sopenharmony_ci      buf.as_string() = fold([](const std::string &a, const binary::symbol &s) {
531bf215546Sopenharmony_ci            return ((a.empty() ? "" : a + ";") + s.name);
532bf215546Sopenharmony_ci         }, std::string(), prog.symbols());
533bf215546Sopenharmony_ci      break;
534bf215546Sopenharmony_ci
535bf215546Sopenharmony_ci   case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT:
536bf215546Sopenharmony_ci   case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT:
537bf215546Sopenharmony_ci      buf.as_scalar<cl_bool>() = CL_FALSE;
538bf215546Sopenharmony_ci      break;
539bf215546Sopenharmony_ci
540bf215546Sopenharmony_ci   case CL_PROGRAM_IL:
541bf215546Sopenharmony_ci      if (prog.il_type() == program::il_type::spirv)
542bf215546Sopenharmony_ci         buf.as_vector<char>() = prog.source();
543bf215546Sopenharmony_ci      else if (r_size)
544bf215546Sopenharmony_ci         *r_size = 0u;
545bf215546Sopenharmony_ci      break;
546bf215546Sopenharmony_ci   default:
547bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
548bf215546Sopenharmony_ci   }
549bf215546Sopenharmony_ci
550bf215546Sopenharmony_ci   return CL_SUCCESS;
551bf215546Sopenharmony_ci
552bf215546Sopenharmony_ci} catch (error &e) {
553bf215546Sopenharmony_ci   return e.get();
554bf215546Sopenharmony_ci}
555bf215546Sopenharmony_ci
556bf215546Sopenharmony_ciCLOVER_API cl_int
557bf215546Sopenharmony_ciclGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
558bf215546Sopenharmony_ci                      cl_program_build_info param,
559bf215546Sopenharmony_ci                      size_t size, void *r_buf, size_t *r_size) try {
560bf215546Sopenharmony_ci   property_buffer buf { r_buf, size, r_size };
561bf215546Sopenharmony_ci   auto &prog = obj(d_prog);
562bf215546Sopenharmony_ci   auto &dev = obj(d_dev);
563bf215546Sopenharmony_ci
564bf215546Sopenharmony_ci   if (!count(dev, prog.context().devices()))
565bf215546Sopenharmony_ci      return CL_INVALID_DEVICE;
566bf215546Sopenharmony_ci
567bf215546Sopenharmony_ci   switch (param) {
568bf215546Sopenharmony_ci   case CL_PROGRAM_BUILD_STATUS:
569bf215546Sopenharmony_ci      buf.as_scalar<cl_build_status>() = prog.build(dev).status();
570bf215546Sopenharmony_ci      break;
571bf215546Sopenharmony_ci
572bf215546Sopenharmony_ci   case CL_PROGRAM_BUILD_OPTIONS:
573bf215546Sopenharmony_ci      buf.as_string() = prog.build(dev).opts;
574bf215546Sopenharmony_ci      break;
575bf215546Sopenharmony_ci
576bf215546Sopenharmony_ci   case CL_PROGRAM_BUILD_LOG:
577bf215546Sopenharmony_ci      buf.as_string() = prog.build(dev).log;
578bf215546Sopenharmony_ci      break;
579bf215546Sopenharmony_ci
580bf215546Sopenharmony_ci   case CL_PROGRAM_BINARY_TYPE:
581bf215546Sopenharmony_ci      buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
582bf215546Sopenharmony_ci      break;
583bf215546Sopenharmony_ci
584bf215546Sopenharmony_ci   case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:
585bf215546Sopenharmony_ci      buf.as_scalar<size_t>() = 0;
586bf215546Sopenharmony_ci      break;
587bf215546Sopenharmony_ci
588bf215546Sopenharmony_ci   default:
589bf215546Sopenharmony_ci      throw error(CL_INVALID_VALUE);
590bf215546Sopenharmony_ci   }
591bf215546Sopenharmony_ci
592bf215546Sopenharmony_ci   return CL_SUCCESS;
593bf215546Sopenharmony_ci
594bf215546Sopenharmony_ci} catch (error &e) {
595bf215546Sopenharmony_ci   return e.get();
596bf215546Sopenharmony_ci}
597