+bool
+nir_deref_instr_has_indirect(nir_deref_instr *instr)
+{
+ while (instr->deref_type != nir_deref_type_var) {
+ /* 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_intN_t(b, 0, deref->dest.ssa.bit_size);
+ 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_amul_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;
+}
+