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 29 using namespace clover; 30 31 namespace { 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.context().devices()); 45 }, objs<allow_empty_tag>(d_devs, num_devs))) 46 throw error(CL_INVALID_DEVICE); 47 } 48 } 49 50 CLOVER_API cl_program 51 clCreateProgramWithSource(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 76 CLOVER_API cl_program 77 clCreateProgramWithBinary(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 131 CLOVER_API cl_program 132 clCreateProgramWithBuiltInKernels(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 153 CLOVER_API cl_int 154 clRetainProgram(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 162 CLOVER_API cl_int 163 clReleaseProgram(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 173 CLOVER_API cl_int 174 clBuildProgram(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 = (d_devs ? objs(d_devs, num_devs) : 180 ref_vector<device>(prog.context().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 } 190 191 return CL_SUCCESS; 192 193 } catch (error &e) { 194 return e.get(); 195 } 196 197 CLOVER_API cl_int 198 clCompileProgram(cl_program d_prog, cl_uint num_devs, 199 const cl_device_id *d_devs, const char *p_opts, 200 cl_uint num_headers, const cl_program *d_header_progs, 201 const char **header_names, 202 void (*pfn_notify)(cl_program, void *), 203 void *user_data) try { 204 auto &prog = obj(d_prog); 205 auto devs = (d_devs ? objs(d_devs, num_devs) : 206 ref_vector<device>(prog.context().devices())); 207 const auto opts = std::string(p_opts ? p_opts : "") + " " + 208 debug_get_option("CLOVER_EXTRA_COMPILE_OPTIONS", ""); 209 header_map headers; 210 211 validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data); 212 213 if (bool(num_headers) != bool(header_names)) 214 throw error(CL_INVALID_VALUE); 215 216 if (!prog.has_source) 217 throw error(CL_INVALID_OPERATION); 218 219 for_each([&](const char *name, const program &header) { 220 if (!header.has_source) 221 throw error(CL_INVALID_OPERATION); 222 223 if (!any_of(key_equals(name), headers)) 224 headers.push_back(std::pair<std::string, std::string>( 225 name, header.source())); 226 }, 227 range(header_names, num_headers), 228 objs<allow_empty_tag>(d_header_progs, num_headers)); 229 230 prog.compile(devs, opts, headers); 231 return CL_SUCCESS; 232 233 } catch (invalid_build_options_error &e) { 234 return CL_INVALID_COMPILER_OPTIONS; 235 236 } catch (build_error &e) { 237 return CL_COMPILE_PROGRAM_FAILURE; 238 239 } catch (error &e) { 240 return e.get(); 241 } 242 243 namespace { 244 ref_vector<device> 245 validate_link_devices(const ref_vector<program> &progs, 246 const ref_vector<device> &all_devs) { 247 std::vector<device *> devs; 248 249 for (auto &dev : all_devs) { 250 const auto has_binary = [&](const program &prog) { 251 const auto t = prog.build(dev).binary_type(); 252 return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT || 253 t == CL_PROGRAM_BINARY_TYPE_LIBRARY; 254 }; 255 256 // According to the CL 1.2 spec, when "all programs specified [..] 257 // contain a compiled binary or library for the device [..] a link is 258 // performed", 259 if (all_of(has_binary, progs)) 260 devs.push_back(&dev); 261 262 // otherwise if "none of the programs contain a compiled binary or 263 // library for that device [..] no link is performed. All other 264 // cases will return a CL_INVALID_OPERATION error." 265 else if (any_of(has_binary, progs)) 266 throw error(CL_INVALID_OPERATION); 267 } 268 269 return map(derefs(), devs); 270 } 271 } 272 273 CLOVER_API cl_program 274 clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs, 275 const char *p_opts, cl_uint num_progs, const cl_program *d_progs, 276 void (*pfn_notify) (cl_program, void *), void *user_data, 277 cl_int *r_errcode) try { 278 auto &ctx = obj(d_ctx); 279 const auto opts = std::string(p_opts ? p_opts : "") + " " + 280 debug_get_option("CLOVER_EXTRA_LINK_OPTIONS", ""); 281 auto progs = objs(d_progs, num_progs); 282 auto prog = create<program>(ctx); 283 auto devs = validate_link_devices(progs, 284 (d_devs ? objs(d_devs, num_devs) : 285 ref_vector<device>(ctx.devices()))); 286 287 validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data); 288 289 try { 290 prog().link(devs, opts, progs); 291 ret_error(r_errcode, CL_SUCCESS); 292 293 } catch (build_error &e) { 294 ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE); 295 } 296 297 return ret_object(prog); 298 299 } catch (invalid_build_options_error &e) { 300 ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS); 301 return NULL; 302 303 } catch (error &e) { 304 ret_error(r_errcode, e); 305 return NULL; 306 } 307 308 CLOVER_API cl_int 309 clUnloadCompiler() { 310 return CL_SUCCESS; 311 } 312 313 CLOVER_API cl_int 314 clUnloadPlatformCompiler(cl_platform_id d_platform) { 315 return CL_SUCCESS; 316 } 317 318 CLOVER_API cl_int 319 clGetProgramInfo(cl_program d_prog, cl_program_info param, 320 size_t size, void *r_buf, size_t *r_size) try { 321 property_buffer buf { r_buf, size, r_size }; 322 auto &prog = obj(d_prog); 323 324 switch (param) { 325 case CL_PROGRAM_REFERENCE_COUNT: 326 buf.as_scalar<cl_uint>() = prog.ref_count(); 327 break; 328 329 case CL_PROGRAM_CONTEXT: 330 buf.as_scalar<cl_context>() = desc(prog.context()); 331 break; 332 333 case CL_PROGRAM_NUM_DEVICES: 334 buf.as_scalar<cl_uint>() = (prog.devices().size() ? 335 prog.devices().size() : 336 prog.context().devices().size()); 337 break; 338 339 case CL_PROGRAM_DEVICES: 340 buf.as_vector<cl_device_id>() = (prog.devices().size() ? 341 descs(prog.devices()) : 342 descs(prog.context().devices())); 343 break; 344 345 case CL_PROGRAM_SOURCE: 346 buf.as_string() = prog.source(); 347 break; 348 349 case CL_PROGRAM_BINARY_SIZES: 350 buf.as_vector<size_t>() = map([&](const device &dev) { 351 return prog.build(dev).binary.size(); 352 }, 353 prog.devices()); 354 break; 355 356 case CL_PROGRAM_BINARIES: 357 buf.as_matrix<unsigned char>() = map([&](const device &dev) { 358 std::stringbuf bin; 359 std::ostream s(&bin); 360 prog.build(dev).binary.serialize(s); 361 return bin.str(); 362 }, 363 prog.devices()); 364 break; 365 366 case CL_PROGRAM_NUM_KERNELS: 367 buf.as_scalar<cl_uint>() = prog.symbols().size(); 368 break; 369 370 case CL_PROGRAM_KERNEL_NAMES: 371 buf.as_string() = fold([](const std::string &a, const module::symbol &s) { 372 return ((a.empty() ? "" : a + ";") + s.name); 373 }, std::string(), prog.symbols()); 374 break; 375 376 default: 377 throw error(CL_INVALID_VALUE); 378 } 379 380 return CL_SUCCESS; 381 382 } catch (error &e) { 383 return e.get(); 384 } 385 386 CLOVER_API cl_int 387 clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev, 388 cl_program_build_info param, 389 size_t size, void *r_buf, size_t *r_size) try { 390 property_buffer buf { r_buf, size, r_size }; 391 auto &prog = obj(d_prog); 392 auto &dev = obj(d_dev); 393 394 if (!count(dev, prog.context().devices())) 395 return CL_INVALID_DEVICE; 396 397 switch (param) { 398 case CL_PROGRAM_BUILD_STATUS: 399 buf.as_scalar<cl_build_status>() = prog.build(dev).status(); 400 break; 401 402 case CL_PROGRAM_BUILD_OPTIONS: 403 buf.as_string() = prog.build(dev).opts; 404 break; 405 406 case CL_PROGRAM_BUILD_LOG: 407 buf.as_string() = prog.build(dev).log; 408 break; 409 410 case CL_PROGRAM_BINARY_TYPE: 411 buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type(); 412 break; 413 414 default: 415 throw error(CL_INVALID_VALUE); 416 } 417 418 return CL_SUCCESS; 419 420 } catch (error &e) { 421 return e.get(); 422 } 423