static nir_ssa_def * sample_plane(nir_builder *b, nir_tex_instr *tex, int plane) { assert(tex->dest.is_ssa); assert(nir_tex_instr_dest_size(tex) == 4); assert(nir_alu_type_get_base_type(tex->dest_type) == nir_type_float); assert(tex->op == nir_texop_tex); assert(tex->coord_components == 2); nir_tex_instr *plane_tex = nir_tex_instr_create(b->shader, 2); nir_src_copy(&plane_tex->src[0].src, &tex->src[0].src, plane_tex); plane_tex->src[0].src_type = nir_tex_src_coord; plane_tex->src[1].src = nir_src_for_ssa(nir_imm_int(b, plane)); plane_tex->src[1].src_type = nir_tex_src_plane; plane_tex->op = nir_texop_tex; plane_tex->sampler_dim = 2; plane_tex->dest_type = nir_type_float; plane_tex->coord_components = 2; plane_tex->texture_index = tex->texture_index; plane_tex->texture = (nir_deref_var *) nir_copy_deref(plane_tex, &tex->texture->deref); plane_tex->sampler_index = tex->sampler_index; plane_tex->sampler = (nir_deref_var *) nir_copy_deref(plane_tex, &tex->sampler->deref); nir_ssa_dest_init(&plane_tex->instr, &plane_tex->dest, 4, 32, NULL); nir_builder_instr_insert(b, &plane_tex->instr); return &plane_tex->dest.ssa; }
static nir_ssa_def * sample_plane(nir_builder *b, nir_tex_instr *tex, int plane) { assert(tex->dest.is_ssa); assert(nir_tex_instr_dest_size(tex) == 4); assert(nir_alu_type_get_base_type(tex->dest_type) == nir_type_float); assert(tex->op == nir_texop_tex); assert(tex->coord_components == 2); nir_tex_instr *plane_tex = nir_tex_instr_create(b->shader, tex->num_srcs + 1); for (unsigned i = 0; i < tex->num_srcs; i++) { nir_src_copy(&plane_tex->src[i].src, &tex->src[i].src, plane_tex); plane_tex->src[i].src_type = tex->src[i].src_type; } plane_tex->src[tex->num_srcs].src = nir_src_for_ssa(nir_imm_int(b, plane)); plane_tex->src[tex->num_srcs].src_type = nir_tex_src_plane; plane_tex->op = nir_texop_tex; plane_tex->sampler_dim = GLSL_SAMPLER_DIM_2D; plane_tex->dest_type = nir_type_float; plane_tex->coord_components = 2; plane_tex->texture_index = tex->texture_index; plane_tex->sampler_index = tex->sampler_index; nir_ssa_dest_init(&plane_tex->instr, &plane_tex->dest, 4, 32, NULL); nir_builder_instr_insert(b, &plane_tex->instr); return &plane_tex->dest.ssa; }
static nir_ssa_def * get_texture_lod(nir_builder *b, nir_tex_instr *tex) { b->cursor = nir_before_instr(&tex->instr); nir_tex_instr *tql; unsigned num_srcs = 0; for (unsigned i = 0; i < tex->num_srcs; i++) { if (tex->src[i].src_type == nir_tex_src_coord || tex->src[i].src_type == nir_tex_src_texture_deref || tex->src[i].src_type == nir_tex_src_sampler_deref || tex->src[i].src_type == nir_tex_src_texture_offset || tex->src[i].src_type == nir_tex_src_sampler_offset || tex->src[i].src_type == nir_tex_src_texture_handle || tex->src[i].src_type == nir_tex_src_sampler_handle) num_srcs++; } tql = nir_tex_instr_create(b->shader, num_srcs); tql->op = nir_texop_lod; tql->coord_components = tex->coord_components; tql->sampler_dim = tex->sampler_dim; tql->is_array = tex->is_array; tql->is_shadow = tex->is_shadow; tql->is_new_style_shadow = tex->is_new_style_shadow; tql->texture_index = tex->texture_index; tql->sampler_index = tex->sampler_index; tql->dest_type = nir_type_float; unsigned idx = 0; for (unsigned i = 0; i < tex->num_srcs; i++) { if (tex->src[i].src_type == nir_tex_src_coord || tex->src[i].src_type == nir_tex_src_texture_deref || tex->src[i].src_type == nir_tex_src_sampler_deref || tex->src[i].src_type == nir_tex_src_texture_offset || tex->src[i].src_type == nir_tex_src_sampler_offset || tex->src[i].src_type == nir_tex_src_texture_handle || tex->src[i].src_type == nir_tex_src_sampler_handle) { nir_src_copy(&tql->src[idx].src, &tex->src[i].src, tql); tql->src[idx].src_type = tex->src[i].src_type; idx++; } } nir_ssa_dest_init(&tql->instr, &tql->dest, 2, 32, NULL); nir_builder_instr_insert(b, &tql->instr); /* The LOD is the y component of the result */ return nir_channel(b, &tql->dest.ssa, 1); }
static nir_ssa_def * get_texture_size(nir_builder *b, nir_tex_instr *tex) { b->cursor = nir_before_instr(&tex->instr); nir_tex_instr *txs; unsigned num_srcs = 1; /* One for the LOD */ for (unsigned i = 0; i < tex->num_srcs; i++) { if (tex->src[i].src_type == nir_tex_src_texture_deref || tex->src[i].src_type == nir_tex_src_sampler_deref || tex->src[i].src_type == nir_tex_src_texture_offset || tex->src[i].src_type == nir_tex_src_sampler_offset || tex->src[i].src_type == nir_tex_src_texture_handle || tex->src[i].src_type == nir_tex_src_sampler_handle) num_srcs++; } txs = nir_tex_instr_create(b->shader, num_srcs); txs->op = nir_texop_txs; txs->sampler_dim = tex->sampler_dim; txs->is_array = tex->is_array; txs->is_shadow = tex->is_shadow; txs->is_new_style_shadow = tex->is_new_style_shadow; txs->texture_index = tex->texture_index; txs->sampler_index = tex->sampler_index; txs->dest_type = nir_type_int; unsigned idx = 0; for (unsigned i = 0; i < tex->num_srcs; i++) { if (tex->src[i].src_type == nir_tex_src_texture_deref || tex->src[i].src_type == nir_tex_src_sampler_deref || tex->src[i].src_type == nir_tex_src_texture_offset || tex->src[i].src_type == nir_tex_src_sampler_offset || tex->src[i].src_type == nir_tex_src_texture_handle || tex->src[i].src_type == nir_tex_src_sampler_handle) { nir_src_copy(&txs->src[idx].src, &tex->src[i].src, txs); txs->src[idx].src_type = tex->src[i].src_type; idx++; } } /* Add in an LOD because some back-ends require it */ txs->src[idx].src = nir_src_for_ssa(nir_imm_int(b, 0)); txs->src[idx].src_type = nir_tex_src_lod; nir_ssa_dest_init(&txs->instr, &txs->dest, nir_tex_instr_dest_size(txs), 32, NULL); nir_builder_instr_insert(b, &txs->instr); return nir_i2f32(b, &txs->dest.ssa); }
static bool lower_locals_to_regs_block(nir_block *block, struct locals_to_regs_state *state) { nir_builder *b = &state->builder; nir_foreach_instr_safe(instr, block) { if (instr->type != nir_instr_type_intrinsic) continue; nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); switch (intrin->intrinsic) { case nir_intrinsic_load_deref: { nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); if (deref->mode != nir_var_function_temp) continue; b->cursor = nir_before_instr(&intrin->instr); nir_alu_instr *mov = nir_alu_instr_create(b->shader, nir_op_imov); mov->src[0].src = get_deref_reg_src(deref, state); mov->dest.write_mask = (1 << intrin->num_components) - 1; if (intrin->dest.is_ssa) { nir_ssa_dest_init(&mov->instr, &mov->dest.dest, intrin->num_components, intrin->dest.ssa.bit_size, NULL); nir_ssa_def_rewrite_uses(&intrin->dest.ssa, nir_src_for_ssa(&mov->dest.dest.ssa)); } else { nir_dest_copy(&mov->dest.dest, &intrin->dest, &mov->instr); } nir_builder_instr_insert(b, &mov->instr); nir_instr_remove(&intrin->instr); state->progress = true; break; } case nir_intrinsic_store_deref: { nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); if (deref->mode != nir_var_function_temp) continue; b->cursor = nir_before_instr(&intrin->instr); nir_src reg_src = get_deref_reg_src(deref, state); nir_alu_instr *mov = nir_alu_instr_create(b->shader, nir_op_imov); nir_src_copy(&mov->src[0].src, &intrin->src[1], mov); mov->dest.write_mask = nir_intrinsic_write_mask(intrin); mov->dest.dest.is_ssa = false; mov->dest.dest.reg.reg = reg_src.reg.reg; mov->dest.dest.reg.base_offset = reg_src.reg.base_offset; mov->dest.dest.reg.indirect = reg_src.reg.indirect; nir_builder_instr_insert(b, &mov->instr); nir_instr_remove(&intrin->instr); state->progress = true; break; } case nir_intrinsic_copy_deref: unreachable("There should be no copies whatsoever at this point"); break; default: continue; } } return true; }
static void lower_instr(nir_intrinsic_instr *instr, lower_atomic_state *state) { nir_intrinsic_op op; switch (instr->intrinsic) { case nir_intrinsic_atomic_counter_read_var: op = nir_intrinsic_atomic_counter_read; break; case nir_intrinsic_atomic_counter_inc_var: op = nir_intrinsic_atomic_counter_inc; break; case nir_intrinsic_atomic_counter_dec_var: op = nir_intrinsic_atomic_counter_dec; break; default: return; } if (instr->variables[0]->var->data.mode != nir_var_uniform && instr->variables[0]->var->data.mode != nir_var_shader_storage) return; /* atomics passed as function arguments can't be lowered */ void *mem_ctx = ralloc_parent(instr); unsigned uniform_loc = instr->variables[0]->var->data.location; nir_intrinsic_instr *new_instr = nir_intrinsic_instr_create(mem_ctx, op); new_instr->const_index[0] = state->shader_program->UniformStorage[uniform_loc].opaque[state->shader->stage].index; nir_load_const_instr *offset_const = nir_load_const_instr_create(mem_ctx, 1); offset_const->value.u[0] = instr->variables[0]->var->data.atomic.offset; nir_instr_insert_before(&instr->instr, &offset_const->instr); nir_ssa_def *offset_def = &offset_const->def; nir_deref *tail = &instr->variables[0]->deref; while (tail->child != NULL) { assert(tail->child->deref_type == nir_deref_type_array); nir_deref_array *deref_array = nir_deref_as_array(tail->child); tail = tail->child; unsigned child_array_elements = tail->child != NULL ? glsl_get_aoa_size(tail->type) : 1; offset_const->value.u[0] += deref_array->base_offset * child_array_elements * ATOMIC_COUNTER_SIZE; if (deref_array->deref_array_type == nir_deref_array_type_indirect) { nir_load_const_instr *atomic_counter_size = nir_load_const_instr_create(mem_ctx, 1); atomic_counter_size->value.u[0] = child_array_elements * ATOMIC_COUNTER_SIZE; nir_instr_insert_before(&instr->instr, &atomic_counter_size->instr); nir_alu_instr *mul = nir_alu_instr_create(mem_ctx, nir_op_imul); nir_ssa_dest_init(&mul->instr, &mul->dest.dest, 1, NULL); mul->dest.write_mask = 0x1; nir_src_copy(&mul->src[0].src, &deref_array->indirect, mul); mul->src[1].src.is_ssa = true; mul->src[1].src.ssa = &atomic_counter_size->def; nir_instr_insert_before(&instr->instr, &mul->instr); nir_alu_instr *add = nir_alu_instr_create(mem_ctx, nir_op_iadd); nir_ssa_dest_init(&add->instr, &add->dest.dest, 1, NULL); add->dest.write_mask = 0x1; add->src[0].src.is_ssa = true; add->src[0].src.ssa = &mul->dest.dest.ssa; add->src[1].src.is_ssa = true; add->src[1].src.ssa = offset_def; nir_instr_insert_before(&instr->instr, &add->instr); offset_def = &add->dest.dest.ssa; } } new_instr->src[0].is_ssa = true; new_instr->src[0].ssa = offset_def; if (instr->dest.is_ssa) { nir_ssa_dest_init(&new_instr->instr, &new_instr->dest, instr->dest.ssa.num_components, NULL); nir_ssa_def_rewrite_uses(&instr->dest.ssa, nir_src_for_ssa(&new_instr->dest.ssa)); } else { nir_dest_copy(&new_instr->dest, &instr->dest, mem_ctx); } nir_instr_insert_before(&instr->instr, &new_instr->instr); nir_instr_remove(&instr->instr); }
nir_foreach_instr_safe(instr, block) { if (instr->type != nir_instr_type_intrinsic) continue; nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); switch (intrin->intrinsic) { case nir_intrinsic_load_var: { if (intrin->variables[0]->var->data.mode != nir_var_local) continue; nir_alu_instr *mov = nir_alu_instr_create(state->shader, nir_op_imov); mov->src[0].src = get_deref_reg_src(intrin->variables[0], &intrin->instr, state); mov->dest.write_mask = (1 << intrin->num_components) - 1; if (intrin->dest.is_ssa) { nir_ssa_dest_init(&mov->instr, &mov->dest.dest, intrin->num_components, intrin->dest.ssa.bit_size, NULL); nir_ssa_def_rewrite_uses(&intrin->dest.ssa, nir_src_for_ssa(&mov->dest.dest.ssa)); } else { nir_dest_copy(&mov->dest.dest, &intrin->dest, &mov->instr); } nir_instr_insert_before(&intrin->instr, &mov->instr); nir_instr_remove(&intrin->instr); state->progress = true; break; } case nir_intrinsic_store_var: { if (intrin->variables[0]->var->data.mode != nir_var_local) continue; nir_src reg_src = get_deref_reg_src(intrin->variables[0], &intrin->instr, state); nir_alu_instr *mov = nir_alu_instr_create(state->shader, nir_op_imov); nir_src_copy(&mov->src[0].src, &intrin->src[0], mov); mov->dest.write_mask = nir_intrinsic_write_mask(intrin); mov->dest.dest.is_ssa = false; mov->dest.dest.reg.reg = reg_src.reg.reg; mov->dest.dest.reg.base_offset = reg_src.reg.base_offset; mov->dest.dest.reg.indirect = reg_src.reg.indirect; nir_instr_insert_before(&intrin->instr, &mov->instr); nir_instr_remove(&intrin->instr); state->progress = true; break; } case nir_intrinsic_copy_var: unreachable("There should be no copies whatsoever at this point"); break; default: continue; } }
static nir_src get_deref_reg_src(nir_deref_var *deref, nir_instr *instr, struct locals_to_regs_state *state) { 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; 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) continue; nir_deref_array *deref_array = nir_deref_as_array(tail); src.reg.base_offset *= glsl_get_length(parent_type); src.reg.base_offset += deref_array->base_offset; if (src.reg.indirect) { nir_load_const_instr *load_const = nir_load_const_instr_create(state->shader, 1, 32); load_const->value.u32[0] = glsl_get_length(parent_type); nir_instr_insert_before(instr, &load_const->instr); nir_alu_instr *mul = nir_alu_instr_create(state->shader, nir_op_imul); mul->src[0].src = *src.reg.indirect; mul->src[1].src.is_ssa = true; mul->src[1].src.ssa = &load_const->def; mul->dest.write_mask = 1; nir_ssa_dest_init(&mul->instr, &mul->dest.dest, 1, 32, NULL); nir_instr_insert_before(instr, &mul->instr); src.reg.indirect->is_ssa = true; src.reg.indirect->ssa = &mul->dest.dest.ssa; } if (deref_array->deref_array_type == nir_deref_array_type_indirect) { if (src.reg.indirect == NULL) { src.reg.indirect = ralloc(state->shader, nir_src); nir_src_copy(src.reg.indirect, &deref_array->indirect, state->shader); } else { nir_alu_instr *add = nir_alu_instr_create(state->shader, nir_op_iadd); add->src[0].src = *src.reg.indirect; nir_src_copy(&add->src[1].src, &deref_array->indirect, add); add->dest.write_mask = 1; nir_ssa_dest_init(&add->instr, &add->dest.dest, 1, 32, NULL); nir_instr_insert_before(instr, &add->instr); src.reg.indirect->is_ssa = true; src.reg.indirect->ssa = &add->dest.dest.ssa; } } } return src; }
static void lower_instr(nir_intrinsic_instr *instr, nir_function_impl *impl) { nir_intrinsic_op op; switch (instr->intrinsic) { case nir_intrinsic_atomic_counter_read_var: op = nir_intrinsic_atomic_counter_read; break; case nir_intrinsic_atomic_counter_inc_var: op = nir_intrinsic_atomic_counter_inc; break; case nir_intrinsic_atomic_counter_dec_var: op = nir_intrinsic_atomic_counter_dec; break; default: return; } if (instr->variables[0]->var->data.mode != nir_var_uniform) return; /* atomics passed as function arguments can't be lowered */ void *mem_ctx = ralloc_parent(instr); nir_intrinsic_instr *new_instr = nir_intrinsic_instr_create(mem_ctx, op); new_instr->const_index[0] = (int) instr->variables[0]->var->data.atomic.buffer_index; nir_load_const_instr *offset_const = nir_load_const_instr_create(mem_ctx, 1); offset_const->value.u[0] = instr->variables[0]->var->data.atomic.offset; nir_instr_insert_before(&instr->instr, &offset_const->instr); nir_ssa_def *offset_def = &offset_const->def; if (instr->variables[0]->deref.child != NULL) { assert(instr->variables[0]->deref.child->deref_type == nir_deref_type_array); nir_deref_array *deref_array = nir_deref_as_array(instr->variables[0]->deref.child); assert(deref_array->deref.child == NULL); offset_const->value.u[0] += deref_array->base_offset * ATOMIC_COUNTER_SIZE; if (deref_array->deref_array_type == nir_deref_array_type_indirect) { nir_load_const_instr *atomic_counter_size = nir_load_const_instr_create(mem_ctx, 1); atomic_counter_size->value.u[0] = ATOMIC_COUNTER_SIZE; nir_instr_insert_before(&instr->instr, &atomic_counter_size->instr); nir_alu_instr *mul = nir_alu_instr_create(mem_ctx, nir_op_imul); nir_ssa_dest_init(&mul->instr, &mul->dest.dest, 1, NULL); mul->dest.write_mask = 0x1; nir_src_copy(&mul->src[0].src, &deref_array->indirect, mem_ctx); mul->src[1].src.is_ssa = true; mul->src[1].src.ssa = &atomic_counter_size->def; nir_instr_insert_before(&instr->instr, &mul->instr); nir_alu_instr *add = nir_alu_instr_create(mem_ctx, nir_op_iadd); nir_ssa_dest_init(&add->instr, &add->dest.dest, 1, NULL); add->dest.write_mask = 0x1; add->src[0].src.is_ssa = true; add->src[0].src.ssa = &mul->dest.dest.ssa; add->src[1].src.is_ssa = true; add->src[1].src.ssa = &offset_const->def; nir_instr_insert_before(&instr->instr, &add->instr); offset_def = &add->dest.dest.ssa; } } new_instr->src[0].is_ssa = true; new_instr->src[0].ssa = offset_def;; if (instr->dest.is_ssa) { nir_ssa_dest_init(&new_instr->instr, &new_instr->dest, instr->dest.ssa.num_components, NULL); nir_ssa_def_rewrite_uses(&instr->dest.ssa, nir_src_for_ssa(&new_instr->dest.ssa), mem_ctx); } else { nir_dest_copy(&new_instr->dest, &instr->dest, mem_ctx); } nir_instr_insert_before(&instr->instr, &new_instr->instr); nir_instr_remove(&instr->instr); }
static bool nir_lower_io_block(nir_block *block, void *void_state) { struct lower_io_state *state = void_state; nir_foreach_instr_safe(block, instr) { if (instr->type != nir_instr_type_intrinsic) continue; nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); switch (intrin->intrinsic) { case nir_intrinsic_load_var: { nir_variable_mode mode = intrin->variables[0]->var->data.mode; if (mode != nir_var_shader_in && mode != nir_var_uniform) continue; bool has_indirect = deref_has_indirect(intrin->variables[0]); /* Figure out the opcode */ nir_intrinsic_op load_op; switch (mode) { case nir_var_shader_in: load_op = has_indirect ? nir_intrinsic_load_input_indirect : nir_intrinsic_load_input; break; case nir_var_uniform: load_op = has_indirect ? nir_intrinsic_load_uniform_indirect : nir_intrinsic_load_uniform; break; default: unreachable("Unknown variable mode"); } nir_intrinsic_instr *load = nir_intrinsic_instr_create(state->mem_ctx, load_op); load->num_components = intrin->num_components; nir_src indirect; unsigned offset = get_io_offset(intrin->variables[0], &intrin->instr, &indirect, state); offset += intrin->variables[0]->var->data.driver_location; load->const_index[0] = offset; if (has_indirect) load->src[0] = indirect; if (intrin->dest.is_ssa) { nir_ssa_dest_init(&load->instr, &load->dest, intrin->num_components, NULL); nir_ssa_def_rewrite_uses(&intrin->dest.ssa, nir_src_for_ssa(&load->dest.ssa), state->mem_ctx); } else { nir_dest_copy(&load->dest, &intrin->dest, state->mem_ctx); } nir_instr_insert_before(&intrin->instr, &load->instr); nir_instr_remove(&intrin->instr); break; } case nir_intrinsic_store_var: { if (intrin->variables[0]->var->data.mode != nir_var_shader_out) continue; bool has_indirect = deref_has_indirect(intrin->variables[0]); nir_intrinsic_op store_op; if (has_indirect) { store_op = nir_intrinsic_store_output_indirect; } else { store_op = nir_intrinsic_store_output; } nir_intrinsic_instr *store = nir_intrinsic_instr_create(state->mem_ctx, store_op); store->num_components = intrin->num_components; nir_src indirect; unsigned offset = get_io_offset(intrin->variables[0], &intrin->instr, &indirect, state); offset += intrin->variables[0]->var->data.driver_location; store->const_index[0] = offset; nir_src_copy(&store->src[0], &intrin->src[0], state->mem_ctx); if (has_indirect) store->src[1] = indirect; nir_instr_insert_before(&intrin->instr, &store->instr); nir_instr_remove(&intrin->instr); break; } default: break; } } return true; }
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; }