* Currently supported transformations:
* - SUB_TO_ADD_NEG
* - DIV_TO_MUL_RCP
+ * - INT_DIV_TO_MUL_RCP
* - EXP_TO_EXP2
+ * - POW_TO_EXP2
* - LOG_TO_LOG2
* - MOD_TO_FRACT
*
* want to recognize add(op0, neg(op1)) or the other way around to
* produce a subtract anyway.
*
- * DIV_TO_MUL_RCP:
- * ---------------
+ * DIV_TO_MUL_RCP and INT_DIV_TO_MUL_RCP:
+ * --------------------------------------
* Breaks an ir_unop_div expression down to op0 * (rcp(op1)).
*
* Many GPUs don't have a divide instruction (945 and 965 included),
* reciprocal. By breaking the operation down, constant reciprocals
* can get constant folded.
*
+ * DIV_TO_MUL_RCP only lowers floating point division; INT_DIV_TO_MUL_RCP
+ * handles the integer case, converting to and from floating point so that
+ * RCP is possible.
+ *
* EXP_TO_EXP2 and LOG_TO_LOG2:
* ----------------------------
* Many GPUs don't have a base e log or exponent instruction, but they
* do have base 2 versions, so this pass converts exp and log to exp2
* and log2 operations.
*
+ * POW_TO_EXP2:
+ * -----------
+ * Many older GPUs don't have an x**y instruction. For these GPUs, convert
+ * x**y to 2**(y * log2(x)).
+ *
* MOD_TO_FRACT:
* -------------
* Breaks an ir_unop_mod expression down to (op1 * fract(op0 / op1))
* opportunity to do things like constant fold the (1.0 / op1) easily.
*/
-#include "main/core.h" /* for M_E */
+#include "main/core.h" /* for M_LOG2E */
#include "glsl_types.h"
#include "ir.h"
#include "ir_optimization.h"
void sub_to_add_neg(ir_expression *);
void div_to_mul_rcp(ir_expression *);
+ void int_div_to_mul_rcp(ir_expression *);
void mod_to_fract(ir_expression *);
void exp_to_exp2(ir_expression *);
+ void pow_to_exp2(ir_expression *);
void log_to_log2(ir_expression *);
};
void
lower_instructions_visitor::div_to_mul_rcp(ir_expression *ir)
{
- if (!ir->operands[1]->type->is_integer()) {
- /* New expression for the 1.0 / op1 */
- ir_rvalue *expr;
- expr = new(ir) ir_expression(ir_unop_rcp,
- ir->operands[1]->type,
- ir->operands[1],
- NULL);
-
- /* op0 / op1 -> op0 * (1.0 / op1) */
- ir->operation = ir_binop_mul;
- ir->operands[1] = expr;
- } else {
- /* Be careful with integer division -- we need to do it as a
- * float and re-truncate, since rcp(n > 1) of an integer would
- * just be 0.
- */
- ir_rvalue *op0, *op1;
- const struct glsl_type *vec_type;
+ assert(ir->operands[1]->type->is_float());
+
+ /* New expression for the 1.0 / op1 */
+ ir_rvalue *expr;
+ expr = new(ir) ir_expression(ir_unop_rcp,
+ ir->operands[1]->type,
+ ir->operands[1]);
+
+ /* op0 / op1 -> op0 * (1.0 / op1) */
+ ir->operation = ir_binop_mul;
+ ir->operands[1] = expr;
- vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
- ir->operands[1]->type->vector_elements,
- ir->operands[1]->type->matrix_columns);
+ this->progress = true;
+}
- if (ir->operands[1]->type->base_type == GLSL_TYPE_INT)
- op1 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[1], NULL);
- else
- op1 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[1], NULL);
+void
+lower_instructions_visitor::int_div_to_mul_rcp(ir_expression *ir)
+{
+ assert(ir->operands[1]->type->is_integer());
+
+ /* Be careful with integer division -- we need to do it as a
+ * float and re-truncate, since rcp(n > 1) of an integer would
+ * just be 0.
+ */
+ ir_rvalue *op0, *op1;
+ const struct glsl_type *vec_type;
- op1 = new(ir) ir_expression(ir_unop_rcp, op1->type, op1, NULL);
+ vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
+ ir->operands[1]->type->vector_elements,
+ ir->operands[1]->type->matrix_columns);
- vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
- ir->operands[0]->type->vector_elements,
- ir->operands[0]->type->matrix_columns);
+ if (ir->operands[1]->type->base_type == GLSL_TYPE_INT)
+ op1 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[1], NULL);
+ else
+ op1 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[1], NULL);
- if (ir->operands[0]->type->base_type == GLSL_TYPE_INT)
- op0 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[0], NULL);
- else
- op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL);
+ op1 = new(ir) ir_expression(ir_unop_rcp, op1->type, op1, NULL);
- op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1);
+ vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
+ ir->operands[0]->type->vector_elements,
+ ir->operands[0]->type->matrix_columns);
+ if (ir->operands[0]->type->base_type == GLSL_TYPE_INT)
+ op0 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[0], NULL);
+ else
+ op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL);
+
+ vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
+ ir->type->vector_elements,
+ ir->type->matrix_columns);
+
+ op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1);
+
+ if (ir->operands[1]->type->base_type == GLSL_TYPE_INT) {
ir->operation = ir_unop_f2i;
ir->operands[0] = op0;
- ir->operands[1] = NULL;
+ } else {
+ ir->operation = ir_unop_i2u;
+ ir->operands[0] = new(ir) ir_expression(ir_unop_f2i, op0);
}
+ ir->operands[1] = NULL;
this->progress = true;
}
void
lower_instructions_visitor::exp_to_exp2(ir_expression *ir)
{
- ir_constant *log2_e = new(ir) ir_constant(log2f(M_E));
+ ir_constant *log2_e = new(ir) ir_constant(float(M_LOG2E));
ir->operation = ir_unop_exp2;
ir->operands[0] = new(ir) ir_expression(ir_binop_mul, ir->operands[0]->type,
this->progress = true;
}
+void
+lower_instructions_visitor::pow_to_exp2(ir_expression *ir)
+{
+ ir_expression *const log2_x =
+ new(ir) ir_expression(ir_unop_log2, ir->operands[0]->type,
+ ir->operands[0]);
+
+ ir->operation = ir_unop_exp2;
+ ir->operands[0] = new(ir) ir_expression(ir_binop_mul, ir->operands[1]->type,
+ ir->operands[1], log2_x);
+ ir->operands[1] = NULL;
+ this->progress = true;
+}
+
void
lower_instructions_visitor::log_to_log2(ir_expression *ir)
{
ir->operation = ir_binop_mul;
ir->operands[0] = new(ir) ir_expression(ir_unop_log2, ir->operands[0]->type,
ir->operands[0], NULL);
- ir->operands[1] = new(ir) ir_constant(1.0f / log2f(M_E));
+ ir->operands[1] = new(ir) ir_constant(float(1.0 / M_LOG2E));
this->progress = true;
}
break;
case ir_binop_div:
- if (lowering(DIV_TO_MUL_RCP))
+ if (ir->operands[1]->type->is_integer() && lowering(INT_DIV_TO_MUL_RCP))
+ int_div_to_mul_rcp(ir);
+ else if (ir->operands[1]->type->is_float() && lowering(DIV_TO_MUL_RCP))
div_to_mul_rcp(ir);
break;
break;
case ir_binop_mod:
- if (lowering(MOD_TO_FRACT))
+ if (lowering(MOD_TO_FRACT) && ir->type->is_float())
mod_to_fract(ir);
break;
+ case ir_binop_pow:
+ if (lowering(POW_TO_EXP2))
+ pow_to_exp2(ir);
+ break;
+
default:
return visit_continue;
}