aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan Romanick <[email protected]>2010-08-02 18:48:25 -0700
committerIan Romanick <[email protected]>2010-08-04 16:47:27 -0700
commit5a7758efbe14dee026245a4f4f4fb3ccf7b2c23b (patch)
treebe214c6f3ea4be5ec4472afa5b3426d5191380f5
parent8e9ce2eb56a087c2544112700ae1abe3f96648dd (diff)
glsl2: Add ir_assignment::write_mask and associated methods
Replace swizzles on the LHS with additional swizzles on the RHS and a write mask in the assignment instruction. As part of this add ir_assignment::set_lhs. Ideally we'd make ir_assignment::lhs private to prevent erroneous writes, but that would require a lot of code butchery at this point. Add ir_assignment constructor that takes an explicit write mask. This is required for ir_assignment::clone, but it can also be used in other places. Without this, ir_assignment clones lose their write masks, and incorrect IR is generated in optimization passes. Add ir_assignment::whole_variable_written method. This method gets the variable on the LHS if the whole variable is written or NULL otherwise. This is different from ir->lhs->whole_variable_referenced() because the latter has no knowledge of the write mask stored in the ir_assignment. Gut all code from ir_to_mesa that handled swizzles on the LHS of assignments. There is probably some other refactoring that could be done here, but that can be left for another day.
-rw-r--r--src/glsl/ir.cpp113
-rw-r--r--src/glsl/ir.h44
-rw-r--r--src/glsl/ir_clone.cpp3
-rw-r--r--src/glsl/ir_constant_variable.cpp2
-rw-r--r--src/glsl/ir_copy_propagation.cpp2
-rw-r--r--src/glsl/ir_dead_code_local.cpp2
-rw-r--r--src/glsl/ir_print_visitor.cpp14
-rw-r--r--src/glsl/ir_tree_grafting.cpp2
-rw-r--r--src/glsl/ir_vec_index_to_swizzle.cpp2
-rw-r--r--src/mesa/program/ir_to_mesa.cpp67
10 files changed, 199 insertions, 52 deletions
diff --git a/src/glsl/ir.cpp b/src/glsl/ir.cpp
index 79cbaa9ea07..c3bade8d549 100644
--- a/src/glsl/ir.cpp
+++ b/src/glsl/ir.cpp
@@ -22,6 +22,7 @@
*/
#include <string.h>
#include "main/imports.h"
+#include "main/macros.h"
#include "ir.h"
#include "ir_visitor.h"
#include "glsl_types.h"
@@ -31,13 +32,121 @@ ir_rvalue::ir_rvalue()
this->type = glsl_type::error_type;
}
+/**
+ * Modify the swizzle make to move one component to another
+ *
+ * \param m IR swizzle to be modified
+ * \param from Component in the RHS that is to be swizzled
+ * \param to Desired swizzle location of \c from
+ */
+static void
+update_rhs_swizzle(ir_swizzle_mask &m, unsigned from, unsigned to)
+{
+ switch (to) {
+ case 0: m.x = from; break;
+ case 1: m.y = from; break;
+ case 2: m.z = from; break;
+ case 3: m.w = from; break;
+ default: assert(!"Should not get here.");
+ }
+
+ m.num_components = MAX2(m.num_components, (to + 1));
+}
+
+void
+ir_assignment::set_lhs(ir_rvalue *lhs)
+{
+ while (lhs != NULL) {
+ ir_swizzle *swiz = lhs->as_swizzle();
+
+ if (swiz == NULL)
+ break;
+
+ unsigned write_mask = 0;
+ ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 };
+
+ for (unsigned i = 0; i < swiz->mask.num_components; i++) {
+ unsigned c = 0;
+
+ switch (i) {
+ case 0: c = swiz->mask.x; break;
+ case 1: c = swiz->mask.y; break;
+ case 2: c = swiz->mask.z; break;
+ case 3: c = swiz->mask.w; break;
+ default: assert(!"Should not get here.");
+ }
+
+ write_mask |= (((this->write_mask >> i) & 1) << c);
+ update_rhs_swizzle(rhs_swiz, i, c);
+ }
+
+ this->write_mask = write_mask;
+ lhs = swiz->val;
+
+ this->rhs = new(this) ir_swizzle(this->rhs, rhs_swiz);
+ }
+
+ assert((lhs == NULL) || lhs->as_dereference());
+
+ this->lhs = (ir_dereference *) lhs;
+}
+
+ir_variable *
+ir_assignment::whole_variable_written()
+{
+ ir_variable *v = this->lhs->whole_variable_referenced();
+
+ if (v == NULL)
+ return NULL;
+
+ if (v->type->is_scalar())
+ return v;
+
+ if (v->type->is_vector()) {
+ const unsigned mask = (1U << v->type->vector_elements) - 1;
+
+ if (mask != this->write_mask)
+ return NULL;
+ }
+
+ /* Either all the vector components are assigned or the variable is some
+ * composite type (and the whole thing is assigned.
+ */
+ return v;
+}
+
+ir_assignment::ir_assignment(ir_dereference *lhs, ir_rvalue *rhs,
+ ir_rvalue *condition, unsigned write_mask)
+{
+ this->ir_type = ir_type_assignment;
+ this->condition = condition;
+ this->rhs = rhs;
+ this->lhs = lhs;
+ this->write_mask = write_mask;
+}
+
ir_assignment::ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs,
ir_rvalue *condition)
{
this->ir_type = ir_type_assignment;
- this->lhs = lhs;
- this->rhs = rhs;
this->condition = condition;
+ this->rhs = rhs;
+
+ /* If the RHS is a vector type, assume that all components of the vector
+ * type are being written to the LHS. The write mask comes from the RHS
+ * because we can have a case where the LHS is a vec4 and the RHS is a
+ * vec3. In that case, the assignment is:
+ *
+ * (assign (...) (xyz) (var_ref lhs) (var_ref rhs))
+ */
+ if (rhs->type->is_vector())
+ this->write_mask = (1U << rhs->type->vector_elements) - 1;
+ else if (rhs->type->is_scalar())
+ this->write_mask = 1;
+ else
+ this->write_mask = 0;
+
+ this->set_lhs(lhs);
}
diff --git a/src/glsl/ir.h b/src/glsl/ir.h
index f964b36083a..98789503e06 100644
--- a/src/glsl/ir.h
+++ b/src/glsl/ir.h
@@ -514,6 +514,16 @@ class ir_assignment : public ir_instruction {
public:
ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, ir_rvalue *condition);
+ /**
+ * Construct an assignment with an explicit write mask
+ *
+ * \note
+ * Since a write mask is supplied, the LHS must already be a bare
+ * \c ir_dereference. The cannot be any swizzles in the LHS.
+ */
+ ir_assignment(ir_dereference *lhs, ir_rvalue *rhs, ir_rvalue *condition,
+ unsigned write_mask);
+
virtual ir_assignment *clone(void *mem_ctx, struct hash_table *ht) const;
virtual ir_constant *constant_expression_value();
@@ -531,9 +541,31 @@ public:
}
/**
+ * Get a whole variable written by an assignment
+ *
+ * If the LHS of the assignment writes a whole variable, the variable is
+ * returned. Otherwise \c NULL is returned. Examples of whole-variable
+ * assignment are:
+ *
+ * - Assigning to a scalar
+ * - Assigning to all components of a vector
+ * - Whole array (or matrix) assignment
+ * - Whole structure assignment
+ */
+ ir_variable *whole_variable_written();
+
+ /**
+ * Set the LHS of an assignment
+ */
+ void set_lhs(ir_rvalue *lhs);
+
+ /**
* Left-hand side of the assignment.
+ *
+ * This should be treated as read only. If you need to set the LHS of an
+ * assignment, use \c ir_assignment::set_lhs.
*/
- ir_rvalue *lhs;
+ ir_dereference *lhs;
/**
* Value being assigned
@@ -544,6 +576,16 @@ public:
* Optional condition for the assignment.
*/
ir_rvalue *condition;
+
+
+ /**
+ * Component mask written
+ *
+ * For non-vector types in the LHS, this field will be zero. For vector
+ * types, a bit will be set for each component that is written. Note that
+ * for \c vec2 and \c vec3 types only the lower bits will ever be set.
+ */
+ unsigned write_mask:4;
};
/* Update ir_expression::num_operands() and operator_strs when
diff --git a/src/glsl/ir_clone.cpp b/src/glsl/ir_clone.cpp
index 59831834bd7..0e202164b32 100644
--- a/src/glsl/ir_clone.cpp
+++ b/src/glsl/ir_clone.cpp
@@ -242,7 +242,8 @@ ir_assignment::clone(void *mem_ctx, struct hash_table *ht) const
return new(mem_ctx) ir_assignment(this->lhs->clone(mem_ctx, ht),
this->rhs->clone(mem_ctx, ht),
- new_condition);
+ new_condition,
+ this->write_mask);
}
ir_function *
diff --git a/src/glsl/ir_constant_variable.cpp b/src/glsl/ir_constant_variable.cpp
index 749e2cf809f..1fb73e765e1 100644
--- a/src/glsl/ir_constant_variable.cpp
+++ b/src/glsl/ir_constant_variable.cpp
@@ -110,7 +110,7 @@ ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
return visit_continue;
}
- ir_variable *var = ir->lhs->whole_variable_referenced();
+ ir_variable *var = ir->whole_variable_written();
if (!var)
return visit_continue;
diff --git a/src/glsl/ir_copy_propagation.cpp b/src/glsl/ir_copy_propagation.cpp
index 57123987322..26588a352c8 100644
--- a/src/glsl/ir_copy_propagation.cpp
+++ b/src/glsl/ir_copy_propagation.cpp
@@ -224,7 +224,7 @@ add_copy(void *ctx, ir_assignment *ir, exec_list *acp)
return;
}
- ir_variable *lhs_var = ir->lhs->whole_variable_referenced();
+ ir_variable *lhs_var = ir->whole_variable_written();
ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
if ((lhs_var != NULL) && (rhs_var != NULL)) {
diff --git a/src/glsl/ir_dead_code_local.cpp b/src/glsl/ir_dead_code_local.cpp
index 7a44ec8a4a4..b22cc558df6 100644
--- a/src/glsl/ir_dead_code_local.cpp
+++ b/src/glsl/ir_dead_code_local.cpp
@@ -137,7 +137,7 @@ process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments)
}
/* Now, check if we did a whole-variable assignment. */
- if (always_assign && (ir->lhs->whole_variable_referenced() != NULL)) {
+ if (always_assign && (ir->whole_variable_written() != NULL)) {
/* We did a whole-variable assignment. So, any instruction in
* the assignment list with the same LHS is dead.
*/
diff --git a/src/glsl/ir_print_visitor.cpp b/src/glsl/ir_print_visitor.cpp
index 73476e7e9b6..39b11bb32cc 100644
--- a/src/glsl/ir_print_visitor.cpp
+++ b/src/glsl/ir_print_visitor.cpp
@@ -296,7 +296,19 @@ void ir_print_visitor::visit(ir_assignment *ir)
else
printf("(constant bool (1))");
- printf(" ");
+
+ char mask[5];
+ unsigned j = 0;
+
+ for (unsigned i = 0; i < 4; i++) {
+ if ((ir->write_mask & (1 << i)) != 0) {
+ mask[j] = "xyzw"[i];
+ j++;
+ }
+ }
+ mask[j] = '\0';
+
+ printf(" (%s) ", mask);
ir->lhs->accept(this);
diff --git a/src/glsl/ir_tree_grafting.cpp b/src/glsl/ir_tree_grafting.cpp
index 6f62de758b2..38034a61977 100644
--- a/src/glsl/ir_tree_grafting.cpp
+++ b/src/glsl/ir_tree_grafting.cpp
@@ -315,7 +315,7 @@ tree_grafting_basic_block(ir_instruction *bb_first,
if (!assign)
continue;
- ir_variable *lhs_var = assign->lhs->whole_variable_referenced();
+ ir_variable *lhs_var = assign->whole_variable_written();
if (!lhs_var)
continue;
diff --git a/src/glsl/ir_vec_index_to_swizzle.cpp b/src/glsl/ir_vec_index_to_swizzle.cpp
index 1e170cbae61..b3de91f8abd 100644
--- a/src/glsl/ir_vec_index_to_swizzle.cpp
+++ b/src/glsl/ir_vec_index_to_swizzle.cpp
@@ -107,7 +107,7 @@ ir_vec_index_to_swizzle_visitor::visit_enter(ir_swizzle *ir)
ir_visitor_status
ir_vec_index_to_swizzle_visitor::visit_enter(ir_assignment *ir)
{
- ir->lhs = convert_vec_index_to_swizzle(ir->lhs);
+ ir->set_lhs(convert_vec_index_to_swizzle(ir->lhs));
ir->rhs = convert_vec_index_to_swizzle(ir->rhs);
return visit_continue;
diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp
index 777b4d91f48..1cec4aa6212 100644
--- a/src/mesa/program/ir_to_mesa.cpp
+++ b/src/mesa/program/ir_to_mesa.cpp
@@ -356,6 +356,7 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir,
ir_to_mesa_dst_reg dst,
ir_to_mesa_src_reg src0)
{
+ assert(dst.writemask != 0);
return ir_to_mesa_emit_op3(ir, op, dst,
src0, ir_to_mesa_undef, ir_to_mesa_undef);
}
@@ -1615,21 +1616,17 @@ ir_to_mesa_visitor::visit(ir_dereference_record *ir)
* We want to be careful in assignment setup to hit the actual storage
* instead of potentially using a temporary like we might with the
* ir_dereference handler.
- *
- * Thanks to ir_swizzle_swizzle, and ir_vec_index_to_swizzle, we
- * should only see potentially one variable array index of a vector,
- * and one swizzle, before getting to actual vec4 storage. So handle
- * those, then go use ir_dereference to handle the rest.
*/
static struct ir_to_mesa_dst_reg
-get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
+get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v,
ir_to_mesa_src_reg *r)
{
- struct ir_to_mesa_dst_reg dst_reg;
- ir_swizzle *swiz;
-
+ /* The LHS must be a dereference. If the LHS is a variable indexed array
+ * access of a vector, it must be separated into a series conditional moves
+ * before reaching this point (see ir_vec_index_to_cond_assign).
+ */
+ assert(ir->as_dereference());
ir_dereference_array *deref_array = ir->as_dereference_array();
- /* This should have been handled by ir_vec_index_to_cond_assign */
if (deref_array) {
assert(!deref_array->array->type->is_vector());
}
@@ -1638,38 +1635,7 @@ get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
* swizzles in it and write swizzles using writemask, though.
*/
ir->accept(v);
- dst_reg = ir_to_mesa_dst_reg_from_src(v->result);
-
- if ((swiz = ir->as_swizzle())) {
- int swizzles[4] = {
- swiz->mask.x,
- swiz->mask.y,
- swiz->mask.z,
- swiz->mask.w
- };
- int new_r_swizzle[4];
- int orig_r_swizzle = r->swizzle;
- int i;
-
- for (i = 0; i < 4; i++) {
- new_r_swizzle[i] = GET_SWZ(orig_r_swizzle, 0);
- }
-
- dst_reg.writemask = 0;
- for (i = 0; i < 4; i++) {
- if (i < swiz->mask.num_components) {
- dst_reg.writemask |= 1 << swizzles[i];
- new_r_swizzle[swizzles[i]] = GET_SWZ(orig_r_swizzle, i);
- }
- }
-
- r->swizzle = MAKE_SWIZZLE4(new_r_swizzle[0],
- new_r_swizzle[1],
- new_r_swizzle[2],
- new_r_swizzle[3]);
- }
-
- return dst_reg;
+ return ir_to_mesa_dst_reg_from_src(v->result);
}
void
@@ -1684,6 +1650,23 @@ ir_to_mesa_visitor::visit(ir_assignment *ir)
l = get_assignment_lhs(ir->lhs, this, &r);
+ /* FINISHME: This should really set to the correct maximal writemask for each
+ * FINISHME: component written (in the loops below). This case can only
+ * FINISHME: occur for matrices, arrays, and structures.
+ */
+ if (ir->write_mask == 0) {
+ assert(!ir->lhs->type->is_scalar() && !ir->lhs->type->is_vector());
+ l.writemask = WRITEMASK_XYZW;
+ } else if (ir->lhs->type->is_scalar()) {
+ /* FINISHME: This hack makes writing to gl_FragData, which lives in the
+ * FINISHME: W component of fragment shader output zero, work correctly.
+ */
+ l.writemask = WRITEMASK_XYZW;
+ } else {
+ assert(ir->lhs->type->is_vector());
+ l.writemask = ir->write_mask;
+ }
+
assert(l.file != PROGRAM_UNDEFINED);
assert(r.file != PROGRAM_UNDEFINED);