1 /*
2 * Copyright © 2024 Imagination Technologies Ltd.
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 /**
8 * \file pco_nir.c
9 *
10 * \brief NIR-specific functions.
11 */
12
13 #include "nir/nir_builder.h"
14 #include "pco.h"
15 #include "pco_internal.h"
16
17 #include <stdio.h>
18
19 /** Base/common SPIR-V to NIR options. */
20 static const struct spirv_to_nir_options pco_base_spirv_options = {
21 .environment = NIR_SPIRV_VULKAN,
22 };
23
24 /** Base/common NIR options. */
25 static const nir_shader_compiler_options pco_base_nir_options = {
26 .fuse_ffma32 = true,
27
28 .lower_fquantize2f16 = true,
29 .lower_layer_fs_input_to_sysval = true,
30 .compact_arrays = true,
31 };
32
33 /**
34 * \brief Sets up device/core-specific SPIR-V to NIR options.
35 *
36 * \param[in] dev_info Device info.
37 * \param[out] spirv_options SPIR-V to NIR options.
38 */
pco_setup_spirv_options(const struct pvr_device_info * dev_info,struct spirv_to_nir_options * spirv_options)39 void pco_setup_spirv_options(const struct pvr_device_info *dev_info,
40 struct spirv_to_nir_options *spirv_options)
41 {
42 memcpy(spirv_options, &pco_base_spirv_options, sizeof(*spirv_options));
43
44 /* TODO: Device/core-dependent options. */
45 puts("finishme: pco_setup_spirv_options");
46 }
47
48 /**
49 * \brief Sets up device/core-specific NIR options.
50 *
51 * \param[in] dev_info Device info.
52 * \param[out] nir_options NIR options.
53 */
pco_setup_nir_options(const struct pvr_device_info * dev_info,nir_shader_compiler_options * nir_options)54 void pco_setup_nir_options(const struct pvr_device_info *dev_info,
55 nir_shader_compiler_options *nir_options)
56 {
57 memcpy(nir_options, &pco_base_nir_options, sizeof(*nir_options));
58
59 /* TODO: Device/core-dependent options. */
60 puts("finishme: pco_setup_nir_options");
61 }
62
63 /**
64 * \brief Runs pre-processing passes on a NIR shader.
65 *
66 * \param[in] ctx PCO compiler context.
67 * \param[in,out] nir NIR shader.
68 */
pco_preprocess_nir(pco_ctx * ctx,nir_shader * nir)69 void pco_preprocess_nir(pco_ctx *ctx, nir_shader *nir)
70 {
71 if (nir->info.internal)
72 NIR_PASS(_, nir, nir_lower_returns);
73
74 NIR_PASS(_, nir, nir_lower_global_vars_to_local);
75 NIR_PASS(_, nir, nir_lower_vars_to_ssa);
76 NIR_PASS(_, nir, nir_split_var_copies);
77 NIR_PASS(_, nir, nir_lower_var_copies);
78 NIR_PASS(_, nir, nir_split_per_member_structs);
79 NIR_PASS(_,
80 nir,
81 nir_split_struct_vars,
82 nir_var_function_temp | nir_var_shader_temp);
83 NIR_PASS(_,
84 nir,
85 nir_split_array_vars,
86 nir_var_function_temp | nir_var_shader_temp);
87 NIR_PASS(_,
88 nir,
89 nir_lower_indirect_derefs,
90 nir_var_shader_in | nir_var_shader_out,
91 UINT32_MAX);
92
93 NIR_PASS(_,
94 nir,
95 nir_remove_dead_variables,
96 nir_var_function_temp | nir_var_shader_temp,
97 NULL);
98 NIR_PASS(_, nir, nir_opt_dce);
99
100 if (pco_should_print_nir(nir)) {
101 puts("after pco_preprocess_nir:");
102 nir_print_shader(nir, stdout);
103 }
104 }
105
106 /**
107 * \brief Returns the GLSL type size.
108 *
109 * \param[in] type Type.
110 * \param[in] bindless Whether the access is bindless.
111 * \return The size.
112 */
glsl_type_size(const struct glsl_type * type,UNUSED bool bindless)113 static int glsl_type_size(const struct glsl_type *type, UNUSED bool bindless)
114 {
115 return glsl_count_attribute_slots(type, false);
116 }
117
118 /**
119 * \brief Returns the vectorization with for a given instruction.
120 *
121 * \param[in] instr Instruction.
122 * \param[in] data User data.
123 * \return The vectorization width.
124 */
vectorize_filter(const nir_instr * instr,UNUSED const void * data)125 static uint8_t vectorize_filter(const nir_instr *instr, UNUSED const void *data)
126 {
127 if (instr->type == nir_instr_type_load_const)
128 return 1;
129
130 if (instr->type != nir_instr_type_alu)
131 return 0;
132
133 /* TODO */
134 nir_alu_instr *alu = nir_instr_as_alu(instr);
135 switch (alu->op) {
136 default:
137 break;
138 }
139
140 /* Basic for now. */
141 return 2;
142 }
143
144 /**
145 * \brief Filters for a varying position load_input in frag shaders.
146 *
147 * \param[in] instr Instruction.
148 * \param[in] data User data.
149 * \return True if the instruction was found.
150 */
frag_pos_filter(const nir_instr * instr,UNUSED const void * data)151 static bool frag_pos_filter(const nir_instr *instr, UNUSED const void *data)
152 {
153 assert(instr->type == nir_instr_type_intrinsic);
154
155 nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
156 if (intr->intrinsic != nir_intrinsic_load_input)
157 return false;
158
159 return nir_intrinsic_io_semantics(intr).location == VARYING_SLOT_POS;
160 }
161
162 /**
163 * \brief Lowers a NIR shader.
164 *
165 * \param[in] ctx PCO compiler context.
166 * \param[in,out] nir NIR shader.
167 * \param[in,out] data Shader data.
168 */
pco_lower_nir(pco_ctx * ctx,nir_shader * nir,pco_data * data)169 void pco_lower_nir(pco_ctx *ctx, nir_shader *nir, pco_data *data)
170 {
171 NIR_PASS(_,
172 nir,
173 nir_lower_io,
174 nir_var_shader_in | nir_var_shader_out,
175 glsl_type_size,
176 nir_lower_io_lower_64bit_to_32);
177
178 NIR_PASS(_, nir, nir_opt_dce);
179 NIR_PASS(_, nir, nir_opt_constant_folding);
180 NIR_PASS(_,
181 nir,
182 nir_io_add_const_offset_to_base,
183 nir_var_shader_in | nir_var_shader_out);
184
185 if (nir->info.stage == MESA_SHADER_FRAGMENT) {
186 NIR_PASS(_, nir, pco_nir_pfo, &data->fs);
187 } else if (nir->info.stage == MESA_SHADER_VERTEX) {
188 NIR_PASS(_, nir, pco_nir_pvi, &data->vs);
189 }
190
191 /* TODO: this should happen in the linking stage to cull unused I/O. */
192 NIR_PASS(_,
193 nir,
194 nir_lower_io_to_scalar,
195 nir_var_shader_in | nir_var_shader_out,
196 NULL,
197 NULL);
198
199 NIR_PASS(_, nir, nir_lower_vars_to_ssa);
200 NIR_PASS(_, nir, nir_opt_copy_prop_vars);
201 NIR_PASS(_, nir, nir_opt_dead_write_vars);
202 NIR_PASS(_, nir, nir_opt_combine_stores, nir_var_all);
203
204 bool progress;
205 NIR_PASS(_, nir, nir_lower_alu);
206 NIR_PASS(_, nir, nir_lower_pack);
207 NIR_PASS(_, nir, nir_opt_algebraic);
208 do {
209 progress = false;
210
211 NIR_PASS(progress, nir, nir_opt_algebraic_late);
212 NIR_PASS(_, nir, nir_opt_constant_folding);
213 NIR_PASS(_, nir, nir_lower_load_const_to_scalar);
214 NIR_PASS(_, nir, nir_copy_prop);
215 NIR_PASS(_, nir, nir_opt_dce);
216 NIR_PASS(_, nir, nir_opt_cse);
217 } while (progress);
218
219 nir_variable_mode vec_modes = nir_var_shader_in;
220 /* Fragment shader needs scalar writes after pfo. */
221 if (nir->info.stage != MESA_SHADER_FRAGMENT)
222 vec_modes |= nir_var_shader_out;
223
224 NIR_PASS(_, nir, nir_opt_vectorize_io, vec_modes);
225
226 /* Special case for frag coords:
227 * - x,y come from (non-consecutive) special regs - always scalar.
228 * - z,w are iterated and driver will make sure they're consecutive.
229 * - TODO: keep scalar for now, but add pass to vectorize.
230 */
231 if (nir->info.stage == MESA_SHADER_FRAGMENT) {
232 NIR_PASS(_,
233 nir,
234 nir_lower_io_to_scalar,
235 nir_var_shader_in,
236 frag_pos_filter,
237 NULL);
238 }
239
240 NIR_PASS(_, nir, nir_lower_alu_to_scalar, NULL, NULL);
241
242 do {
243 progress = false;
244
245 NIR_PASS(progress, nir, nir_copy_prop);
246 NIR_PASS(progress, nir, nir_opt_dce);
247 NIR_PASS(progress, nir, nir_opt_cse);
248 NIR_PASS(progress, nir, nir_opt_constant_folding);
249 NIR_PASS(progress, nir, nir_opt_undef);
250 } while (progress);
251
252 if (pco_should_print_nir(nir)) {
253 puts("after pco_lower_nir:");
254 nir_print_shader(nir, stdout);
255 }
256 }
257
258 /**
259 * \brief Gather fragment shader data pass.
260 *
261 * \param[in] b NIR builder.
262 * \param[in] intr NIR intrinsic instruction.
263 * \param[in,out] cb_data Callback data.
264 * \return True if the shader was modified (always return false).
265 */
gather_fs_data_pass(UNUSED struct nir_builder * b,nir_intrinsic_instr * intr,void * cb_data)266 static bool gather_fs_data_pass(UNUSED struct nir_builder *b,
267 nir_intrinsic_instr *intr,
268 void *cb_data)
269 {
270 /* Check whether the shader accesses z/w. */
271 if (intr->intrinsic != nir_intrinsic_load_input)
272 return false;
273
274 struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr);
275 if (io_semantics.location != VARYING_SLOT_POS)
276 return false;
277
278 unsigned component = nir_intrinsic_component(intr);
279 unsigned chans = intr->def.num_components;
280
281 pco_data *data = cb_data;
282
283 data->fs.uses.z |= (component + chans > 2);
284 data->fs.uses.w |= (component + chans > 3);
285
286 return false;
287 }
288
289 /**
290 * \brief Gathers fragment shader data.
291 *
292 * \param[in] nir NIR shader.
293 * \param[in,out] data Shader data.
294 */
gather_fs_data(nir_shader * nir,pco_data * data)295 static void gather_fs_data(nir_shader *nir, pco_data *data)
296 {
297 nir_shader_intrinsics_pass(nir, gather_fs_data_pass, nir_metadata_all, data);
298
299 /* If any inputs use smooth shading, then w is needed. */
300 if (!data->fs.uses.w) {
301 nir_foreach_shader_in_variable (var, nir) {
302 if (var->data.interpolation > INTERP_MODE_SMOOTH)
303 continue;
304
305 data->fs.uses.w = true;
306 break;
307 }
308 }
309 }
310
311 /**
312 * \brief Gathers shader data.
313 *
314 * \param[in] nir NIR shader.
315 * \param[in,out] data Shader data.
316 */
gather_data(nir_shader * nir,pco_data * data)317 static void gather_data(nir_shader *nir, pco_data *data)
318 {
319 switch (nir->info.stage) {
320 case MESA_SHADER_FRAGMENT:
321 return gather_fs_data(nir, data);
322
323 case MESA_SHADER_VERTEX:
324 /* TODO */
325 break;
326
327 default:
328 unreachable();
329 }
330 }
331
332 /**
333 * \brief Runs post-processing passes on a NIR shader.
334 *
335 * \param[in] ctx PCO compiler context.
336 * \param[in,out] nir NIR shader.
337 * \param[in,out] data Shader data.
338 */
pco_postprocess_nir(pco_ctx * ctx,nir_shader * nir,pco_data * data)339 void pco_postprocess_nir(pco_ctx *ctx, nir_shader *nir, pco_data *data)
340 {
341 NIR_PASS(_, nir, nir_move_vec_src_uses_to_dest, false);
342
343 /* Re-index everything. */
344 nir_foreach_function_with_impl (_, impl, nir) {
345 nir_index_blocks(impl);
346 nir_index_instrs(impl);
347 nir_index_ssa_defs(impl);
348 }
349
350 nir_shader_gather_info(nir, nir_shader_get_entrypoint(nir));
351
352 gather_data(nir, data);
353
354 if (pco_should_print_nir(nir)) {
355 puts("after pco_postprocess_nir:");
356 nir_print_shader(nir, stdout);
357 }
358 }
359
360 /**
361 * \brief Performs linking optimizations on consecutive NIR shader stages.
362 *
363 * \param[in] ctx PCO compiler context.
364 * \param[in,out] producer NIR producer shader.
365 * \param[in,out] consumer NIR consumer shader.
366 */
pco_link_nir(pco_ctx * ctx,nir_shader * producer,nir_shader * consumer)367 void pco_link_nir(pco_ctx *ctx, nir_shader *producer, nir_shader *consumer)
368 {
369 /* TODO */
370 puts("finishme: pco_link_nir");
371
372 if (pco_should_print_nir(producer)) {
373 puts("producer after pco_link_nir:");
374 nir_print_shader(producer, stdout);
375 }
376
377 if (pco_should_print_nir(consumer)) {
378 puts("consumer after pco_link_nir:");
379 nir_print_shader(consumer, stdout);
380 }
381 }
382
383 /**
384 * \brief Checks whether two varying variables are the same.
385 *
386 * \param[in] out_var The first varying being compared.
387 * \param[in] in_var The second varying being compared.
388 * \return True if the varyings match.
389 */
varyings_match(nir_variable * out_var,nir_variable * in_var)390 static bool varyings_match(nir_variable *out_var, nir_variable *in_var)
391 {
392 return in_var->data.location == out_var->data.location &&
393 in_var->data.location_frac == out_var->data.location_frac &&
394 in_var->type == out_var->type;
395 }
396
397 /**
398 * \brief Performs reverse linking optimizations on consecutive NIR shader
399 * stages.
400 *
401 * \param[in] ctx PCO compiler context.
402 * \param[in,out] producer NIR producer shader.
403 * \param[in,out] consumer NIR consumer shader.
404 */
pco_rev_link_nir(pco_ctx * ctx,nir_shader * producer,nir_shader * consumer)405 void pco_rev_link_nir(pco_ctx *ctx, nir_shader *producer, nir_shader *consumer)
406 {
407 /* TODO */
408 puts("finishme: pco_rev_link_nir");
409
410 /* Propagate back/adjust the interpolation qualifiers. */
411 nir_foreach_shader_in_variable (in_var, consumer) {
412 if (in_var->data.location == VARYING_SLOT_POS ||
413 in_var->data.location == VARYING_SLOT_PNTC) {
414 in_var->data.interpolation = INTERP_MODE_NOPERSPECTIVE;
415 } else if (in_var->data.interpolation == INTERP_MODE_NONE) {
416 in_var->data.interpolation = INTERP_MODE_SMOOTH;
417 }
418
419 nir_foreach_shader_out_variable (out_var, producer) {
420 if (!varyings_match(out_var, in_var))
421 continue;
422
423 out_var->data.interpolation = in_var->data.interpolation;
424 break;
425 }
426 }
427
428 if (pco_should_print_nir(producer)) {
429 puts("producer after pco_rev_link_nir:");
430 nir_print_shader(producer, stdout);
431 }
432
433 if (pco_should_print_nir(consumer)) {
434 puts("consumer after pco_rev_link_nir:");
435 nir_print_shader(consumer, stdout);
436 }
437 }
438