#include "glsl_types.h"
#include "main/macros.h"
+/**
+ * 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 || index->type->base_type == GLSL_TYPE_UINT);
+ 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::bvec(components),
+ 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_instruction *ir)
+is_array_or_matrix(const ir_rvalue *ir)
{
return (ir->type->is_array() || ir->type->is_matrix());
}
+namespace {
/**
* Replace a dereference of a variable with a specified r-value
*
ir_variable* var;
assignment_generator()
+ : base_ir(NULL),
+ rvalue(NULL),
+ old_index(NULL),
+ is_write(false),
+ write_mask(0),
+ var(NULL)
{
}
for (unsigned i = first; i < end; i += 4) {
const unsigned comps = MIN2(condition_components, end - i);
- ir_rvalue *broadcast_index =
- new(this->mem_ctx) ir_dereference_variable(index);
-
- if (comps) {
- const ir_swizzle_mask m = { 0, 0, 0, 0, comps, false };
- broadcast_index = new(this->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] = i;
- test_indices_data.i[1] = i + 1;
- test_indices_data.i[2] = i + 2;
- test_indices_data.i[3] = i + 3;
- ir_constant *const test_indices =
- new(this->mem_ctx) ir_constant(broadcast_index->type,
- &test_indices_data);
-
- ir_rvalue *const condition_val =
- new(this->mem_ctx) ir_expression(ir_binop_equal,
- &glsl_type::bool_type[comps - 1],
- broadcast_index,
- test_indices);
-
- ir_variable *const condition =
- new(this->mem_ctx) ir_variable(condition_val->type,
- "dereference_array_condition",
- ir_var_temporary);
- list->push_tail(condition);
-
ir_rvalue *const cond_deref =
- new(this->mem_ctx) ir_dereference_variable(condition);
- list->push_tail(new(this->mem_ctx) ir_assignment(cond_deref,
- condition_val, 0));
+ compare_index_block(list, index, i, comps, this->mem_ctx);
if (comps == 1) {
- ir_rvalue *const cond_deref =
- new(this->mem_ctx) ir_dereference_variable(condition);
-
- this->generator.generate(i, cond_deref, list);
+ this->generator.generate(i, cond_deref->clone(this->mem_ctx, NULL),
+ list);
} else {
for (unsigned j = 0; j < comps; j++) {
- ir_rvalue *const cond_deref =
- new(this->mem_ctx) ir_dereference_variable(condition);
ir_rvalue *const cond_swiz =
- new(this->mem_ctx) ir_swizzle(cond_deref, j, 0, 0, 0, 1);
+ new(this->mem_ctx) ir_swizzle(cond_deref->clone(this->mem_ctx, NULL),
+ j, 0, 0, 0, 1);
this->generator.generate(i + j, cond_swiz, list);
}
class variable_index_to_cond_assign_visitor : public ir_rvalue_visitor {
public:
- variable_index_to_cond_assign_visitor(bool lower_input,
- bool lower_output,
- bool lower_temp,
- bool lower_uniform)
+ variable_index_to_cond_assign_visitor(gl_shader_stage stage,
+ bool lower_input,
+ bool lower_output,
+ bool lower_temp,
+ bool lower_uniform)
{
this->progress = false;
+ this->stage = stage;
this->lower_inputs = lower_input;
this->lower_outputs = lower_output;
this->lower_temps = lower_temp;
}
bool progress;
+
+ gl_shader_stage stage;
bool lower_inputs;
bool lower_outputs;
bool lower_temps;
bool storage_type_needs_lowering(ir_dereference_array *deref) const
{
- if (deref->array->ir_type == ir_type_constant)
+ /* If a variable isn't eventually the target of this dereference, then
+ * it must be a constant or some sort of anonymous temporary storage.
+ *
+ * FINISHME: Is this correct? Most drivers treat arrays of constants as
+ * FINISHME: uniforms. It seems like this should do the same.
+ */
+ const ir_variable *const var = deref->array->variable_referenced();
+ if (var == NULL)
return this->lower_temps;
- const ir_variable *const var = deref->array->variable_referenced();
- switch (var->mode) {
+ switch (var->data.mode) {
case ir_var_auto:
case ir_var_temporary:
return this->lower_temps;
+
case ir_var_uniform:
+ case ir_var_shader_storage:
return this->lower_uniforms;
- case ir_var_in:
+
+ case ir_var_shader_shared:
+ return false;
+
+ case ir_var_function_in:
case ir_var_const_in:
- return (var->location == -1) ? this->lower_temps : this->lower_inputs;
- case ir_var_out:
- return (var->location == -1) ? this->lower_temps : this->lower_outputs;
- case ir_var_inout:
+ return this->lower_temps;
+
+ case ir_var_shader_in:
+ /* The input array size is unknown at compiler time for non-patch
+ * inputs in TCS and TES. The arrays are sized to
+ * the implementation-dependent limit "gl_MaxPatchVertices", but
+ * the real size is stored in the "gl_PatchVerticesIn" built-in
+ * uniform.
+ *
+ * The TCS input array size is specified by
+ * glPatchParameteri(GL_PATCH_VERTICES).
+ *
+ * The TES input array size is specified by the "vertices" output
+ * layout qualifier in TCS.
+ */
+ if ((stage == MESA_SHADER_TESS_CTRL ||
+ stage == MESA_SHADER_TESS_EVAL) && !var->data.patch)
+ return false;
+ return this->lower_inputs;
+
+ case ir_var_function_out:
+ /* TCS non-patch outputs can only be indexed with "gl_InvocationID".
+ * Other expressions are not allowed.
+ */
+ if (stage == MESA_SHADER_TESS_CTRL && !var->data.patch)
+ return false;
+ return this->lower_temps;
+
+ case ir_var_shader_out:
+ return this->lower_outputs;
+
+ case ir_var_function_inout:
return this->lower_temps;
}
switch_generator sg(ag, index, 4, 4);
- exec_list list;
- sg.generate(0, length, &list);
- base_ir->insert_before(&list);
+ /* If the original assignment has a condition, respect that original
+ * condition! This is acomplished by wrapping the new conditional
+ * assignments in an if-statement that uses the original condition.
+ */
+ if ((orig_assign != NULL) && (orig_assign->condition != NULL)) {
+ /* No need to clone the condition because the IR that it hangs on is
+ * going to be removed from the instruction sequence.
+ */
+ ir_if *if_stmt = new(mem_ctx) ir_if(orig_assign->condition);
+
+ sg.generate(0, length, &if_stmt->then_instructions);
+ base_ir->insert_before(if_stmt);
+ } else {
+ exec_list list;
+
+ sg.generate(0, length, &list);
+ base_ir->insert_before(&list);
+ }
return var;
}
}
};
+} /* anonymous namespace */
+
bool
-lower_variable_index_to_cond_assign(exec_list *instructions,
- bool lower_input,
- bool lower_output,
- bool lower_temp,
- bool lower_uniform)
+lower_variable_index_to_cond_assign(gl_shader_stage stage,
+ exec_list *instructions,
+ bool lower_input,
+ bool lower_output,
+ bool lower_temp,
+ bool lower_uniform)
{
- variable_index_to_cond_assign_visitor v(lower_input,
- lower_output,
- lower_temp,
- lower_uniform);
+ variable_index_to_cond_assign_visitor v(stage,
+ lower_input,
+ lower_output,
+ lower_temp,
+ lower_uniform);
/* Continue lowering until no progress is made. If there are multiple
* levels of indirection (e.g., non-constant indexing of array elements and
* matrix columns of an array of matrix), each pass will only lower one
* level of indirection.
*/
+ bool progress_ever = false;
do {
v.progress = false;
visit_list_elements(&v, instructions);
+ progress_ever = v.progress || progress_ever;
} while (v.progress);
- return v.progress;
+ return progress_ever;
}