glsl2: Make ir_algebraic reassociate add/mul operands for constant folding.
authorEric Anholt <eric@anholt.net>
Tue, 27 Jul 2010 06:56:19 +0000 (23:56 -0700)
committerEric Anholt <eric@anholt.net>
Tue, 10 Aug 2010 04:42:17 +0000 (21:42 -0700)
It's rather easy to produce two constant multiplies separated by other
multiplies while writing a BRDF shader, and non-obvious enough in the
resulting codegen that I didn't catch it in my demo code until just
recently.  Cuts 3 965 instructions from my demo (<1%), and 20 from
glsl-fs-raytrace (1.3%).

src/glsl/ir_algebraic.cpp

index 43d8f9e699ca207e40bc0bfd5f0b309a729e7497..86fb7e49c03b9956aea280a92d2b95bbac430ec4 100644 (file)
@@ -58,7 +58,14 @@ public:
    virtual ir_visitor_status visit_leave(ir_texture *);
 
    ir_rvalue *handle_expression(ir_rvalue *in_ir);
-
+   bool reassociate_constant(ir_expression *ir1,
+                            int const_index,
+                            ir_constant *constant,
+                            ir_expression *ir2);
+   void reassociate_operands(ir_expression *ir1,
+                            int op1,
+                            ir_expression *ir2,
+                            int op2);
    bool progress;
 };
 
@@ -138,6 +145,84 @@ is_vec_one(ir_constant *ir)
    return true;
 }
 
+static void
+update_type(ir_expression *ir)
+{
+   if (ir->operands[0]->type->is_vector())
+      ir->type = ir->operands[0]->type;
+   else
+      ir->type = ir->operands[1]->type;
+}
+
+void
+ir_algebraic_visitor::reassociate_operands(ir_expression *ir1,
+                                          int op1,
+                                          ir_expression *ir2,
+                                          int op2)
+{
+   ir_rvalue *temp = ir2->operands[op2];
+   ir2->operands[op2] = ir1->operands[op1];
+   ir1->operands[op1] = temp;
+
+   /* Update the type of ir2.  The type of ir1 won't have changed --
+    * base types matched, and at least one of the operands of the 2
+    * binops is still a vector if any of them were.
+    */
+   update_type(ir2);
+
+   this->progress = true;
+}
+
+/**
+ * Reassociates a constant down a tree of adds or multiplies.
+ *
+ * Consider (2 * (a * (b * 0.5))).  We want to send up with a * b.
+ */
+bool
+ir_algebraic_visitor::reassociate_constant(ir_expression *ir1, int const_index,
+                                          ir_constant *constant,
+                                          ir_expression *ir2)
+{
+   if (!ir2 || ir1->operation != ir2->operation)
+      return false;
+
+   /* Don't want to even think about matrices. */
+   if (ir1->operands[0]->type->is_matrix() ||
+       ir1->operands[0]->type->is_matrix() ||
+       ir2->operands[1]->type->is_matrix() ||
+       ir2->operands[1]->type->is_matrix())
+      return false;
+
+   ir_constant *ir2_const[2];
+   ir2_const[0] = ir2->operands[0]->constant_expression_value();
+   ir2_const[1] = ir2->operands[1]->constant_expression_value();
+
+   if (ir2_const[0] && ir2_const[1])
+      return false;
+
+   if (ir2_const[0]) {
+      reassociate_operands(ir1, const_index, ir2, 1);
+      return true;
+   } else if (ir2_const[1]) {
+      reassociate_operands(ir1, const_index, ir2, 0);
+      return true;
+   }
+
+   if (reassociate_constant(ir1, const_index, constant,
+                           ir2->operands[0]->as_expression())) {
+      update_type(ir2);
+      return true;
+   }
+
+   if (reassociate_constant(ir1, const_index, constant,
+                           ir2->operands[1]->as_expression())) {
+      update_type(ir2);
+      return true;
+   }
+
+   return false;
+}
+
 ir_rvalue *
 ir_algebraic_visitor::handle_expression(ir_rvalue *in_ir)
 {
@@ -201,6 +286,16 @@ ir_algebraic_visitor::handle_expression(ir_rvalue *in_ir)
         this->progress = true;
         return ir->operands[0];
       }
+
+      /* Reassociate addition of constants so that we can do constant
+       * folding.
+       */
+      if (op_const[0] && !op_const[1])
+        reassociate_constant(ir, 0, op_const[0],
+                             ir->operands[1]->as_expression());
+      if (op_const[1] && !op_const[0])
+        reassociate_constant(ir, 1, op_const[1],
+                             ir->operands[0]->as_expression());
       break;
 
    case ir_binop_sub:
@@ -231,6 +326,17 @@ ir_algebraic_visitor::handle_expression(ir_rvalue *in_ir)
         this->progress = true;
         return ir_constant::zero(ir, ir->type);
       }
+
+      /* Reassociate multiplication of constants so that we can do
+       * constant folding.
+       */
+      if (op_const[0] && !op_const[1])
+        reassociate_constant(ir, 0, op_const[0],
+                             ir->operands[1]->as_expression());
+      if (op_const[1] && !op_const[0])
+        reassociate_constant(ir, 1, op_const[1],
+                             ir->operands[0]->as_expression());
+
       break;
 
    case ir_binop_div: