static bool derefs_equal(const void *void_a, const void *void_b) { const nir_deref_var *a_var = void_a; const nir_deref_var *b_var = void_b; if (a_var->var != b_var->var) return false; for (const nir_deref *a = a_var->deref.child, *b = b_var->deref.child; a != NULL; a = a->child, b = b->child) { if (a->deref_type != b->deref_type) return false; if (a->deref_type == nir_deref_type_struct) { if (nir_deref_as_struct(a)->index != nir_deref_as_struct(b)->index) return false; } /* Do nothing for arrays. They're all the same. */ assert((a->child == NULL) == (b->child == NULL)); if((a->child == NULL) != (b->child == NULL)) return false; } return true; }
static void validate_deref_chain(nir_deref *deref, validate_state *state) { assert(deref->child == NULL || ralloc_parent(deref->child) == deref); nir_deref *parent = NULL; while (deref != NULL) { switch (deref->deref_type) { case nir_deref_type_array: assert(deref->type == glsl_get_array_element(parent->type)); if (nir_deref_as_array(deref)->deref_array_type == nir_deref_array_type_indirect) validate_src(&nir_deref_as_array(deref)->indirect, state); break; case nir_deref_type_struct: assert(deref->type == glsl_get_struct_field(parent->type, nir_deref_as_struct(deref)->index)); break; case nir_deref_type_var: break; default: assert(!"Invalid deref type"); break; } parent = deref; deref = deref->child; } }
/* Calculate the sampler index based on array indicies and also * calculate the base uniform location for struct members. */ static void calc_sampler_offsets(nir_deref *tail, nir_tex_instr *instr, unsigned *array_elements, nir_ssa_def **indirect, nir_builder *b, unsigned *location) { if (tail->child == NULL) return; switch (tail->child->deref_type) { case nir_deref_type_array: { nir_deref_array *deref_array = nir_deref_as_array(tail->child); assert(deref_array->deref_array_type != nir_deref_array_type_wildcard); calc_sampler_offsets(tail->child, instr, array_elements, indirect, b, location); instr->sampler_index += deref_array->base_offset * *array_elements; if (deref_array->deref_array_type == nir_deref_array_type_indirect) { nir_ssa_def *mul = nir_imul(b, nir_imm_int(b, *array_elements), nir_ssa_for_src(b, deref_array->indirect, 1)); nir_instr_rewrite_src(&instr->instr, &deref_array->indirect, NIR_SRC_INIT); if (*indirect) { *indirect = nir_iadd(b, *indirect, mul); } else { *indirect = mul; } } *array_elements *= glsl_get_length(tail->type); break; } case nir_deref_type_struct: { nir_deref_struct *deref_struct = nir_deref_as_struct(tail->child); *location += glsl_get_record_location_offset(tail->type, deref_struct->index); calc_sampler_offsets(tail->child, instr, array_elements, indirect, b, location); break; } default: unreachable("Invalid deref type"); break; } }
static void validate_deref_chain(nir_deref *deref, nir_variable_mode mode, validate_state *state) { validate_assert(state, deref->child == NULL || ralloc_parent(deref->child) == deref); nir_deref *parent = NULL; while (deref != NULL) { switch (deref->deref_type) { case nir_deref_type_array: if (mode == nir_var_shared) { /* Shared variables have a bit more relaxed rules because we need * to be able to handle array derefs on vectors. Fortunately, * nir_lower_io handles these just fine. */ validate_assert(state, glsl_type_is_array(parent->type) || glsl_type_is_matrix(parent->type) || glsl_type_is_vector(parent->type)); } else { /* Most of NIR cannot handle array derefs on vectors */ validate_assert(state, glsl_type_is_array(parent->type) || glsl_type_is_matrix(parent->type)); } validate_assert(state, deref->type == glsl_get_array_element(parent->type)); if (nir_deref_as_array(deref)->deref_array_type == nir_deref_array_type_indirect) validate_src(&nir_deref_as_array(deref)->indirect, state, 32, 1); break; case nir_deref_type_struct: assume(parent); /* cannot happen: deref change starts w/ nir_deref_var */ validate_assert(state, deref->type == glsl_get_struct_field(parent->type, nir_deref_as_struct(deref)->index)); break; case nir_deref_type_var: break; default: validate_assert(state, !"Invalid deref type"); break; } parent = deref; deref = deref->child; } }
/* \sa deref_may_be_aliased */ static bool deref_may_be_aliased_node(struct deref_node *node, nir_deref *deref, struct lower_variables_state *state) { if (deref->child == NULL) { return false; } else { switch (deref->child->deref_type) { case nir_deref_type_array: { nir_deref_array *arr = nir_deref_as_array(deref->child); if (arr->deref_array_type == nir_deref_array_type_indirect) return true; /* If there is an indirect at this level, we're aliased. */ if (node->indirect) return true; assert(arr->deref_array_type == nir_deref_array_type_direct); if (node->children[arr->base_offset] && deref_may_be_aliased_node(node->children[arr->base_offset], deref->child, state)) return true; if (node->wildcard && deref_may_be_aliased_node(node->wildcard, deref->child, state)) return true; return false; } case nir_deref_type_struct: { nir_deref_struct *str = nir_deref_as_struct(deref->child); if (node->children[str->index]) { return deref_may_be_aliased_node(node->children[str->index], deref->child, state); } else { return false; } } default: unreachable("Invalid nir_deref child type"); } } }
/* 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; const nir_deref_var *deref_var = void_deref; hash = _mesa_fnv32_1a_accumulate(hash, deref_var->var); for (const nir_deref *deref = deref_var->deref.child; deref; deref = deref->child) { if (deref->deref_type == nir_deref_type_struct) { const nir_deref_struct *deref_struct = nir_deref_as_struct(deref); hash = _mesa_fnv32_1a_accumulate(hash, deref_struct->index); } } return hash; }
/* \sa foreach_deref_node_match */ static bool foreach_deref_node_worker(struct deref_node *node, nir_deref *deref, bool (* cb)(struct deref_node *node, struct lower_variables_state *state), struct lower_variables_state *state) { if (deref->child == NULL) { return cb(node, state); } else { switch (deref->child->deref_type) { case nir_deref_type_array: { nir_deref_array *arr = nir_deref_as_array(deref->child); assert(arr->deref_array_type == nir_deref_array_type_direct); if (node->children[arr->base_offset] && !foreach_deref_node_worker(node->children[arr->base_offset], deref->child, cb, state)) return false; if (node->wildcard && !foreach_deref_node_worker(node->wildcard, deref->child, cb, state)) return false; return true; } case nir_deref_type_struct: { nir_deref_struct *str = nir_deref_as_struct(deref->child); return foreach_deref_node_worker(node->children[str->index], deref->child, cb, state); } default: unreachable("Invalid deref child type"); } } }
/* Gets the deref_node for the given deref chain and creates it if it * doesn't yet exist. If the deref is fully-qualified and direct and * state->add_to_direct_deref_nodes is true, it will be added to the hash * table of of fully-qualified direct derefs. */ static struct deref_node * get_deref_node(nir_deref_var *deref, struct lower_variables_state *state) { bool is_direct = true; /* Start at the base of the chain. */ struct deref_node *node = get_deref_node_for_var(deref->var, state); assert(deref->deref.type == node->type); for (nir_deref *tail = deref->deref.child; tail; tail = tail->child) { switch (tail->deref_type) { case nir_deref_type_struct: { nir_deref_struct *deref_struct = nir_deref_as_struct(tail); assert(deref_struct->index < glsl_get_length(node->type)); if (node->children[deref_struct->index] == NULL) node->children[deref_struct->index] = deref_node_create(node, tail->type, state->dead_ctx); node = node->children[deref_struct->index]; break; } case nir_deref_type_array: { nir_deref_array *arr = nir_deref_as_array(tail); switch (arr->deref_array_type) { case nir_deref_array_type_direct: /* This is possible if a loop unrolls and generates an * out-of-bounds offset. We need to handle this at least * somewhat gracefully. */ if (arr->base_offset >= glsl_get_length(node->type)) return NULL; if (node->children[arr->base_offset] == NULL) node->children[arr->base_offset] = deref_node_create(node, tail->type, state->dead_ctx); node = node->children[arr->base_offset]; break; case nir_deref_array_type_indirect: if (node->indirect == NULL) node->indirect = deref_node_create(node, tail->type, state->dead_ctx); node = node->indirect; is_direct = false; break; case nir_deref_array_type_wildcard: if (node->wildcard == NULL) node->wildcard = deref_node_create(node, tail->type, state->dead_ctx); node = node->wildcard; is_direct = false; break; default: unreachable("Invalid array deref type"); } break; } default: unreachable("Invalid deref type"); } } assert(node); /* Only insert if it isn't already in the list. */ if (is_direct && state->add_to_direct_deref_nodes && node->direct_derefs_link.next == NULL) { node->deref = deref; assert(deref->var != NULL); exec_list_push_tail(&state->direct_deref_nodes, &node->direct_derefs_link); } return node; }
static unsigned get_io_offset(nir_deref_var *deref, nir_instr *instr, nir_src *indirect, struct lower_io_state *state) { bool found_indirect = false; unsigned base_offset = 0; nir_deref *tail = &deref->deref; while (tail->child != NULL) { const struct glsl_type *parent_type = tail->type; tail = tail->child; if (tail->deref_type == nir_deref_type_array) { nir_deref_array *deref_array = nir_deref_as_array(tail); unsigned size = type_size(tail->type); base_offset += size * deref_array->base_offset; if (deref_array->deref_array_type == nir_deref_array_type_indirect) { nir_load_const_instr *load_const = nir_load_const_instr_create(state->mem_ctx, 1); load_const->value.u[0] = size; nir_instr_insert_before(instr, &load_const->instr); nir_alu_instr *mul = nir_alu_instr_create(state->mem_ctx, nir_op_imul); mul->src[0].src.is_ssa = true; mul->src[0].src.ssa = &load_const->def; nir_src_copy(&mul->src[1].src, &deref_array->indirect, state->mem_ctx); mul->dest.write_mask = 1; nir_ssa_dest_init(&mul->instr, &mul->dest.dest, 1, NULL); nir_instr_insert_before(instr, &mul->instr); if (found_indirect) { nir_alu_instr *add = nir_alu_instr_create(state->mem_ctx, nir_op_iadd); add->src[0].src = *indirect; add->src[1].src.is_ssa = true; add->src[1].src.ssa = &mul->dest.dest.ssa; add->dest.write_mask = 1; nir_ssa_dest_init(&add->instr, &add->dest.dest, 1, NULL); nir_instr_insert_before(instr, &add->instr); indirect->is_ssa = true; indirect->ssa = &add->dest.dest.ssa; } else { indirect->is_ssa = true; indirect->ssa = &mul->dest.dest.ssa; found_indirect = true; } } } else if (tail->deref_type == nir_deref_type_struct) { nir_deref_struct *deref_struct = nir_deref_as_struct(tail); for (unsigned i = 0; i < deref_struct->index; i++) base_offset += type_size(glsl_get_struct_field(parent_type, i)); } } return base_offset; }
static void lower_sampler(nir_tex_instr *instr, struct gl_shader_program *shader_program, const struct gl_program *prog, void *mem_ctx) { if (instr->sampler == NULL) return; /* Get the name and the offset */ instr->sampler_index = 0; bool has_indirect = false; char *name = ralloc_strdup(mem_ctx, instr->sampler->var->name); for (nir_deref *deref = &instr->sampler->deref; deref->child; deref = deref->child) { switch (deref->child->deref_type) { case nir_deref_type_array: { nir_deref_array *deref_array = nir_deref_as_array(deref->child); /* XXX: We're assuming here that the indirect is the last array * thing we have. This should be ok for now as we don't support * arrays_of_arrays yet. */ assert(!has_indirect); instr->sampler_index *= glsl_get_length(deref->type); switch (deref_array->deref_array_type) { case nir_deref_array_type_direct: instr->sampler_index += deref_array->base_offset; if (deref_array->deref.child) ralloc_asprintf_append(&name, "[%u]", deref_array->base_offset); break; case nir_deref_array_type_indirect: { assert(!has_indirect); instr->src = reralloc(mem_ctx, instr->src, nir_tex_src, instr->num_srcs + 1); memset(&instr->src[instr->num_srcs], 0, sizeof *instr->src); instr->src[instr->num_srcs].src_type = nir_tex_src_sampler_offset; instr->num_srcs++; nir_instr_rewrite_src(&instr->instr, &instr->src[instr->num_srcs - 1].src, deref_array->indirect); instr->sampler_array_size = glsl_get_length(deref->type); nir_src empty; memset(&empty, 0, sizeof empty); nir_instr_rewrite_src(&instr->instr, &deref_array->indirect, empty); if (deref_array->deref.child) ralloc_strcat(&name, "[0]"); break; } case nir_deref_array_type_wildcard: unreachable("Cannot copy samplers"); default: unreachable("Invalid deref array type"); } break; } case nir_deref_type_struct: { nir_deref_struct *deref_struct = nir_deref_as_struct(deref->child); const char *field = glsl_get_struct_elem_name(deref->type, deref_struct->index); ralloc_asprintf_append(&name, ".%s", field); break; } default: unreachable("Invalid deref type"); break; } } instr->sampler_index += get_sampler_index(shader_program, name, prog); instr->sampler = NULL; }