glsl: Add lowering pass for ir_bin_imul_high
authorIan Romanick <ian.d.romanick@intel.com>
Mon, 27 Jun 2016 22:21:21 +0000 (15:21 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Tue, 19 Jul 2016 19:19:29 +0000 (12:19 -0700)
This isn't the lowering pass you want.  Most GPUs that can support GLSL
1.30 have a multiply unit that can do something more interesting than
32x32->32.  Many have 32x16->48.  Any GPU that does, should do the
lowering in the backend.  This is just the thing that will always work.

Signed-off-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
src/compiler/glsl/ir_optimization.h
src/compiler/glsl/lower_instructions.cpp

index 2c7e6155ecb87b2a0777a6390844a5b0194912eb..c29260a45cac088916de637ac4971034de71c5dd 100644 (file)
@@ -48,6 +48,7 @@
 #define REVERSE_TO_SHIFTS         0x10000
 #define FIND_LSB_TO_FLOAT_CAST    0x20000
 #define FIND_MSB_TO_FLOAT_CAST    0x40000
+#define IMUL_HIGH_TO_MUL          0x80000
 
 /**
  * \see class lower_packing_builtins_visitor
index 4be60273e0d7d48a6de5dc5967d88f9ff0cb40cb..a9720f2163808004b6aaa8241429d2db1d8a5102 100644 (file)
@@ -165,6 +165,7 @@ private:
    void reverse_to_shifts(ir_expression *ir);
    void find_lsb_to_float_cast(ir_expression *ir);
    void find_msb_to_float_cast(ir_expression *ir);
+   void imul_high_to_mul(ir_expression *ir);
 };
 
 } /* anonymous namespace */
@@ -1412,6 +1413,149 @@ lower_instructions_visitor::find_msb_to_float_cast(ir_expression *ir)
    this->progress = true;
 }
 
