1 /*
2 * Copyright 2024 Valve Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "nak_private.h"
7 #include "nir_builder.h"
8
9 /*
10 * Divergent loops can define convergent values that escape the loop. eg.
11 *
12 * div loop {
13 * con %72 = ...
14 * ...
15 * }
16 * ... = @ldcx_nv (%72, 0x30)
17 *
18 * When that happens, we would like to have the opportunity to move the value
19 * into a uniform register once we reach uniform control flow. This pass allows
20 * that by inserting nir_intrinsic_as_uniform after the loop-closed phi, which
21 * becomes r2ur in the backend.
22 *
23 * With nir_to_lcssa and this pass, our example becomes something like:
24 *
25 * div loop {
26 * con %72 = ...
27 * ...
28 * }
29 * con %73 = phi(%72, %72)
30 * con %74 = @as_uniform(%73)
31 * ... = @ldcx_nv (%74, 0x30)
32 *
33 * nir_to_lcssa(nir, false, false) and nir_divergence_analysis must be run
34 * before this pass.
35 */
36
37 static bool
lower_block(nir_builder * b,nir_block * block)38 lower_block(nir_builder *b, nir_block *block)
39 {
40 bool progress = false;
41
42 b->cursor = nir_after_phis(block);
43 nir_foreach_phi_safe(phi, block) {
44 if (phi->def.divergent) {
45 continue;
46 }
47
48 nir_def* x = nir_as_uniform(b, &phi->def);
49 x->divergent = false;
50 nir_def_rewrite_uses_after(&phi->def, x, x->parent_instr);
51
52 progress = true;
53 }
54
55 return progress;
56 }
57
58
59 static bool
lower_cf_list(nir_builder * b,struct exec_list * cf_list)60 lower_cf_list(nir_builder *b, struct exec_list *cf_list)
61 {
62 bool progress = false;
63
64 foreach_list_typed_safe(nir_cf_node, node, node, cf_list) {
65 switch (node->type) {
66 case nir_cf_node_block:
67 break;
68
69 case nir_cf_node_if: {
70 nir_if *nif = nir_cf_node_as_if(node);
71 if (nir_src_is_divergent(&nif->condition)) {
72 nir_block *succ = nir_cf_node_as_block(nir_cf_node_next(node));
73 progress |= lower_block(b, succ);
74 } else {
75 progress |= lower_cf_list(b, &nif->then_list);
76 progress |= lower_cf_list(b, &nif->else_list);
77 }
78 break;
79 }
80
81 case nir_cf_node_loop: {
82 nir_loop *loop = nir_cf_node_as_loop(node);
83 if (nir_loop_is_divergent(loop)) {
84 nir_block *succ = nir_cf_node_as_block(nir_cf_node_next(node));
85 progress |= lower_block(b, succ);
86 } else {
87 progress |= lower_cf_list(b, &loop->body);
88 progress |= lower_cf_list(b, &loop->continue_list);
89 }
90 break;
91 }
92
93 default:
94 unreachable("Unknown CF node type");
95 }
96 }
97
98 return progress;
99 }
100
101 bool
nak_nir_mark_lcssa_invariants(nir_shader * shader)102 nak_nir_mark_lcssa_invariants(nir_shader *shader)
103 {
104 bool progress = false;
105
106 nir_foreach_function_impl(impl, shader) {
107 nir_builder b = nir_builder_create(impl);
108
109 if (lower_cf_list(&b, &impl->body)) {
110 progress = true;
111 nir_metadata_preserve(impl, nir_metadata_control_flow);
112 } else {
113 nir_metadata_preserve(impl, nir_metadata_all);
114 }
115 }
116
117 return progress;
118 }
119