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