*/
#include <string.h>
#include "main/imports.h"
+#include "main/macros.h"
#include "ir.h"
#include "ir_visitor.h"
#include "glsl_types.h"
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);
}
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();
return this;
}
+ /**
+ * 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
* 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
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 *
return visit_continue;
}
- ir_variable *var = ir->lhs->whole_variable_referenced();
+ ir_variable *var = ir->whole_variable_written();
if (!var)
return visit_continue;
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)) {
}
/* 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.
*/
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);
if (!assign)
continue;
- ir_variable *lhs_var = assign->lhs->whole_variable_referenced();
+ ir_variable *lhs_var = assign->whole_variable_written();
if (!lhs_var)
continue;
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;
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);
}
* 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());
}
* 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
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);