summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2019-04-10 10:24:46 +1000
committerDave Airlie <airlied@linux.ie>2020-09-01 21:44:07 +0000
commit5be69ed6b9c8746889aa517ddb96ab9cfebabd46 (patch)
treef334f4ca9ec26bd71d2cb46156c293269d1f6972
parent96fbe9a02243aed0ae1fc75002ebaaed3de9d687 (diff)
clover: handle libclc shader (v3)libclc-overwritten
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 <jenatali@microsoft.com> make disk cache optional
-rw-r--r--src/gallium/frontends/clover/core/device.cpp23
-rw-r--r--src/gallium/frontends/clover/core/device.hpp11
-rw-r--r--src/gallium/frontends/clover/meson.build8
-rw-r--r--src/gallium/frontends/clover/nir/invocation.cpp91
-rw-r--r--src/gallium/frontends/clover/nir/invocation.hpp10
-rw-r--r--src/gallium/frontends/clover/spirv/invocation.cpp39
-rw-r--r--src/gallium/frontends/clover/spirv/invocation.hpp8
7 files changed, 176 insertions, 14 deletions
diff --git a/src/gallium/frontends/clover/core/device.cpp b/src/gallium/frontends/clover/core/device.cpp
index 7f3d970ea5f..8ffc58fc2f2 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 <fstream>
using namespace clover;
@@ -45,14 +48,17 @@ 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)) {
+ load_clc();
+ clc_nir = lazy<std::shared_ptr<nir_shader>>([&] () { std::string log; return std::shared_ptr<nir_shader>(nir::libclc_spirv_to_nir(clc, *this, log), ralloc_free); });
return;
+ }
#endif
}
if (pipe)
@@ -61,12 +67,25 @@ 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)
pipe_loader_release(&ldev, 1);
}
+void
+device::load_clc() {
+#ifdef HAVE_CLOVER_SPIRV
+ if (supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED)) {
+ std::string log;
+ clc_cache = nir::create_clc_disk_cache();
+ clc = spirv::load_clc(*this);
+ }
+#endif
+}
+
bool
device::operator==(const device &dev) const {
return this == &dev;
diff --git a/src/gallium/frontends/clover/core/device.hpp b/src/gallium/frontends/clover/core/device.hpp
index 2cd3a54762e..38cd2646263 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;
@@ -41,6 +46,9 @@ namespace clover {
~device();
device(const device &dev) = delete;
+
+ void load_clc();
+
device &
operator=(const device &dev) = delete;
@@ -101,6 +109,9 @@ namespace clover {
return svm_support() & CL_DEVICE_SVM_FINE_GRAIN_SYSTEM;
}
+ module clc;
+ lazy<std::shared_ptr<struct nir_shader>> clc_nir;
+ struct 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..5812d3447b4 100644
--- a/src/gallium/frontends/clover/meson.build
+++ b/src/gallium/frontends/clover/meson.build
@@ -78,7 +78,9 @@ libclspirv = static_library(
'clspirv',
files('spirv/invocation.cpp', 'spirv/invocation.hpp'),
include_directories : clover_incs,
- cpp_args : [clover_opencl_cpp_args, clover_spirv_cpp_args],
+ cpp_args : [clover_opencl_cpp_args, clover_spirv_cpp_args,
+ '-DLIBCLC_LIBEXECDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('libexecdir')),
+ ],
gnu_symbol_visibility : 'hidden',
dependencies : [dep_spirv_tools],
)
@@ -88,7 +90,9 @@ libclnir = static_library(
files('nir/invocation.cpp', 'nir/invocation.hpp', 'nir/nir_lower_libclc.c', 'nir/nir_lower_libclc.h'),
include_directories : [clover_incs, inc_mesa],
dependencies : idep_nir,
- cpp_args : [clover_opencl_cpp_args, clover_spirv_cpp_args],
+ cpp_args : [clover_opencl_cpp_args, clover_spirv_cpp_args,
+ '-DLIBCLC_LIBEXECDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('libexecdir')),
+ ],
gnu_symbol_visibility : 'hidden',
)
diff --git a/src/gallium/frontends/clover/nir/invocation.cpp b/src/gallium/frontends/clover/nir/invocation.cpp
index 8ea6d3cf4d0..da9e62b2159 100644
--- a/src/gallium/frontends/clover/nir/invocation.cpp
+++ b/src/gallium/frontends/clover/nir/invocation.cpp
@@ -37,6 +37,10 @@
#include <compiler/spirv/nir_spirv.h>
#include <util/u_math.h>
+extern "C" {
+#include "nir_lower_libclc.h"
+}
+
using namespace clover;
#ifdef HAVE_CLOVER_SPIRV
@@ -131,8 +135,8 @@ clover_lower_nir(nir_shader *nir, std::vector<module::argument> &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;
@@ -156,6 +160,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 &section = mod.secs[0];
+ const auto *binary =
+ reinterpret_cast<const pipe_binary_program_header *>(section.data.data());
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(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_shader> nir = dev.clc_nir;
+ spirv_options.clc_shader = nir.get();
module m;
// We only insert one section.
@@ -189,6 +274,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..0aa848da514 100644
--- a/src/gallium/frontends/clover/nir/invocation.hpp
+++ b/src/gallium/frontends/clover/nir/invocation.hpp
@@ -24,12 +24,22 @@
#define CLOVER_NIR_INVOCATION_HPP
#include "core/module.hpp"
+#include <util/disk_cache.h>
+
+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..d86ff4cf905 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<char> &binary,
- const device &dev, std::string &r_log) {
+ const device &dev, std::string &r_log,
+ bool validate) {
std::vector<char> 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))
@@ -626,7 +627,6 @@ clover::spirv::link_program(const std::vector<module> &modules,
const char *message) {
r_log += format_validator_msg(level, source, position, message);
};
-
for (const auto &mod : modules) {
const auto &msec = find([](const module::section &sec) {
return sec.type == module::section::text_intermediate ||
@@ -675,7 +675,8 @@ clover::spirv::link_program(const std::vector<module> &modules,
bool
clover::spirv::is_valid_spirv(const std::vector<char> &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 +688,8 @@ clover::spirv::is_valid_spirv(const std::vector<char> &binary,
spvtools::SpirvTools spvTool(target_env);
spvTool.SetMessageConsumer(validator_consumer);
+ if (!validate)
+ return true;
return spvTool.Validate(reinterpret_cast<const uint32_t *>(binary.data()),
binary.size() / 4u);
}
@@ -731,13 +734,14 @@ clover::spirv::supported_versions() {
bool
clover::spirv::is_valid_spirv(const std::vector<char> &/*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<char> &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 +770,26 @@ clover::spirv::supported_versions() {
return {};
}
#endif
+
+module
+clover::spirv::load_clc(const device &dev)
+{
+ std::vector<char> 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<std::size_t>(length));
+ file.read(&ilfile.front(), static_cast<std::size_t>(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<char> &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<char> &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<uint32_t> supported_versions();
+
+ // Load the SPIR-V for the CLC module.
+ module load_clc(const device &dev);
}
}