summaryrefslogtreecommitdiffstats
path: root/src/compiler/glsl/lower_instructions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/glsl/lower_instructions.cpp')
-rw-r--r--src/compiler/glsl/lower_instructions.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/compiler/glsl/lower_instructions.cpp b/src/compiler/glsl/lower_instructions.cpp
index 4be60273e0d..a9720f21638 100644
--- a/src/compiler/glsl/lower_instructions.cpp
+++ b/src/compiler/glsl/lower_instructions.cpp
@@ -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;
}