static nir_register * get_reg_for_deref(nir_deref_instr *deref, struct locals_to_regs_state *state) { uint32_t hash = hash_deref(deref); assert(nir_deref_instr_get_variable(deref)->constant_initializer == NULL); struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(state->regs_table, hash, deref); if (entry) return entry->data; unsigned array_size = 1; for (nir_deref_instr *d = deref; d; d = nir_deref_instr_parent(d)) { if (d->deref_type == nir_deref_type_array) array_size *= glsl_get_length(nir_deref_instr_parent(d)->type); } assert(glsl_type_is_vector_or_scalar(deref->type)); nir_register *reg = nir_local_reg_create(state->builder.impl); reg->num_components = glsl_get_vector_elements(deref->type); reg->num_array_elems = array_size > 1 ? array_size : 0; reg->bit_size = glsl_get_bit_size(deref->type); _mesa_hash_table_insert_pre_hashed(state->regs_table, hash, deref, reg); return reg; }
static bool derefs_equal(const void *void_a, const void *void_b) { for (const nir_deref_instr *a = void_a, *b = void_b; a || b; a = nir_deref_instr_parent(a), b = nir_deref_instr_parent(b)) { if (a->deref_type != b->deref_type) return false; switch (a->deref_type) { case nir_deref_type_var: return a->var == b->var; case nir_deref_type_array: continue; /* Do nothing */ case nir_deref_type_struct: if (a->strct.index != b->strct.index) return false; continue; default: unreachable("Invalid deref type"); } } unreachable("We should have hit a variable dereference"); }
static nir_src get_deref_reg_src(nir_deref_instr *deref, struct locals_to_regs_state *state) { nir_builder *b = &state->builder; nir_src src; src.is_ssa = false; src.reg.reg = get_reg_for_deref(deref, state); src.reg.base_offset = 0; src.reg.indirect = NULL; /* It is possible for a user to create a shader that has an array with a * single element and then proceed to access it indirectly. Indirectly * accessing a non-array register is not allowed in NIR. In order to * handle this case we just convert it to a direct reference. */ if (src.reg.reg->num_array_elems == 0) return src; unsigned inner_array_size = 1; for (const nir_deref_instr *d = deref; d; d = nir_deref_instr_parent(d)) { if (d->deref_type != nir_deref_type_array) continue; if (nir_src_is_const(d->arr.index) && !src.reg.indirect) { src.reg.base_offset += nir_src_as_uint(d->arr.index) * inner_array_size; } else { if (src.reg.indirect) { assert(src.reg.base_offset == 0); } else { src.reg.indirect = ralloc(b->shader, nir_src); *src.reg.indirect = nir_src_for_ssa(nir_imm_int(b, src.reg.base_offset)); src.reg.base_offset = 0; } assert(src.reg.indirect->is_ssa); nir_ssa_def *index = nir_i2i(b, nir_ssa_for_src(b, d->arr.index, 1), 32); src.reg.indirect->ssa = nir_iadd(b, src.reg.indirect->ssa, nir_imul(b, index, nir_imm_int(b, inner_array_size))); } inner_array_size *= glsl_get_length(nir_deref_instr_parent(d)->type); } return src; }
/* The following two functions implement a hash and equality check for * variable dreferences. When the hash or equality function encounters an * array, it ignores the offset and whether it is direct or indirect * entirely. */ static uint32_t hash_deref(const void *void_deref) { uint32_t hash = _mesa_fnv32_1a_offset_bias; for (const nir_deref_instr *deref = void_deref; deref; deref = nir_deref_instr_parent(deref)) { switch (deref->deref_type) { case nir_deref_type_var: return _mesa_fnv32_1a_accumulate(hash, deref->var); case nir_deref_type_array: continue; /* Do nothing */ case nir_deref_type_struct: hash = _mesa_fnv32_1a_accumulate(hash, deref->strct.index); continue; default: unreachable("Invalid deref type"); } } unreachable("We should have hit a variable dereference"); }
static bool deref_has_indirect(nir_deref_instr *deref) { while (deref->deref_type != nir_deref_type_var) { if (deref->deref_type == nir_deref_type_array && nir_src_as_const_value(deref->arr.index) == NULL) return true; deref = nir_deref_instr_parent(deref); } return false; }
static unsigned get_io_offset(nir_deref_instr *deref, bool is_vertex_input) { unsigned offset = 0; for (nir_deref_instr *d = deref; d; d = nir_deref_instr_parent(d)) { if (d->deref_type == nir_deref_type_array) { if (!nir_src_is_const(d->arr.index)) return -1; offset += glsl_count_attribute_slots(d->type, is_vertex_input) * nir_src_as_uint(d->arr.index); } /* TODO: we can get the offset for structs here see nir_lower_io() */ } return offset; }