032fb03cfd8bf84e4468cd255f9783d2f52f1652
[mesa.git] / src / gallium / frontends / clover / nir / invocation.cpp
1 //
2 // Copyright 2019 Karol Herbst
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 shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22
23 #include "invocation.hpp"
24
25 #include <tuple>
26
27 #include "core/device.hpp"
28 #include "core/error.hpp"
29 #include "pipe/p_state.h"
30 #include "util/algorithm.hpp"
31 #include "util/functional.hpp"
32
33 #include <compiler/glsl_types.h>
34 #include <compiler/nir/nir_serialize.h>
35 #include <compiler/spirv/nir_spirv.h>
36 #include <util/u_math.h>
37
38 using namespace clover;
39
40 #ifdef HAVE_CLOVER_SPIRV
41
42 // Refs and unrefs the glsl_type_singleton.
43 static class glsl_type_ref {
44 public:
45 glsl_type_ref() {
46 glsl_type_singleton_init_or_ref();
47 }
48
49 ~glsl_type_ref() {
50 glsl_type_singleton_decref();
51 }
52 } glsl_type_ref;
53
54 static const nir_shader_compiler_options *
55 dev_get_nir_compiler_options(const device &dev)
56 {
57 const void *co = dev.get_compiler_options(PIPE_SHADER_IR_NIR);
58 return static_cast<const nir_shader_compiler_options*>(co);
59 }
60
61 static void debug_function(void *private_data,
62 enum nir_spirv_debug_level level, size_t spirv_offset,
63 const char *message)
64 {
65 assert(private_data);
66 auto r_log = reinterpret_cast<std::string *>(private_data);
67 *r_log += message;
68 }
69
70 module clover::nir::spirv_to_nir(const module &mod, const device &dev,
71 std::string &r_log)
72 {
73 struct spirv_to_nir_options spirv_options = {};
74 spirv_options.environment = NIR_SPIRV_OPENCL;
75 if (dev.address_bits() == 32u) {
76 spirv_options.shared_addr_format = nir_address_format_32bit_offset;
77 spirv_options.global_addr_format = nir_address_format_32bit_global;
78 spirv_options.temp_addr_format = nir_address_format_32bit_global;
79 } else {
80 spirv_options.shared_addr_format = nir_address_format_32bit_offset_as_64bit;
81 spirv_options.global_addr_format = nir_address_format_64bit_global;
82 spirv_options.temp_addr_format = nir_address_format_64bit_global;
83 }
84 spirv_options.caps.address = true;
85 spirv_options.caps.float64 = true;
86 spirv_options.caps.int8 = true;
87 spirv_options.caps.int16 = true;
88 spirv_options.caps.int64 = true;
89 spirv_options.caps.kernel = true;
90 spirv_options.caps.int64_atomics = dev.has_int64_atomics();
91 spirv_options.constant_as_global = true;
92 spirv_options.debug.func = &debug_function;
93 spirv_options.debug.private_data = &r_log;
94
95 module m;
96 // We only insert one section.
97 assert(mod.secs.size() == 1);
98 auto &section = mod.secs[0];
99
100 module::resource_id section_id = 0;
101 for (const auto &sym : mod.syms) {
102 assert(sym.section == 0);
103
104 const auto *binary =
105 reinterpret_cast<const pipe_binary_program_header *>(section.data.data());
106 const uint32_t *data = reinterpret_cast<const uint32_t *>(binary->blob);
107 const size_t num_words = binary->num_bytes / 4;
108 const char *name = sym.name.c_str();
109 auto *compiler_options = dev_get_nir_compiler_options(dev);
110
111 nir_shader *nir = spirv_to_nir(data, num_words, nullptr, 0,
112 MESA_SHADER_KERNEL, name,
113 &spirv_options, compiler_options);
114 if (!nir) {
115 r_log += "Translation from SPIR-V to NIR for kernel \"" + sym.name +
116 "\" failed.\n";
117 throw build_error();
118 }
119
120 nir->info.cs.local_size_variable = true;
121 nir_validate_shader(nir, "clover");
122
123 // Calculate input offsets.
124 unsigned offset = 0;
125 nir_foreach_shader_in_variable_safe(var, nir) {
126 offset = align(offset, glsl_get_cl_alignment(var->type));
127 var->data.driver_location = offset;
128 offset += glsl_get_cl_size(var->type);
129 }
130
131 // Inline all functions first.
132 // according to the comment on nir_inline_functions
133 NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
134 NIR_PASS_V(nir, nir_lower_returns);
135 NIR_PASS_V(nir, nir_inline_functions);
136 NIR_PASS_V(nir, nir_copy_prop);
137 NIR_PASS_V(nir, nir_opt_deref);
138
139 // Pick off the single entrypoint that we want.
140 foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
141 if (!func->is_entrypoint)
142 exec_node_remove(&func->node);
143 }
144 assert(exec_list_length(&nir->functions) == 1);
145
146 nir_validate_shader(nir, "clover after function inlining");
147
148 NIR_PASS_V(nir, nir_lower_variable_initializers,
149 static_cast<nir_variable_mode>(~nir_var_function_temp));
150
151 // copy propagate to prepare for lower_explicit_io
152 NIR_PASS_V(nir, nir_split_var_copies);
153 NIR_PASS_V(nir, nir_opt_copy_prop_vars);
154 NIR_PASS_V(nir, nir_lower_var_copies);
155 NIR_PASS_V(nir, nir_lower_vars_to_ssa);
156 NIR_PASS_V(nir, nir_opt_dce);
157
158 NIR_PASS_V(nir, nir_lower_vars_to_explicit_types, nir_var_mem_shared,
159 glsl_get_cl_type_size_align);
160
161 /* use offsets for shader_in and shared memory */
162 NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_shader_in,
163 nir_address_format_32bit_offset);
164
165 NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_mem_shared,
166 spirv_options.shared_addr_format);
167
168 NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_mem_global,
169 spirv_options.global_addr_format);
170
171 NIR_PASS_V(nir, nir_lower_system_values);
172 if (compiler_options->lower_int64_options)
173 NIR_PASS_V(nir, nir_lower_int64);
174
175 NIR_PASS_V(nir, nir_opt_dce);
176
177 struct blob blob;
178 blob_init(&blob);
179 nir_serialize(&blob, nir, false);
180
181 const pipe_binary_program_header header { uint32_t(blob.size) };
182 module::section text { section_id, module::section::text_executable, header.num_bytes, {} };
183 text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
184 reinterpret_cast<const char *>(&header) + sizeof(header));
185 text.data.insert(text.data.end(), blob.data, blob.data + blob.size);
186
187 m.syms.emplace_back(sym.name, section_id, 0, sym.args);
188 m.secs.push_back(text);
189 section_id++;
190 }
191 return m;
192 }
193 #else
194 module clover::nir::spirv_to_nir(const module &mod, const device &dev, std::string &r_log)
195 {
196 r_log += "SPIR-V support in clover is not enabled.\n";
197 throw error(CL_LINKER_NOT_AVAILABLE);
198 }
199 #endif