2 * Copyright © 2015 Intel Corporation
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:
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
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
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.
34 #include "nir_builder.h"
36 struct lower_io_state
{
38 nir_function_impl
*entrypoint
;
39 struct exec_list old_outputs
;
40 struct exec_list old_inputs
;
44 emit_copies(nir_builder
*b
, struct exec_list
*dest_vars
,
45 struct exec_list
*src_vars
)
47 assert(exec_list_length(dest_vars
) == exec_list_length(src_vars
));
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
);
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
57 if (src
->data
.mode
== nir_var_shader_out
&&
58 !src
->data
.fb_fetch_output
)
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.
65 if (dest
->data
.read_only
)
68 nir_copy_var(b
, dest
, src
);
73 emit_output_copies_impl(struct lower_io_state
*state
, nir_function_impl
*impl
)
76 nir_builder_init(&b
, impl
);
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.
82 nir_foreach_block(block
, impl
) {
83 nir_foreach_instr(instr
, block
) {
84 if (instr
->type
!= nir_instr_type_intrinsic
)
87 nir_intrinsic_instr
*intrin
= nir_instr_as_intrinsic(instr
);
88 if (intrin
->intrinsic
== nir_intrinsic_emit_vertex
) {
89 b
.cursor
= nir_before_instr(&intrin
->instr
);
90 emit_copies(&b
, &state
->shader
->outputs
, &state
->old_outputs
);
94 } else if (impl
== state
->entrypoint
) {
95 b
.cursor
= nir_before_block(nir_start_block(impl
));
96 emit_copies(&b
, &state
->old_outputs
, &state
->shader
->outputs
);
98 /* For all other shader types, we need to do the copies right before
99 * the jumps to the end block.
101 struct set_entry
*block_entry
;
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
);
111 emit_input_copies_impl(struct lower_io_state
*state
, nir_function_impl
*impl
)
113 if (impl
== state
->entrypoint
) {
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
);
121 static nir_variable
*
122 create_shadow_temp(struct lower_io_state
*state
, nir_variable
*var
)
124 nir_variable
*nvar
= ralloc(state
->shader
, nir_variable
);
125 memcpy(nvar
, var
, sizeof *nvar
);
127 /* The original is now the temporary */
128 nir_variable
*temp
= var
;
130 /* Reparent the name to the new variable */
131 ralloc_steal(nvar
, nvar
->name
);
133 assert(nvar
->constant_initializer
== NULL
);
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_global
;
139 temp
->data
.read_only
= false;
140 temp
->data
.fb_fetch_output
= false;
141 temp
->data
.compact
= false;
147 nir_lower_io_to_temporaries(nir_shader
*shader
, nir_function_impl
*entrypoint
,
148 bool outputs
, bool inputs
)
150 struct lower_io_state state
;
152 if (shader
->info
.stage
== MESA_SHADER_TESS_CTRL
)
155 state
.shader
= shader
;
156 state
.entrypoint
= entrypoint
;
159 exec_list_move_nodes_to(&shader
->inputs
, &state
.old_inputs
);
161 exec_list_make_empty(&state
.old_inputs
);
164 exec_list_move_nodes_to(&shader
->outputs
, &state
.old_outputs
);
166 exec_list_make_empty(&state
.old_outputs
);
168 /* Walk over all of the outputs turn each output into a temporary and
169 * make a new variable for the actual output.
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
);
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
);
182 nir_foreach_function(function
, shader
) {
183 if (function
->impl
== NULL
)
187 emit_input_copies_impl(&state
, function
->impl
);
190 emit_output_copies_impl(&state
, function
->impl
);
192 nir_metadata_preserve(function
->impl
, nir_metadata_block_index
|
193 nir_metadata_dominance
);
196 exec_list_append(&shader
->globals
, &state
.old_inputs
);
197 exec_list_append(&shader
->globals
, &state
.old_outputs
);
199 nir_fixup_deref_modes(shader
);