Path: blob/21.2-virgl/src/gallium/frontends/clover/api/program.cpp
4572 views
//1// Copyright 2012 Francisco Jerez2//3// Permission is hereby granted, free of charge, to any person obtaining a4// copy of this software and associated documentation files (the "Software"),5// to deal in the Software without restriction, including without limitation6// the rights to use, copy, modify, merge, publish, distribute, sublicense,7// and/or sell copies of the Software, and to permit persons to whom the8// Software is furnished to do so, subject to the following conditions:9//10// The above copyright notice and this permission notice shall be included in11// all copies or substantial portions of the Software.12//13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR17// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19// OTHER DEALINGS IN THE SOFTWARE.20//2122#include "api/util.hpp"23#include "core/program.hpp"24#include "spirv/invocation.hpp"25#include "util/u_debug.h"2627#include <limits>28#include <sstream>2930using namespace clover;3132namespace {3334std::string35build_options(const char *p_opts, const char *p_debug) {36auto opts = std::string(p_opts ? p_opts : "");37std::string extra_opts = debug_get_option(p_debug, "");3839return detokenize(std::vector<std::string>{opts, extra_opts}, " ");40}4142class build_notifier {43public:44build_notifier(cl_program prog,45void (*notifer)(cl_program, void *), void *data) :46prog_(prog), notifer(notifer), data_(data) { }4748~build_notifier() {49if (notifer)50notifer(prog_, data_);51}5253private:54cl_program prog_;55void (*notifer)(cl_program, void *);56void *data_;57};5859void60validate_build_common(const program &prog, cl_uint num_devs,61const cl_device_id *d_devs,62void (*pfn_notify)(cl_program, void *),63void *user_data) {64if (!pfn_notify && user_data)65throw error(CL_INVALID_VALUE);6667if (prog.kernel_ref_count())68throw error(CL_INVALID_OPERATION);6970if (any_of([&](const device &dev) {71return !count(dev, prog.devices());72}, objs<allow_empty_tag>(d_devs, num_devs)))73throw error(CL_INVALID_DEVICE);74}7576enum program::il_type77identify_and_validate_il(const std::string &il,78const cl_version opencl_version,79const context::notify_action ¬ify) {8081enum program::il_type il_type = program::il_type::none;8283#ifdef HAVE_CLOVER_SPIRV84if (spirv::is_binary_spirv(il)) {85std::string log;86if (!spirv::is_valid_spirv(il, opencl_version, log)) {87if (notify) {88notify(log.c_str());89}90throw error(CL_INVALID_VALUE);91}92il_type = program::il_type::spirv;93}94#endif9596return il_type;97}98}99100CLOVER_API cl_program101clCreateProgramWithSource(cl_context d_ctx, cl_uint count,102const char **strings, const size_t *lengths,103cl_int *r_errcode) try {104auto &ctx = obj(d_ctx);105std::string source;106107if (!count || !strings ||108any_of(is_zero(), range(strings, count)))109throw error(CL_INVALID_VALUE);110111// Concatenate all the provided fragments together112for (unsigned i = 0; i < count; ++i)113source += (lengths && lengths[i] ?114std::string(strings[i], strings[i] + lengths[i]) :115std::string(strings[i]));116117// ...and create a program object for them.118ret_error(r_errcode, CL_SUCCESS);119return new program(ctx, std::move(source), program::il_type::source);120121} catch (error &e) {122ret_error(r_errcode, e);123return NULL;124}125126CLOVER_API cl_program127clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,128const cl_device_id *d_devs,129const size_t *lengths,130const unsigned char **binaries,131cl_int *r_status, cl_int *r_errcode) try {132auto &ctx = obj(d_ctx);133auto devs = objs(d_devs, n);134135if (!lengths || !binaries)136throw error(CL_INVALID_VALUE);137138if (any_of([&](const device &dev) {139return !count(dev, ctx.devices());140}, devs))141throw error(CL_INVALID_DEVICE);142143// Deserialize the provided binaries,144std::vector<std::pair<cl_int, module>> result = map(145[](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {146if (!p || !l)147return { CL_INVALID_VALUE, {} };148149try {150std::stringbuf bin( std::string{ (char*)p, l } );151std::istream s(&bin);152153return { CL_SUCCESS, module::deserialize(s) };154155} catch (std::istream::failure &e) {156return { CL_INVALID_BINARY, {} };157}158},159range(binaries, n),160range(lengths, n));161162// update the status array,163if (r_status)164copy(map(keys(), result), r_status);165166if (any_of(key_equals(CL_INVALID_VALUE), result))167throw error(CL_INVALID_VALUE);168169if (any_of(key_equals(CL_INVALID_BINARY), result))170throw error(CL_INVALID_BINARY);171172// initialize a program object with them.173ret_error(r_errcode, CL_SUCCESS);174return new program(ctx, devs, map(values(), result));175176} catch (error &e) {177ret_error(r_errcode, e);178return NULL;179}180181cl_program182clover::CreateProgramWithILKHR(cl_context d_ctx, const void *il,183size_t length, cl_int *r_errcode) try {184auto &ctx = obj(d_ctx);185186if (!il || !length)187throw error(CL_INVALID_VALUE);188189// Compute the highest OpenCL version supported by all devices associated to190// the context. That is the version used for validating the SPIR-V binary.191cl_version min_opencl_version = std::numeric_limits<uint32_t>::max();192for (const device &dev : ctx.devices()) {193const cl_version opencl_version = dev.device_version();194min_opencl_version = std::min(opencl_version, min_opencl_version);195}196197const char *stream = reinterpret_cast<const char *>(il);198std::string binary(stream, stream + length);199const enum program::il_type il_type = identify_and_validate_il(binary,200min_opencl_version,201ctx.notify);202203if (il_type == program::il_type::none)204throw error(CL_INVALID_VALUE);205206// Initialize a program object with it.207ret_error(r_errcode, CL_SUCCESS);208return new program(ctx, std::move(binary), il_type);209210} catch (error &e) {211ret_error(r_errcode, e);212return NULL;213}214215CLOVER_API cl_program216clCreateProgramWithIL(cl_context d_ctx,217const void *il,218size_t length,219cl_int *r_errcode) {220return CreateProgramWithILKHR(d_ctx, il, length, r_errcode);221}222223CLOVER_API cl_program224clCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,225const cl_device_id *d_devs,226const char *kernel_names,227cl_int *r_errcode) try {228auto &ctx = obj(d_ctx);229auto devs = objs(d_devs, n);230231if (any_of([&](const device &dev) {232return !count(dev, ctx.devices());233}, devs))234throw error(CL_INVALID_DEVICE);235236// No currently supported built-in kernels.237throw error(CL_INVALID_VALUE);238239} catch (error &e) {240ret_error(r_errcode, e);241return NULL;242}243244245CLOVER_API cl_int246clRetainProgram(cl_program d_prog) try {247obj(d_prog).retain();248return CL_SUCCESS;249250} catch (error &e) {251return e.get();252}253254CLOVER_API cl_int255clReleaseProgram(cl_program d_prog) try {256if (obj(d_prog).release())257delete pobj(d_prog);258259return CL_SUCCESS;260261} catch (error &e) {262return e.get();263}264265CLOVER_API cl_int266clBuildProgram(cl_program d_prog, cl_uint num_devs,267const cl_device_id *d_devs, const char *p_opts,268void (*pfn_notify)(cl_program, void *),269void *user_data) try {270auto &prog = obj(d_prog);271auto devs =272(d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));273const auto opts = build_options(p_opts, "CLOVER_EXTRA_BUILD_OPTIONS");274275validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);276277auto notifier = build_notifier(d_prog, pfn_notify, user_data);278279if (prog.il_type() != program::il_type::none) {280prog.compile(devs, opts);281prog.link(devs, opts, { prog });282} else if (any_of([&](const device &dev){283return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;284}, devs)) {285// According to the OpenCL 1.2 specification, “if program is created286// with clCreateProgramWithBinary, then the program binary must be an287// executable binary (not a compiled binary or library).”288throw error(CL_INVALID_BINARY);289}290291return CL_SUCCESS;292293} catch (error &e) {294return e.get();295}296297CLOVER_API cl_int298clCompileProgram(cl_program d_prog, cl_uint num_devs,299const cl_device_id *d_devs, const char *p_opts,300cl_uint num_headers, const cl_program *d_header_progs,301const char **header_names,302void (*pfn_notify)(cl_program, void *),303void *user_data) try {304auto &prog = obj(d_prog);305auto devs =306(d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));307const auto opts = build_options(p_opts, "CLOVER_EXTRA_COMPILE_OPTIONS");308header_map headers;309310validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);311312auto notifier = build_notifier(d_prog, pfn_notify, user_data);313314if (bool(num_headers) != bool(header_names))315throw error(CL_INVALID_VALUE);316317if (prog.il_type() == program::il_type::none)318throw error(CL_INVALID_OPERATION);319320for_each([&](const char *name, const program &header) {321if (header.il_type() == program::il_type::none)322throw error(CL_INVALID_OPERATION);323324if (!any_of(key_equals(name), headers))325headers.push_back(std::pair<std::string, std::string>(326name, header.source()));327},328range(header_names, num_headers),329objs<allow_empty_tag>(d_header_progs, num_headers));330331prog.compile(devs, opts, headers);332return CL_SUCCESS;333334} catch (invalid_build_options_error &e) {335return CL_INVALID_COMPILER_OPTIONS;336337} catch (build_error &e) {338return CL_COMPILE_PROGRAM_FAILURE;339340} catch (error &e) {341return e.get();342}343344namespace {345ref_vector<device>346validate_link_devices(const ref_vector<program> &progs,347const ref_vector<device> &all_devs,348const std::string &opts) {349std::vector<device *> devs;350const bool create_library =351opts.find("-create-library") != std::string::npos;352const bool enable_link_options =353opts.find("-enable-link-options") != std::string::npos;354const bool has_link_options =355opts.find("-cl-denorms-are-zero") != std::string::npos ||356opts.find("-cl-no-signed-zeroes") != std::string::npos ||357opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||358opts.find("-cl-finite-math-only") != std::string::npos ||359opts.find("-cl-fast-relaxed-math") != std::string::npos ||360opts.find("-cl-no-subgroup-ifp") != std::string::npos;361362// According to the OpenCL 1.2 specification, "[the363// -enable-link-options] option must be specified with the364// create-library option".365if (enable_link_options && !create_library)366throw error(CL_INVALID_LINKER_OPTIONS);367368// According to the OpenCL 1.2 specification, "the369// [program linking options] can be specified when linking a program370// executable".371if (has_link_options && create_library)372throw error(CL_INVALID_LINKER_OPTIONS);373374for (auto &dev : all_devs) {375const auto has_binary = [&](const program &prog) {376const auto t = prog.build(dev).binary_type();377return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||378t == CL_PROGRAM_BINARY_TYPE_LIBRARY;379};380381// According to the OpenCL 1.2 specification, a library is made of382// “compiled binaries specified in input_programs argument to383// clLinkProgram“; compiled binaries does not refer to libraries:384// “input_programs is an array of program objects that are compiled385// binaries or libraries that are to be linked to create the program386// executable”.387if (create_library && any_of([&](const program &prog) {388const auto t = prog.build(dev).binary_type();389return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;390}, progs))391throw error(CL_INVALID_OPERATION);392393// According to the CL 1.2 spec, when "all programs specified [..]394// contain a compiled binary or library for the device [..] a link is395// performed",396else if (all_of(has_binary, progs))397devs.push_back(&dev);398399// otherwise if "none of the programs contain a compiled binary or400// library for that device [..] no link is performed. All other401// cases will return a CL_INVALID_OPERATION error."402else if (any_of(has_binary, progs))403throw error(CL_INVALID_OPERATION);404405// According to the OpenCL 1.2 specification, "[t]he linker may apply406// [program linking options] to all compiled program objects407// specified to clLinkProgram. The linker may apply these options408// only to libraries which were created with the409// -enable-link-option."410else if (has_link_options && any_of([&](const program &prog) {411const auto t = prog.build(dev).binary_type();412return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||413(t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&414prog.build(dev).opts.find("-enable-link-options") !=415std::string::npos));416}, progs))417throw error(CL_INVALID_LINKER_OPTIONS);418}419420return map(derefs(), devs);421}422}423424CLOVER_API cl_program425clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,426const char *p_opts, cl_uint num_progs, const cl_program *d_progs,427void (*pfn_notify) (cl_program, void *), void *user_data,428cl_int *r_errcode) try {429auto &ctx = obj(d_ctx);430const auto opts = build_options(p_opts, "CLOVER_EXTRA_LINK_OPTIONS");431auto progs = objs(d_progs, num_progs);432auto all_devs =433(d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));434auto prog = create<program>(ctx, all_devs);435auto r_prog = ret_object(prog);436437auto notifier = build_notifier(r_prog, pfn_notify, user_data);438439auto devs = validate_link_devices(progs, all_devs, opts);440441validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);442443try {444prog().link(devs, opts, progs);445ret_error(r_errcode, CL_SUCCESS);446447} catch (build_error &e) {448ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);449}450451return r_prog;452453} catch (invalid_build_options_error &e) {454ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);455return NULL;456457} catch (error &e) {458ret_error(r_errcode, e);459return NULL;460}461462CLOVER_API cl_int463clUnloadCompiler() {464return CL_SUCCESS;465}466467CLOVER_API cl_int468clUnloadPlatformCompiler(cl_platform_id d_platform) {469return CL_SUCCESS;470}471472CLOVER_API cl_int473clGetProgramInfo(cl_program d_prog, cl_program_info param,474size_t size, void *r_buf, size_t *r_size) try {475property_buffer buf { r_buf, size, r_size };476auto &prog = obj(d_prog);477478switch (param) {479case CL_PROGRAM_REFERENCE_COUNT:480buf.as_scalar<cl_uint>() = prog.ref_count();481break;482483case CL_PROGRAM_CONTEXT:484buf.as_scalar<cl_context>() = desc(prog.context());485break;486487case CL_PROGRAM_NUM_DEVICES:488buf.as_scalar<cl_uint>() = (prog.devices().size() ?489prog.devices().size() :490prog.context().devices().size());491break;492493case CL_PROGRAM_DEVICES:494buf.as_vector<cl_device_id>() = (prog.devices().size() ?495descs(prog.devices()) :496descs(prog.context().devices()));497break;498499case CL_PROGRAM_SOURCE:500buf.as_string() = prog.source();501break;502503case CL_PROGRAM_BINARY_SIZES:504buf.as_vector<size_t>() = map([&](const device &dev) {505return prog.build(dev).binary.size();506},507prog.devices());508break;509510case CL_PROGRAM_BINARIES:511buf.as_matrix<unsigned char>() = map([&](const device &dev) {512std::stringbuf bin;513std::ostream s(&bin);514prog.build(dev).binary.serialize(s);515return bin.str();516},517prog.devices());518break;519520case CL_PROGRAM_NUM_KERNELS:521buf.as_scalar<cl_uint>() = prog.symbols().size();522break;523524case CL_PROGRAM_KERNEL_NAMES:525buf.as_string() = fold([](const std::string &a, const module::symbol &s) {526return ((a.empty() ? "" : a + ";") + s.name);527}, std::string(), prog.symbols());528break;529530case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT:531case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT:532buf.as_scalar<cl_bool>() = CL_FALSE;533break;534535case CL_PROGRAM_IL:536if (prog.il_type() != program::il_type::none)537buf.as_string() = prog.source();538else if (r_size)539*r_size = 0u;540break;541default:542throw error(CL_INVALID_VALUE);543}544545return CL_SUCCESS;546547} catch (error &e) {548return e.get();549}550551CLOVER_API cl_int552clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,553cl_program_build_info param,554size_t size, void *r_buf, size_t *r_size) try {555property_buffer buf { r_buf, size, r_size };556auto &prog = obj(d_prog);557auto &dev = obj(d_dev);558559if (!count(dev, prog.context().devices()))560return CL_INVALID_DEVICE;561562switch (param) {563case CL_PROGRAM_BUILD_STATUS:564buf.as_scalar<cl_build_status>() = prog.build(dev).status();565break;566567case CL_PROGRAM_BUILD_OPTIONS:568buf.as_string() = prog.build(dev).opts;569break;570571case CL_PROGRAM_BUILD_LOG:572buf.as_string() = prog.build(dev).log;573break;574575case CL_PROGRAM_BINARY_TYPE:576buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();577break;578579case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:580buf.as_scalar<size_t>() = 0;581break;582583default:584throw error(CL_INVALID_VALUE);585}586587return CL_SUCCESS;588589} catch (error &e) {590return e.get();591}592593594