Home | History | Annotate | Download | only in api
      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