summaryrefslogtreecommitdiff
path: root/src/mesa/program/ir_to_mesa.cpp
diff options
context:
space:
mode:
authorIan Romanick <ian.d.romanick@intel.com>2010-11-18 16:11:25 -0800
committerIan Romanick <ian.d.romanick@intel.com>2010-11-18 18:19:45 -0800
commitc05ccc1ebde177646ac09c1bd6d1b4719e745f82 (patch)
tree00f466ea133c468504d2f91e1839a65111ed8811 /src/mesa/program/ir_to_mesa.cpp
parentad87f2ddc7e6b95e3dd5bbe4d2b19703d305c74e (diff)
ir_to_mesa: Generate smarter code for some conditional moves
Condiation moves with a condition of (a < 0), (a > 0), (a <= 0), or (a >= 0) can be generated with "a" directly as an operand of the CMP instruction. This doesn't help much now, but it will help with assembly shaders that use the CMP instruction.
Diffstat (limited to 'src/mesa/program/ir_to_mesa.cpp')
-rw-r--r--src/mesa/program/ir_to_mesa.cpp111
1 files changed, 99 insertions, 12 deletions
diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp
index 0458625ab0..870fd6f25e 100644
--- a/src/mesa/program/ir_to_mesa.cpp
+++ b/src/mesa/program/ir_to_mesa.cpp
@@ -285,6 +285,8 @@ public:
GLboolean try_emit_mad(ir_expression *ir,
int mul_operand);
+ bool process_move_condition(ir_rvalue *ir);
+
void *mem_ctx;
};
@@ -1326,6 +1328,93 @@ get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v)
return ir_to_mesa_dst_reg_from_src(v->result);
}
+/**
+ * Process the condition of a conditional assignment
+ *
+ * Examines the condition of a conditional assignment to generate the optimal
+ * first operand of a \c CMP instruction. If the condition is a relational
+ * operator with 0 (e.g., \c ir_binop_less), the value being compared will be
+ * used as the source for the \c CMP instruction. Otherwise the comparison
+ * is processed to a boolean result, and the boolean result is used as the
+ * operand to the CMP instruction.
+ */
+bool
+ir_to_mesa_visitor::process_move_condition(ir_rvalue *ir)
+{
+ ir_rvalue *src_ir = ir;
+ bool negate = true;
+ bool switch_order = false;
+
+ ir_expression *const expr = ir->as_expression();
+ if ((expr != NULL) && (expr->get_num_operands() == 2)) {
+ bool zero_on_left = false;
+
+ if (expr->operands[0]->is_zero()) {
+ src_ir = expr->operands[1];
+ zero_on_left = true;
+ } else if (expr->operands[1]->is_zero()) {
+ src_ir = expr->operands[0];
+ zero_on_left = false;
+ }
+
+ /* a is - 0 + - 0 +
+ * (a < 0) T F F ( a < 0) T F F
+ * (0 < a) F F T (-a < 0) F F T
+ * (a <= 0) T T F (-a < 0) F F T (swap order of other operands)
+ * (0 <= a) F T T ( a < 0) T F F (swap order of other operands)
+ * (a > 0) F F T (-a < 0) F F T
+ * (0 > a) T F F ( a < 0) T F F
+ * (a >= 0) F T T ( a < 0) T F F (swap order of other operands)
+ * (0 >= a) T T F (-a < 0) F F T (swap order of other operands)
+ *
+ * Note that exchanging the order of 0 and 'a' in the comparison simply
+ * means that the value of 'a' should be negated.
+ */
+ if (src_ir != ir) {
+ switch (expr->operation) {
+ case ir_binop_less:
+ switch_order = false;
+ negate = zero_on_left;
+ break;
+
+ case ir_binop_greater:
+ switch_order = false;
+ negate = !zero_on_left;
+ break;
+
+ case ir_binop_lequal:
+ switch_order = true;
+ negate = !zero_on_left;
+ break;
+
+ case ir_binop_gequal:
+ switch_order = true;
+ negate = zero_on_left;
+ break;
+
+ default:
+ /* This isn't the right kind of comparison afterall, so make sure
+ * the whole condition is visited.
+ */
+ src_ir = ir;
+ break;
+ }
+ }
+ }
+
+ src_ir->accept(this);
+
+ /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves, and the
+ * condition we produced is 0.0 or 1.0. By flipping the sign, we can
+ * choose which value OPCODE_CMP produces without an extra instruction
+ * computing the condition.
+ */
+ if (negate)
+ this->result.negate = ~this->result.negate;
+
+ return switch_order;
+}
+
void
ir_to_mesa_visitor::visit(ir_assignment *ir)
{
@@ -1385,20 +1474,18 @@ ir_to_mesa_visitor::visit(ir_assignment *ir)
assert(r.file != PROGRAM_UNDEFINED);
if (ir->condition) {
- ir_to_mesa_src_reg condition;
-
- ir->condition->accept(this);
- condition = this->result;
+ const bool switch_order = this->process_move_condition(ir->condition);
+ ir_to_mesa_src_reg condition = this->result;
- /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves,
- * and the condition we produced is 0.0 or 1.0. By flipping the
- * sign, we can choose which value OPCODE_CMP produces without
- * an extra computing the condition.
- */
- condition.negate = ~condition.negate;
for (i = 0; i < type_size(ir->lhs->type); i++) {
- ir_to_mesa_emit_op3(ir, OPCODE_CMP, l,
- condition, r, ir_to_mesa_src_reg_from_dst(l));
+ if (switch_order) {
+ ir_to_mesa_emit_op3(ir, OPCODE_CMP, l,
+ condition, ir_to_mesa_src_reg_from_dst(l), r);
+ } else {
+ ir_to_mesa_emit_op3(ir, OPCODE_CMP, l,
+ condition, r, ir_to_mesa_src_reg_from_dst(l));
+ }
+
l.index++;
r.index++;
}