Path: blob/21.2-virgl/src/compiler/spirv/nir_load_libclc.c
4545 views
/*1* Copyright © 2020 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223#include "nir.h"24#include "nir_serialize.h"25#include "nir_spirv.h"26#include "util/mesa-sha1.h"2728#ifdef DYNAMIC_LIBCLC_PATH29#include <fcntl.h>30#include <sys/types.h>31#include <sys/stat.h>32#include <sys/mman.h>33#include <unistd.h>34#endif3536#ifdef HAVE_STATIC_LIBCLC_ZSTD37#include <zstd.h>38#endif3940#ifdef HAVE_STATIC_LIBCLC_SPIRV41#include "spirv-mesa3d-.spv.h"42#endif4344#ifdef HAVE_STATIC_LIBCLC_SPIRV6445#include "spirv64-mesa3d-.spv.h"46#endif4748struct clc_file {49unsigned bit_size;50const char *static_data;51size_t static_data_size;52const char *sys_path;53};5455static const struct clc_file libclc_files[] = {56{57.bit_size = 32,58#ifdef HAVE_STATIC_LIBCLC_SPIRV59.static_data = libclc_spirv_mesa3d_spv,60.static_data_size = sizeof(libclc_spirv_mesa3d_spv),61#endif62#ifdef DYNAMIC_LIBCLC_PATH63.sys_path = DYNAMIC_LIBCLC_PATH "spirv-mesa3d-.spv",64#endif65},66{67.bit_size = 64,68#ifdef HAVE_STATIC_LIBCLC_SPIRV6469.static_data = libclc_spirv64_mesa3d_spv,70.static_data_size = sizeof(libclc_spirv64_mesa3d_spv),71#endif72#ifdef DYNAMIC_LIBCLC_PATH73.sys_path = DYNAMIC_LIBCLC_PATH "spirv64-mesa3d-.spv",74#endif75},76};7778static const struct clc_file *79get_libclc_file(unsigned ptr_bit_size)80{81assert(ptr_bit_size == 32 || ptr_bit_size == 64);82return &libclc_files[ptr_bit_size / 64];83}8485struct clc_data {86const struct clc_file *file;8788unsigned char cache_key[20];8990int fd;91const void *data;92size_t size;93};9495static bool96open_clc_data(struct clc_data *clc, unsigned ptr_bit_size)97{98memset(clc, 0, sizeof(*clc));99clc->file = get_libclc_file(ptr_bit_size);100clc->fd = -1;101102if (clc->file->static_data) {103snprintf((char *)clc->cache_key, sizeof(clc->cache_key),104"libclc-spirv%d", ptr_bit_size);105return true;106}107108#ifdef DYNAMIC_LIBCLC_PATH109if (clc->file->sys_path != NULL) {110int fd = open(clc->file->sys_path, O_RDONLY);111if (fd < 0)112return false;113114struct stat stat;115int ret = fstat(fd, &stat);116if (ret < 0) {117fprintf(stderr, "fstat failed on %s: %m\n", clc->file->sys_path);118close(fd);119return false;120}121122struct mesa_sha1 ctx;123_mesa_sha1_init(&ctx);124_mesa_sha1_update(&ctx, clc->file->sys_path, strlen(clc->file->sys_path));125_mesa_sha1_update(&ctx, &stat.st_mtim, sizeof(stat.st_mtim));126_mesa_sha1_final(&ctx, clc->cache_key);127128clc->fd = fd;129130return true;131}132#endif133134return false;135}136137#define SPIRV_WORD_SIZE 4138139static bool140map_clc_data(struct clc_data *clc)141{142if (clc->file->static_data) {143#ifdef HAVE_STATIC_LIBCLC_ZSTD144unsigned long long cmp_size =145ZSTD_getFrameContentSize(clc->file->static_data,146clc->file->static_data_size);147if (cmp_size == ZSTD_CONTENTSIZE_UNKNOWN ||148cmp_size == ZSTD_CONTENTSIZE_ERROR) {149fprintf(stderr, "Could not determine the decompressed size of the "150"libclc SPIR-V\n");151return false;152}153154size_t frame_size =155ZSTD_findFrameCompressedSize(clc->file->static_data,156clc->file->static_data_size);157if (ZSTD_isError(frame_size)) {158fprintf(stderr, "Could not determine the size of the first ZSTD frame "159"when decompressing libclc SPIR-V: %s\n",160ZSTD_getErrorName(frame_size));161return false;162}163164void *dest = malloc(cmp_size + 1);165size_t size = ZSTD_decompress(dest, cmp_size, clc->file->static_data,166frame_size);167if (ZSTD_isError(size)) {168free(dest);169fprintf(stderr, "Error decompressing libclc SPIR-V: %s\n",170ZSTD_getErrorName(size));171return false;172}173174clc->data = dest;175clc->size = size;176#else177clc->data = clc->file->static_data;178clc->size = clc->file->static_data_size;179#endif180return true;181}182183#ifdef DYNAMIC_LIBCLC_PATH184if (clc->file->sys_path != NULL) {185off_t len = lseek(clc->fd, 0, SEEK_END);186if (len % SPIRV_WORD_SIZE != 0) {187fprintf(stderr, "File length isn't a multiple of the word size\n");188return false;189}190clc->size = len;191192clc->data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, clc->fd, 0);193if (clc->data == MAP_FAILED) {194fprintf(stderr, "Failed to mmap libclc SPIR-V: %m\n");195return false;196}197198return true;199}200#endif201202return true;203}204205static void206close_clc_data(struct clc_data *clc)207{208if (clc->file->static_data) {209#ifdef HAVE_STATIC_LIBCLC_ZSTD210free((void *)clc->data);211#endif212return;213}214215#ifdef DYNAMIC_LIBCLC_PATH216if (clc->file->sys_path != NULL) {217if (clc->data)218munmap((void *)clc->data, clc->size);219close(clc->fd);220}221#endif222}223224/** Returns true if libclc is found225*226* If libclc is compiled in statically, this always returns true. If we227* depend on a dynamic libclc, this opens and tries to stat the file.228*/229bool230nir_can_find_libclc(unsigned ptr_bit_size)231{232struct clc_data clc;233if (open_clc_data(&clc, ptr_bit_size)) {234close_clc_data(&clc);235return true;236} else {237return false;238}239}240241nir_shader *242nir_load_libclc_shader(unsigned ptr_bit_size,243struct disk_cache *disk_cache,244const struct spirv_to_nir_options *spirv_options,245const nir_shader_compiler_options *nir_options)246{247assert(ptr_bit_size ==248nir_address_format_bit_size(spirv_options->global_addr_format));249250struct clc_data clc;251if (!open_clc_data(&clc, ptr_bit_size))252return NULL;253254#ifdef ENABLE_SHADER_CACHE255cache_key cache_key;256if (disk_cache) {257disk_cache_compute_key(disk_cache, clc.cache_key,258sizeof(clc.cache_key), cache_key);259260size_t buffer_size;261uint8_t *buffer = disk_cache_get(disk_cache, cache_key, &buffer_size);262if (buffer) {263struct blob_reader blob;264blob_reader_init(&blob, buffer, buffer_size);265nir_shader *nir = nir_deserialize(NULL, nir_options, &blob);266free(buffer);267close_clc_data(&clc);268return nir;269}270}271#endif272273if (!map_clc_data(&clc)) {274close_clc_data(&clc);275return NULL;276}277278struct spirv_to_nir_options spirv_lib_options = *spirv_options;279spirv_lib_options.create_library = true;280281assert(clc.size % SPIRV_WORD_SIZE == 0);282nir_shader *nir = spirv_to_nir(clc.data, clc.size / SPIRV_WORD_SIZE,283NULL, 0, MESA_SHADER_KERNEL, NULL,284&spirv_lib_options, nir_options);285nir_validate_shader(nir, "after nir_load_clc_shader");286287/* nir_inline_libclc will assume that the functions in this shader are288* already ready to lower. This means we need to inline any function_temp289* initializers and lower any early returns.290*/291nir->info.internal = true;292NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);293NIR_PASS_V(nir, nir_lower_returns);294295/* TODO: One day, we may want to run some optimizations on the libclc296* shader once and cache them to save time in each shader call.297*/298299#ifdef ENABLE_SHADER_CACHE300if (disk_cache) {301struct blob blob;302blob_init(&blob);303nir_serialize(&blob, nir, false);304disk_cache_put(disk_cache, cache_key, blob.data, blob.size, NULL);305}306#endif307308close_clc_data(&clc);309return nir;310}311312313