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 <jenatali@microsoft.com> Reviewed-by: Karol Herbst <kherbst@redhat.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6035>
This commit is contained in:
@@ -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,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::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,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)
|
||||
|
@@ -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<std::shared_ptr<nir_shader>> clc_nir;
|
||||
disk_cache *clc_cache;
|
||||
private:
|
||||
pipe_screen *pipe;
|
||||
pipe_loader_device *ldev;
|
||||
|
@@ -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',
|
||||
|
@@ -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
|
||||
@@ -132,8 +136,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;
|
||||
@@ -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<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.
|
||||
@@ -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);
|
||||
|
@@ -24,10 +24,19 @@
|
||||
#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);
|
||||
}
|
||||
|
@@ -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))
|
||||
@@ -675,7 +676,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 +689,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 +735,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 +771,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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user