From 39bf61aa37b0336aa27d8e3d2bf64587f07ff477 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 21 Mar 2018 16:48:35 -0700 Subject: [PATCH] nir: Add a concept of per-member structs and a lowering pass This adds a concept of "members" to a variable with an interface type. It allows you to specify the full variable data for each member of the interface instead of once for the variable. We also add a lowering pass to lower those variables to a sequence of variables and rewrite all the derefs accordingly. Acked-by: Rob Clark Acked-by: Bas Nieuwenhuizen Acked-by: Dave Airlie Reviewed-by: Kenneth Graunke --- src/compiler/Makefile.sources | 1 + src/compiler/nir/meson.build | 1 + src/compiler/nir/nir.h | 12 + src/compiler/nir/nir_clone.c | 8 + src/compiler/nir/nir_serialize.c | 12 + .../nir/nir_split_per_member_structs.c | 290 ++++++++++++++++++ src/compiler/nir/nir_validate.c | 7 + 7 files changed, 331 insertions(+) create mode 100644 src/compiler/nir/nir_split_per_member_structs.c diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources index b59886afb92..21d84bd7d51 100644 --- a/src/compiler/Makefile.sources +++ b/src/compiler/Makefile.sources @@ -295,6 +295,7 @@ NIR_FILES = \ nir/nir_search_helpers.h \ nir/nir_serialize.c \ nir/nir_serialize.h \ + nir/nir_split_per_member_structs.c \ nir/nir_split_var_copies.c \ nir/nir_sweep.c \ nir/nir_to_lcssa.c \ diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build index 9d50782513e..d5df696a4a8 100644 --- a/src/compiler/nir/meson.build +++ b/src/compiler/nir/meson.build @@ -180,6 +180,7 @@ files_libnir = files( 'nir_search_helpers.h', 'nir_serialize.c', 'nir_serialize.h', + 'nir_split_per_member_structs.c', 'nir_split_var_copies.c', 'nir_sweep.c', 'nir_to_lcssa.c', diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index 99ec43c1778..9e4aa1df5bd 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -370,6 +370,17 @@ typedef struct nir_variable { * \sa ir_variable::location */ const struct glsl_type *interface_type; + + /** + * Description of per-member data for per-member struct variables + * + * This is used for variables which are actually an amalgamation of + * multiple entities such as a struct of built-in values or a struct of + * inputs each with their own layout specifier. This is only allowed on + * variables with a struct or array of array of struct type. + */ + unsigned num_members; + struct nir_variable_data *members; } nir_variable; #define nir_foreach_variable(var, var_list) \ @@ -2678,6 +2689,7 @@ void nir_dump_cfg(nir_shader *shader, FILE *fp); int nir_gs_count_vertices(const nir_shader *shader); bool nir_split_var_copies(nir_shader *shader); +bool nir_split_per_member_structs(nir_shader *shader); bool nir_lower_returns_impl(nir_function_impl *impl); bool nir_lower_returns(nir_shader *shader); diff --git a/src/compiler/nir/nir_clone.c b/src/compiler/nir/nir_clone.c index 7236f0872a7..76121d05a7e 100644 --- a/src/compiler/nir/nir_clone.c +++ b/src/compiler/nir/nir_clone.c @@ -161,6 +161,14 @@ nir_variable_clone(const nir_variable *var, nir_shader *shader) } nvar->interface_type = var->interface_type; + nvar->num_members = var->num_members; + if (var->num_members) { + nvar->members = ralloc_array(nvar, struct nir_variable_data, + var->num_members); + memcpy(nvar->members, var->members, + var->num_members * sizeof(*var->members)); + } + return nvar; } diff --git a/src/compiler/nir/nir_serialize.c b/src/compiler/nir/nir_serialize.c index 155205d2198..39f6d8298d7 100644 --- a/src/compiler/nir/nir_serialize.c +++ b/src/compiler/nir/nir_serialize.c @@ -149,6 +149,11 @@ write_variable(write_ctx *ctx, const nir_variable *var) blob_write_uint32(ctx->blob, !!(var->interface_type)); if (var->interface_type) encode_type_to_blob(ctx->blob, var->interface_type); + blob_write_uint32(ctx->blob, var->num_members); + if (var->num_members > 0) { + blob_write_bytes(ctx->blob, (uint8_t *) var->members, + var->num_members * sizeof(*var->members)); + } } static nir_variable * @@ -180,6 +185,13 @@ read_variable(read_ctx *ctx) var->interface_type = decode_type_from_blob(ctx->blob); else var->interface_type = NULL; + var->num_members = blob_read_uint32(ctx->blob); + if (var->num_members > 0) { + var->members = ralloc_array(var, struct nir_variable_data, + var->num_members); + blob_copy_bytes(ctx->blob, (uint8_t *) var->members, + var->num_members * sizeof(*var->members)); + } return var; } diff --git a/src/compiler/nir/nir_split_per_member_structs.c b/src/compiler/nir/nir_split_per_member_structs.c new file mode 100644 index 00000000000..8fd51f12bc1 --- /dev/null +++ b/src/compiler/nir/nir_split_per_member_structs.c @@ -0,0 +1,290 @@ +/* + * 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_deref.h" + +struct split_struct_state { + void *dead_ctx; + + struct hash_table *var_to_member_map; +}; + +static nir_variable * +find_var_member(struct nir_variable *var, unsigned member, + struct hash_table *var_to_member_map) +{ + struct hash_entry *map_entry = + _mesa_hash_table_search(var_to_member_map, var); + if (map_entry == NULL) + return NULL; + + nir_variable **members = map_entry->data; + assert(member < var->num_members); + return members[member]; +} + +static const struct glsl_type * +member_type(const struct glsl_type *type, unsigned index) +{ + if (glsl_type_is_array(type)) { + const struct glsl_type *elem = + member_type(glsl_get_array_element(type), index); + return glsl_get_array_instance(elem, glsl_get_length(type)); + } else { + assert(glsl_type_is_struct(type)); + assert(index < glsl_get_length(type)); + return glsl_get_struct_field(type, index); + } +} + +static void +split_variable(struct nir_variable *var, nir_shader *shader, + struct hash_table *var_to_member_map, void *dead_ctx) +{ + assert(var->state_slots == NULL); + + /* Constant initializers are currently not handled */ + assert(var->constant_initializer == NULL); + + nir_variable **members = + ralloc_array(dead_ctx, nir_variable *, var->num_members); + + for (unsigned i = 0; i < var->num_members; i++) { + char *member_name = NULL; + if (var->name) { + /* Calculate a reasonable variable name */ + member_name = ralloc_strdup(dead_ctx, var->name); + const struct glsl_type *t = var->type; + while (glsl_type_is_array(t)) { + ralloc_strcat(&member_name, "[*]"); + t = glsl_get_array_element(t); + } + const char *field_name = glsl_get_struct_elem_name(t, i); + if (field_name) { + member_name = ralloc_asprintf(dead_ctx, "%s.%s", + member_name, field_name); + } else { + member_name = ralloc_asprintf(dead_ctx, "%s.@%d", member_name, i); + } + } + + members[i] = + nir_variable_create(shader, var->members[i].mode, + member_type(var->type, i), member_name); + if (var->interface_type) { + members[i]->interface_type = + glsl_get_struct_field(var->interface_type, i); + } + members[i]->data = var->members[i]; + } + + _mesa_hash_table_insert(var_to_member_map, var, members); +} + +static bool +split_variables_in_list(struct exec_list *var_list, nir_shader *shader, + struct hash_table *var_to_member_map, void *dead_ctx) +{ + bool progress = false; + + nir_foreach_variable_safe(var, var_list) { + if (var->num_members == 0) + continue; + + split_variable(var, shader, var_to_member_map, dead_ctx); + exec_node_remove(&var->node); + progress = true; + } + + return progress; +} + +static nir_deref_instr * +build_member_deref(nir_builder *b, nir_deref_instr *deref, nir_variable *member) +{ + if (deref->deref_type == nir_deref_type_var) { + return nir_build_deref_var(b, member); + } else { + nir_deref_instr *parent = + build_member_deref(b, nir_deref_instr_parent(deref), member); + return nir_build_deref_follower(b, parent, deref); + } +} + +static void +rewrite_deref_instr(nir_builder *b, nir_deref_instr *deref, + struct hash_table *var_to_member_map) +{ + /* We must be a struct deref */ + if (deref->deref_type != nir_deref_type_struct) + return; + + nir_deref_instr *base; + for (base = nir_deref_instr_parent(deref); + base && base->deref_type != nir_deref_type_var; + base = nir_deref_instr_parent(base)) { + + /* If this struct is nested inside another, bail */ + if (base->deref_type == nir_deref_type_struct) + return; + } + + /* We must be on a variable with members */ + if (!base || base->var->num_members == 0) + return; + + nir_variable *member = find_var_member(base->var, deref->strct.index, + var_to_member_map); + assert(member); + + b->cursor = nir_before_instr(&deref->instr); + nir_deref_instr *member_deref = + build_member_deref(b, nir_deref_instr_parent(deref), member); + nir_ssa_def_rewrite_uses(&deref->dest.ssa, + nir_src_for_ssa(&member_deref->dest.ssa)); + + /* The referenced variable is no longer valid, clean up the deref */ + nir_deref_instr_remove_if_unused(deref); +} + +static void +rewrite_deref_var(nir_instr *instr, nir_deref_var **deref, + struct hash_table *var_to_member_map) +{ + if ((*deref)->var->members == 0) + return; + + nir_deref_struct *strct = NULL; + for (nir_deref *d = (*deref)->deref.child; d; d = d->child) { + if (d->deref_type == nir_deref_type_struct) { + strct = nir_deref_as_struct(d); + break; + } + } + assert(strct); + + nir_variable *member = find_var_member((*deref)->var, strct->index, + var_to_member_map); + + nir_deref_var *head = nir_deref_var_create(ralloc_parent(*deref), member); + nir_deref *tail = &head->deref; + for (nir_deref *d = (*deref)->deref.child; + d != &strct->deref; d = d->child) { + nir_deref_array *arr = nir_deref_as_array(d); + + nir_deref_array *narr = nir_deref_array_create(tail); + narr->deref.type = glsl_get_array_element(tail->type); + narr->deref_array_type = arr->deref_array_type; + narr->base_offset = arr->base_offset; + + if (arr->deref_array_type == nir_deref_array_type_indirect) + nir_instr_move_src(instr, &narr->indirect, &arr->indirect); + + assert(tail->child == NULL); + tail->child = &narr->deref; + tail = &narr->deref; + } + + ralloc_steal(tail, strct->deref.child); + tail->child = strct->deref.child; + + ralloc_free(*deref); + *deref = head; +} + +static void +rewrite_intrinsic_instr(nir_intrinsic_instr *intrin, + struct hash_table *var_to_member_map) +{ + for (unsigned i = 0; + i < nir_intrinsic_infos[intrin->intrinsic].num_variables; i++) { + rewrite_deref_var(&intrin->instr, &intrin->variables[i], + var_to_member_map); + } +} + +static void +rewrite_tex_instr(nir_tex_instr *tex, struct hash_table *var_to_member_map) +{ + if (tex->texture) + rewrite_deref_var(&tex->instr, &tex->texture, var_to_member_map); + if (tex->sampler) + rewrite_deref_var(&tex->instr, &tex->sampler, var_to_member_map); +} + +bool +nir_split_per_member_structs(nir_shader *shader) +{ + bool progress = false; + void *dead_ctx = ralloc_context(NULL); + struct hash_table *var_to_member_map = + _mesa_hash_table_create(dead_ctx, _mesa_hash_pointer, + _mesa_key_pointer_equal); + + progress |= split_variables_in_list(&shader->inputs, shader, + var_to_member_map, dead_ctx); + progress |= split_variables_in_list(&shader->outputs, shader, + var_to_member_map, dead_ctx); + progress |= split_variables_in_list(&shader->system_values, shader, + var_to_member_map, dead_ctx); + if (!progress) + return false; + + nir_foreach_function(function, shader) { + if (!function->impl) + continue; + + nir_builder b; + nir_builder_init(&b, function->impl); + nir_foreach_block(block, function->impl) { + nir_foreach_instr_safe(instr, block) { + switch (instr->type) { + case nir_instr_type_deref: + rewrite_deref_instr(&b, nir_instr_as_deref(instr), + var_to_member_map); + break; + + case nir_instr_type_intrinsic: + rewrite_intrinsic_instr(nir_instr_as_intrinsic(instr), + var_to_member_map); + break; + + case nir_instr_type_tex: + rewrite_tex_instr(nir_instr_as_tex(instr), var_to_member_map); + break; + + case nir_instr_type_call: + unreachable("Functions must be inlined before this pass"); + + default: + break; + } + } + } + } + + ralloc_free(dead_ctx); + + return progress; +} diff --git a/src/compiler/nir/nir_validate.c b/src/compiler/nir/nir_validate.c index 191e3b72325..5144886c926 100644 --- a/src/compiler/nir/nir_validate.c +++ b/src/compiler/nir/nir_validate.c @@ -1100,6 +1100,13 @@ validate_var_decl(nir_variable *var, bool is_global, validate_state *state) } } + if (var->num_members > 0) { + const struct glsl_type *without_array = glsl_without_array(var->type); + validate_assert(state, glsl_type_is_struct(without_array)); + validate_assert(state, var->num_members == glsl_get_length(without_array)); + validate_assert(state, var->members != NULL); + } + /* * TODO validate some things ir_validate.cpp does (requires more GLSL type * support) -- 2.30.2