[Mesa-dev] [PATCH v2 6/9] nir: Add a large constants optimization pass
Jason Ekstrand
jason at jlekstrand.net
Sat Jun 30 05:02:19 UTC 2018
On Fri, Jun 29, 2018 at 9:56 PM, Timothy Arceri <tarceri at 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 <tarceri at 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;
>> +}
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/mesa-dev/attachments/20180629/583eda0f/attachment-0001.html>
More information about the mesa-dev
mailing list