• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2014 Intel Corporation
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 (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Connor Abbott (cwabbott0@gmail.com)
25  *
26  */
27 
28 #include "nir.h"
29 #include "nir_builder.h"
30 
31 static bool
deref_used_for_not_store(nir_deref_instr * deref)32 deref_used_for_not_store(nir_deref_instr *deref)
33 {
34    nir_foreach_use(src, &deref->def) {
35       switch (nir_src_parent_instr(src)->type) {
36       case nir_instr_type_deref:
37          if (deref_used_for_not_store(nir_instr_as_deref(nir_src_parent_instr(src))))
38             return true;
39          break;
40 
41       case nir_instr_type_intrinsic: {
42          nir_intrinsic_instr *intrin =
43             nir_instr_as_intrinsic(nir_src_parent_instr(src));
44          /* The first source of copy and store intrinsics is the deref to
45           * write.  Don't record those.
46           */
47          if ((intrin->intrinsic != nir_intrinsic_store_deref &&
48               intrin->intrinsic != nir_intrinsic_copy_deref) ||
49              src != &intrin->src[0])
50             return true;
51          break;
52       }
53 
54       default:
55          /* If it's used by any other instruction type (most likely a texture
56           * or call instruction), consider it used.
57           */
58          return true;
59       }
60    }
61 
62    return false;
63 }
64 
65 static void
add_var_use_deref(nir_deref_instr * deref,struct set * live)66 add_var_use_deref(nir_deref_instr *deref, struct set *live)
67 {
68    if (deref->deref_type != nir_deref_type_var)
69       return;
70 
71    /* Since these local variables don't escape the shader, writing doesn't
72     * make them live.  Only keep them if they are used by some intrinsic.
73     */
74    if ((deref->var->data.mode & (nir_var_function_temp |
75                                  nir_var_shader_temp)) &&
76        !deref_used_for_not_store(deref))
77       return;
78 
79    /*
80     * Shared memory blocks (interface type) alias each other, so be
81     * conservative in that case.
82     */
83    if ((deref->var->data.mode & nir_var_mem_shared) &&
84        !glsl_type_is_interface(deref->var->type) &&
85        !deref_used_for_not_store(deref))
86       return;
87 
88    nir_variable *var = deref->var;
89    do {
90       _mesa_set_add(live, var);
91       /* Also mark the chain of variables used to initialize it. */
92       var = var->pointer_initializer;
93    } while (var);
94 }
95 
96 static void
add_var_use_shader(nir_shader * shader,struct set * live,nir_variable_mode modes)97 add_var_use_shader(nir_shader *shader, struct set *live, nir_variable_mode modes)
98 {
99    nir_foreach_function_impl(impl, shader) {
100       nir_foreach_block(block, impl) {
101          nir_foreach_instr(instr, block) {
102             if (instr->type == nir_instr_type_deref)
103                add_var_use_deref(nir_instr_as_deref(instr), live);
104          }
105       }
106    }
107 }
108 
109 static bool
remove_dead_var_writes(nir_builder * b,nir_instr * instr,void * _)110 remove_dead_var_writes(nir_builder *b, nir_instr *instr, void *_)
111 {
112    if (instr->type == nir_instr_type_deref) {
113       nir_deref_instr *deref = nir_instr_as_deref(instr);
114       if (deref->deref_type == nir_deref_type_cast &&
115           !nir_deref_instr_parent(deref))
116          return false;
117 
118       nir_variable_mode parent_modes;
119       if (deref->deref_type == nir_deref_type_var)
120          parent_modes = deref->var->data.mode;
121       else
122          parent_modes = nir_deref_instr_parent(deref)->modes;
123 
124       /* If the parent mode is 0, then it references a dead variable.
125        * Flag this deref as dead and remove it.
126        */
127       if (parent_modes != 0)
128          return false;
129 
130       deref->modes = 0;
131    } else if (instr->type == nir_instr_type_intrinsic) {
132       nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
133       if (!(intrin->intrinsic == nir_intrinsic_copy_deref ||
134             intrin->intrinsic == nir_intrinsic_store_deref) ||
135           nir_src_as_deref(intrin->src[0])->modes != 0)
136          return false;
137    } else {
138       return false;
139    }
140 
141    nir_instr_remove(instr);
142    return true;
143 }
144 
145 static bool
remove_dead_vars(struct exec_list * var_list,nir_variable_mode modes,struct set * live,const nir_remove_dead_variables_options * opts)146 remove_dead_vars(struct exec_list *var_list, nir_variable_mode modes,
147                  struct set *live, const nir_remove_dead_variables_options *opts)
148 {
149    bool progress = false;
150 
151    nir_foreach_variable_in_list_safe(var, var_list) {
152       if (!(var->data.mode & modes))
153          continue;
154 
155       if (opts && opts->can_remove_var &&
156           !opts->can_remove_var(var, opts->can_remove_var_data))
157          continue;
158 
159       struct set_entry *entry = _mesa_set_search(live, var);
160       if (entry == NULL) {
161          /* Mark this variable as dead by setting the mode to 0 */
162          var->data.mode = 0;
163          exec_node_remove(&var->node);
164          progress = true;
165       }
166    }
167 
168    return progress;
169 }
170 
171 bool
nir_remove_dead_variables(nir_shader * shader,nir_variable_mode modes,const nir_remove_dead_variables_options * opts)172 nir_remove_dead_variables(nir_shader *shader, nir_variable_mode modes,
173                           const nir_remove_dead_variables_options *opts)
174 {
175    bool progress = false;
176    struct set *live = _mesa_pointer_set_create(NULL);
177 
178    add_var_use_shader(shader, live, modes);
179 
180    if (modes & ~nir_var_function_temp) {
181       progress = remove_dead_vars(&shader->variables, modes,
182                                   live, opts) ||
183                  progress;
184    }
185 
186    if (modes & nir_var_function_temp) {
187       nir_foreach_function_impl(impl, shader) {
188          if (remove_dead_vars(&impl->locals,
189                               nir_var_function_temp,
190                               live, opts))
191             progress = true;
192       }
193    }
194 
195    _mesa_set_destroy(live, NULL);
196 
197    if (progress) {
198       nir_shader_instructions_pass(shader, remove_dead_var_writes,
199                                    nir_metadata_control_flow, NULL);
200    } else {
201       nir_shader_preserve_all_metadata(shader);
202    }
203 
204    return progress;
205 }
206