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:
Dave Airlie
2019-04-10 10:24:46 +10:00
committed by Marge Bot
parent 14bc2dcaae
commit f33b417652
7 changed files with 159 additions and 14 deletions

View File

@@ -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)

View File

@@ -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;

View File

@@ -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',

View File

@@ -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 &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.
@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}