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 variables to a temporary plus an
26 * output variable with a single copy at each exit point of the shader.
27 * This way the output variable is only ever written.
32 struct lower_io_state
{
34 nir_function
*entrypoint
;
35 struct exec_list old_outputs
;
39 emit_copies(nir_cursor cursor
, nir_shader
*shader
, struct exec_list
*new_vars
,
40 struct exec_list
*old_vars
)
42 assert(exec_list_length(new_vars
) == exec_list_length(old_vars
));
44 foreach_two_lists(new_node
, new_vars
, old_node
, old_vars
) {
45 nir_variable
*newv
= exec_node_data(nir_variable
, new_node
, node
);
46 nir_variable
*temp
= exec_node_data(nir_variable
, old_node
, node
);
48 nir_intrinsic_instr
*copy
=
49 nir_intrinsic_instr_create(shader
, nir_intrinsic_copy_var
);
50 copy
->variables
[0] = nir_deref_var_create(copy
, newv
);
51 copy
->variables
[1] = nir_deref_var_create(copy
, temp
);
53 nir_instr_insert(cursor
, ©
->instr
);
58 emit_output_copies_impl(struct lower_io_state
*state
, nir_function_impl
*impl
)
60 if (state
->shader
->stage
== MESA_SHADER_GEOMETRY
) {
61 /* For geometry shaders, we have to emit the output copies right
62 * before each EmitVertex call.
64 nir_foreach_block(block
, impl
) {
65 nir_foreach_instr(instr
, block
) {
66 if (instr
->type
!= nir_instr_type_intrinsic
)
69 nir_intrinsic_instr
*intrin
= nir_instr_as_intrinsic(instr
);
70 if (intrin
->intrinsic
== nir_intrinsic_emit_vertex
) {
71 nir_cursor cursor
= nir_before_instr(&intrin
->instr
);
72 emit_copies(cursor
, state
->shader
, &state
->shader
->outputs
,
77 } else if (impl
->function
== state
->entrypoint
) {
78 /* For all other shader types, we need to do the copies right before
79 * the jumps to the end block.
81 struct set_entry
*block_entry
;
82 set_foreach(impl
->end_block
->predecessors
, block_entry
) {
83 struct nir_block
*block
= (void *)block_entry
->key
;
84 nir_cursor cursor
= nir_after_block_before_jump(block
);
85 emit_copies(cursor
, state
->shader
, &state
->shader
->outputs
,
92 create_shadow_temp(struct lower_io_state
*state
, nir_variable
*var
)
94 nir_variable
*nvar
= ralloc(state
->shader
, nir_variable
);
95 memcpy(nvar
, var
, sizeof *nvar
);
97 /* The original is now the temporary */
98 nir_variable
*temp
= var
;
100 /* Reparent the name to the new variable */
101 ralloc_steal(nvar
, nvar
->name
);
103 /* Reparent the constant initializer (if any) */
104 ralloc_steal(nvar
, nvar
->constant_initializer
);
106 /* Give the output a new name with @out-temp appended */
107 const char *mode
= "out";
108 temp
->name
= ralloc_asprintf(var
, "%s@%s-temp", mode
, nvar
->name
);
109 temp
->data
.mode
= nir_var_global
;
110 temp
->constant_initializer
= NULL
;
116 nir_lower_io_to_temporaries(nir_shader
*shader
, nir_function
*entrypoint
)
118 struct lower_io_state state
;
120 if (shader
->stage
== MESA_SHADER_TESS_CTRL
)
123 state
.shader
= shader
;
124 state
.entrypoint
= entrypoint
;
125 exec_list_move_nodes_to(&shader
->outputs
, &state
.old_outputs
);
127 /* Walk over all of the outputs turn each output into a temporary and
128 * make a new variable for the actual output.
130 nir_foreach_variable(var
, &state
.old_outputs
) {
131 nir_variable
*output
= create_shadow_temp(&state
, var
);
132 exec_list_push_tail(&shader
->outputs
, &output
->node
);
135 nir_foreach_function(function
, shader
) {
136 if (function
->impl
== NULL
)
139 emit_output_copies_impl(&state
, function
->impl
);
141 nir_metadata_preserve(function
->impl
, nir_metadata_block_index
|
142 nir_metadata_dominance
);
145 exec_list_append(&shader
->globals
, &state
.old_outputs
);