From f33b417652ceae711cc34601f51d62beec2d22f1 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 10 Apr 2019 10:24:46 +1000 Subject: [PATCH] clover: handle libclc shader (v3) This works by taking the spirv produced by libclc which contains a lot of mangled function entrypoints identified with LinkageAttribute decorations. This patch just sets up clover to load the libclc blob and convert it to library nir, and support inlining application nir with calls to libclc. v2: Add a disk cache support for this object, to avoid the spirv parsing overheads each time. move spirv->nir to lazy instantiation to avoid the mess with glsl types and constructor ordering. v3: make disk cache optional v1-Reviewed-by: Jesse Natalie Reviewed-by: Karol Herbst Part-of: --- src/gallium/frontends/clover/core/device.cpp | 13 ++- src/gallium/frontends/clover/core/device.hpp | 8 ++ src/gallium/frontends/clover/meson.build | 6 +- .../frontends/clover/nir/invocation.cpp | 91 ++++++++++++++++++- .../frontends/clover/nir/invocation.hpp | 9 ++ .../frontends/clover/spirv/invocation.cpp | 38 +++++++- .../frontends/clover/spirv/invocation.hpp | 8 +- 7 files changed, 159 insertions(+), 14 deletions(-) diff --git a/src/gallium/frontends/clover/core/device.cpp b/src/gallium/frontends/clover/core/device.cpp index 7f3d970ea5f..dc0766c4243 100644 --- a/src/gallium/frontends/clover/core/device.cpp +++ b/src/gallium/frontends/clover/core/device.cpp @@ -28,6 +28,9 @@ #include "pipe/p_state.h" #include "util/bitscan.h" #include "util/u_debug.h" +#include "spirv/invocation.hpp" +#include "nir/invocation.hpp" +#include using namespace clover; @@ -45,14 +48,18 @@ namespace { } device::device(clover::platform &platform, pipe_loader_device *ldev) : - platform(platform), ldev(ldev) { + platform(platform), clc_cache(NULL), ldev(ldev) { pipe = pipe_loader_create_screen(ldev); if (pipe && pipe->get_param(pipe, PIPE_CAP_COMPUTE)) { if (supports_ir(PIPE_SHADER_IR_NATIVE)) return; #ifdef HAVE_CLOVER_SPIRV - if (supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED)) + if (supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED)) { + clc_cache = nir::create_clc_disk_cache(); + clc = spirv::load_clc(*this); + clc_nir = lazy>([&] () { std::string log; return std::shared_ptr(nir::libclc_spirv_to_nir(clc, *this, log), ralloc_free); }); return; + } #endif } if (pipe) @@ -61,6 +68,8 @@ device::device(clover::platform &platform, pipe_loader_device *ldev) : } device::~device() { + if (clc_cache) + disk_cache_destroy(clc_cache); if (pipe) pipe->destroy(pipe); if (ldev) diff --git a/src/gallium/frontends/clover/core/device.hpp b/src/gallium/frontends/clover/core/device.hpp index 2cd3a54762e..2599787716a 100644 --- a/src/gallium/frontends/clover/core/device.hpp +++ b/src/gallium/frontends/clover/core/device.hpp @@ -28,8 +28,13 @@ #include "core/object.hpp" #include "core/format.hpp" +#include "core/module.hpp" +#include "util/lazy.hpp" #include "pipe-loader/pipe_loader.h" +struct nir_shader; +struct disk_cache; + namespace clover { class platform; class root_resource; @@ -101,6 +106,9 @@ namespace clover { return svm_support() & CL_DEVICE_SVM_FINE_GRAIN_SYSTEM; } + module clc; + lazy> clc_nir; + disk_cache *clc_cache; private: pipe_screen *pipe; pipe_loader_device *ldev; diff --git a/src/gallium/frontends/clover/meson.build b/src/gallium/frontends/clover/meson.build index 64c77f5f3ff..49ab9d5c04d 100644 --- a/src/gallium/frontends/clover/meson.build +++ b/src/gallium/frontends/clover/meson.build @@ -25,7 +25,9 @@ clover_opencl_cpp_args = [ '-DCL_USE_DEPRECATED_OPENCL_1_1_APIS', '-DCL_USE_DEPRECATED_OPENCL_1_2_APIS', '-DCL_USE_DEPRECATED_OPENCL_2_0_APIS', - '-DCL_USE_DEPRECATED_OPENCL_2_1_APIS' + '-DCL_USE_DEPRECATED_OPENCL_2_1_APIS', + '-DLIBCLC_INCLUDEDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('includedir')), + '-DLIBCLC_LIBEXECDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('libexecdir')) ] clover_spirv_cpp_args = [] clover_incs = [inc_include, inc_src, inc_gallium, inc_gallium_aux] @@ -63,8 +65,6 @@ libclllvm = static_library( clover_cpp_args, clover_opencl_cpp_args, clover_spirv_cpp_args, - '-DLIBCLC_INCLUDEDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('includedir')), - '-DLIBCLC_LIBEXECDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('libexecdir')), '-DCLANG_RESOURCE_DIR="@0@"'.format(join_paths( dep_llvm.get_configtool_variable('libdir'), 'clang', dep_llvm.version(), 'include', diff --git a/src/gallium/frontends/clover/nir/invocation.cpp b/src/gallium/frontends/clover/nir/invocation.cpp index f2c53fc4b22..5d737d03c56 100644 --- a/src/gallium/frontends/clover/nir/invocation.cpp +++ b/src/gallium/frontends/clover/nir/invocation.cpp @@ -37,6 +37,10 @@ #include #include +extern "C" { +#include "nir_lower_libclc.h" +} + using namespace clover; #ifdef HAVE_CLOVER_SPIRV @@ -132,8 +136,8 @@ clover_lower_nir(nir_shader *nir, std::vector &args, uint32_t clover_lower_nir_filter, clover_lower_nir_instr, &state); } -module clover::nir::spirv_to_nir(const module &mod, const device &dev, - std::string &r_log) +static spirv_to_nir_options +create_spirv_options(const device &dev, std::string &r_log) { struct spirv_to_nir_options spirv_options = {}; spirv_options.environment = NIR_SPIRV_OPENCL; @@ -157,6 +161,87 @@ module clover::nir::spirv_to_nir(const module &mod, const device &dev, spirv_options.caps.int64_atomics = dev.has_int64_atomics(); spirv_options.debug.func = &debug_function; spirv_options.debug.private_data = &r_log; + return spirv_options; +} + +struct disk_cache *clover::nir::create_clc_disk_cache(void) +{ + struct mesa_sha1 ctx; + unsigned char sha1[20]; + char cache_id[20 * 2 + 1]; + _mesa_sha1_init(&ctx); + + if (!disk_cache_get_function_identifier((void *)clover::nir::create_clc_disk_cache, &ctx)) + return NULL; + + _mesa_sha1_final(&ctx, sha1); + + disk_cache_format_hex_id(cache_id, sha1, 20 * 2); + return disk_cache_create("clover-clc", cache_id, 0); +} + +nir_shader *clover::nir::libclc_spirv_to_nir(const module &mod, const device &dev, + std::string &r_log) +{ + spirv_to_nir_options spirv_options = create_spirv_options(dev, r_log); + spirv_options.create_library = true; + + auto §ion = mod.secs[0]; + const auto *binary = + reinterpret_cast(section.data.data()); + const uint32_t *data = reinterpret_cast(binary->blob); + const size_t num_words = binary->num_bytes / 4; + auto *compiler_options = dev_get_nir_compiler_options(dev); + unsigned char clc_cache_key[20]; + unsigned char sha1[CACHE_KEY_SIZE]; + /* caching ftw. */ + struct mesa_sha1 ctx; + + size_t binary_size = 0; + uint8_t *buffer = NULL; + if (dev.clc_cache) { + _mesa_sha1_init(&ctx); + _mesa_sha1_update(&ctx, data, num_words * 4); + _mesa_sha1_final(&ctx, clc_cache_key); + + disk_cache_compute_key(dev.clc_cache, clc_cache_key, 20, sha1); + + buffer = (uint8_t *)disk_cache_get(dev.clc_cache, sha1, &binary_size); + } + + nir_shader *nir; + if (!buffer) { + nir = spirv_to_nir(data, num_words, nullptr, 0, + MESA_SHADER_KERNEL, "clcspirv", + &spirv_options, compiler_options); + nir_validate_shader(nir, "clover-libclc"); + nir->info.internal = true; + NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp); + NIR_PASS_V(nir, nir_lower_returns); + + if (dev.clc_cache) { + struct blob blob = { 0 }; + blob_init(&blob); + nir_serialize(&blob, nir, true); + disk_cache_put(dev.clc_cache, sha1, blob.data, blob.size, NULL); + blob_finish(&blob); + } + } else { + struct blob_reader blob_read; + blob_reader_init(&blob_read, buffer, binary_size); + nir = nir_deserialize(NULL, compiler_options, &blob_read); + free(buffer); + } + + return nir; +} + +module clover::nir::spirv_to_nir(const module &mod, const device &dev, + std::string &r_log) +{ + spirv_to_nir_options spirv_options = create_spirv_options(dev, r_log); + std::shared_ptr nir = dev.clc_nir; + spirv_options.clc_shader = nir.get(); module m; // We only insert one section. @@ -190,6 +275,8 @@ module clover::nir::spirv_to_nir(const module &mod, const device &dev, // according to the comment on nir_inline_functions NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp); NIR_PASS_V(nir, nir_lower_returns); + NIR_PASS_V(nir, nir_lower_libclc, spirv_options.clc_shader); + NIR_PASS_V(nir, nir_inline_functions); NIR_PASS_V(nir, nir_copy_prop); NIR_PASS_V(nir, nir_opt_deref); diff --git a/src/gallium/frontends/clover/nir/invocation.hpp b/src/gallium/frontends/clover/nir/invocation.hpp index 41407a79765..62ae4ecd247 100644 --- a/src/gallium/frontends/clover/nir/invocation.hpp +++ b/src/gallium/frontends/clover/nir/invocation.hpp @@ -24,10 +24,19 @@ #define CLOVER_NIR_INVOCATION_HPP #include "core/module.hpp" +#include + +struct nir_shader; namespace clover { class device; namespace nir { + // converts libclc spirv into nir + nir_shader *libclc_spirv_to_nir(const module &mod, const device &dev, + std::string &r_log); + + struct disk_cache *create_clc_disk_cache(void); + // converts a given spirv module to nir module spirv_to_nir(const module &mod, const device &dev, std::string &r_log); } diff --git a/src/gallium/frontends/clover/spirv/invocation.cpp b/src/gallium/frontends/clover/spirv/invocation.cpp index c3404f38b80..9b33e66d373 100644 --- a/src/gallium/frontends/clover/spirv/invocation.cpp +++ b/src/gallium/frontends/clover/spirv/invocation.cpp @@ -569,10 +569,11 @@ namespace { module clover::spirv::compile_program(const std::vector &binary, - const device &dev, std::string &r_log) { + const device &dev, std::string &r_log, + bool validate) { std::vector source = spirv_to_cpu(binary); - if (!is_valid_spirv(source, dev.device_version(), r_log)) + if (!is_valid_spirv(source, dev.device_version(), r_log, validate)) throw build_error(); if (!check_capabilities(dev, source, r_log)) @@ -675,7 +676,8 @@ clover::spirv::link_program(const std::vector &modules, bool clover::spirv::is_valid_spirv(const std::vector &binary, const std::string &opencl_version, - std::string &r_log) { + std::string &r_log, + bool validate) { auto const validator_consumer = [&r_log](spv_message_level_t level, const char *source, const spv_position_t &position, const char *message) { @@ -687,6 +689,8 @@ clover::spirv::is_valid_spirv(const std::vector &binary, spvtools::SpirvTools spvTool(target_env); spvTool.SetMessageConsumer(validator_consumer); + if (!validate) + return true; return spvTool.Validate(reinterpret_cast(binary.data()), binary.size() / 4u); } @@ -731,13 +735,14 @@ clover::spirv::supported_versions() { bool clover::spirv::is_valid_spirv(const std::vector &/*binary*/, const std::string &/*opencl_version*/, - std::string &/*r_log*/) { + std::string &/*r_log*/, bool /*validate*/) { return false; } module clover::spirv::compile_program(const std::vector &binary, - const device &dev, std::string &r_log) { + const device &dev, std::string &r_log, + bool validate) { r_log += "SPIR-V support in clover is not enabled.\n"; throw build_error(); } @@ -766,3 +771,26 @@ clover::spirv::supported_versions() { return {}; } #endif + +module +clover::spirv::load_clc(const device &dev) +{ + std::vector ilfile; + std::ifstream file; + std::string name32 = "spirv-mesa3d-.spv"; + std::string name64 = "spirv64-mesa3d-.spv"; + file.open(LIBCLC_LIBEXECDIR + (dev.address_bits() == 64 ? name64 : name32), std::ifstream::in | std::ifstream::binary); + if (!file.good()) + throw error(CL_COMPILER_NOT_AVAILABLE); + + file.seekg(0, std::ios::end); + std::streampos length(file.tellg()); + if (length) { + file.seekg(0, std::ios::beg); + ilfile.resize(static_cast(length)); + file.read(&ilfile.front(), static_cast(length)); + } + + std::string log; + return spirv::compile_program(ilfile, dev, log, false); +} diff --git a/src/gallium/frontends/clover/spirv/invocation.hpp b/src/gallium/frontends/clover/spirv/invocation.hpp index 27f8d8c1934..9d954671183 100644 --- a/src/gallium/frontends/clover/spirv/invocation.hpp +++ b/src/gallium/frontends/clover/spirv/invocation.hpp @@ -38,11 +38,12 @@ namespace clover { // warnings and errors are appended to |r_log|. bool is_valid_spirv(const std::vector &binary, const std::string &opencl_version, - std::string &r_log); + std::string &r_log, bool validate = true); // Creates a clover module out of the given SPIR-V binary. module compile_program(const std::vector &binary, - const device &dev, std::string &r_log); + const device &dev, std::string &r_log, + bool validate = true); // Combines multiple clover modules into a single one, resolving // link dependencies between them. @@ -59,6 +60,9 @@ namespace clover { // Returns a vector (sorted in increasing order) of supported SPIR-V // versions. std::vector supported_versions(); + + // Load the SPIR-V for the CLC module. + module load_clc(const device &dev); } }