+/**
+ * Generate a comparison value for a block of indices
+ *
+ * Lowering passes for non-constant indexing of arrays, matrices, or vectors
+ * can use this to generate blocks of index comparison values.
+ *
+ * \param instructions List where new instructions will be appended
+ * \param index \c ir_variable containing the desired index
+ * \param base Base value for this block of comparisons
+ * \param components Number of unique index values to compare. This must
+ * be on the range [1, 4].
+ * \param mem_ctx ralloc memory context to be used for all allocations.
+ *
+ * \returns
+ * An \c ir_rvalue that \b must be cloned for each use in conditional
+ * assignments, etc.
+ */
+ir_rvalue *
+compare_index_block(exec_list *instructions, ir_variable *index,
+ unsigned base, unsigned components, void *mem_ctx)
+{
+ ir_rvalue *broadcast_index = new(mem_ctx) ir_dereference_variable(index);
+
+ assert(index->type->is_scalar());
+ assert(index->type->base_type == GLSL_TYPE_INT);
+ assert(components >= 1 && components <= 4);
+
+ if (components > 1) {
+ const ir_swizzle_mask m = { 0, 0, 0, 0, components, false };
+ broadcast_index = new(mem_ctx) ir_swizzle(broadcast_index, m);
+ }
+
+ /* Compare the desired index value with the next block of four indices.
+ */
+ ir_constant_data test_indices_data;
+ memset(&test_indices_data, 0, sizeof(test_indices_data));
+ test_indices_data.i[0] = base;
+ test_indices_data.i[1] = base + 1;
+ test_indices_data.i[2] = base + 2;
+ test_indices_data.i[3] = base + 3;
+
+ ir_constant *const test_indices =
+ new(mem_ctx) ir_constant(broadcast_index->type,
+ &test_indices_data);
+
+ ir_rvalue *const condition_val =
+ new(mem_ctx) ir_expression(ir_binop_equal,
+ &glsl_type::bool_type[components - 1],
+ broadcast_index,
+ test_indices);
+
+ ir_variable *const condition =
+ new(mem_ctx) ir_variable(condition_val->type,
+ "dereference_condition",
+ ir_var_temporary);
+ instructions->push_tail(condition);
+
+ ir_rvalue *const cond_deref =
+ new(mem_ctx) ir_dereference_variable(condition);
+ instructions->push_tail(new(mem_ctx) ir_assignment(cond_deref, condition_val, 0));
+
+ return cond_deref;
+}
+
+static inline bool
+is_array_or_matrix(const ir_rvalue *ir)
+{
+ return (ir->type->is_array() || ir->type->is_matrix());
+}
+
+/**
+ * Replace a dereference of a variable with a specified r-value
+ *
+ * Each time a dereference of the specified value is replaced, the r-value
+ * tree is cloned.
+ */
+class deref_replacer : public ir_rvalue_visitor {
+public:
+ deref_replacer(const ir_variable *variable_to_replace, ir_rvalue *value)
+ : variable_to_replace(variable_to_replace), value(value),
+ progress(false)
+ {
+ assert(this->variable_to_replace != NULL);
+ assert(this->value != NULL);
+ }
+
+ virtual void handle_rvalue(ir_rvalue **rvalue)
+ {
+ ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
+
+ if ((dv != NULL) && (dv->var == this->variable_to_replace)) {
+ this->progress = true;
+ *rvalue = this->value->clone(ralloc_parent(*rvalue), NULL);
+ }
+ }
+
+ const ir_variable *variable_to_replace;
+ ir_rvalue *value;
+ bool progress;
+};
+
+/**
+ * Find a variable index dereference of an array in an rvalue tree
+ */
+class find_variable_index : public ir_hierarchical_visitor {
+public:
+ find_variable_index()
+ : deref(NULL)
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit_enter(ir_dereference_array *ir)
+ {
+ if (is_array_or_matrix(ir->array)
+ && (ir->array_index->as_constant() == NULL)) {
+ this->deref = ir;
+ return visit_stop;
+ }
+
+ return visit_continue;
+ }
+
+ /**
+ * First array dereference found in the tree that has a non-constant index.
+ */
+ ir_dereference_array *deref;
+};
+