nir/locals_to_regs: Initialize registers with constant initializers
Reviewed-by: Connor Abbott <cwabbott0@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
#include "nir.h"
|
||||
#include "nir_array.h"
|
||||
|
||||
struct locals_to_regs_state {
|
||||
nir_shader *shader;
|
||||
@@ -33,6 +34,12 @@ struct locals_to_regs_state {
|
||||
|
||||
/* A hash table mapping derefs to registers */
|
||||
struct hash_table *regs_table;
|
||||
|
||||
/* A growing array of derefs that we have encountered. There is exactly
|
||||
* one element of this array per element in the hash table. This is
|
||||
* used to make adding register initialization code deterministic.
|
||||
*/
|
||||
nir_array derefs_array;
|
||||
};
|
||||
|
||||
/* The following two functions implement a hash and equality check for
|
||||
@@ -112,6 +119,7 @@ get_reg_for_deref(nir_deref_var *deref, struct locals_to_regs_state *state)
|
||||
reg->num_array_elems = array_size > 1 ? array_size : 0;
|
||||
|
||||
_mesa_hash_table_insert_pre_hashed(state->regs_table, hash, deref, reg);
|
||||
nir_array_add(&state->derefs_array, nir_deref_var *, deref);
|
||||
|
||||
return reg;
|
||||
}
|
||||
@@ -250,6 +258,82 @@ lower_locals_to_regs_block(nir_block *block, void *void_state)
|
||||
return true;
|
||||
}
|
||||
|
||||
static nir_block *
|
||||
compute_reg_usedef_lca(nir_register *reg)
|
||||
{
|
||||
struct set_entry *entry;
|
||||
nir_block *lca = NULL;
|
||||
|
||||
set_foreach(reg->defs, entry)
|
||||
lca = nir_dominance_lca(lca, ((nir_instr *)entry->key)->block);
|
||||
|
||||
set_foreach(reg->uses, entry)
|
||||
lca = nir_dominance_lca(lca, ((nir_instr *)entry->key)->block);
|
||||
|
||||
set_foreach(reg->if_uses, entry) {
|
||||
nir_if *if_stmt = (nir_if *)entry->key;
|
||||
nir_cf_node *prev_node = nir_cf_node_prev(&if_stmt->cf_node);
|
||||
assert(prev_node->type == nir_cf_node_block);
|
||||
lca = nir_dominance_lca(lca, nir_cf_node_as_block(prev_node));
|
||||
}
|
||||
|
||||
return lca;
|
||||
}
|
||||
|
||||
static void
|
||||
insert_constant_initializer(nir_deref_var *deref_head, nir_deref *deref_tail,
|
||||
nir_block *block,
|
||||
struct locals_to_regs_state *state)
|
||||
{
|
||||
if (deref_tail->child) {
|
||||
switch (deref_tail->child->deref_type) {
|
||||
case nir_deref_type_array: {
|
||||
unsigned array_elems = glsl_get_length(deref_tail->type);
|
||||
|
||||
nir_deref_array arr_deref;
|
||||
arr_deref.deref = *deref_tail->child;
|
||||
arr_deref.deref_array_type = nir_deref_array_type_direct;
|
||||
|
||||
nir_deref *old_child = deref_tail->child;
|
||||
deref_tail->child = &arr_deref.deref;
|
||||
for (unsigned i = 0; i < array_elems; i++) {
|
||||
arr_deref.base_offset = i;
|
||||
insert_constant_initializer(deref_head, &arr_deref.deref,
|
||||
block, state);
|
||||
}
|
||||
deref_tail->child = old_child;
|
||||
return;
|
||||
}
|
||||
|
||||
case nir_deref_type_struct:
|
||||
insert_constant_initializer(deref_head, deref_tail->child,
|
||||
block, state);
|
||||
return;
|
||||
|
||||
default:
|
||||
unreachable("Invalid deref child type");
|
||||
}
|
||||
}
|
||||
|
||||
assert(deref_tail->child == NULL);
|
||||
|
||||
nir_load_const_instr *load =
|
||||
nir_deref_get_const_initializer_load(state->shader, deref_head);
|
||||
nir_instr_insert_before_block(block, &load->instr);
|
||||
|
||||
nir_src reg_src = get_deref_reg_src(deref_head, &load->instr, state);
|
||||
|
||||
nir_alu_instr *mov = nir_alu_instr_create(state->shader, nir_op_imov);
|
||||
mov->src[0].src = nir_src_for_ssa(&load->def);
|
||||
mov->dest.write_mask = (1 << load->def.num_components) - 1;
|
||||
mov->dest.dest.is_ssa = false;
|
||||
mov->dest.dest.reg.reg = reg_src.reg.reg;
|
||||
mov->dest.dest.reg.base_offset = reg_src.reg.base_offset;
|
||||
mov->dest.dest.reg.indirect = reg_src.reg.indirect;
|
||||
|
||||
nir_instr_insert_after(&load->instr, &mov->instr);
|
||||
}
|
||||
|
||||
static void
|
||||
nir_lower_locals_to_regs_impl(nir_function_impl *impl)
|
||||
{
|
||||
@@ -258,12 +342,31 @@ nir_lower_locals_to_regs_impl(nir_function_impl *impl)
|
||||
state.shader = impl->overload->function->shader;
|
||||
state.impl = impl;
|
||||
state.regs_table = _mesa_hash_table_create(NULL, hash_deref, derefs_equal);
|
||||
nir_array_init(&state.derefs_array, NULL);
|
||||
|
||||
nir_metadata_require(impl, nir_metadata_dominance);
|
||||
|
||||
nir_foreach_block(impl, lower_locals_to_regs_block, &state);
|
||||
|
||||
nir_array_foreach(&state.derefs_array, nir_deref_var *, deref_ptr) {
|
||||
nir_deref_var *deref = *deref_ptr;
|
||||
struct hash_entry *deref_entry =
|
||||
_mesa_hash_table_search(state.regs_table, deref);
|
||||
assert(deref_entry && deref_entry->key == deref);
|
||||
nir_register *reg = (nir_register *)deref_entry->data;
|
||||
|
||||
if (deref->var->constant_initializer == NULL)
|
||||
continue;
|
||||
|
||||
nir_block *usedef_lca = compute_reg_usedef_lca(reg);
|
||||
|
||||
insert_constant_initializer(deref, &deref->deref, usedef_lca, &state);
|
||||
}
|
||||
|
||||
nir_metadata_preserve(impl, nir_metadata_block_index |
|
||||
nir_metadata_dominance);
|
||||
|
||||
nir_array_fini(&state.derefs_array);
|
||||
_mesa_hash_table_destroy(state.regs_table, NULL);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user