• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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