+static nir_ssa_def *
+vtn_resource_reindex(struct vtn_builder *b, enum vtn_variable_mode mode,
+ nir_ssa_def *base_index, nir_ssa_def *offset_index)
+{
+ nir_intrinsic_instr *instr =
+ nir_intrinsic_instr_create(b->nb.shader,
+ nir_intrinsic_vulkan_resource_reindex);
+ instr->src[0] = nir_src_for_ssa(base_index);
+ instr->src[1] = nir_src_for_ssa(offset_index);
+ nir_intrinsic_set_desc_type(instr, vk_desc_type_for_mode(b, mode));
+
+ const struct glsl_type *index_type =
+ b->options->lower_ubo_ssbo_access_to_offsets ?
+ glsl_uint_type() : vtn_ptr_type_for_mode(b, mode);
+
+ instr->num_components = glsl_get_vector_elements(index_type);
+ nir_ssa_dest_init(&instr->instr, &instr->dest, instr->num_components,
+ glsl_get_bit_size(index_type), NULL);
+ nir_builder_instr_insert(&b->nb, &instr->instr);
+
+ return &instr->dest.ssa;
+}
+
+static nir_ssa_def *
+vtn_descriptor_load(struct vtn_builder *b, enum vtn_variable_mode mode,
+ nir_ssa_def *desc_index)
+{
+ nir_intrinsic_instr *desc_load =
+ nir_intrinsic_instr_create(b->nb.shader,
+ nir_intrinsic_load_vulkan_descriptor);
+ desc_load->src[0] = nir_src_for_ssa(desc_index);
+ nir_intrinsic_set_desc_type(desc_load, vk_desc_type_for_mode(b, mode));
+
+ const struct glsl_type *ptr_type = vtn_ptr_type_for_mode(b, mode);
+
+ desc_load->num_components = glsl_get_vector_elements(ptr_type);
+ nir_ssa_dest_init(&desc_load->instr, &desc_load->dest,
+ desc_load->num_components,
+ glsl_get_bit_size(ptr_type), NULL);
+ nir_builder_instr_insert(&b->nb, &desc_load->instr);
+
+ return &desc_load->dest.ssa;
+}
+
+/* Dereference the given base pointer by the access chain */
+static struct vtn_pointer *
+vtn_nir_deref_pointer_dereference(struct vtn_builder *b,
+ struct vtn_pointer *base,
+ struct vtn_access_chain *deref_chain)
+{
+ struct vtn_type *type = base->type;
+ enum gl_access_qualifier access = base->access;
+ unsigned idx = 0;
+
+ nir_deref_instr *tail;
+ if (base->deref) {
+ tail = base->deref;
+ } else if (vtn_pointer_is_external_block(b, base)) {
+ nir_ssa_def *block_index = base->block_index;
+
+ /* We dereferencing an external block pointer. Correctness of this
+ * operation relies on one particular line in the SPIR-V spec, section
+ * entitled "Validation Rules for Shader Capabilities":
+ *
+ * "Block and BufferBlock decorations cannot decorate a structure
+ * type that is nested at any level inside another structure type
+ * decorated with Block or BufferBlock."
+ *
+ * This means that we can detect the point where we cross over from
+ * descriptor indexing to buffer indexing by looking for the block
+ * decorated struct type. Anything before the block decorated struct
+ * type is a descriptor indexing operation and anything after the block
+ * decorated struct is a buffer offset operation.
+ */
+
+ /* Figure out the descriptor array index if any
+ *
+ * Some of the Vulkan CTS tests with hand-rolled SPIR-V have been known
+ * to forget the Block or BufferBlock decoration from time to time.
+ * It's more robust if we check for both !block_index and for the type
+ * to contain a block. This way there's a decent chance that arrays of
+ * UBOs/SSBOs will work correctly even if variable pointers are
+ * completley toast.
+ */
+ nir_ssa_def *desc_arr_idx = NULL;
+ if (!block_index || vtn_type_contains_block(b, type)) {
+ /* If our type contains a block, then we're still outside the block
+ * and we need to process enough levels of dereferences to get inside
+ * of it.
+ */
+ if (deref_chain->ptr_as_array) {
+ unsigned aoa_size = glsl_get_aoa_size(type->type);
+ desc_arr_idx = vtn_access_link_as_ssa(b, deref_chain->link[idx],
+ MAX2(aoa_size, 1), 32);
+ idx++;
+ }
+
+ for (; idx < deref_chain->length; idx++) {
+ if (type->base_type != vtn_base_type_array) {
+ vtn_assert(type->base_type == vtn_base_type_struct);
+ break;
+ }
+
+ unsigned aoa_size = glsl_get_aoa_size(type->array_element->type);
+ nir_ssa_def *arr_offset =
+ vtn_access_link_as_ssa(b, deref_chain->link[idx],
+ MAX2(aoa_size, 1), 32);
+ if (desc_arr_idx)
+ desc_arr_idx = nir_iadd(&b->nb, desc_arr_idx, arr_offset);
+ else
+ desc_arr_idx = arr_offset;
+
+ type = type->array_element;
+ access |= type->access;
+ }
+ }
+
+ if (!block_index) {
+ vtn_assert(base->var && base->type);
+ block_index = vtn_variable_resource_index(b, base->var, desc_arr_idx);
+ } else if (desc_arr_idx) {
+ block_index = vtn_resource_reindex(b, base->mode,
+ block_index, desc_arr_idx);
+ }
+
+ if (idx == deref_chain->length) {
+ /* The entire deref was consumed in finding the block index. Return
+ * a pointer which just has a block index and a later access chain
+ * will dereference deeper.
+ */
+ struct vtn_pointer *ptr = rzalloc(b, struct vtn_pointer);
+ ptr->mode = base->mode;
+ ptr->type = type;
+ ptr->block_index = block_index;
+ ptr->access = access;
+ return ptr;
+ }
+
+ /* If we got here, there's more access chain to handle and we have the
+ * final block index. Insert a descriptor load and cast to a deref to
+ * start the deref chain.
+ */
+ nir_ssa_def *desc = vtn_descriptor_load(b, base->mode, block_index);
+
+ assert(base->mode == vtn_variable_mode_ssbo ||
+ base->mode == vtn_variable_mode_ubo);
+ nir_variable_mode nir_mode =
+ base->mode == vtn_variable_mode_ssbo ? nir_var_mem_ssbo : nir_var_mem_ubo;
+
+ tail = nir_build_deref_cast(&b->nb, desc, nir_mode, type->type,
+ base->ptr_type->stride);
+ } else {
+ assert(base->var && base->var->var);
+ tail = nir_build_deref_var(&b->nb, base->var->var);
+ if (base->ptr_type && base->ptr_type->type) {
+ tail->dest.ssa.num_components =
+ glsl_get_vector_elements(base->ptr_type->type);
+ tail->dest.ssa.bit_size = glsl_get_bit_size(base->ptr_type->type);
+ }
+ }
+
+ if (idx == 0 && deref_chain->ptr_as_array) {
+ /* We start with a deref cast to get the stride. Hopefully, we'll be
+ * able to delete that cast eventually.
+ */
+ tail = nir_build_deref_cast(&b->nb, &tail->dest.ssa, tail->mode,
+ tail->type, base->ptr_type->stride);
+
+ nir_ssa_def *index = vtn_access_link_as_ssa(b, deref_chain->link[0], 1,
+ tail->dest.ssa.bit_size);
+ tail = nir_build_deref_ptr_as_array(&b->nb, tail, index);
+ idx++;
+ }
+
+ for (; idx < deref_chain->length; idx++) {
+ if (glsl_type_is_struct_or_ifc(type->type)) {
+ vtn_assert(deref_chain->link[idx].mode == vtn_access_mode_literal);
+ unsigned field = deref_chain->link[idx].id;
+ tail = nir_build_deref_struct(&b->nb, tail, field);
+ type = type->members[field];
+ } else {
+ nir_ssa_def *arr_index =
+ vtn_access_link_as_ssa(b, deref_chain->link[idx], 1,
+ tail->dest.ssa.bit_size);
+ tail = nir_build_deref_array(&b->nb, tail, arr_index);
+ type = type->array_element;
+ }
+
+ access |= type->access;
+ }
+
+ struct vtn_pointer *ptr = rzalloc(b, struct vtn_pointer);
+ ptr->mode = base->mode;
+ ptr->type = type;
+ ptr->var = base->var;
+ ptr->deref = tail;
+ ptr->access = access;
+
+ return ptr;
+}
+