From 35b8f6f40bb45dbffb04dd883ac6644300832981 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Mon, 11 Mar 2019 18:47:39 -0500 Subject: [PATCH] nir: Add a new pass to lower array dereferences on vectors This pass was originally written for lowering TCS output reads and writes but it is also applicable just about anything including UBOs, SSBOs, and shared variables. Reviewed-by: Caio Marcelo de Oliveira Filho --- src/compiler/Makefile.sources | 1 + src/compiler/nir/meson.build | 1 + src/compiler/nir/nir.h | 10 + .../nir/nir_lower_array_deref_of_vec.c | 190 ++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 src/compiler/nir/nir_lower_array_deref_of_vec.c diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources index 30e3b1826ec..722cfbb25a8 100644 --- a/src/compiler/Makefile.sources +++ b/src/compiler/Makefile.sources @@ -230,6 +230,7 @@ NIR_FILES = \ nir/nir_lower_alpha_test.c \ nir/nir_lower_alu.c \ nir/nir_lower_alu_to_scalar.c \ + nir/nir_lower_array_deref_of_vec.c \ nir/nir_lower_atomics_to_ssbo.c \ nir/nir_lower_bitmap.c \ nir/nir_lower_bit_size.c \ diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build index b46bf8f2ebf..4f1efb5c6d3 100644 --- a/src/compiler/nir/meson.build +++ b/src/compiler/nir/meson.build @@ -112,6 +112,7 @@ files_libnir = files( 'nir_lower_alu.c', 'nir_lower_alu_to_scalar.c', 'nir_lower_alpha_test.c', + 'nir_lower_array_deref_of_vec.c', 'nir_lower_atomics_to_ssbo.c', 'nir_lower_bitmap.c', 'nir_lower_bool_to_float.c', diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index b25842834ae..067287fab1c 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -2973,6 +2973,16 @@ void nir_fixup_deref_modes(nir_shader *shader); bool nir_lower_global_vars_to_local(nir_shader *shader); +typedef enum { + nir_lower_direct_array_deref_of_vec_load = (1 << 0), + nir_lower_indirect_array_deref_of_vec_load = (1 << 1), + nir_lower_direct_array_deref_of_vec_store = (1 << 2), + nir_lower_indirect_array_deref_of_vec_store = (1 << 3), +} nir_lower_array_deref_of_vec_options; + +bool nir_lower_array_deref_of_vec(nir_shader *shader, nir_variable_mode modes, + nir_lower_array_deref_of_vec_options options); + bool nir_lower_indirect_derefs(nir_shader *shader, nir_variable_mode modes); bool nir_lower_locals_to_regs(nir_shader *shader); diff --git a/src/compiler/nir/nir_lower_array_deref_of_vec.c b/src/compiler/nir/nir_lower_array_deref_of_vec.c new file mode 100644 index 00000000000..2a70dd1ddbc --- /dev/null +++ b/src/compiler/nir/nir_lower_array_deref_of_vec.c @@ -0,0 +1,190 @@ +/* + * Copyright © 2019 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" + +static void +build_write_masked_store(nir_builder *b, nir_deref_instr *vec_deref, + nir_ssa_def *value, unsigned component) +{ + assert(value->num_components == 1); + unsigned num_components = glsl_get_components(vec_deref->type); + assert(num_components > 1 && num_components <= NIR_MAX_VEC_COMPONENTS); + + nir_ssa_def *u = nir_ssa_undef(b, 1, value->bit_size); + nir_ssa_def *comps[NIR_MAX_VEC_COMPONENTS]; + for (unsigned i = 0; i < num_components; i++) + comps[i] = (i == component) ? value : u; + + nir_ssa_def *vec = nir_vec(b, comps, num_components); + nir_store_deref(b, vec_deref, vec, (1u << component)); +} + +static void +build_write_masked_stores(nir_builder *b, nir_deref_instr *vec_deref, + nir_ssa_def *value, nir_ssa_def *index, + unsigned start, unsigned end) +{ + if (start == end - 1) { + build_write_masked_store(b, vec_deref, value, start); + } else { + unsigned mid = start + (end - start) / 2; + nir_push_if(b, nir_ilt(b, index, nir_imm_int(b, mid))); + build_write_masked_stores(b, vec_deref, value, index, start, mid); + nir_push_else(b, NULL); + build_write_masked_stores(b, vec_deref, value, index, mid, end); + nir_pop_if(b, NULL); + } +} + +static bool +nir_lower_array_deref_of_vec_impl(nir_function_impl *impl, + nir_variable_mode modes, + nir_lower_array_deref_of_vec_options options) +{ + bool progress = false; + + 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_intrinsic) + continue; + + nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); + assert(intrin->intrinsic != nir_intrinsic_copy_deref); + + if (intrin->intrinsic != nir_intrinsic_load_deref && + intrin->intrinsic != nir_intrinsic_interp_deref_at_centroid && + intrin->intrinsic != nir_intrinsic_interp_deref_at_sample && + intrin->intrinsic != nir_intrinsic_interp_deref_at_offset && + intrin->intrinsic != nir_intrinsic_store_deref) + continue; + + nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); + if (!(deref->mode & modes)) + continue; + + /* We only care about array derefs that act on vectors */ + if (deref->deref_type != nir_deref_type_array) + continue; + + nir_deref_instr *vec_deref = nir_deref_instr_parent(deref); + if (!glsl_type_is_vector(vec_deref->type)) + continue; + + assert(intrin->num_components == 1); + unsigned num_components = glsl_get_components(vec_deref->type); + assert(num_components > 1 && num_components <= NIR_MAX_VEC_COMPONENTS); + + b.cursor = nir_after_instr(&intrin->instr); + + if (intrin->intrinsic == nir_intrinsic_store_deref) { + assert(intrin->src[1].is_ssa); + nir_ssa_def *value = intrin->src[1].ssa; + + if (nir_src_is_const(deref->arr.index)) { + if (!(options & nir_lower_direct_array_deref_of_vec_store)) + continue; + + unsigned index = nir_src_as_uint(deref->arr.index); + /* If index is OOB, we throw the old store away and don't + * replace it with anything. + */ + if (index < num_components) + build_write_masked_store(&b, vec_deref, value, index); + } else { + if (!(options & nir_lower_indirect_array_deref_of_vec_store)) + continue; + + nir_ssa_def *index = nir_ssa_for_src(&b, deref->arr.index, 1); + build_write_masked_stores(&b, vec_deref, value, index, + 0, num_components); + } + nir_instr_remove(&intrin->instr); + + progress = true; + } else { + if (nir_src_is_const(deref->arr.index)) { + if (!(options & nir_lower_direct_array_deref_of_vec_load)) + continue; + } else { + if (!(options & nir_lower_indirect_array_deref_of_vec_load)) + continue; + } + + /* Turn the load into a vector load */ + nir_instr_rewrite_src(&intrin->instr, &intrin->src[0], + nir_src_for_ssa(&vec_deref->dest.ssa)); + intrin->dest.ssa.num_components = num_components; + intrin->num_components = num_components; + + nir_ssa_def *index = nir_ssa_for_src(&b, deref->arr.index, 1); + nir_ssa_def *scalar = + nir_vector_extract(&b, &intrin->dest.ssa, index); + if (scalar->parent_instr->type == nir_instr_type_ssa_undef) { + nir_ssa_def_rewrite_uses(&intrin->dest.ssa, + nir_src_for_ssa(scalar)); + nir_instr_remove(&intrin->instr); + } else { + nir_ssa_def_rewrite_uses_after(&intrin->dest.ssa, + nir_src_for_ssa(scalar), + scalar->parent_instr); + } + progress = true; + } + } + } + + if (progress) { + nir_metadata_preserve(impl, nir_metadata_block_index | + nir_metadata_dominance); + } + + return progress; +} + +/* Lowers away array dereferences on vectors + * + * These are allowed on certain variable types such as SSBOs and TCS outputs. + * However, not everyone can actually handle them everywhere. There are also + * cases where we want to lower them for performance reasons. + * + * This patch assumes that copy_deref instructions have already been lowered. + */ +bool +nir_lower_array_deref_of_vec(nir_shader *shader, nir_variable_mode modes, + nir_lower_array_deref_of_vec_options options) +{ + bool progress = false; + + nir_foreach_function(function, shader) { + if (function->impl && + nir_lower_array_deref_of_vec_impl(function->impl, modes, options)) + progress = true; + } + + return progress; +} -- 2.30.2