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 REVERSE_TO_SHIFTS 0x10000
|
||||||
#define FIND_LSB_TO_FLOAT_CAST 0x20000
|
#define FIND_LSB_TO_FLOAT_CAST 0x20000
|
||||||
#define FIND_MSB_TO_FLOAT_CAST 0x40000
|
#define FIND_MSB_TO_FLOAT_CAST 0x40000
|
||||||
|
#define IMUL_HIGH_TO_MUL 0x80000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \see class lower_packing_builtins_visitor
|
* \see class lower_packing_builtins_visitor
|
||||||
|
@@ -165,6 +165,7 @@ private:
|
|||||||
void reverse_to_shifts(ir_expression *ir);
|
void reverse_to_shifts(ir_expression *ir);
|
||||||
void find_lsb_to_float_cast(ir_expression *ir);
|
void find_lsb_to_float_cast(ir_expression *ir);
|
||||||
void find_msb_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 */
|
} /* anonymous namespace */
|
||||||
@@ -1412,6 +1413,149 @@ lower_instructions_visitor::find_msb_to_float_cast(ir_expression *ir)
|
|||||||
this->progress = true;
|
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
|
ir_visitor_status
|
||||||
lower_instructions_visitor::visit_leave(ir_expression *ir)
|
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);
|
find_msb_to_float_cast(ir);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ir_binop_imul_high:
|
||||||
|
if (lowering(IMUL_HIGH_TO_MUL))
|
||||||
|
imul_high_to_mul(ir);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return visit_continue;
|
return visit_continue;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user