/* * Copyright © 2018 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "nir.h" #include "nir_builder.h" #include "nir_deref.h" struct split_var_state { void *mem_ctx; nir_shader *shader; nir_function_impl *impl; nir_variable *base_var; }; struct field { struct field *parent; const struct glsl_type *type; unsigned num_fields; struct field *fields; nir_variable *var; }; static const struct glsl_type * wrap_type_in_array(const struct glsl_type *type, const struct glsl_type *array_type) { if (!glsl_type_is_array(array_type)) return type; const struct glsl_type *elem_type = wrap_type_in_array(type, glsl_get_array_element(array_type)); return glsl_array_type(elem_type, glsl_get_length(array_type)); } static void init_field_for_type(struct field *field, struct field *parent, const struct glsl_type *type, const char *name, struct split_var_state *state) { *field = (struct field) { .parent = parent, .type = type, }; const struct glsl_type *struct_type = glsl_without_array(type); if (glsl_type_is_struct(struct_type)) { field->num_fields = glsl_get_length(struct_type), field->fields = ralloc_array(state->mem_ctx, struct field, field->num_fields); for (unsigned i = 0; i < field->num_fields; i++) { char *field_name = NULL; if (name) { field_name = ralloc_asprintf(state->mem_ctx, "%s_%s", name, glsl_get_struct_elem_name(struct_type, i)); } else { field_name = ralloc_asprintf(state->mem_ctx, "{unnamed %s}_%s", glsl_get_type_name(struct_type), glsl_get_struct_elem_name(struct_type, i)); } init_field_for_type(&field->fields[i], field, glsl_get_struct_field(struct_type, i), field_name, state); } } else { const struct glsl_type *var_type = type; for (struct field *f = field->parent; f; f = f->parent) var_type = wrap_type_in_array(var_type, f->type); nir_variable_mode mode = state->base_var->data.mode; if (mode == nir_var_local) { field->var = nir_local_variable_create(state->impl, var_type, name); } else { field->var = nir_variable_create(state->shader, mode, var_type, name); } } } static bool split_var_list_structs(nir_shader *shader, nir_function_impl *impl, struct exec_list *vars, struct hash_table *var_field_map, void *mem_ctx) { struct split_var_state state = { .mem_ctx = mem_ctx, .shader = shader, .impl = impl, }; struct exec_list split_vars; exec_list_make_empty(&split_vars); /* To avoid list confusion (we'll be adding things as we split variables), * pull all of the variables we plan to split off of the list */ nir_foreach_variable_safe(var, vars) { if (!glsl_type_is_struct(glsl_without_array(var->type))) continue; exec_node_remove(&var->node); exec_list_push_tail(&split_vars, &var->node); } nir_foreach_variable(var, &split_vars) { state.base_var = var; struct field *root_field = ralloc(mem_ctx, struct field); init_field_for_type(root_field, NULL, var->type, var->name, &state); _mesa_hash_table_insert(var_field_map, var, root_field); } return !exec_list_is_empty(&split_vars); } static void split_struct_derefs_impl(nir_function_impl *impl, struct hash_table *var_field_map, nir_variable_mode modes, void *mem_ctx) { nir_builder b; nir_builder_init(&b, impl); nir_foreach_block(block, impl) { nir_foreach_instr_safe(instr, block) { if (instr->type != nir_instr_type_deref) continue; nir_deref_instr *deref = nir_instr_as_deref(instr); if (!(deref->mode & modes)) continue; /* Clean up any dead derefs we find lying around. They may refer to * variables we're planning to split. */ if (nir_deref_instr_remove_if_unused(deref)) continue; if (!glsl_type_is_vector_or_scalar(deref->type)) continue; nir_variable *base_var = nir_deref_instr_get_variable(deref); struct hash_entry *entry = _mesa_hash_table_search(var_field_map, base_var); if (!entry) continue; struct field *root_field = entry->data; nir_deref_path path; nir_deref_path_init(&path, deref, mem_ctx); struct field *tail_field = root_field; for (unsigned i = 0; path.path[i]; i++) { if (path.path[i]->deref_type != nir_deref_type_struct) continue; assert(i > 0); assert(glsl_type_is_struct(path.path[i - 1]->type)); assert(path.path[i - 1]->type == glsl_without_array(tail_field->type)); tail_field = &tail_field->fields[path.path[i]->strct.index]; } nir_variable *split_var = tail_field->var; nir_deref_instr *new_deref = NULL; for (unsigned i = 0; path.path[i]; i++) { nir_deref_instr *p = path.path[i]; b.cursor = nir_after_instr(&p->instr); switch (p->deref_type) { case nir_deref_type_var: assert(new_deref == NULL); new_deref = nir_build_deref_var(&b, split_var); break; case nir_deref_type_array: case nir_deref_type_array_wildcard: new_deref = nir_build_deref_follower(&b, new_deref, p); break; case nir_deref_type_struct: /* Nothing to do; we're splitting structs */ break; default: unreachable("Invalid deref type in path"); } } assert(new_deref->type == deref->type); nir_ssa_def_rewrite_uses(&deref->dest.ssa, nir_src_for_ssa(&new_deref->dest.ssa)); nir_deref_instr_remove_if_unused(deref); } } } /** A pass for splitting structs into multiple variables * * This pass splits arrays of structs into multiple variables, one for each * (possibly nested) structure member. After this pass completes, no * variables of the given mode will contain a struct type. */ bool nir_split_struct_vars(nir_shader *shader, nir_variable_mode modes) { void *mem_ctx = ralloc_context(NULL); struct hash_table *var_field_map = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer, _mesa_key_pointer_equal); assert((modes & (nir_var_global | nir_var_local)) == modes); bool has_global_splits = false; if (modes & nir_var_global) { has_global_splits = split_var_list_structs(shader, NULL, &shader->globals, var_field_map, mem_ctx); } bool progress = false; nir_foreach_function(function, shader) { if (!function->impl) continue; bool has_local_splits = false; if (modes & nir_var_local) { has_local_splits = split_var_list_structs(shader, function->impl, &function->impl->locals, var_field_map, mem_ctx); } if (has_global_splits || has_local_splits) { split_struct_derefs_impl(function->impl, var_field_map, modes, mem_ctx); nir_metadata_preserve(function->impl, nir_metadata_block_index | nir_metadata_dominance); progress = true; } } ralloc_free(mem_ctx); return progress; }