glsl2: Add ir_assignment::write_mask and associated methods
authorIan Romanick <ian.d.romanick@intel.com>
Tue, 3 Aug 2010 01:48:25 +0000 (18:48 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Wed, 4 Aug 2010 23:47:27 +0000 (16:47 -0700)
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.

src/glsl/ir.cpp
src/glsl/ir.h
src/glsl/ir_clone.cpp
src/glsl/ir_constant_variable.cpp
src/glsl/ir_copy_propagation.cpp
src/glsl/ir_dead_code_local.cpp
src/glsl/ir_print_visitor.cpp
src/glsl/ir_tree_grafting.cpp
src/glsl/ir_vec_index_to_swizzle.cpp
src/mesa/program/ir_to_mesa.cpp

index 79cbaa9ea07b5d7e1f3ef78f17de8bcbdad8380a..c3bade8d5495f8facead7da57a5076147b21cd5b 100644 (file)
@@ -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);
 }
 
 
index f964b36083a652fbd88d0d8273d1c1196f41e62f..98789503e061b9034543ee1e049c9bf6329cc14b 100644 (file)
@@ -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();
@@ -530,10 +540,32 @@ public:
       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
@@ -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
index 59831834bd7309588c4589915b64afa90a3e8531..0e202164b325e7895e618c3f2e7115ea408660a5 100644 (file)
@@ -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 *
index 749e2cf809f75fc4ca3ad83b5823d4ff1ebc569d..1fb73e765e1c842692d2b500d61bb01100a737dc 100644 (file)
@@ -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;
 
index 57123987322f15b807d7cbb8419d5c1b663ccd05..26588a352c821b5227e53bdb18d4ebd61a6b0031 100644 (file)
@@ -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)) {
index 7a44ec8a4a45ef999455ea8ea34d2dc1f335c684..b22cc558df6392f88e6beccebcc135b4e3ddf52b 100644 (file)
@@ -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.
        */
index 73476e7e9b6791bd365d306c4d1f9cde8a02f794..39b11bb32cc78065042c11713e86bc1882315b04 100644 (file)
@@ -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);
 
index 6f62de758b243286f88467b7bcfacdd7b3ba021c..38034a6197797f208aca36a5d9532ad2c6302f03 100644 (file)
@@ -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;
 
index 1e170cbae61cc0cf39656282d1ee99cff7820db1..b3de91f8abdd3b235cb148a061e55dd0902b5be8 100644 (file)
@@ -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;
index 777b4d91f486f60e6653f9b13ade89d33832bcf0..1cec4aa6212a46a7f03e19d0a568a8d45f363867 100644 (file)
@@ -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);