+void
+lower_instructions_visitor::imul_high_to_mul(ir_expression *ir)
+{
+   /*   ABCD
+    * * EFGH
+    * ======
+    * (GH * CD) + (GH * AB) << 16 + (EF * CD) << 16 + (EF * AB) << 32
+    *
+    * In GLSL, (a * b) becomes
+    *
+    * uint m1 = (a & 0x0000ffffu) * (b & 0x0000ffffu);
+    * uint m2 = (a & 0x0000ffffu) * (b >> 16);
+    * uint m3 = (a >> 16)         * (b & 0x0000ffffu);
+    * uint m4 = (a >> 16)         * (b >> 16);
+    *
+    * uint c1;
+    * uint c2;
+    * uint lo_result;
+    * uint hi_result;
+    *
+    * lo_result = uaddCarry(m1, m2 << 16, c1);
+    * hi_result = m4 + c1;
+    * lo_result = uaddCarry(lo_result, m3 << 16, c2);
+    * hi_result = hi_result + c2;
+    * hi_result = hi_result + (m2 >> 16) + (m3 >> 16);
+    */
+   const unsigned elements = ir->operands[0]->type->vector_elements;
+   ir_variable *src1 =
+      new(ir) ir_variable(glsl_type::uvec(elements), "src1", ir_var_temporary);
+   ir_variable *src1h =
+      new(ir) ir_variable(glsl_type::uvec(elements), "src1h", ir_var_temporary);
+   ir_variable *src1l =
+      new(ir) ir_variable(glsl_type::uvec(elements), "src1l", ir_var_temporary);
+   ir_variable *src2 =
+      new(ir) ir_variable(glsl_type::uvec(elements), "src2", ir_var_temporary);
+   ir_variable *src2h =
+      new(ir) ir_variable(glsl_type::uvec(elements), "src2h", ir_var_temporary);
+   ir_variable *src2l =
+      new(ir) ir_variable(glsl_type::uvec(elements), "src2l", ir_var_temporary);
+   ir_variable *t1 =
+      new(ir) ir_variable(glsl_type::uvec(elements), "t1", ir_var_temporary);
+   ir_variable *t2 =
+      new(ir) ir_variable(glsl_type::uvec(elements), "t2", ir_var_temporary);
+   ir_variable *lo =
+      new(ir) ir_variable(glsl_type::uvec(elements), "lo", ir_var_temporary);
+   ir_variable *hi =
+      new(ir) ir_variable(glsl_type::uvec(elements), "hi", ir_var_temporary);
+   ir_variable *different_signs = NULL;
+   ir_constant *c0000FFFF = new(ir) ir_constant(0x0000FFFFu, elements);
+   ir_constant *c16 = new(ir) ir_constant(16u, elements);
+
+   ir_instruction &i = *base_ir;
+
+   i.insert_before(src1);
+   i.insert_before(src2);
+   i.insert_before(src1h);
+   i.insert_before(src2h);
+   i.insert_before(src1l);
+   i.insert_before(src2l);
+
+   if (ir->operands[0]->type->base_type == GLSL_TYPE_UINT) {
+      i.insert_before(assign(src1, ir->operands[0]));
+      i.insert_before(assign(src2, ir->operands[1]));
+   } else {
+      assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT);
+
+      ir_variable *itmp1 =
+         new(ir) ir_variable(glsl_type::ivec(elements), "itmp1", ir_var_temporary);
+      ir_variable *itmp2 =
+         new(ir) ir_variable(glsl_type::ivec(elements), "itmp2", ir_var_temporary);
+      ir_constant *c0 = new(ir) ir_constant(int(0), elements);
+
+      i.insert_before(itmp1);
+      i.insert_before(itmp2);
+      i.insert_before(assign(itmp1, ir->operands[0]));
+      i.insert_before(assign(itmp2, ir->operands[1]));
+
+      different_signs =
+         new(ir) ir_variable(glsl_type::bvec(elements), "different_signs",
+                             ir_var_temporary);
+
+      i.insert_before(different_signs);
+      i.insert_before(assign(different_signs, expr(ir_binop_logic_xor,
+                                                   less(itmp1, c0),
+                                                   less(itmp2, c0->clone(ir, NULL)))));
+
+      i.insert_before(assign(src1, i2u(abs(itmp1))));
+      i.insert_before(assign(src2, i2u(abs(itmp2))));
+   }
+
+   i.insert_before(assign(src1l, bit_and(src1, c0000FFFF)));
+   i.insert_before(assign(src2l, bit_and(src2, c0000FFFF->clone(ir, NULL))));
+   i.insert_before(assign(src1h, rshift(src1, c16)));
+   i.insert_before(assign(src2h, rshift(src2, c16->clone(ir, NULL))));
+
+   i.insert_before(lo);
+   i.insert_before(hi);
+   i.insert_before(t1);
+   i.insert_before(t2);
+
+   i.insert_before(assign(lo, mul(src1l, src2l)));
+   i.insert_before(assign(t1, mul(src1l, src2h)));
+   i.insert_before(assign(t2, mul(src1h, src2l)));
+   i.insert_before(assign(hi, mul(src1h, src2h)));
+
+   i.insert_before(assign(hi, add(hi, carry(lo, lshift(t1, c16->clone(ir, NULL))))));
+   i.insert_before(assign(lo,           add(lo, lshift(t1, c16->clone(ir, NULL)))));
+
+   i.insert_before(assign(hi, add(hi, carry(lo, lshift(t2, c16->clone(ir, NULL))))));
+   i.insert_before(assign(lo,           add(lo, lshift(t2, c16->clone(ir, NULL)))));
+
+   if (different_signs == NULL) {
+      assert(ir->operands[0]->type->base_type == GLSL_TYPE_UINT);
+
+      ir->operation = ir_binop_add;
+      ir->operands[0] = add(hi, rshift(t1, c16->clone(ir, NULL)));
+      ir->operands[1] = rshift(t2, c16->clone(ir, NULL));
+   } else {
+      assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT);
+
+      i.insert_before(assign(hi, add(add(hi, rshift(t1, c16->clone(ir, NULL))),
+                                     rshift(t2, c16->clone(ir, NULL)))));
+
+      /* For channels where different_signs is set we have to perform a 64-bit
+       * negation.  This is *not* the same as just negating the high 32-bits.
+       * Consider -3 * 2.  The high 32-bits is 0, but the desired result is
+       * -1, not -0!  Recall -x == ~x + 1.
+       */
+      ir_variable *neg_hi =
+         new(ir) ir_variable(glsl_type::ivec(elements), "neg_hi", ir_var_temporary);
+      ir_constant *c1 = new(ir) ir_constant(1u, elements);
+
+      i.insert_before(neg_hi);
+      i.insert_before(assign(neg_hi, add(bit_not(u2i(hi)),
+                                         u2i(carry(bit_not(lo), c1)))));
+
+      ir->operation = ir_triop_csel;
+      ir->operands[0] = new(ir) ir_dereference_variable(different_signs);
+      ir->operands[1] = new(ir) ir_dereference_variable(neg_hi);
+      ir->operands[2] = u2i(hi);
+   }
+}
+
 ir_visitor_status
 lower_instructions_visitor::visit_leave(ir_expression *ir)
 {
@@ -1544,6 +1688,11 @@ lower_instructions_visitor::visit_leave(ir_expression *ir)
          find_msb_to_float_cast(ir);
       break;
 
+   case ir_binop_imul_high:
+      if (lowering(IMUL_HIGH_TO_MUL))
+         imul_high_to_mul(ir);
+      break;
+
    default:
       return visit_continue;
    }