nir: Add missing nir_var_mem_global to various passes
[mesa.git] / src / compiler / nir / nir_opt_access.c
1 /*
2 * Copyright © 2019 Valve 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
24 #include "nir.h"
25
26 /* This pass optimizes GL access qualifiers. So far it does two things:
27 *
28 * - Infer readonly when it's missing.
29 * - Infer ACCESS_CAN_REORDER when the following are true:
30 * - Either there are no writes, or ACCESS_NON_WRITEABLE and ACCESS_RESTRICT
31 * are both set. In either case there are no writes to the underlying
32 * memory.
33 * - If ACCESS_COHERENT is set, then there must be no memory barriers
34 * involving the access. Coherent accesses may return different results
35 * before and after barriers.
36 * - ACCESS_VOLATILE is not set.
37 *
38 * If these conditions are true, then image and buffer reads may be treated as
39 * if they were uniform buffer reads, i.e. they may be arbitrarily moved,
40 * combined, rematerialized etc.
41 */
42
43 struct access_state {
44 struct set *vars_written;
45 bool images_written;
46 bool buffers_written;
47 bool image_barriers;
48 bool buffer_barriers;
49 };
50
51 static void
52 gather_intrinsic(struct access_state *state, nir_intrinsic_instr *instr)
53 {
54 nir_variable *var;
55 switch (instr->intrinsic) {
56 case nir_intrinsic_image_deref_store:
57 case nir_intrinsic_image_deref_atomic_add:
58 case nir_intrinsic_image_deref_atomic_imin:
59 case nir_intrinsic_image_deref_atomic_umin:
60 case nir_intrinsic_image_deref_atomic_imax:
61 case nir_intrinsic_image_deref_atomic_umax:
62 case nir_intrinsic_image_deref_atomic_and:
63 case nir_intrinsic_image_deref_atomic_or:
64 case nir_intrinsic_image_deref_atomic_xor:
65 case nir_intrinsic_image_deref_atomic_exchange:
66 case nir_intrinsic_image_deref_atomic_comp_swap:
67 case nir_intrinsic_image_deref_atomic_fadd:
68 var = nir_intrinsic_get_var(instr, 0);
69
70 /* In OpenGL, buffer images use normal buffer objects, whereas other
71 * image types use textures which cannot alias with buffer objects.
72 * Therefore we have to group buffer samplers together with SSBO's.
73 */
74 if (glsl_get_sampler_dim(glsl_without_array(var->type)) ==
75 GLSL_SAMPLER_DIM_BUF)
76 state->buffers_written = true;
77 else
78 state->images_written = true;
79
80 if (var->data.mode == nir_var_uniform)
81 _mesa_set_add(state->vars_written, var);
82 break;
83
84 case nir_intrinsic_bindless_image_store:
85 case nir_intrinsic_bindless_image_atomic_add:
86 case nir_intrinsic_bindless_image_atomic_imin:
87 case nir_intrinsic_bindless_image_atomic_umin:
88 case nir_intrinsic_bindless_image_atomic_imax:
89 case nir_intrinsic_bindless_image_atomic_umax:
90 case nir_intrinsic_bindless_image_atomic_and:
91 case nir_intrinsic_bindless_image_atomic_or:
92 case nir_intrinsic_bindless_image_atomic_xor:
93 case nir_intrinsic_bindless_image_atomic_exchange:
94 case nir_intrinsic_bindless_image_atomic_comp_swap:
95 case nir_intrinsic_bindless_image_atomic_fadd:
96 if (nir_intrinsic_image_dim(instr) == GLSL_SAMPLER_DIM_BUF)
97 state->buffers_written = true;
98 else
99 state->images_written = true;
100 break;
101
102 case nir_intrinsic_store_deref:
103 case nir_intrinsic_deref_atomic_add:
104 case nir_intrinsic_deref_atomic_imin:
105 case nir_intrinsic_deref_atomic_umin:
106 case nir_intrinsic_deref_atomic_imax:
107 case nir_intrinsic_deref_atomic_umax:
108 case nir_intrinsic_deref_atomic_and:
109 case nir_intrinsic_deref_atomic_or:
110 case nir_intrinsic_deref_atomic_xor:
111 case nir_intrinsic_deref_atomic_exchange:
112 case nir_intrinsic_deref_atomic_comp_swap:
113 case nir_intrinsic_deref_atomic_fadd:
114 case nir_intrinsic_deref_atomic_fmin:
115 case nir_intrinsic_deref_atomic_fmax:
116 case nir_intrinsic_deref_atomic_fcomp_swap:
117 var = nir_intrinsic_get_var(instr, 0);
118 if (var->data.mode != nir_var_mem_ssbo)
119 break;
120
121 _mesa_set_add(state->vars_written, var);
122 state->buffers_written = true;
123
124 case nir_intrinsic_memory_barrier:
125 state->buffer_barriers = true;
126 state->image_barriers = true;
127 break;
128
129 case nir_intrinsic_memory_barrier_buffer:
130 state->buffer_barriers = true;
131 break;
132
133 case nir_intrinsic_memory_barrier_image:
134 state->image_barriers = true;
135 break;
136
137 case nir_intrinsic_scoped_memory_barrier:
138 /* TODO: Could be more granular if we had nir_var_mem_image. */
139 if (nir_intrinsic_memory_modes(instr) & (nir_var_mem_ubo |
140 nir_var_mem_ssbo |
141 nir_var_uniform |
142 nir_var_mem_global)) {
143 state->buffer_barriers = true;
144 state->image_barriers = true;
145 }
146 break;
147
148 default:
149 break;
150 }
151 }
152
153 static bool
154 process_variable(struct access_state *state, nir_variable *var)
155 {
156 if (var->data.mode != nir_var_mem_ssbo &&
157 !(var->data.mode == nir_var_uniform &&
158 glsl_type_is_image(var->type)))
159 return false;
160
161 /* Ignore variables we've already marked */
162 if (var->data.access & ACCESS_CAN_REORDER)
163 return false;
164
165 if (!(var->data.access & ACCESS_NON_WRITEABLE) &&
166 !_mesa_set_search(state->vars_written, var)) {
167 var->data.access |= ACCESS_NON_WRITEABLE;
168 return true;
169 }
170
171 return false;
172 }
173
174 static bool
175 can_reorder(struct access_state *state, enum gl_access_qualifier access,
176 bool is_buffer, bool is_ssbo)
177 {
178 bool is_any_written = is_buffer ? state->buffers_written :
179 state->images_written;
180
181 /* Can we guarantee that the underlying memory is never written? */
182 if (!is_any_written ||
183 ((access & ACCESS_NON_WRITEABLE) &&
184 (access & ACCESS_RESTRICT))) {
185 /* Note: memoryBarrierBuffer() is only guaranteed to flush buffer
186 * variables and not imageBuffer's, so we only consider the GL-level
187 * type here.
188 */
189 bool is_any_barrier = is_ssbo ?
190 state->buffer_barriers : state->image_barriers;
191
192 return (!is_any_barrier || !(access & ACCESS_COHERENT)) &&
193 !(access & ACCESS_VOLATILE);
194 }
195
196 return false;
197 }
198
199 static bool
200 process_intrinsic(struct access_state *state, nir_intrinsic_instr *instr)
201 {
202 switch (instr->intrinsic) {
203 case nir_intrinsic_bindless_image_load:
204 if (nir_intrinsic_access(instr) & ACCESS_CAN_REORDER)
205 return false;
206
207 /* We have less information about bindless intrinsics, since we can't
208 * always trace uses back to the variable. Don't try and infer if it's
209 * read-only, unless there are no image writes at all.
210 */
211 bool progress = false;
212 bool is_buffer =
213 nir_intrinsic_image_dim(instr) == GLSL_SAMPLER_DIM_BUF;
214
215 bool is_any_written =
216 is_buffer ? state->buffers_written : state->images_written;
217
218 if (!(nir_intrinsic_access(instr) & ACCESS_NON_WRITEABLE) &&
219 !is_any_written) {
220 progress = true;
221 nir_intrinsic_set_access(instr,
222 nir_intrinsic_access(instr) |
223 ACCESS_NON_WRITEABLE);
224 }
225
226 if (can_reorder(state, nir_intrinsic_access(instr), is_buffer, false)) {
227 progress = true;
228 nir_intrinsic_set_access(instr,
229 nir_intrinsic_access(instr) |
230 ACCESS_CAN_REORDER);
231 }
232
233 return progress;
234
235 case nir_intrinsic_load_deref:
236 case nir_intrinsic_image_deref_load: {
237 nir_variable *var = nir_intrinsic_get_var(instr, 0);
238
239 if (instr->intrinsic == nir_intrinsic_load_deref &&
240 var->data.mode != nir_var_mem_ssbo)
241 return false;
242
243 if (nir_intrinsic_access(instr) & ACCESS_CAN_REORDER)
244 return false;
245
246 bool progress = false;
247
248 /* Check if we were able to mark the whole variable non-writeable */
249 if (!(nir_intrinsic_access(instr) & ACCESS_NON_WRITEABLE) &&
250 var->data.access & ACCESS_NON_WRITEABLE) {
251 progress = true;
252 nir_intrinsic_set_access(instr,
253 nir_intrinsic_access(instr) |
254 ACCESS_NON_WRITEABLE);
255 }
256
257 bool is_ssbo = var->data.mode == nir_var_mem_ssbo;
258
259 bool is_buffer = is_ssbo ||
260 glsl_get_sampler_dim(glsl_without_array(var->type)) == GLSL_SAMPLER_DIM_BUF;
261
262 if (can_reorder(state, nir_intrinsic_access(instr), is_buffer, is_ssbo)) {
263 progress = true;
264 nir_intrinsic_set_access(instr,
265 nir_intrinsic_access(instr) |
266 ACCESS_CAN_REORDER);
267 }
268
269 return progress;
270 }
271
272 default:
273 return false;
274 }
275 }
276
277 static bool
278 opt_access_impl(struct access_state *state,
279 nir_function_impl *impl)
280 {
281 bool progress = false;
282
283 nir_foreach_block(block, impl) {
284 nir_foreach_instr(instr, block) {
285 if (instr->type == nir_instr_type_intrinsic)
286 progress |= process_intrinsic(state,
287 nir_instr_as_intrinsic(instr));
288 }
289 }
290
291 if (progress) {
292 nir_metadata_preserve(impl,
293 nir_metadata_block_index |
294 nir_metadata_dominance |
295 nir_metadata_live_ssa_defs |
296 nir_metadata_loop_analysis);
297 }
298
299
300 return progress;
301 }
302
303 bool
304 nir_opt_access(nir_shader *shader)
305 {
306 struct access_state state = {
307 .vars_written = _mesa_pointer_set_create(NULL),
308 };
309
310 bool var_progress = false;
311 bool progress = false;
312
313 nir_foreach_function(func, shader) {
314 if (func->impl) {
315 nir_foreach_block(block, func->impl) {
316 nir_foreach_instr(instr, block) {
317 if (instr->type == nir_instr_type_intrinsic)
318 gather_intrinsic(&state, nir_instr_as_intrinsic(instr));
319 }
320 }
321 }
322 }
323
324 nir_foreach_variable(var, &shader->uniforms)
325 var_progress |= process_variable(&state, var);
326
327 nir_foreach_function(func, shader) {
328 if (func->impl) {
329 progress |= opt_access_impl(&state, func->impl);
330
331 /* If we make a change to the uniforms, update all the impls. */
332 if (var_progress) {
333 nir_metadata_preserve(func->impl,
334 nir_metadata_block_index |
335 nir_metadata_dominance |
336 nir_metadata_live_ssa_defs |
337 nir_metadata_loop_analysis);
338 }
339 }
340 }
341
342 progress |= var_progress;
343
344 _mesa_set_destroy(state.vars_written, NULL);
345 return progress;
346 }