glsl: Add lowering pass for ir_bin_imul_high
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>
This commit is contained in:
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user