spirv: Add a shared libclc loader

Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7034>
This commit is contained in:
Jason Ekstrand 2020-10-06 11:26:07 -05:00 committed by Marge Bot
parent 5ffdb1092b
commit ef453f5439
5 changed files with 378 additions and 1 deletions

View File

@ -156,6 +156,13 @@ option(
value : false,
description : 'build gallium "clover" OpenCL frontend with SPIR-V binary support.',
)
option(
'static-libclc',
type : 'array',
value : [],
choices : ['spirv', 'spirv64', 'all'],
description : 'Link libclc SPIR-V statically.',
)
option(
'd3d-drivers-path',
type : 'string',

View File

@ -376,6 +376,7 @@ SPIRV_FILES = \
spirv/GLSL.ext.AMD.h \
spirv/GLSL.std.450.h \
spirv/gl_spirv.c \
spirv/nir_load_libclc.c \
spirv/nir_spirv.h \
spirv/OpenCL.std.h \
spirv/spirv.h \

View File

@ -251,6 +251,7 @@ files_libnir = files(
'../spirv/GLSL.ext.AMD.h',
'../spirv/GLSL.std.450.h',
'../spirv/gl_spirv.c',
'../spirv/nir_load_libclc.c',
'../spirv/nir_spirv.h',
'../spirv/OpenCL.std.h',
'../spirv/spirv.h',
@ -266,13 +267,62 @@ files_libnir = files(
'../spirv/vtn_variables.c',
)
_libnir_args = []
if dep_clc.found()
_basedir = dep_clc.get_variable(pkgconfig : 'libexecdir')
_static_libclc = get_option('static-libclc')
if _static_libclc.length() > 0
if _static_libclc.contains('all')
_static_libclc = ['spirv', 'spirv64']
endif
prog_zstd = find_program('zstd', required : false)
_zstd_static_libclc = dep_zstd.found() and prog_zstd.found()
if _zstd_static_libclc
_libnir_args += '-DHAVE_STATIC_LIBCLC_ZSTD'
endif
foreach s : _static_libclc
_libnir_args += '-DHAVE_STATIC_LIBCLC_@0@'.format(s.to_upper())
f = '@0@-mesa3d-.spv'.format(s)
_libclc_file = _basedir / f
if _zstd_static_libclc
_libclc_file = custom_target(
'@0@.zstd'.format(f),
command : [prog_zstd, '-f', '@INPUT@', '-o', '@OUTPUT@'],
input : [_libclc_file],
output : '@0@.zstd'.format(f),
)
endif
files_libnir += custom_target(
'@0@.h'.format(f),
command : [
prog_python, files_xxd, '-b', '@INPUT@', '@OUTPUT@',
'-n', 'libclc_@0@_mesa3d_spv'.format(s),
],
input : [_libclc_file],
output : '@0@.h'.format(f),
depend_files : files_xxd,
)
endforeach
else
_libnir_args += ['-DDYNAMIC_LIBCLC_PATH="@0@/"'.format(_basedir)]
if not cc.has_function('mmap')
error('mmap required for dynamic libCLC loading')
endif
endif
endif
_libnir = static_library(
'nir',
[files_libnir, spirv_info_c, nir_opt_algebraic_c, nir_opcodes_c,
nir_opcodes_h, nir_constant_expressions_c, nir_builder_opcodes_h,
vtn_gather_types_c, nir_intrinsics_c, nir_intrinsics_h],
include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, inc_gallium_aux, inc_compiler, include_directories('../spirv')],
c_args : [c_msvc_compat_args, no_override_init_args],
c_args : [c_msvc_compat_args, no_override_init_args, _libnir_args],
gnu_symbol_visibility : 'hidden',
link_with : libcompiler,
build_by_default : false,

View File

@ -0,0 +1,310 @@
/*
* Copyright © 2020 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "nir.h"
#include "nir_serialize.h"
#include "nir_spirv.h"
#include "util/mesa-sha1.h"
#ifdef DYNAMIC_LIBCLC_PATH
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
#ifdef HAVE_STATIC_LIBCLC_ZSTD
#include <zstd.h>
#endif
#ifdef HAVE_STATIC_LIBCLC_SPIRV
#include "spirv-mesa3d-.spv.h"
#endif
#ifdef HAVE_STATIC_LIBCLC_SPIRV64
#include "spirv64-mesa3d-.spv.h"
#endif
struct clc_file {
unsigned bit_size;
const char *static_data;
size_t static_data_size;
const char *sys_path;
};
static const struct clc_file libclc_files[] = {
{
.bit_size = 32,
#ifdef HAVE_STATIC_LIBCLC_SPIRV
.static_data = libclc_spirv_mesa3d_spv,
.static_data_size = sizeof(libclc_spirv_mesa3d_spv),
#endif
#ifdef DYNAMIC_LIBCLC_PATH
.sys_path = DYNAMIC_LIBCLC_PATH "spirv-mesa3d-.spv",
#endif
},
{
.bit_size = 64,
#ifdef HAVE_STATIC_LIBCLC_SPIRV64
.static_data = libclc_spirv64_mesa3d_spv,
.static_data_size = sizeof(libclc_spirv64_mesa3d_spv),
#endif
#ifdef DYNAMIC_LIBCLC_PATH
.sys_path = DYNAMIC_LIBCLC_PATH "spirv64-mesa3d-.spv",
#endif
},
};
static const struct clc_file *
get_libclc_file(unsigned ptr_bit_size)
{
assert(ptr_bit_size == 32 || ptr_bit_size == 64);
return &libclc_files[ptr_bit_size / 64];
}
struct clc_data {
const struct clc_file *file;
unsigned char cache_key[20];
int fd;
const void *data;
size_t size;
};
static bool
open_clc_data(struct clc_data *clc, unsigned ptr_bit_size)
{
memset(clc, 0, sizeof(*clc));
clc->file = get_libclc_file(ptr_bit_size);
clc->fd = -1;
if (clc->file->static_data) {
snprintf((char *)clc->cache_key, sizeof(clc->cache_key),
"libclc-spirv%d", ptr_bit_size);
return true;
}
#ifdef DYNAMIC_LIBCLC_PATH
if (clc->file->sys_path != NULL) {
int fd = open(clc->file->sys_path, O_RDONLY);
if (fd < 0)
return false;
struct stat stat;
int ret = fstat(fd, &stat);
if (ret < 0) {
fprintf(stderr, "fstat failed on %s: %m\n", clc->file->sys_path);
close(fd);
return false;
}
struct mesa_sha1 ctx;
_mesa_sha1_init(&ctx);
_mesa_sha1_update(&ctx, clc->file->sys_path, strlen(clc->file->sys_path));
_mesa_sha1_update(&ctx, &stat.st_mtim, sizeof(stat.st_mtim));
_mesa_sha1_final(&ctx, clc->cache_key);
clc->fd = fd;
return true;
}
#endif
return false;
}
#define SPIRV_WORD_SIZE 4
static bool
map_clc_data(struct clc_data *clc)
{
if (clc->file->static_data) {
#ifdef HAVE_STATIC_LIBCLC_ZSTD
unsigned long long cmp_size =
ZSTD_getFrameContentSize(clc->file->static_data,
clc->file->static_data_size);
if (cmp_size == ZSTD_CONTENTSIZE_UNKNOWN ||
cmp_size == ZSTD_CONTENTSIZE_ERROR) {
fprintf(stderr, "Could not determine the decompressed size of the "
"libclc SPIR-V\n");
return false;
}
size_t frame_size =
ZSTD_findFrameCompressedSize(clc->file->static_data,
clc->file->static_data_size);
if (ZSTD_isError(frame_size)) {
fprintf(stderr, "Could not determine the size of the first ZSTD frame "
"when decompressing libclc SPIR-V: %s\n",
ZSTD_getErrorName(frame_size));
return false;
}
void *dest = malloc(cmp_size + 1);
size_t size = ZSTD_decompress(dest, cmp_size, clc->file->static_data,
frame_size);
if (ZSTD_isError(size)) {
free(dest);
fprintf(stderr, "Error decompressing libclc SPIR-V: %s\n",
ZSTD_getErrorName(size));
return false;
}
clc->data = dest;
clc->size = size;
#else
clc->data = clc->file->static_data;
clc->size = clc->file->static_data_size;
#endif
return true;
}
#ifdef DYNAMIC_LIBCLC_PATH
if (clc->file->sys_path != NULL) {
off_t len = lseek(clc->fd, 0, SEEK_END);
if (len % SPIRV_WORD_SIZE != 0) {
fprintf(stderr, "File length isn't a multiple of the word size\n");
return false;
}
clc->size = len;
clc->data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, clc->fd, 0);
if (clc->data == MAP_FAILED) {
fprintf(stderr, "Failed to mmap libclc SPIR-V: %m\n");
return false;
}
return true;
}
#endif
return true;
}
static void
close_clc_data(struct clc_data *clc)
{
if (clc->file->static_data) {
#ifdef HAVE_STATIC_LIBCLC_ZSTD
free((void *)clc->data);
#endif
return;
}
#ifdef DYNAMIC_LIBCLC_PATH
if (clc->file->sys_path != NULL) {
if (clc->data)
munmap((void *)clc->data, clc->size);
close(clc->fd);
}
#endif
}
/** Returns true if libclc is found
*
* If libclc is compiled in statically, this always returns true. If we
* depend on a dynamic libclc, this opens and tries to stat the file.
*/
bool
nir_can_find_libclc(unsigned ptr_bit_size)
{
struct clc_data clc;
if (open_clc_data(&clc, ptr_bit_size)) {
close_clc_data(&clc);
return true;
} else {
return false;
}
}
nir_shader *
nir_load_libclc_shader(unsigned ptr_bit_size,
struct disk_cache *disk_cache,
const struct spirv_to_nir_options *spirv_options,
const nir_shader_compiler_options *nir_options)
{
assert(ptr_bit_size ==
nir_address_format_bit_size(spirv_options->global_addr_format));
struct clc_data clc;
if (!open_clc_data(&clc, ptr_bit_size))
return NULL;
#ifdef ENABLE_SHADER_CACHE
cache_key cache_key;
if (disk_cache) {
disk_cache_compute_key(disk_cache, clc.cache_key,
sizeof(clc.cache_key), cache_key);
size_t buffer_size;
uint8_t *buffer = disk_cache_get(disk_cache, cache_key, &buffer_size);
if (buffer) {
struct blob_reader blob;
blob_reader_init(&blob, buffer, buffer_size);
nir_shader *nir = nir_deserialize(NULL, nir_options, &blob);
free(buffer);
close_clc_data(&clc);
return nir;
}
}
#endif
if (!map_clc_data(&clc)) {
close_clc_data(&clc);
return NULL;
}
struct spirv_to_nir_options spirv_lib_options = *spirv_options;
spirv_lib_options.create_library = true;
assert(clc.size % SPIRV_WORD_SIZE == 0);
nir_shader *nir = spirv_to_nir(clc.data, clc.size / SPIRV_WORD_SIZE,
NULL, 0, MESA_SHADER_KERNEL, NULL,
&spirv_lib_options, nir_options);
nir_validate_shader(nir, "after nir_load_clc_shader");
/* nir_inline_libclc will assume that the functions in this shader are
* already ready to lower. This means we need to inline any function_temp
* initializers and lower any early returns.
*/
NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
NIR_PASS_V(nir, nir_lower_returns);
/* TODO: One day, we may want to run some optimizations on the libclc
* shader once and cache them to save time in each shader call.
*/
#ifdef ENABLE_SHADER_CACHE
if (disk_cache) {
struct blob blob;
blob_init(&blob);
nir_serialize(&blob, nir, false);
disk_cache_put(disk_cache, cache_key, blob.data, blob.size, NULL);
}
#endif
close_clc_data(&clc);
return nir;
}

View File

@ -28,6 +28,7 @@
#ifndef _NIR_SPIRV_H_
#define _NIR_SPIRV_H_
#include "util/disk_cache.h"
#include "compiler/nir/nir.h"
#include "compiler/shader_info.h"
@ -102,6 +103,14 @@ nir_shader *spirv_to_nir(const uint32_t *words, size_t word_count,
const struct spirv_to_nir_options *options,
const nir_shader_compiler_options *nir_options);
bool nir_can_find_libclc(unsigned ptr_bit_size);
nir_shader *
nir_load_libclc_shader(unsigned ptr_bit_size,
struct disk_cache *disk_cache,
const struct spirv_to_nir_options *spirv_options,
const nir_shader_compiler_options *nir_options);
#ifdef __cplusplus
}
#endif