On Fri, Jun 29, 2018 at 9:56 PM, Timothy Arceri <tarc...@itsqueeze.com> wrote:
> On 30/06/18 10:13, Jason Ekstrand wrote: > >> This pass searches for reasonably large local variables which can be >> statically proven to be constant and moves them into shader constant >> data. This is especially useful when large tables are baked into the >> shader source code because they can be moved into a UBO by the driver to >> reduce register pressure and make indirect access cheaper. >> >> v2 (Jason Ekstrand): >> - Use a size/align function to ensure we get the right alignments >> - Use the newly added deref offset helpers >> --- >> src/compiler/Makefile.sources | 1 + >> src/compiler/nir/meson.build | 1 + >> src/compiler/nir/nir.h | 4 + >> src/compiler/nir/nir_opt_large_constants.c | 301 +++++++++++++++++++++ >> 4 files changed, 307 insertions(+) >> create mode 100644 src/compiler/nir/nir_opt_large_constants.c >> >> diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.source >> s >> index 0fcbc5c5c5b..9e3fbdc2612 100644 >> --- a/src/compiler/Makefile.sources >> +++ b/src/compiler/Makefile.sources >> @@ -276,6 +276,7 @@ NIR_FILES = \ >> nir/nir_opt_if.c \ >> nir/nir_opt_intrinsics.c \ >> nir/nir_opt_loop_unroll.c \ >> + nir/nir_opt_large_constants.c \ >> nir/nir_opt_move_comparisons.c \ >> nir/nir_opt_move_load_ubo.c \ >> nir/nir_opt_peephole_select.c \ >> diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build >> index eb7fb7b121e..28aa8de7014 100644 >> --- a/src/compiler/nir/meson.build >> +++ b/src/compiler/nir/meson.build >> @@ -160,6 +160,7 @@ files_libnir = files( >> 'nir_opt_global_to_local.c', >> 'nir_opt_if.c', >> 'nir_opt_intrinsics.c', >> + 'nir_opt_large_constants.c', >> 'nir_opt_loop_unroll.c', >> 'nir_opt_move_comparisons.c', >> 'nir_opt_move_load_ubo.c', >> diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h >> index cc5f88d6f54..92ab3a699cc 100644 >> --- a/src/compiler/nir/nir.h >> +++ b/src/compiler/nir/nir.h >> @@ -2905,6 +2905,10 @@ bool nir_opt_if(nir_shader *shader); >> bool nir_opt_intrinsics(nir_shader *shader); >> +bool nir_opt_large_constants(nir_shader *shader, >> + glsl_type_size_align_func size_align, >> + unsigned threshold); >> + >> bool nir_opt_loop_unroll(nir_shader *shader, nir_variable_mode >> indirect_mask); >> bool nir_opt_move_comparisons(nir_shader *shader); >> diff --git a/src/compiler/nir/nir_opt_large_constants.c >> b/src/compiler/nir/nir_opt_large_constants.c >> new file mode 100644 >> index 00000000000..027c6e8e5b5 >> --- /dev/null >> +++ b/src/compiler/nir/nir_opt_large_constants.c >> @@ -0,0 +1,301 @@ >> +/* >> + * 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 var_info { >> + bool is_constant; >> + bool found_read; >> +}; >> + >> +static nir_ssa_def * >> +build_constant_load(nir_builder *b, nir_deref_instr *deref, >> + glsl_type_size_align_func size_align) >> +{ >> + nir_variable *var = nir_deref_instr_get_variable(deref); >> + >> + const unsigned bit_size = glsl_get_bit_size(deref->type); >> + const unsigned num_components = glsl_get_vector_elements(deref >> ->type); >> + >> + UNUSED unsigned var_size, var_align; >> + size_align(var->type, &var_size, &var_align); >> + assert(var->data.location % var_align == 0); >> + >> + nir_intrinsic_instr *load = >> + nir_intrinsic_instr_create(b->shader, >> nir_intrinsic_load_constant); >> + load->num_components = num_components; >> + nir_intrinsic_set_base(load, var->data.location); >> + nir_intrinsic_set_range(load, var_size); >> + load->src[0] = nir_src_for_ssa(nir_build_deref_offset(b, deref, >> size_align)); >> + nir_ssa_dest_init(&load->instr, &load->dest, >> + num_components, bit_size, NULL); >> + nir_builder_instr_insert(b, &load->instr); >> + >> + return &load->dest.ssa; >> +} >> + >> +static void >> +handle_constant_store(nir_builder *b, nir_intrinsic_instr *store, >> + glsl_type_size_align_func size_align) >> +{ >> + nir_deref_instr *deref = nir_src_as_deref(store->src[0]); >> + assert(!nir_deref_instr_has_indirect(deref)); >> + >> + nir_variable *var = nir_deref_instr_get_variable(deref); >> + >> + const unsigned bit_size = glsl_get_bit_size(deref->type); >> + const unsigned num_components = glsl_get_vector_elements(deref >> ->type); >> + >> + char *dst = (char *)b->shader->constant_data + >> + var->data.location + >> + nir_deref_instr_get_const_offset(deref, size_align); >> + >> + nir_const_value *val = nir_src_as_const_value(store->src[1]); >> + switch (bit_size) { >> + case 8: >> + for (unsigned i = 0; i < num_components; i++) >> + ((uint8_t *)dst)[i] = val->u8[i]; >> + break; >> + >> + case 16: >> + for (unsigned i = 0; i < num_components; i++) >> + ((uint16_t *)dst)[i] = val->u16[i]; >> + break; >> + >> + case 32: >> + for (unsigned i = 0; i < num_components; i++) >> + ((uint32_t *)dst)[i] = val->u32[i]; >> + break; >> + >> + case 64: >> + for (unsigned i = 0; i < num_components; i++) >> + ((uint64_t *)dst)[i] = val->u64[i]; >> + break; >> + >> + default: >> + unreachable("Invalid bit size"); >> + } >> +} >> + >> +/** Lower large constant variables to shader constant data >> + * >> + * This pass looks for large (type_size(var->type) > threshold) variables >> + * which are statically constant and moves them into shader constant >> data. >> + * This is especially useful when large tables are baked into the shader >> + * source code because they can be moved into a UBO by the driver to >> reduce >> + * register pressure and make indirect access cheaper. >> + */ >> +bool >> +nir_opt_large_constants(nir_shader *shader, >> + glsl_type_size_align_func size_align, >> + unsigned threshold) >> +{ >> + /* Default to a natural alignment if none is provided */ >> + if (size_align == NULL) >> + size_align = glsl_get_natural_size_align_bytes; >> + >> + /* This only works with a single entrypoint */ >> + nir_function_impl *impl = nir_shader_get_entrypoint(shader); >> + >> + /* This pass can only be run once */ >> + assert(shader->constant_data == NULL && shader->constant_data_size == >> 0); >> + >> + /* The index parameter is unused for local variables so we'll use it >> for >> + * indexing into our array of variable metadata. >> + */ >> + unsigned num_locals = 0; >> + nir_foreach_variable(var, &impl->locals) >> + var->data.index = num_locals++; >> + >> + struct var_info *var_infos = malloc(num_locals * sizeof(struct >> var_info)); >> + for (unsigned i = 0; i < num_locals; i++) { >> + var_infos[i] = (struct var_info) { >> + .is_constant = true, >> + .found_read = false, >> + }; >> + } >> + >> + /* First, walk through the shader and figure out what variables we can >> + * lower to the constant blob. >> + */ >> + bool first_block = true; >> + nir_foreach_block(block, impl) { >> + nir_foreach_instr(instr, block) { >> + if (instr->type != nir_instr_type_intrinsic) >> + continue; >> + >> + nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); >> + >> + bool src_is_const = false; >> + nir_deref_instr *src_deref = NULL, *dst_deref = NULL; >> + switch (intrin->intrinsic) { >> + case nir_intrinsic_store_deref: >> + dst_deref = nir_src_as_deref(intrin->src[0]); >> + src_is_const = nir_src_as_const_value(intrin->src[1]); >> + break; >> + >> + case nir_intrinsic_load_deref: >> + src_deref = nir_src_as_deref(intrin->src[0]); >> + break; >> + >> + case nir_intrinsic_copy_deref: >> > > Can we add a comment here? Something like: > > /* We always assume the src and therefore the dst are not constants > * here. Copy and constant propagation passes should have taken > * care of this in most cases anyway. > */ > > Does that sound correct? > Sounds fine with me. > Otherwise this series is: > > Reviewed-by: Timothy Arceri <tarc...@itsqueeze.com> > Thanks! --Jason > Although I don't know the code i patch 8 very well so maybe someone else > might want to take a look over that. > > > + dst_deref = nir_src_as_deref(intrin->src[0]); >> + src_deref = nir_src_as_deref(intrin->src[1]); >> + src_is_const = false > + break; >> + >> + default: >> + continue; >> + } >> + >> + if (dst_deref && dst_deref->mode == nir_var_local) { >> + nir_variable *var = nir_deref_instr_get_variable(dst_deref); >> + assert(var->data.mode == nir_var_local); >> + >> + /* We only consider variables constant if they only have >> constant >> + * stores, all the stores come before any reads, and all >> stores >> + * come in the first block. We also can't handle indirect >> stores. >> + */ >> + struct var_info *info = &var_infos[var->data.index]; >> + if (!src_is_const || info->found_read || !first_block || >> + nir_deref_instr_has_indirect(dst_deref)) >> + info->is_constant = false; >> + } >> + >> + if (src_deref && src_deref->mode == nir_var_local) { >> + nir_variable *var = nir_deref_instr_get_variable(src_deref); >> + assert(var->data.mode == nir_var_local); >> + >> + var_infos[var->data.index].found_read = true; >> + } >> + } >> + first_block = false; >> + } >> + >> + shader->constant_data_size = 0; >> + nir_foreach_variable(var, &impl->locals) { >> + struct var_info *info = &var_infos[var->data.index]; >> + if (!info->is_constant) >> + continue; >> + >> + unsigned var_size, var_align; >> + size_align(var->type, &var_size, &var_align); >> + if (var_size <= threshold || !info->found_read) { >> + /* Don't bother lowering small stuff or data that's never read >> */ >> + info->is_constant = false; >> + continue; >> + } >> + >> + var->data.location = ALIGN_POT(shader->constant_data_size, >> var_align); >> + shader->constant_data_size = var->data.location + var_size; >> + } >> + >> + if (shader->constant_data_size == 0) { >> + free(var_infos); >> + return false; >> + } >> + >> + shader->constant_data = rzalloc_size(shader, >> shader->constant_data_size); >> + >> + 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); >> + >> + switch (intrin->intrinsic) { >> + case nir_intrinsic_load_deref: { >> + nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); >> + if (deref->mode != nir_var_local) >> + continue; >> + >> + nir_variable *var = nir_deref_instr_get_variable(deref); >> + struct var_info *info = &var_infos[var->data.index]; >> + if (info->is_constant) { >> + b.cursor = nir_after_instr(&intrin->instr); >> + nir_ssa_def *val = build_constant_load(&b, deref, >> size_align); >> + nir_ssa_def_rewrite_uses(&intrin->dest.ssa, >> + nir_src_for_ssa(val)); >> + nir_instr_remove(&intrin->instr); >> + nir_deref_instr_remove_if_unused(deref); >> + } >> + break; >> + } >> + >> + case nir_intrinsic_store_deref: { >> + nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); >> + if (deref->mode != nir_var_local) >> + continue; >> + >> + nir_variable *var = nir_deref_instr_get_variable(deref); >> + struct var_info *info = &var_infos[var->data.index]; >> + if (info->is_constant) { >> + b.cursor = nir_after_instr(&intrin->instr); >> + handle_constant_store(&b, intrin, size_align); >> + nir_instr_remove(&intrin->instr); >> + nir_deref_instr_remove_if_unused(deref); >> + } >> + break; >> + } >> + >> + case nir_intrinsic_copy_deref: { >> + nir_deref_instr *deref = nir_src_as_deref(intrin->src[1]); >> + if (deref->mode != nir_var_local) >> + continue; >> + >> + nir_variable *var = nir_deref_instr_get_variable(deref); >> + struct var_info *info = &var_infos[var->data.index]; >> + if (info->is_constant) { >> + b.cursor = nir_after_instr(&intrin->instr); >> + nir_ssa_def *val = build_constant_load(&b, deref, >> size_align); >> + nir_store_deref(&b, nir_src_as_deref(intrin->src[0]), >> val, ~0); >> + nir_instr_remove(&intrin->instr); >> + nir_deref_instr_remove_if_unused(deref); >> + } >> + break; >> + } >> + >> + default: >> + continue; >> + } >> + } >> + } >> + >> + /* Clean up the now unused variables */ >> + nir_foreach_variable_safe(var, &impl->locals) { >> + if (var_infos[var->data.index].is_constant) >> + exec_node_remove(&var->node); >> + } >> + >> + free(var_infos); >> + >> + nir_metadata_preserve(impl, nir_metadata_block_index | >> + nir_metadata_dominance); >> + return true; >> +} >> >>
_______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev