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