nir: add nir_num_variable_modes and nir_var_mem_push_const
[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 state->buffer_barriers = true;
143 state->image_barriers = true;
144 }
145 break;
146
147 default:
148 break;
149 }
150 }
151
152 static bool
153 process_variable(struct access_state *state, nir_variable *var)
154 {
155 if (var->data.mode != nir_var_mem_ssbo &&
156 !(var->data.mode == nir_var_uniform &&
157 glsl_type_is_image(var->type)))
158 return false;
159
160 /* Ignore variables we've already marked */
161 if (var->data.access & ACCESS_CAN_REORDER)
162 return false;
163
164 if (!(var->data.access & ACCESS_NON_WRITEABLE) &&
165 !_mesa_set_search(state->vars_written, var)) {
166 var->data.access |= ACCESS_NON_WRITEABLE;
167 return true;
168 }
169
170 return false;
171 }
172
173 static bool
174 can_reorder(struct access_state *state, enum gl_access_qualifier access,
175 bool is_buffer, bool is_ssbo)
176 {
177 bool is_any_written = is_buffer ? state->buffers_written :
178 state->images_written;
179
180 /* Can we guarantee that the underlying memory is never written? */
181 if (!is_any_written ||
182 ((access & ACCESS_NON_WRITEABLE) &&
183 (access & ACCESS_RESTRICT))) {
184 /* Note: memoryBarrierBuffer() is only guaranteed to flush buffer
185 * variables and not imageBuffer's, so we only consider the GL-level
186 * type here.
187 */
188 bool is_any_barrier = is_ssbo ?
189 state->buffer_barriers : state->image_barriers;
190
191 return (!is_any_barrier || !(access & ACCESS_COHERENT)) &&
192 !(access & ACCESS_VOLATILE);
193 }
194
195 return false;
196 }
197
198 static bool
199 process_intrinsic(struct access_state *state, nir_intrinsic_instr *instr)
200 {
201 switch (instr->intrinsic) {
202 case nir_intrinsic_bindless_image_load:
203 if (nir_intrinsic_access(instr) & ACCESS_CAN_REORDER)
204 return false;
205
206 /* We have less information about bindless intrinsics, since we can't
207 * always trace uses back to the variable. Don't try and infer if it's
208 * read-only, unless there are no image writes at all.
209 */
210 bool progress = false;
211 bool is_buffer =
212 nir_intrinsic_image_dim(instr) == GLSL_SAMPLER_DIM_BUF;
213
214 bool is_any_written =
215 is_buffer ? state->buffers_written : state->images_written;
216
217 if (!(nir_intrinsic_access(instr) & ACCESS_NON_WRITEABLE) &&
218 !is_any_written) {
219 progress = true;
220 nir_intrinsic_set_access(instr,
221 nir_intrinsic_access(instr) |
222 ACCESS_NON_WRITEABLE);
223 }
224
225 if (can_reorder(state, nir_intrinsic_access(instr), is_buffer, false)) {
226 progress = true;
227 nir_intrinsic_set_access(instr,
228 nir_intrinsic_access(instr) |
229 ACCESS_CAN_REORDER);
230 }
231
232 return progress;
233
234 case nir_intrinsic_load_deref:
235 case nir_intrinsic_image_deref_load: {
236 nir_variable *var = nir_intrinsic_get_var(instr, 0);
237
238 if (instr->intrinsic == nir_intrinsic_load_deref &&
239 var->data.mode != nir_var_mem_ssbo)
240 return false;
241
242 if (nir_intrinsic_access(instr) & ACCESS_CAN_REORDER)
243 return false;
244
245 bool progress = false;
246
247 /* Check if we were able to mark the whole variable non-writeable */
248 if (!(nir_intrinsic_access(instr) & ACCESS_NON_WRITEABLE) &&
249 var->data.access & ACCESS_NON_WRITEABLE) {
250 progress = true;
251 nir_intrinsic_set_access(instr,
252 nir_intrinsic_access(instr) |
253 ACCESS_NON_WRITEABLE);
254 }
255
256 bool is_ssbo = var->data.mode == nir_var_mem_ssbo;
257
258 bool is_buffer = is_ssbo ||
259 glsl_get_sampler_dim(glsl_without_array(var->type)) == GLSL_SAMPLER_DIM_BUF;
260
261 if (can_reorder(state, nir_intrinsic_access(instr), is_buffer, is_ssbo)) {
262 progress = true;
263 nir_intrinsic_set_access(instr,
264 nir_intrinsic_access(instr) |
265 ACCESS_CAN_REORDER);
266 }
267
268 return progress;
269 }
270
271 default:
272 return false;
273 }
274 }
275
276 static bool
277 opt_access_impl(struct access_state *state,
278 nir_function_impl *impl)
279 {
280 bool progress = false;
281
282 nir_foreach_block(block, impl) {
283 nir_foreach_instr(instr, block) {
284 if (instr->type == nir_instr_type_intrinsic)
285 progress |= process_intrinsic(state,
286 nir_instr_as_intrinsic(instr));
287 }
288 }
289
290 if (progress) {
291 nir_metadata_preserve(impl,
292 nir_metadata_block_index |
293 nir_metadata_dominance |
294 nir_metadata_live_ssa_defs |
295 nir_metadata_loop_analysis);
296 }
297
298
299 return progress;
300 }
301
302 bool
303 nir_opt_access(nir_shader *shader)
304 {
305 struct access_state state = {
306 .vars_written = _mesa_pointer_set_create(NULL),
307 };
308
309 bool var_progress = false;
310 bool progress = false;
311
312 nir_foreach_function(func, shader) {
313 if (func->impl) {
314 nir_foreach_block(block, func->impl) {
315 nir_foreach_instr(instr, block) {
316 if (instr->type == nir_instr_type_intrinsic)
317 gather_intrinsic(&state, nir_instr_as_intrinsic(instr));
318 }
319 }
320 }
321 }
322
323 nir_foreach_variable(var, &shader->uniforms)
324 var_progress |= process_variable(&state, var);
325
326 nir_foreach_function(func, shader) {
327 if (func->impl) {
328 progress |= opt_access_impl(&state, func->impl);
329
330 /* If we make a change to the uniforms, update all the impls. */
331 if (var_progress) {
332 nir_metadata_preserve(func->impl,
333 nir_metadata_block_index |
334 nir_metadata_dominance |
335 nir_metadata_live_ssa_defs |
336 nir_metadata_loop_analysis);
337 }
338 }
339 }
340
341 progress |= var_progress;
342
343 _mesa_set_destroy(state.vars_written, NULL);
344 return progress;
345 }