• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2020 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "nir.h"
25 #include "nir_serialize.h"
26 #include "nir_spirv.h"
27 #include "util/mesa-sha1.h"
28 
29 #ifdef DYNAMIC_LIBCLC_PATH
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/mman.h>
34 #include <unistd.h>
35 #endif
36 
37 #ifdef HAVE_STATIC_LIBCLC_ZSTD
38 #include <zstd.h>
39 #endif
40 
41 #ifdef HAVE_STATIC_LIBCLC_SPIRV
42 #include "spirv-mesa3d-.spv.h"
43 #endif
44 
45 #ifdef HAVE_STATIC_LIBCLC_SPIRV64
46 #include "spirv64-mesa3d-.spv.h"
47 #endif
48 
49 struct clc_file {
50    unsigned bit_size;
51    const char *static_data;
52    size_t static_data_size;
53    const char *sys_path;
54 };
55 
56 static const struct clc_file libclc_files[] = {
57    {
58       .bit_size = 32,
59 #ifdef HAVE_STATIC_LIBCLC_SPIRV
60       .static_data = libclc_spirv_mesa3d_spv,
61       .static_data_size = sizeof(libclc_spirv_mesa3d_spv),
62 #endif
63 #ifdef DYNAMIC_LIBCLC_PATH
64       .sys_path = DYNAMIC_LIBCLC_PATH "spirv-mesa3d-.spv",
65 #endif
66    },
67    {
68       .bit_size = 64,
69 #ifdef HAVE_STATIC_LIBCLC_SPIRV64
70       .static_data = libclc_spirv64_mesa3d_spv,
71       .static_data_size = sizeof(libclc_spirv64_mesa3d_spv),
72 #endif
73 #ifdef DYNAMIC_LIBCLC_PATH
74       .sys_path = DYNAMIC_LIBCLC_PATH "spirv64-mesa3d-.spv",
75 #endif
76    },
77 };
78 
79 static const struct clc_file *
get_libclc_file(unsigned ptr_bit_size)80 get_libclc_file(unsigned ptr_bit_size)
81 {
82    assert(ptr_bit_size == 32 || ptr_bit_size == 64);
83    return &libclc_files[ptr_bit_size / 64];
84 }
85 
86 struct clc_data {
87    const struct clc_file *file;
88 
89    unsigned char cache_key[20];
90 
91    int fd;
92    const void *data;
93    size_t size;
94 };
95 
96 static bool
open_clc_data(struct clc_data * clc,unsigned ptr_bit_size)97 open_clc_data(struct clc_data *clc, unsigned ptr_bit_size)
98 {
99    memset(clc, 0, sizeof(*clc));
100    clc->file = get_libclc_file(ptr_bit_size);
101    clc->fd = -1;
102 
103    if (clc->file->static_data) {
104       snprintf((char *)clc->cache_key, sizeof(clc->cache_key),
105                "libclc-spirv%d", ptr_bit_size);
106       return true;
107    }
108 
109 #ifdef DYNAMIC_LIBCLC_PATH
110    if (clc->file->sys_path != NULL) {
111       int fd = open(clc->file->sys_path, O_RDONLY);
112       if (fd < 0)
113          return false;
114 
115       struct stat stat;
116       int ret = fstat(fd, &stat);
117       if (ret < 0) {
118          fprintf(stderr, "fstat failed on %s: %m\n", clc->file->sys_path);
119          close(fd);
120          return false;
121       }
122 
123       struct mesa_sha1 ctx;
124       _mesa_sha1_init(&ctx);
125       _mesa_sha1_update(&ctx, clc->file->sys_path, strlen(clc->file->sys_path));
126       _mesa_sha1_update(&ctx, &stat.st_mtim, sizeof(stat.st_mtim));
127       _mesa_sha1_final(&ctx, clc->cache_key);
128 
129       clc->fd = fd;
130 
131       return true;
132    }
133 #endif
134 
135    return false;
136 }
137 
138 #define SPIRV_WORD_SIZE 4
139 
140 static bool
map_clc_data(struct clc_data * clc)141 map_clc_data(struct clc_data *clc)
142 {
143    if (clc->file->static_data) {
144 #ifdef HAVE_STATIC_LIBCLC_ZSTD
145       unsigned long long cmp_size =
146          ZSTD_getFrameContentSize(clc->file->static_data,
147                                   clc->file->static_data_size);
148       if (cmp_size == ZSTD_CONTENTSIZE_UNKNOWN ||
149           cmp_size == ZSTD_CONTENTSIZE_ERROR) {
150          fprintf(stderr, "Could not determine the decompressed size of the "
151                          "libclc SPIR-V\n");
152          return false;
153       }
154 
155       size_t frame_size =
156          ZSTD_findFrameCompressedSize(clc->file->static_data,
157                                       clc->file->static_data_size);
158       if (ZSTD_isError(frame_size)) {
159          fprintf(stderr, "Could not determine the size of the first ZSTD frame "
160                          "when decompressing libclc SPIR-V: %s\n",
161                  ZSTD_getErrorName(frame_size));
162          return false;
163       }
164 
165       void *dest = malloc(cmp_size + 1);
166       size_t size = ZSTD_decompress(dest, cmp_size, clc->file->static_data,
167                                     frame_size);
168       if (ZSTD_isError(size)) {
169          free(dest);
170          fprintf(stderr, "Error decompressing libclc SPIR-V: %s\n",
171                  ZSTD_getErrorName(size));
172          return false;
173       }
174 
175       clc->data = dest;
176       clc->size = size;
177 #else
178       clc->data = clc->file->static_data;
179       clc->size = clc->file->static_data_size;
180 #endif
181       return true;
182    }
183 
184 #ifdef DYNAMIC_LIBCLC_PATH
185    if (clc->file->sys_path != NULL) {
186       off_t len = lseek(clc->fd, 0, SEEK_END);
187       if (len % SPIRV_WORD_SIZE != 0) {
188          fprintf(stderr, "File length isn't a multiple of the word size\n");
189          return false;
190       }
191       clc->size = len;
192 
193       clc->data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, clc->fd, 0);
194       if (clc->data == MAP_FAILED) {
195          fprintf(stderr, "Failed to mmap libclc SPIR-V: %m\n");
196          return false;
197       }
198 
199       return true;
200    }
201 #endif
202 
203    return true;
204 }
205 
206 static void
close_clc_data(struct clc_data * clc)207 close_clc_data(struct clc_data *clc)
208 {
209    if (clc->file->static_data) {
210 #ifdef HAVE_STATIC_LIBCLC_ZSTD
211       free((void *)clc->data);
212 #endif
213       return;
214    }
215 
216 #ifdef DYNAMIC_LIBCLC_PATH
217    if (clc->file->sys_path != NULL) {
218       if (clc->data)
219          munmap((void *)clc->data, clc->size);
220       close(clc->fd);
221    }
222 #endif
223 }
224 
225 /** Returns true if libclc is found
226  *
227  * If libclc is compiled in statically, this always returns true.  If we
228  * depend on a dynamic libclc, this opens and tries to stat the file.
229  */
230 bool
nir_can_find_libclc(unsigned ptr_bit_size)231 nir_can_find_libclc(unsigned ptr_bit_size)
232 {
233    struct clc_data clc;
234    if (open_clc_data(&clc, ptr_bit_size)) {
235       close_clc_data(&clc);
236       return true;
237    } else {
238       return false;
239    }
240 }
241 
242 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)243 nir_load_libclc_shader(unsigned ptr_bit_size,
244                        struct disk_cache *disk_cache,
245                        const struct spirv_to_nir_options *spirv_options,
246                        const nir_shader_compiler_options *nir_options)
247 {
248    assert(ptr_bit_size ==
249           nir_address_format_bit_size(spirv_options->global_addr_format));
250 
251    struct clc_data clc;
252    if (!open_clc_data(&clc, ptr_bit_size))
253       return NULL;
254 
255 #ifdef ENABLE_SHADER_CACHE
256    cache_key cache_key;
257    if (disk_cache) {
258       disk_cache_compute_key(disk_cache, clc.cache_key,
259                              sizeof(clc.cache_key), cache_key);
260 
261       size_t buffer_size;
262       uint8_t *buffer = disk_cache_get(disk_cache, cache_key, &buffer_size);
263       if (buffer) {
264          struct blob_reader blob;
265          blob_reader_init(&blob, buffer, buffer_size);
266          nir_shader *nir = nir_deserialize(NULL, nir_options, &blob);
267          free(buffer);
268          close_clc_data(&clc);
269          return nir;
270       }
271    }
272 #endif
273 
274    if (!map_clc_data(&clc)) {
275       close_clc_data(&clc);
276       return NULL;
277    }
278 
279    struct spirv_to_nir_options spirv_lib_options = *spirv_options;
280    spirv_lib_options.create_library = true;
281 
282    assert(clc.size % SPIRV_WORD_SIZE == 0);
283    nir_shader *nir = spirv_to_nir(clc.data, clc.size / SPIRV_WORD_SIZE,
284                                   NULL, 0, MESA_SHADER_KERNEL, NULL,
285                                   &spirv_lib_options, nir_options);
286    nir_validate_shader(nir, "after nir_load_clc_shader");
287 
288    /* nir_inline_libclc will assume that the functions in this shader are
289     * already ready to lower.  This means we need to inline any function_temp
290     * initializers and lower any early returns.
291     */
292    nir->info.internal = true;
293    NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
294    NIR_PASS_V(nir, nir_lower_returns);
295 
296    /* TODO: One day, we may want to run some optimizations on the libclc
297     * shader once and cache them to save time in each shader call.
298     */
299 
300 #ifdef ENABLE_SHADER_CACHE
301    if (disk_cache) {
302       struct blob blob;
303       blob_init(&blob);
304       nir_serialize(&blob, nir, false);
305       disk_cache_put(disk_cache, cache_key, blob.data, blob.size, NULL);
306    }
307 #endif
308 
309    close_clc_data(&clc);
310    return nir;
311 }
312