1 /*
2  * Copyright © 2016 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  *    Jason Ekstrand (jason@jlekstrand.net)
25  *
26  */
27 
28 /*
29  * This lowering pass converts references to variables with loads/stores to
30  * scratch space based on a few configurable parameters.
31  */
32 
33 #include "nir.h"
34 #include "nir_builder.h"
35 #include "nir_deref.h"
36 
37 static void
lower_load_store(nir_builder * b,nir_intrinsic_instr * intrin,glsl_type_size_align_func size_align)38 lower_load_store(nir_builder *b,
39                  nir_intrinsic_instr *intrin,
40                  glsl_type_size_align_func size_align)
41 {
42    b->cursor = nir_before_instr(&intrin->instr);
43 
44    nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
45    nir_variable *var = nir_deref_instr_get_variable(deref);
46 
47    nir_ssa_def *offset =
48       nir_iadd_imm(b, nir_build_deref_offset(b, deref, size_align),
49                       var->data.location);
50 
51    unsigned align, UNUSED size;
52    size_align(deref->type, &size, &align);
53 
54    if (intrin->intrinsic == nir_intrinsic_load_deref) {
55       unsigned bit_size = intrin->dest.ssa.bit_size;
56       nir_ssa_def *value = nir_load_scratch(
57          b, intrin->num_components, bit_size == 1 ? 32 : bit_size, offset, .align_mul=align);
58       if (bit_size == 1)
59          value = nir_b2b1(b, value);
60 
61       nir_ssa_def_rewrite_uses(&intrin->dest.ssa, value);
62    } else {
63       assert(intrin->intrinsic == nir_intrinsic_store_deref);
64 
65       assert(intrin->src[1].is_ssa);
66       nir_ssa_def *value = intrin->src[1].ssa;
67       if (value->bit_size == 1)
68          value = nir_b2b32(b, value);
69 
70       nir_store_scratch(b, value, offset, .align_mul=align,
71                            .write_mask=nir_intrinsic_write_mask(intrin));
72    }
73 
74    nir_instr_remove(&intrin->instr);
75    nir_deref_instr_remove_if_unused(deref);
76 }
77 
only_used_for_load_store(nir_deref_instr * deref)78 static bool only_used_for_load_store(nir_deref_instr *deref)
79 {
80    nir_foreach_use(src, &deref->dest.ssa) {
81       if (!src->parent_instr)
82          return false;
83       if (src->parent_instr->type == nir_instr_type_deref) {
84           if (!only_used_for_load_store(nir_instr_as_deref(src->parent_instr)))
85             return false;
86       } else if (src->parent_instr->type != nir_instr_type_intrinsic) {
87          return false;
88       } else {
89          nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(src->parent_instr);
90          if (intrin->intrinsic != nir_intrinsic_load_deref &&
91              intrin->intrinsic != nir_intrinsic_store_deref)
92             return false;
93       }
94    }
95    return true;
96 }
97 
98 bool
nir_lower_vars_to_scratch(nir_shader * shader,nir_variable_mode modes,int size_threshold,glsl_type_size_align_func size_align)99 nir_lower_vars_to_scratch(nir_shader *shader,
100                           nir_variable_mode modes,
101                           int size_threshold,
102                           glsl_type_size_align_func size_align)
103 {
104    struct set *set = _mesa_pointer_set_create(NULL);
105 
106    /* First, we walk the instructions and flag any variables we want to lower
107     * by removing them from their respective list and setting the mode to 0.
108     */
109    nir_foreach_function(function, shader) {
110       nir_foreach_block(block, function->impl) {
111          nir_foreach_instr(instr, block) {
112             if (instr->type != nir_instr_type_intrinsic)
113                continue;
114 
115             nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
116             if (intrin->intrinsic != nir_intrinsic_load_deref &&
117                 intrin->intrinsic != nir_intrinsic_store_deref)
118                continue;
119 
120             nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
121             if (!nir_deref_mode_is_one_of(deref, modes))
122                continue;
123 
124             if (!nir_deref_instr_has_indirect(nir_src_as_deref(intrin->src[0])))
125                continue;
126 
127             nir_variable *var = nir_deref_instr_get_variable(deref);
128             if (!var)
129                continue;
130 
131             /* We set var->mode to 0 to indicate that a variable will be moved
132              * to scratch.  Don't assign a scratch location twice.
133              */
134             if (var->data.mode == 0)
135                continue;
136 
137             unsigned var_size, var_align;
138             size_align(var->type, &var_size, &var_align);
139             if (var_size <= size_threshold)
140                continue;
141 
142             _mesa_set_add(set, var);
143          }
144       }
145    }
146 
147    if (set->entries == 0) {
148       _mesa_set_destroy(set, NULL);
149       return false;
150    }
151 
152    nir_foreach_function(function, shader) {
153       nir_foreach_block(block, function->impl) {
154          nir_foreach_instr(instr, block) {
155             if (instr->type != nir_instr_type_deref)
156                continue;
157 
158             nir_deref_instr *deref = nir_instr_as_deref(instr);
159             if (deref->deref_type != nir_deref_type_var)
160                continue;
161 
162             struct set_entry *entry = _mesa_set_search(set, deref->var);
163             if (!entry)
164                continue;
165 
166             if (!only_used_for_load_store(deref))
167                _mesa_set_remove(set, entry);
168          }
169       }
170    }
171 
172    set_foreach(set, entry) {
173       nir_variable* var = (void*)entry->key;
174 
175       /* Remove it from its list */
176       exec_node_remove(&var->node);
177       /* Invalid mode used to flag "moving to scratch" */
178       var->data.mode = 0;
179 
180       /* We don't allocate space here as iteration in this loop is
181        * non-deterministic due to the nir_variable pointers. */
182       var->data.location = INT_MAX;
183    }
184 
185    bool progress = false;
186    nir_foreach_function(function, shader) {
187       if (!function->impl)
188          continue;
189 
190       nir_builder build;
191       nir_builder_init(&build, function->impl);
192 
193       bool impl_progress = false;
194       nir_foreach_block(block, function->impl) {
195          nir_foreach_instr_safe(instr, block) {
196             if (instr->type != nir_instr_type_intrinsic)
197                continue;
198 
199             nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
200             if (intrin->intrinsic != nir_intrinsic_load_deref &&
201                 intrin->intrinsic != nir_intrinsic_store_deref)
202                continue;
203 
204             nir_variable *var = nir_intrinsic_get_var(intrin, 0);
205             /* Variables flagged for lowering above have mode == 0 */
206             if (!var || var->data.mode)
207                continue;
208 
209             if (var->data.location == INT_MAX) {
210                unsigned var_size, var_align;
211                size_align(var->type, &var_size, &var_align);
212 
213                var->data.location = ALIGN_POT(shader->scratch_size, var_align);
214                shader->scratch_size = var->data.location + var_size;
215             }
216 
217             lower_load_store(&build, intrin, size_align);
218             impl_progress = true;
219          }
220       }
221 
222       if (impl_progress) {
223          progress = true;
224          nir_metadata_preserve(function->impl, nir_metadata_block_index |
225                                                nir_metadata_dominance);
226       } else {
227          nir_metadata_preserve(function->impl, nir_metadata_all);
228       }
229    }
230 
231    _mesa_set_destroy(set, NULL);
232 
233    return progress;
234 }
235