+ /* Consider casts to be indirects */
+ if (instr->deref_type == nir_deref_type_cast)
+ return true;
+
+ if ((instr->deref_type == nir_deref_type_array ||
+ instr->deref_type == nir_deref_type_ptr_as_array) &&
+ !nir_src_is_const(instr->arr.index))
+ return true;
+
+ instr = nir_deref_instr_parent(instr);
+ }
+
+ return false;
+}
+
+bool
+nir_deref_instr_is_known_out_of_bounds(nir_deref_instr *instr)
+{
+ for (; instr; instr = nir_deref_instr_parent(instr)) {
+ if (instr->deref_type == nir_deref_type_array &&
+ nir_src_is_const(instr->arr.index) &&
+ nir_src_as_uint(instr->arr.index) >=
+ glsl_get_length(nir_deref_instr_parent(instr)->type))
+ return true;
+ }
+
+ return false;
+}
+
+bool
+nir_deref_instr_has_complex_use(nir_deref_instr *deref)
+{
+ nir_foreach_use(use_src, &deref->dest.ssa) {
+ nir_instr *use_instr = use_src->parent_instr;
+
+ switch (use_instr->type) {
+ case nir_instr_type_deref: {
+ nir_deref_instr *use_deref = nir_instr_as_deref(use_instr);
+
+ /* A var deref has no sources */
+ assert(use_deref->deref_type != nir_deref_type_var);
+
+ /* If a deref shows up in an array index or something like that, it's
+ * a complex use.
+ */
+ if (use_src != &use_deref->parent)
+ return true;
+
+ /* Anything that isn't a basic struct or array deref is considered to
+ * be a "complex" use. In particular, we don't allow ptr_as_array
+ * because we assume that opt_deref will turn any non-complex
+ * ptr_as_array derefs into regular array derefs eventually so passes
+ * which only want to handle simple derefs will pick them up in a
+ * later pass.
+ */
+ if (use_deref->deref_type != nir_deref_type_struct &&
+ use_deref->deref_type != nir_deref_type_array_wildcard &&
+ use_deref->deref_type != nir_deref_type_array)
+ return true;
+
+ if (nir_deref_instr_has_complex_use(use_deref))
+ return true;
+
+ continue;
+ }
+
+ case nir_instr_type_intrinsic: {
+ nir_intrinsic_instr *use_intrin = nir_instr_as_intrinsic(use_instr);
+ switch (use_intrin->intrinsic) {
+ case nir_intrinsic_load_deref:
+ assert(use_src == &use_intrin->src[0]);
+ continue;
+
+ case nir_intrinsic_copy_deref:
+ assert(use_src == &use_intrin->src[0] ||
+ use_src == &use_intrin->src[1]);
+ continue;
+
+ case nir_intrinsic_store_deref:
+ /* A use in src[1] of a store means we're taking that pointer and
+ * writing it to a variable. Because we have no idea who will
+ * read that variable and what they will do with the pointer, it's
+ * considered a "complex" use. A use in src[0], on the other
+ * hand, is a simple use because we're just going to dereference
+ * it and write a value there.
+ */
+ if (use_src == &use_intrin->src[0])
+ continue;
+ return true;
+
+ default:
+ return true;
+ }
+ unreachable("Switch default failed");
+ }
+
+ default:
+ return true;
+ }
+ }
+
+ nir_foreach_if_use(use, &deref->dest.ssa)
+ return true;
+
+ return false;
+}
+
+unsigned
+nir_deref_instr_ptr_as_array_stride(nir_deref_instr *deref)
+{
+ switch (deref->deref_type) {
+ case nir_deref_type_array:
+ return glsl_get_explicit_stride(nir_deref_instr_parent(deref)->type);
+ case nir_deref_type_ptr_as_array:
+ return nir_deref_instr_ptr_as_array_stride(nir_deref_instr_parent(deref));
+ case nir_deref_type_cast:
+ return deref->cast.ptr_stride;
+ default:
+ return 0;
+ }
+}
+
+static unsigned
+type_get_array_stride(const struct glsl_type *elem_type,
+ glsl_type_size_align_func size_align)
+{
+ unsigned elem_size, elem_align;
+ size_align(elem_type, &elem_size, &elem_align);
+ return ALIGN_POT(elem_size, elem_align);
+}
+
+static unsigned
+struct_type_get_field_offset(const struct glsl_type *struct_type,
+ glsl_type_size_align_func size_align,
+ unsigned field_idx)
+{
+ assert(glsl_type_is_struct_or_ifc(struct_type));
+ unsigned offset = 0;
+ for (unsigned i = 0; i <= field_idx; i++) {
+ unsigned elem_size, elem_align;
+ size_align(glsl_get_struct_field(struct_type, i), &elem_size, &elem_align);
+ offset = ALIGN_POT(offset, elem_align);
+ if (i < field_idx)
+ offset += elem_size;
+ }
+ return offset;
+}
+
+unsigned
+nir_deref_instr_get_const_offset(nir_deref_instr *deref,
+ glsl_type_size_align_func size_align)
+{
+ nir_deref_path path;
+ nir_deref_path_init(&path, deref, NULL);
+
+ assert(path.path[0]->deref_type == nir_deref_type_var);
+
+ unsigned offset = 0;
+ for (nir_deref_instr **p = &path.path[1]; *p; p++) {
+ if ((*p)->deref_type == nir_deref_type_array) {
+ offset += nir_src_as_uint((*p)->arr.index) *
+ type_get_array_stride((*p)->type, size_align);
+ } else if ((*p)->deref_type == nir_deref_type_struct) {
+ /* p starts at path[1], so this is safe */
+ nir_deref_instr *parent = *(p - 1);
+ offset += struct_type_get_field_offset(parent->type, size_align,
+ (*p)->strct.index);
+ } else {
+ unreachable("Unsupported deref type");
+ }
+ }
+
+ nir_deref_path_finish(&path);
+
+ return offset;
+}
+
+nir_ssa_def *
+nir_build_deref_offset(nir_builder *b, nir_deref_instr *deref,
+ glsl_type_size_align_func size_align)
+{
+ nir_deref_path path;
+ nir_deref_path_init(&path, deref, NULL);
+
+ assert(path.path[0]->deref_type == nir_deref_type_var);
+
+ nir_ssa_def *offset = nir_imm_int(b, 0);
+ for (nir_deref_instr **p = &path.path[1]; *p; p++) {
+ if ((*p)->deref_type == nir_deref_type_array) {
+ nir_ssa_def *index = nir_ssa_for_src(b, (*p)->arr.index, 1);
+ int stride = type_get_array_stride((*p)->type, size_align);
+ offset = nir_iadd(b, offset, nir_imul_imm(b, index, stride));
+ } else if ((*p)->deref_type == nir_deref_type_struct) {
+ /* p starts at path[1], so this is safe */
+ nir_deref_instr *parent = *(p - 1);
+ unsigned field_offset =
+ struct_type_get_field_offset(parent->type, size_align,
+ (*p)->strct.index);
+ offset = nir_iadd_imm(b, offset, field_offset);
+ } else {
+ unreachable("Unsupported deref type");
+ }
+ }
+
+ nir_deref_path_finish(&path);
+
+ return offset;
+}
+
+bool
+nir_remove_dead_derefs_impl(nir_function_impl *impl)
+{
+ bool progress = false;
+
+ nir_foreach_block(block, impl) {
+ nir_foreach_instr_safe(instr, block) {
+ if (instr->type == nir_instr_type_deref &&
+ nir_deref_instr_remove_if_unused(nir_instr_as_deref(instr)))
+ progress = true;
+ }
+ }
+
+ if (progress)
+ nir_metadata_preserve(impl, nir_metadata_block_index |
+ nir_metadata_dominance);
+
+ return progress;
+}
+
+bool
+nir_remove_dead_derefs(nir_shader *shader)
+{
+ bool progress = false;
+ nir_foreach_function(function, shader) {
+ if (function->impl && nir_remove_dead_derefs_impl(function->impl))
+ progress = true;
+ }
+
+ return progress;
+}
+
+void
+nir_fixup_deref_modes(nir_shader *shader)
+{
+ nir_foreach_function(function, shader) {
+ if (!function->impl)
+ continue;
+
+ nir_foreach_block(block, function->impl) {
+ nir_foreach_instr(instr, block) {
+ if (instr->type != nir_instr_type_deref)
+ continue;
+
+ nir_deref_instr *deref = nir_instr_as_deref(instr);
+ if (deref->deref_type == nir_deref_type_cast)
+ continue;
+
+ nir_variable_mode parent_mode;
+ if (deref->deref_type == nir_deref_type_var) {
+ parent_mode = deref->var->data.mode;
+ } else {
+ assert(deref->parent.is_ssa);
+ nir_deref_instr *parent =
+ nir_instr_as_deref(deref->parent.ssa->parent_instr);
+ parent_mode = parent->mode;
+ }
+
+ deref->mode = parent_mode;
+ }
+ }
+ }
+}
+
+static bool
+modes_may_alias(nir_variable_mode a, nir_variable_mode b)
+{
+ /* Generic pointers can alias with SSBOs */
+ if ((a == nir_var_mem_ssbo || a == nir_var_mem_global) &&
+ (b == nir_var_mem_ssbo || b == nir_var_mem_global))
+ return true;
+
+ /* In the general case, pointers can only alias if they have the same mode.
+ *
+ * NOTE: In future, with things like OpenCL generic pointers, this may not
+ * be true and will have to be re-evaluated. However, with graphics only,
+ * it should be safe.
+ */
+ return a == b;
+}
+
+static bool
+deref_path_contains_coherent_decoration(nir_deref_path *path)
+{
+ assert(path->path[0]->deref_type == nir_deref_type_var);
+
+ if (path->path[0]->var->data.image.access & ACCESS_COHERENT)
+ return true;
+
+ for (nir_deref_instr **p = &path->path[1]; *p; p++) {
+ if ((*p)->deref_type != nir_deref_type_struct)
+ continue;
+
+ const struct glsl_type *struct_type = (*(p - 1))->type;
+ const struct glsl_struct_field *field =
+ glsl_get_struct_field_data(struct_type, (*p)->strct.index);
+ if (field->memory_coherent)
+ return true;
+ }
+
+ return false;
+}
+
+nir_deref_compare_result
+nir_compare_deref_paths(nir_deref_path *a_path,
+ nir_deref_path *b_path)
+{
+ if (!modes_may_alias(b_path->path[0]->mode, a_path->path[0]->mode))
+ return nir_derefs_do_not_alias;
+
+ if (a_path->path[0]->deref_type != b_path->path[0]->deref_type)
+ return nir_derefs_may_alias_bit;
+
+ if (a_path->path[0]->deref_type == nir_deref_type_var) {
+ if (a_path->path[0]->var != b_path->path[0]->var) {
+ /* Shader and function temporaries aren't backed by memory so two
+ * distinct variables never alias.
+ */
+ static const nir_variable_mode temp_var_modes =
+ nir_var_shader_temp | nir_var_function_temp;
+ if ((a_path->path[0]->mode & temp_var_modes) ||
+ (b_path->path[0]->mode & temp_var_modes))
+ return nir_derefs_do_not_alias;
+
+ /* If they are both declared coherent or have coherent somewhere in
+ * their path (due to a member of an interface being declared
+ * coherent), we have to assume we that we could have any kind of
+ * aliasing. Otherwise, they could still alias but the client didn't
+ * tell us and that's their fault.
+ */
+ if (deref_path_contains_coherent_decoration(a_path) &&
+ deref_path_contains_coherent_decoration(b_path))
+ return nir_derefs_may_alias_bit;
+
+ /* If we can chase the deref all the way back to the variable and
+ * they're not the same variable and at least one is not declared
+ * coherent, we know they can't possibly alias.
+ */
+ return nir_derefs_do_not_alias;
+ }
+ } else {
+ assert(a_path->path[0]->deref_type == nir_deref_type_cast);
+ /* If they're not exactly the same cast, it's hard to compare them so we
+ * just assume they alias. Comparing casts is tricky as there are lots
+ * of things such as mode, type, etc. to make sure work out; for now, we
+ * just assume nit_opt_deref will combine them and compare the deref
+ * instructions.
+ *
+ * TODO: At some point in the future, we could be clever and understand
+ * that a float[] and int[] have the same layout and aliasing structure
+ * but double[] and vec3[] do not and we could potentially be a bit
+ * smarter here.
+ */
+ if (a_path->path[0] != b_path->path[0])
+ return nir_derefs_may_alias_bit;
+ }
+
+ /* Start off assuming they fully compare. We ignore equality for now. In
+ * the end, we'll determine that by containment.
+ */
+ nir_deref_compare_result result = nir_derefs_may_alias_bit |
+ nir_derefs_a_contains_b_bit |
+ nir_derefs_b_contains_a_bit;
+
+ nir_deref_instr **a_p = &a_path->path[1];
+ nir_deref_instr **b_p = &b_path->path[1];
+ while (*a_p != NULL && *a_p == *b_p) {
+ a_p++;
+ b_p++;
+ }
+
+ /* We're at either the tail or the divergence point between the two deref
+ * paths. Look to see if either contains a ptr_as_array deref. It it
+ * does we don't know how to safely make any inferences. Hopefully,
+ * nir_opt_deref will clean most of these up and we can start inferring
+ * things again.
+ *
+ * In theory, we could do a bit better. For instance, we could detect the
+ * case where we have exactly one ptr_as_array deref in the chain after the
+ * divergence point and it's matched in both chains and the two chains have
+ * different constant indices.
+ */
+ for (nir_deref_instr **t_p = a_p; *t_p; t_p++) {
+ if ((*t_p)->deref_type == nir_deref_type_ptr_as_array)
+ return nir_derefs_may_alias_bit;
+ }
+ for (nir_deref_instr **t_p = b_p; *t_p; t_p++) {
+ if ((*t_p)->deref_type == nir_deref_type_ptr_as_array)
+ return nir_derefs_may_alias_bit;
+ }
+
+ while (*a_p != NULL && *b_p != NULL) {
+ nir_deref_instr *a_tail = *(a_p++);
+ nir_deref_instr *b_tail = *(b_p++);
+
+ switch (a_tail->deref_type) {