1/* 2 * Copyright © 2018 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#include "nir.h" 25#include "nir_deref.h" 26 27struct split_struct_state { 28 void *dead_ctx; 29 30 struct hash_table *var_to_member_map; 31}; 32 33static nir_variable * 34find_var_member(struct nir_variable *var, unsigned member, 35 struct hash_table *var_to_member_map) 36{ 37 struct hash_entry *map_entry = 38 _mesa_hash_table_search(var_to_member_map, var); 39 if (map_entry == NULL) 40 return NULL; 41 42 nir_variable **members = map_entry->data; 43 assert(member < var->num_members); 44 return members[member]; 45} 46 47static const struct glsl_type * 48member_type(const struct glsl_type *type, unsigned index) 49{ 50 if (glsl_type_is_array(type)) { 51 const struct glsl_type *elem = 52 member_type(glsl_get_array_element(type), index); 53 assert(glsl_get_explicit_stride(type) == 0); 54 return glsl_array_type(elem, glsl_get_length(type), 0); 55 } else { 56 assert(glsl_type_is_struct_or_ifc(type)); 57 assert(index < glsl_get_length(type)); 58 return glsl_get_struct_field(type, index); 59 } 60} 61 62static void 63split_variable(struct nir_variable *var, nir_shader *shader, 64 struct hash_table *var_to_member_map, void *dead_ctx) 65{ 66 assert(var->state_slots == NULL); 67 68 /* Constant initializers are currently not handled */ 69 assert(var->constant_initializer == NULL && var->pointer_initializer == NULL); 70 71 nir_variable **members = 72 ralloc_array(dead_ctx, nir_variable *, var->num_members); 73 74 for (unsigned i = 0; i < var->num_members; i++) { 75 char *member_name = NULL; 76 if (var->name) { 77 /* Calculate a reasonable variable name */ 78 member_name = ralloc_strdup(dead_ctx, var->name); 79 const struct glsl_type *t = var->type; 80 while (glsl_type_is_array(t)) { 81 ralloc_strcat(&member_name, "[*]"); 82 t = glsl_get_array_element(t); 83 } 84 const char *field_name = glsl_get_struct_elem_name(t, i); 85 if (field_name) { 86 member_name = ralloc_asprintf(dead_ctx, "%s.%s", 87 member_name, field_name); 88 } else { 89 member_name = ralloc_asprintf(dead_ctx, "%s.@%d", member_name, i); 90 } 91 } 92 93 members[i] = 94 nir_variable_create(shader, var->members[i].mode, 95 member_type(var->type, i), member_name); 96 if (var->interface_type) { 97 members[i]->interface_type = 98 glsl_get_struct_field(var->interface_type, i); 99 } 100 members[i]->data = var->members[i]; 101 } 102 103 _mesa_hash_table_insert(var_to_member_map, var, members); 104} 105 106static nir_deref_instr * 107build_member_deref(nir_builder *b, nir_deref_instr *deref, nir_variable *member) 108{ 109 if (deref->deref_type == nir_deref_type_var) { 110 return nir_build_deref_var(b, member); 111 } else { 112 nir_deref_instr *parent = 113 build_member_deref(b, nir_deref_instr_parent(deref), member); 114 return nir_build_deref_follower(b, parent, deref); 115 } 116} 117 118static void 119rewrite_deref_instr(nir_builder *b, nir_deref_instr *deref, 120 struct hash_table *var_to_member_map) 121{ 122 /* We must be a struct deref */ 123 if (deref->deref_type != nir_deref_type_struct) 124 return; 125 126 nir_deref_instr *base; 127 for (base = nir_deref_instr_parent(deref); 128 base && base->deref_type != nir_deref_type_var; 129 base = nir_deref_instr_parent(base)) { 130 131 /* If this struct is nested inside another, bail */ 132 if (base->deref_type == nir_deref_type_struct) 133 return; 134 } 135 136 /* We must be on a variable with members */ 137 if (!base || base->var->num_members == 0) 138 return; 139 140 nir_variable *member = find_var_member(base->var, deref->strct.index, 141 var_to_member_map); 142 assert(member); 143 144 b->cursor = nir_before_instr(&deref->instr); 145 nir_deref_instr *member_deref = 146 build_member_deref(b, nir_deref_instr_parent(deref), member); 147 nir_ssa_def_rewrite_uses(&deref->dest.ssa, 148 &member_deref->dest.ssa); 149 150 /* The referenced variable is no longer valid, clean up the deref */ 151 nir_deref_instr_remove_if_unused(deref); 152} 153 154bool 155nir_split_per_member_structs(nir_shader *shader) 156{ 157 bool progress = false; 158 void *dead_ctx = ralloc_context(NULL); 159 struct hash_table *var_to_member_map = 160 _mesa_pointer_hash_table_create(dead_ctx); 161 162 nir_foreach_variable_with_modes_safe(var, shader, nir_var_shader_in | 163 nir_var_shader_out | 164 nir_var_system_value) { 165 if (var->num_members == 0) 166 continue; 167 168 split_variable(var, shader, var_to_member_map, dead_ctx); 169 exec_node_remove(&var->node); 170 progress = true; 171 } 172 173 if (!progress) { 174 ralloc_free(dead_ctx); 175 return false; 176 } 177 178 nir_foreach_function(function, shader) { 179 if (!function->impl) 180 continue; 181 182 nir_builder b; 183 nir_builder_init(&b, function->impl); 184 nir_foreach_block(block, function->impl) { 185 nir_foreach_instr_safe(instr, block) { 186 if (instr->type == nir_instr_type_deref) { 187 rewrite_deref_instr(&b, nir_instr_as_deref(instr), 188 var_to_member_map); 189 } 190 } 191 } 192 193 nir_metadata_preserve(function->impl, 194 nir_metadata_block_index | 195 nir_metadata_dominance); 196 } 197 198 ralloc_free(dead_ctx); 199 200 return progress; 201} 202