summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatt Turner <[email protected]>2013-08-03 11:02:59 -0700
committerMatt Turner <[email protected]>2013-09-17 16:59:23 -0700
commitd0b8ea60b7999d50c0f136b1170b1ddb4688d796 (patch)
tree3fd9ec5dc6042c83e08f3af3bb339b95ca1fb40a /src
parent5561251b58c976a70125bb07dc1c6cc2bd2541f4 (diff)
glsl: Add ldexp_to_arith lowering pass.
Reviewed-by: Paul Berry <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/glsl/ir_optimization.h1
-rw-r--r--src/glsl/lower_instructions.cpp128
2 files changed, 129 insertions, 0 deletions
diff --git a/src/glsl/ir_optimization.h b/src/glsl/ir_optimization.h
index b79c2b78791..074686c5d65 100644
--- a/src/glsl/ir_optimization.h
+++ b/src/glsl/ir_optimization.h
@@ -38,6 +38,7 @@
#define INT_DIV_TO_MUL_RCP 0x40
#define LRP_TO_ARITH 0x80
#define BITFIELD_INSERT_TO_BFM_BFI 0x100
+#define LDEXP_TO_ARITH 0x200
/**
* \see class lower_packing_builtins_visitor
diff --git a/src/glsl/lower_instructions.cpp b/src/glsl/lower_instructions.cpp
index d32ec80d6bb..cb530489593 100644
--- a/src/glsl/lower_instructions.cpp
+++ b/src/glsl/lower_instructions.cpp
@@ -37,6 +37,7 @@
* - POW_TO_EXP2
* - LOG_TO_LOG2
* - MOD_TO_FRACT
+ * - LDEXP_TO_ARITH
* - LRP_TO_ARITH
* - BITFIELD_INSERT_TO_BFM_BFI
*
@@ -82,6 +83,10 @@
* if we have to break it down like this anyway, it gives an
* opportunity to do things like constant fold the (1.0 / op1) easily.
*
+ * LDEXP_TO_ARITH:
+ * -------------
+ * Converts ir_binop_ldexp to arithmetic and bit operations.
+ *
* LRP_TO_ARITH:
* -------------
* Converts ir_triop_lrp to (op0 * (1.0f - op2)) + (op1 * op2).
@@ -125,6 +130,7 @@ private:
void log_to_log2(ir_expression *);
void lrp_to_arith(ir_expression *);
void bitfield_insert_to_bfm_bfi(ir_expression *);
+ void ldexp_to_arith(ir_expression *);
};
/**
@@ -332,6 +338,123 @@ lower_instructions_visitor::bitfield_insert_to_bfm_bfi(ir_expression *ir)
this->progress = true;
}
+void
+lower_instructions_visitor::ldexp_to_arith(ir_expression *ir)
+{
+ /* Translates
+ * ir_binop_ldexp x exp
+ * into
+ *
+ * extracted_biased_exp = rshift(bitcast_f2i(abs(x)), exp_shift);
+ * resulting_biased_exp = extracted_biased_exp + exp;
+ *
+ * if (resulting_biased_exp < 1) {
+ * return copysign(0.0, x);
+ * }
+ *
+ * return bitcast_u2f((bitcast_f2u(x) & sign_mantissa_mask) |
+ * lshift(i2u(resulting_biased_exp), exp_shift));
+ *
+ * which we can't actually implement as such, since the GLSL IR doesn't
+ * have vectorized if-statements. We actually implement it without branches
+ * using conditional-select:
+ *
+ * extracted_biased_exp = rshift(bitcast_f2i(abs(x)), exp_shift);
+ * resulting_biased_exp = extracted_biased_exp + exp;
+ *
+ * is_not_zero_or_underflow = gequal(resulting_biased_exp, 1);
+ * x = csel(is_not_zero_or_underflow, x, copysign(0.0f, x));
+ * resulting_biased_exp = csel(is_not_zero_or_underflow,
+ * resulting_biased_exp, 0);
+ *
+ * return bitcast_u2f((bitcast_f2u(x) & sign_mantissa_mask) |
+ * lshift(i2u(resulting_biased_exp), exp_shift));
+ */
+
+ const unsigned vec_elem = ir->type->vector_elements;
+
+ /* Types */
+ const glsl_type *ivec = glsl_type::get_instance(GLSL_TYPE_INT, vec_elem, 1);
+ const glsl_type *bvec = glsl_type::get_instance(GLSL_TYPE_BOOL, vec_elem, 1);
+
+ /* Constants */
+ ir_constant *zeroi = ir_constant::zero(ir, ivec);
+ ir_constant *zerof = ir_constant::zero(ir, ir->type);
+
+ ir_constant *sign_mantissa_mask = new(ir) ir_constant(0x807fffffu, vec_elem);
+ ir_constant *sign_mask = new(ir) ir_constant(0x80000000u, vec_elem);
+
+ ir_constant *exp_shift = new(ir) ir_constant(23u, vec_elem);
+
+ /* Temporary variables */
+ ir_variable *x = new(ir) ir_variable(ir->type, "x", ir_var_temporary);
+ ir_variable *exp = new(ir) ir_variable(ivec, "exp", ir_var_temporary);
+
+ ir_variable *zero_sign_x = new(ir) ir_variable(ir->type, "zero_sign_x",
+ ir_var_temporary);
+
+ ir_variable *extracted_biased_exp =
+ new(ir) ir_variable(ivec, "extracted_biased_exp", ir_var_temporary);
+ ir_variable *resulting_biased_exp =
+ new(ir) ir_variable(ivec, "resulting_biased_exp", ir_var_temporary);
+
+ ir_variable *is_not_zero_or_underflow =
+ new(ir) ir_variable(bvec, "is_not_zero_or_underflow", ir_var_temporary);
+
+ ir_instruction &i = *base_ir;
+
+ /* Copy <x> and <exp> arguments. */
+ i.insert_before(x);
+ i.insert_before(assign(x, ir->operands[0]));
+ i.insert_before(exp);
+ i.insert_before(assign(exp, ir->operands[1]));
+
+ /* Extract the biased exponent from <x>. */
+ i.insert_before(extracted_biased_exp);
+ i.insert_before(assign(extracted_biased_exp,
+ rshift(bitcast_f2i(abs(x)), exp_shift)));
+
+ i.insert_before(resulting_biased_exp);
+ i.insert_before(assign(resulting_biased_exp,
+ add(extracted_biased_exp, exp)));
+
+ /* Test if result is ±0.0, subnormal, or underflow by checking if the
+ * resulting biased exponent would be less than 0x1. If so, the result is
+ * 0.0 with the sign of x. (Actually, invert the conditions so that
+ * immediate values are the second arguments, which is better for i965)
+ */
+ i.insert_before(zero_sign_x);
+ i.insert_before(assign(zero_sign_x,
+ bitcast_u2f(bit_or(bit_and(bitcast_f2u(x), sign_mask),
+ bitcast_f2u(zerof)))));
+
+ i.insert_before(is_not_zero_or_underflow);
+ i.insert_before(assign(is_not_zero_or_underflow,
+ gequal(resulting_biased_exp,
+ new(ir) ir_constant(0x1, vec_elem))));
+ i.insert_before(assign(x, csel(is_not_zero_or_underflow,
+ x, zero_sign_x)));
+ i.insert_before(assign(resulting_biased_exp,
+ csel(is_not_zero_or_underflow,
+ resulting_biased_exp, zeroi)));
+
+ /* We could test for overflows by checking if the resulting biased exponent
+ * would be greater than 0xFE. Turns out we don't need to because the GLSL
+ * spec says:
+ *
+ * "If this product is too large to be represented in the
+ * floating-point type, the result is undefined."
+ */
+
+ ir_constant *exp_shift_clone = exp_shift->clone(ir, NULL);
+ ir->operation = ir_unop_bitcast_u2f;
+ ir->operands[0] = bit_or(bit_and(bitcast_f2u(x), sign_mantissa_mask),
+ lshift(i2u(resulting_biased_exp), exp_shift_clone));
+ ir->operands[1] = NULL;
+
+ this->progress = true;
+}
+
ir_visitor_status
lower_instructions_visitor::visit_leave(ir_expression *ir)
{
@@ -378,6 +501,11 @@ lower_instructions_visitor::visit_leave(ir_expression *ir)
bitfield_insert_to_bfm_bfi(ir);
break;
+ case ir_binop_ldexp:
+ if (lowering(LDEXP_TO_ARITH))
+ ldexp_to_arith(ir);
+ break;
+
default:
return visit_continue;
}