nir: Add a pass to combine store_derefs to same vector
[mesa.git] / src / compiler / nir / nir_lower_io_to_temporaries.c
1 /*
2 * Copyright © 2015 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
24 /*
25 * Implements a pass that lowers output and/or input variables to a
26 * temporary plus an output variable with a single copy at each exit
27 * point of the shader and/or an input variable with a single copy
28 * at the entrance point of the shader. This way the output variable
29 * is only ever written once and/or input is only read once, and there
30 * are no indirect outut/input accesses.
31 */
32
33 #include "nir.h"
34 #include "nir_builder.h"
35
36 struct lower_io_state {
37 nir_shader *shader;
38 nir_function_impl *entrypoint;
39 struct exec_list old_outputs;
40 struct exec_list old_inputs;
41 };
42
43 static void
44 emit_copies(nir_builder *b, struct exec_list *dest_vars,
45 struct exec_list *src_vars)
46 {
47 assert(exec_list_length(dest_vars) == exec_list_length(src_vars));
48
49 foreach_two_lists(dest_node, dest_vars, src_node, src_vars) {
50 nir_variable *dest = exec_node_data(nir_variable, dest_node, node);
51 nir_variable *src = exec_node_data(nir_variable, src_node, node);
52
53 /* No need to copy the contents of a non-fb_fetch_output output variable
54 * to the temporary allocated for it, since its initial value is
55 * undefined.
56 */
57 if (src->data.mode == nir_var_shader_out &&
58 !src->data.fb_fetch_output)
59 continue;
60
61 /* Can't copy the contents of the temporary back to a read-only
62 * interface variable. The value of the temporary won't have been
63 * modified by the shader anyway.
64 */
65 if (dest->data.read_only)
66 continue;
67
68 nir_copy_var(b, dest, src);
69 }
70 }
71
72 static void
73 emit_output_copies_impl(struct lower_io_state *state, nir_function_impl *impl)
74 {
75 nir_builder b;
76 nir_builder_init(&b, impl);
77
78 if (state->shader->info.stage == MESA_SHADER_GEOMETRY) {
79 /* For geometry shaders, we have to emit the output copies right
80 * before each EmitVertex call.
81 */
82 nir_foreach_block(block, impl) {
83 nir_foreach_instr(instr, block) {
84 if (instr->type != nir_instr_type_intrinsic)
85 continue;
86
87 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
88 if (intrin->intrinsic == nir_intrinsic_emit_vertex ||
89 intrin->intrinsic == nir_intrinsic_emit_vertex_with_counter) {
90 b.cursor = nir_before_instr(&intrin->instr);
91 emit_copies(&b, &state->shader->outputs, &state->old_outputs);
92 }
93 }
94 }
95 } else if (impl == state->entrypoint) {
96 b.cursor = nir_before_block(nir_start_block(impl));
97 emit_copies(&b, &state->old_outputs, &state->shader->outputs);
98
99 /* For all other shader types, we need to do the copies right before
100 * the jumps to the end block.
101 */
102 set_foreach(impl->end_block->predecessors, block_entry) {
103 struct nir_block *block = (void *)block_entry->key;
104 b.cursor = nir_after_block_before_jump(block);
105 emit_copies(&b, &state->shader->outputs, &state->old_outputs);
106 }
107 }
108 }
109
110 static void
111 emit_input_copies_impl(struct lower_io_state *state, nir_function_impl *impl)
112 {
113 if (impl == state->entrypoint) {
114 nir_builder b;
115 nir_builder_init(&b, impl);
116 b.cursor = nir_before_block(nir_start_block(impl));
117 emit_copies(&b, &state->old_inputs, &state->shader->inputs);
118 }
119 }
120
121 static nir_variable *
122 create_shadow_temp(struct lower_io_state *state, nir_variable *var)
123 {
124 nir_variable *nvar = ralloc(state->shader, nir_variable);
125 memcpy(nvar, var, sizeof *nvar);
126
127 /* The original is now the temporary */
128 nir_variable *temp = var;
129
130 /* Reparent the name to the new variable */
131 ralloc_steal(nvar, nvar->name);
132
133 assert(nvar->constant_initializer == NULL);
134
135 /* Give the original a new name with @<mode>-temp appended */
136 const char *mode = (temp->data.mode == nir_var_shader_in) ? "in" : "out";
137 temp->name = ralloc_asprintf(var, "%s@%s-temp", mode, nvar->name);
138 temp->data.mode = nir_var_shader_temp;
139 temp->data.read_only = false;
140 temp->data.fb_fetch_output = false;
141 temp->data.compact = false;
142
143 return nvar;
144 }
145
146 void
147 nir_lower_io_to_temporaries(nir_shader *shader, nir_function_impl *entrypoint,
148 bool outputs, bool inputs)
149 {
150 struct lower_io_state state;
151
152 if (shader->info.stage == MESA_SHADER_TESS_CTRL)
153 return;
154
155 state.shader = shader;
156 state.entrypoint = entrypoint;
157
158 if (inputs)
159 exec_list_move_nodes_to(&shader->inputs, &state.old_inputs);
160 else
161 exec_list_make_empty(&state.old_inputs);
162
163 if (outputs)
164 exec_list_move_nodes_to(&shader->outputs, &state.old_outputs);
165 else
166 exec_list_make_empty(&state.old_outputs);
167
168 /* Walk over all of the outputs turn each output into a temporary and
169 * make a new variable for the actual output.
170 */
171 nir_foreach_variable(var, &state.old_outputs) {
172 nir_variable *output = create_shadow_temp(&state, var);
173 exec_list_push_tail(&shader->outputs, &output->node);
174 }
175
176 /* and same for inputs: */
177 nir_foreach_variable(var, &state.old_inputs) {
178 nir_variable *input = create_shadow_temp(&state, var);
179 exec_list_push_tail(&shader->inputs, &input->node);
180 }
181
182 nir_foreach_function(function, shader) {
183 if (function->impl == NULL)
184 continue;
185
186 if (inputs)
187 emit_input_copies_impl(&state, function->impl);
188
189 if (outputs)
190 emit_output_copies_impl(&state, function->impl);
191
192 nir_metadata_preserve(function->impl, nir_metadata_block_index |
193 nir_metadata_dominance);
194 }
195
196 exec_list_append(&shader->globals, &state.old_inputs);
197 exec_list_append(&shader->globals, &state.old_outputs);
198
199 nir_fixup_deref_modes(shader);
200 }