1b8e80941Smrg/*
2b8e80941Smrg * Copyright © 2016 Intel Corporation
3b8e80941Smrg *
4b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5b8e80941Smrg * copy of this software and associated documentation files (the "Software"),
6b8e80941Smrg * to deal in the Software without restriction, including without limitation
7b8e80941Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b8e80941Smrg * and/or sell copies of the Software, and to permit persons to whom the
9b8e80941Smrg * Software is furnished to do so, subject to the following conditions:
10b8e80941Smrg *
11b8e80941Smrg * The above copyright notice and this permission notice (including the next
12b8e80941Smrg * paragraph) shall be included in all copies or substantial portions of the
13b8e80941Smrg * Software.
14b8e80941Smrg *
15b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18b8e80941Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19b8e80941Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20b8e80941Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21b8e80941Smrg * DEALINGS IN THE SOFTWARE.
22b8e80941Smrg */
23b8e80941Smrg
24b8e80941Smrg#include "nir.h"
25b8e80941Smrg
26b8e80941Smrgstatic void
27b8e80941Smrgadd_src(nir_src *src, struct set *invariants)
28b8e80941Smrg{
29b8e80941Smrg   if (src->is_ssa) {
30b8e80941Smrg      _mesa_set_add(invariants, src->ssa);
31b8e80941Smrg   } else {
32b8e80941Smrg      _mesa_set_add(invariants, src->reg.reg);
33b8e80941Smrg   }
34b8e80941Smrg}
35b8e80941Smrg
36b8e80941Smrgstatic bool
37b8e80941Smrgadd_src_cb(nir_src *src, void *state)
38b8e80941Smrg{
39b8e80941Smrg   add_src(src, state);
40b8e80941Smrg   return true;
41b8e80941Smrg}
42b8e80941Smrg
43b8e80941Smrgstatic bool
44b8e80941Smrgdest_is_invariant(nir_dest *dest, struct set *invariants)
45b8e80941Smrg{
46b8e80941Smrg   if (dest->is_ssa) {
47b8e80941Smrg      return _mesa_set_search(invariants, &dest->ssa);
48b8e80941Smrg   } else {
49b8e80941Smrg      return _mesa_set_search(invariants, dest->reg.reg);
50b8e80941Smrg   }
51b8e80941Smrg}
52b8e80941Smrg
53b8e80941Smrgstatic void
54b8e80941Smrgadd_cf_node(nir_cf_node *cf, struct set *invariants)
55b8e80941Smrg{
56b8e80941Smrg   if (cf->type == nir_cf_node_if) {
57b8e80941Smrg      nir_if *if_stmt = nir_cf_node_as_if(cf);
58b8e80941Smrg      add_src(&if_stmt->condition, invariants);
59b8e80941Smrg   }
60b8e80941Smrg
61b8e80941Smrg   if (cf->parent)
62b8e80941Smrg      add_cf_node(cf->parent, invariants);
63b8e80941Smrg}
64b8e80941Smrg
65b8e80941Smrgstatic void
66b8e80941Smrgadd_var(nir_variable *var, struct set *invariants)
67b8e80941Smrg{
68b8e80941Smrg   /* Because we pass the result of nir_intrinsic_get_var directly to this
69b8e80941Smrg    * function, it's possible for var to be NULL if, for instance, there's a
70b8e80941Smrg    * cast somewhere in the chain.
71b8e80941Smrg    */
72b8e80941Smrg   if (var != NULL)
73b8e80941Smrg      _mesa_set_add(invariants, var);
74b8e80941Smrg}
75b8e80941Smrg
76b8e80941Smrgstatic bool
77b8e80941Smrgvar_is_invariant(nir_variable *var, struct set * invariants)
78b8e80941Smrg{
79b8e80941Smrg   /* Because we pass the result of nir_intrinsic_get_var directly to this
80b8e80941Smrg    * function, it's possible for var to be NULL if, for instance, there's a
81b8e80941Smrg    * cast somewhere in the chain.
82b8e80941Smrg    */
83b8e80941Smrg   return var && (var->data.invariant || _mesa_set_search(invariants, var));
84b8e80941Smrg}
85b8e80941Smrg
86b8e80941Smrgstatic void
87b8e80941Smrgpropagate_invariant_instr(nir_instr *instr, struct set *invariants)
88b8e80941Smrg{
89b8e80941Smrg   switch (instr->type) {
90b8e80941Smrg   case nir_instr_type_alu: {
91b8e80941Smrg      nir_alu_instr *alu = nir_instr_as_alu(instr);
92b8e80941Smrg      if (!dest_is_invariant(&alu->dest.dest, invariants))
93b8e80941Smrg         break;
94b8e80941Smrg
95b8e80941Smrg      alu->exact = true;
96b8e80941Smrg      nir_foreach_src(instr, add_src_cb, invariants);
97b8e80941Smrg      break;
98b8e80941Smrg   }
99b8e80941Smrg
100b8e80941Smrg   case nir_instr_type_tex: {
101b8e80941Smrg      nir_tex_instr *tex = nir_instr_as_tex(instr);
102b8e80941Smrg      if (dest_is_invariant(&tex->dest, invariants))
103b8e80941Smrg         nir_foreach_src(instr, add_src_cb, invariants);
104b8e80941Smrg      break;
105b8e80941Smrg   }
106b8e80941Smrg
107b8e80941Smrg   case nir_instr_type_intrinsic: {
108b8e80941Smrg      nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
109b8e80941Smrg      switch (intrin->intrinsic) {
110b8e80941Smrg      case nir_intrinsic_copy_deref:
111b8e80941Smrg         /* If the destination is invariant then so is the source */
112b8e80941Smrg         if (var_is_invariant(nir_intrinsic_get_var(intrin, 0), invariants))
113b8e80941Smrg            add_var(nir_intrinsic_get_var(intrin, 1), invariants);
114b8e80941Smrg         break;
115b8e80941Smrg
116b8e80941Smrg      case nir_intrinsic_load_deref:
117b8e80941Smrg         if (dest_is_invariant(&intrin->dest, invariants))
118b8e80941Smrg            add_var(nir_intrinsic_get_var(intrin, 0), invariants);
119b8e80941Smrg         break;
120b8e80941Smrg
121b8e80941Smrg      case nir_intrinsic_store_deref:
122b8e80941Smrg         if (var_is_invariant(nir_intrinsic_get_var(intrin, 0), invariants))
123b8e80941Smrg            add_src(&intrin->src[1], invariants);
124b8e80941Smrg         break;
125b8e80941Smrg
126b8e80941Smrg      default:
127b8e80941Smrg         /* Nothing to do */
128b8e80941Smrg         break;
129b8e80941Smrg      }
130b8e80941Smrg   }
131b8e80941Smrg
132b8e80941Smrg   case nir_instr_type_deref:
133b8e80941Smrg   case nir_instr_type_jump:
134b8e80941Smrg   case nir_instr_type_ssa_undef:
135b8e80941Smrg   case nir_instr_type_load_const:
136b8e80941Smrg      break; /* Nothing to do */
137b8e80941Smrg
138b8e80941Smrg   case nir_instr_type_phi: {
139b8e80941Smrg      nir_phi_instr *phi = nir_instr_as_phi(instr);
140b8e80941Smrg      if (!dest_is_invariant(&phi->dest, invariants))
141b8e80941Smrg         break;
142b8e80941Smrg
143b8e80941Smrg      nir_foreach_phi_src(src, phi) {
144b8e80941Smrg         add_src(&src->src, invariants);
145b8e80941Smrg         add_cf_node(&src->pred->cf_node, invariants);
146b8e80941Smrg      }
147b8e80941Smrg      break;
148b8e80941Smrg   }
149b8e80941Smrg
150b8e80941Smrg   case nir_instr_type_call:
151b8e80941Smrg      unreachable("This pass must be run after function inlining");
152b8e80941Smrg
153b8e80941Smrg   case nir_instr_type_parallel_copy:
154b8e80941Smrg   default:
155b8e80941Smrg      unreachable("Cannot have this instruction type");
156b8e80941Smrg   }
157b8e80941Smrg}
158b8e80941Smrg
159b8e80941Smrgstatic bool
160b8e80941Smrgpropagate_invariant_impl(nir_function_impl *impl, struct set *invariants)
161b8e80941Smrg{
162b8e80941Smrg   bool progress = false;
163b8e80941Smrg
164b8e80941Smrg   while (true) {
165b8e80941Smrg      uint32_t prev_entries = invariants->entries;
166b8e80941Smrg
167b8e80941Smrg      nir_foreach_block_reverse(block, impl) {
168b8e80941Smrg         nir_foreach_instr_reverse(instr, block)
169b8e80941Smrg            propagate_invariant_instr(instr, invariants);
170b8e80941Smrg      }
171b8e80941Smrg
172b8e80941Smrg      /* Keep running until we make no more progress. */
173b8e80941Smrg      if (invariants->entries > prev_entries) {
174b8e80941Smrg         progress = true;
175b8e80941Smrg         continue;
176b8e80941Smrg      } else {
177b8e80941Smrg         break;
178b8e80941Smrg      }
179b8e80941Smrg   }
180b8e80941Smrg
181b8e80941Smrg   if (progress) {
182b8e80941Smrg      nir_metadata_preserve(impl, nir_metadata_block_index |
183b8e80941Smrg                                  nir_metadata_dominance |
184b8e80941Smrg                                  nir_metadata_live_ssa_defs);
185b8e80941Smrg   }
186b8e80941Smrg
187b8e80941Smrg   return progress;
188b8e80941Smrg}
189b8e80941Smrg
190b8e80941Smrgbool
191b8e80941Smrgnir_propagate_invariant(nir_shader *shader)
192b8e80941Smrg{
193b8e80941Smrg   /* Hash set of invariant things */
194b8e80941Smrg   struct set *invariants = _mesa_pointer_set_create(NULL);
195b8e80941Smrg
196b8e80941Smrg   bool progress = false;
197b8e80941Smrg   nir_foreach_function(function, shader) {
198b8e80941Smrg      if (function->impl && propagate_invariant_impl(function->impl, invariants))
199b8e80941Smrg         progress = true;
200b8e80941Smrg   }
201b8e80941Smrg
202b8e80941Smrg   _mesa_set_destroy(invariants, NULL);
203b8e80941Smrg
204b8e80941Smrg   return progress;
205b8e80941Smrg}
206