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