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_clc_helpers.h"
26 #include "nir_serialize.h"
27 #include "nir_spirv.h"
28 #include "util/mesa-sha1.h"
29
30 #ifdef DYNAMIC_LIBCLC_PATH
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/mman.h>
35 #include <unistd.h>
36 #endif
37
38 #ifdef HAVE_STATIC_LIBCLC_ZSTD
39 #include <zstd.h>
40 #endif
41
42 #ifdef HAVE_STATIC_LIBCLC_SPIRV
43 #include "spirv-mesa3d-.spv.h"
44 #endif
45
46 #ifdef HAVE_STATIC_LIBCLC_SPIRV64
47 #include "spirv64-mesa3d-.spv.h"
48 #endif
49
50 struct clc_file {
51 unsigned bit_size;
52 const char *static_data;
53 size_t static_data_size;
54 const char *sys_path;
55 };
56
57 static const struct clc_file libclc_files[] = {
58 {
59 .bit_size = 32,
60 #ifdef HAVE_STATIC_LIBCLC_SPIRV
61 .static_data = libclc_spirv_mesa3d_spv,
62 .static_data_size = sizeof(libclc_spirv_mesa3d_spv),
63 #endif
64 #ifdef DYNAMIC_LIBCLC_PATH
65 .sys_path = DYNAMIC_LIBCLC_PATH "spirv-mesa3d-.spv",
66 #endif
67 },
68 {
69 .bit_size = 64,
70 #ifdef HAVE_STATIC_LIBCLC_SPIRV64
71 .static_data = libclc_spirv64_mesa3d_spv,
72 .static_data_size = sizeof(libclc_spirv64_mesa3d_spv),
73 #endif
74 #ifdef DYNAMIC_LIBCLC_PATH
75 .sys_path = DYNAMIC_LIBCLC_PATH "spirv64-mesa3d-.spv",
76 #endif
77 },
78 };
79
80 static const struct clc_file *
get_libclc_file(unsigned ptr_bit_size)81 get_libclc_file(unsigned ptr_bit_size)
82 {
83 assert(ptr_bit_size == 32 || ptr_bit_size == 64);
84 return &libclc_files[ptr_bit_size / 64];
85 }
86
87 struct clc_data {
88 const struct clc_file *file;
89
90 unsigned char cache_key[20];
91
92 int fd;
93 const void *data;
94 size_t size;
95 };
96
97 static bool
open_clc_data(struct clc_data * clc,unsigned ptr_bit_size)98 open_clc_data(struct clc_data *clc, unsigned ptr_bit_size)
99 {
100 memset(clc, 0, sizeof(*clc));
101 clc->file = get_libclc_file(ptr_bit_size);
102 clc->fd = -1;
103
104 if (clc->file->static_data) {
105 snprintf((char *)clc->cache_key, sizeof(clc->cache_key),
106 "libclc-spirv%d", ptr_bit_size);
107 return true;
108 }
109
110 #ifdef DYNAMIC_LIBCLC_PATH
111 if (clc->file->sys_path != NULL) {
112 int fd = open(clc->file->sys_path, O_RDONLY);
113 if (fd < 0)
114 return false;
115
116 struct stat stat;
117 int ret = fstat(fd, &stat);
118 if (ret < 0) {
119 fprintf(stderr, "fstat failed on %s: %m\n", clc->file->sys_path);
120 close(fd);
121 return false;
122 }
123
124 struct mesa_sha1 ctx;
125 _mesa_sha1_init(&ctx);
126 _mesa_sha1_update(&ctx, clc->file->sys_path, strlen(clc->file->sys_path));
127 _mesa_sha1_update(&ctx, &stat.st_mtim, sizeof(stat.st_mtim));
128 _mesa_sha1_final(&ctx, clc->cache_key);
129
130 clc->fd = fd;
131
132 return true;
133 }
134 #endif
135
136 return false;
137 }
138
139 #define SPIRV_WORD_SIZE 4
140
141 static bool
map_clc_data(struct clc_data * clc)142 map_clc_data(struct clc_data *clc)
143 {
144 if (clc->file->static_data) {
145 #ifdef HAVE_STATIC_LIBCLC_ZSTD
146 unsigned long long cmp_size =
147 ZSTD_getFrameContentSize(clc->file->static_data,
148 clc->file->static_data_size);
149 if (cmp_size == ZSTD_CONTENTSIZE_UNKNOWN ||
150 cmp_size == ZSTD_CONTENTSIZE_ERROR) {
151 fprintf(stderr, "Could not determine the decompressed size of the "
152 "libclc SPIR-V\n");
153 return false;
154 }
155
156 size_t frame_size =
157 ZSTD_findFrameCompressedSize(clc->file->static_data,
158 clc->file->static_data_size);
159 if (ZSTD_isError(frame_size)) {
160 fprintf(stderr, "Could not determine the size of the first ZSTD frame "
161 "when decompressing libclc SPIR-V: %s\n",
162 ZSTD_getErrorName(frame_size));
163 return false;
164 }
165
166 void *dest = malloc(cmp_size + 1);
167 size_t size = ZSTD_decompress(dest, cmp_size, clc->file->static_data,
168 frame_size);
169 if (ZSTD_isError(size)) {
170 free(dest);
171 fprintf(stderr, "Error decompressing libclc SPIR-V: %s\n",
172 ZSTD_getErrorName(size));
173 return false;
174 }
175
176 clc->data = dest;
177 clc->size = size;
178 #else
179 clc->data = clc->file->static_data;
180 clc->size = clc->file->static_data_size;
181 #endif
182 return true;
183 }
184
185 #ifdef DYNAMIC_LIBCLC_PATH
186 if (clc->file->sys_path != NULL) {
187 off_t len = lseek(clc->fd, 0, SEEK_END);
188 if (len % SPIRV_WORD_SIZE != 0) {
189 fprintf(stderr, "File length isn't a multiple of the word size\n");
190 return false;
191 }
192 clc->size = len;
193
194 clc->data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, clc->fd, 0);
195 if (clc->data == MAP_FAILED) {
196 fprintf(stderr, "Failed to mmap libclc SPIR-V: %m\n");
197 return false;
198 }
199
200 return true;
201 }
202 #endif
203
204 return true;
205 }
206
207 static void
close_clc_data(struct clc_data * clc)208 close_clc_data(struct clc_data *clc)
209 {
210 if (clc->file->static_data) {
211 #ifdef HAVE_STATIC_LIBCLC_ZSTD
212 free((void *)clc->data);
213 #endif
214 return;
215 }
216
217 #ifdef DYNAMIC_LIBCLC_PATH
218 if (clc->file->sys_path != NULL) {
219 if (clc->data)
220 munmap((void *)clc->data, clc->size);
221 close(clc->fd);
222 }
223 #endif
224 }
225
226 /** Returns true if libclc is found
227 *
228 * If libclc is compiled in statically, this always returns true. If we
229 * depend on a dynamic libclc, this opens and tries to stat the file.
230 */
231 bool
nir_can_find_libclc(unsigned ptr_bit_size)232 nir_can_find_libclc(unsigned ptr_bit_size)
233 {
234 struct clc_data clc;
235 if (open_clc_data(&clc, ptr_bit_size)) {
236 close_clc_data(&clc);
237 return true;
238 } else {
239 return false;
240 }
241 }
242
243 /** Adds generic pointer variants of libclc functions
244 *
245 * Libclc currently doesn't contain generic variants for a bunch of functions
246 * like `frexp` but the OpenCL spec with generic pointers requires them. We
247 * really should fix libclc but, in the mean time, we can easily duplicate
248 * every function that works on global memory and make it also work on generic
249 * memory.
250 */
251 static void
libclc_add_generic_variants(nir_shader * shader)252 libclc_add_generic_variants(nir_shader *shader)
253 {
254 nir_foreach_function(func, shader) {
255 /* These don't need generic variants */
256 if (strstr(func->name, "async_work_group_strided_copy"))
257 continue;
258
259 char *U3AS1 = strstr(func->name, "U3AS1");
260 if (U3AS1 == NULL)
261 continue;
262
263 ptrdiff_t offset_1 = U3AS1 - func->name + 4;
264 assert(offset_1 < strlen(func->name) && func->name[offset_1] == '1');
265
266 char *generic_name = ralloc_strdup(shader, func->name);
267 assert(generic_name[offset_1] == '1');
268 generic_name[offset_1] = '4';
269
270 if (nir_shader_get_function_for_name(shader, generic_name))
271 continue;
272
273 nir_function *gfunc = nir_function_create(shader, generic_name);
274 gfunc->num_params = func->num_params;
275 gfunc->params = ralloc_array(shader, nir_parameter, gfunc->num_params);
276 for (unsigned i = 0; i < gfunc->num_params; i++)
277 gfunc->params[i] = func->params[i];
278
279 nir_function_set_impl(gfunc, nir_function_impl_clone(shader, func->impl));
280
281 /* Rewrite any global pointers to generic */
282 nir_foreach_block(block, gfunc->impl) {
283 nir_foreach_instr(instr, block) {
284 if (instr->type != nir_instr_type_deref)
285 continue;
286
287 nir_deref_instr *deref = nir_instr_as_deref(instr);
288 if (!nir_deref_mode_may_be(deref, nir_var_mem_global))
289 continue;
290
291 assert(deref->type != nir_deref_type_var);
292 assert(nir_deref_mode_is(deref, nir_var_mem_global));
293
294 deref->modes = nir_var_mem_generic;
295 }
296 }
297
298 nir_metadata_preserve(gfunc->impl, nir_metadata_none);
299 }
300 }
301
302 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,bool optimize)303 nir_load_libclc_shader(unsigned ptr_bit_size,
304 struct disk_cache *disk_cache,
305 const struct spirv_to_nir_options *spirv_options,
306 const nir_shader_compiler_options *nir_options,
307 bool optimize)
308 {
309 assert(ptr_bit_size ==
310 nir_address_format_bit_size(spirv_options->global_addr_format));
311
312 struct clc_data clc;
313 if (!open_clc_data(&clc, ptr_bit_size))
314 return NULL;
315
316 #ifdef ENABLE_SHADER_CACHE
317 cache_key cache_key;
318 if (disk_cache) {
319 disk_cache_compute_key(disk_cache, clc.cache_key,
320 sizeof(clc.cache_key), cache_key);
321
322 size_t buffer_size;
323 uint8_t *buffer = disk_cache_get(disk_cache, cache_key, &buffer_size);
324 if (buffer) {
325 struct blob_reader blob;
326 blob_reader_init(&blob, buffer, buffer_size);
327 nir_shader *nir = nir_deserialize(NULL, nir_options, &blob);
328 free(buffer);
329 close_clc_data(&clc);
330 return nir;
331 }
332 }
333 #endif
334
335 if (!map_clc_data(&clc)) {
336 close_clc_data(&clc);
337 return NULL;
338 }
339
340 struct spirv_to_nir_options spirv_lib_options = *spirv_options;
341 spirv_lib_options.create_library = true;
342
343 assert(clc.size % SPIRV_WORD_SIZE == 0);
344 nir_shader *nir = spirv_to_nir(clc.data, clc.size / SPIRV_WORD_SIZE,
345 NULL, 0, MESA_SHADER_KERNEL, NULL,
346 &spirv_lib_options, nir_options);
347 nir_validate_shader(nir, "after nir_load_clc_shader");
348
349 /* nir_inline_libclc will assume that the functions in this shader are
350 * already ready to lower. This means we need to inline any function_temp
351 * initializers and lower any early returns.
352 */
353 nir->info.internal = true;
354 NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
355 NIR_PASS_V(nir, nir_lower_returns);
356
357 NIR_PASS_V(nir, libclc_add_generic_variants);
358
359 /* Run some optimization passes. Those used here should be considered safe
360 * for all use cases and drivers.
361 */
362 if (optimize) {
363 NIR_PASS_V(nir, nir_split_var_copies);
364
365 bool progress;
366 do {
367 progress = false;
368 NIR_PASS(progress, nir, nir_opt_copy_prop_vars);
369 NIR_PASS(progress, nir, nir_lower_var_copies);
370 NIR_PASS(progress, nir, nir_lower_vars_to_ssa);
371 NIR_PASS(progress, nir, nir_copy_prop);
372 NIR_PASS(progress, nir, nir_opt_remove_phis);
373 NIR_PASS(progress, nir, nir_opt_dce);
374 NIR_PASS(progress, nir, nir_opt_if, false);
375 NIR_PASS(progress, nir, nir_opt_dead_cf);
376 NIR_PASS(progress, nir, nir_opt_cse);
377 /* drivers run this pass, so don't be too aggressive. More aggressive
378 * values only increase effectiveness by <5%
379 */
380 NIR_PASS(progress, nir, nir_opt_peephole_select, 0, false, false);
381 NIR_PASS(progress, nir, nir_opt_algebraic);
382 NIR_PASS(progress, nir, nir_opt_constant_folding);
383 NIR_PASS(progress, nir, nir_opt_undef);
384 NIR_PASS(progress, nir, nir_opt_deref);
385 } while(progress);
386
387 nir_sweep(nir);
388 }
389
390 #ifdef ENABLE_SHADER_CACHE
391 if (disk_cache) {
392 struct blob blob;
393 blob_init(&blob);
394 nir_serialize(&blob, nir, false);
395 disk_cache_put(disk_cache, cache_key, blob.data, blob.size, NULL);
396 blob_finish(&blob);
397 }
398 #endif
399
400 close_clc_data(&clc);
401 return nir;
402 }
403