nir/range-analysis: Range tracking for bcsel

This could be squashed with the previous commit.  I kept it separate to
ease review.

v2: Add some missing cases.  Use nir_src_is_const helper.  Both
suggested by Caio.  Use a table for mapping source ranges to a result
range.

Reviewed-by: Caio Marcelo de Oliveira Filho <caio.oliveira@intel.com>
This commit is contained in:
Ian Romanick
2018-01-24 20:23:15 +08:00
parent 3009cbed50
commit 586602c5d9

View File

@@ -238,6 +238,83 @@ analyze_expression(const nir_alu_instr *instr, unsigned src,
r = (struct ssa_result_range){ge_zero, alu->op == nir_op_b2f32}; r = (struct ssa_result_range){ge_zero, alu->op == nir_op_b2f32};
break; break;
case nir_op_bcsel: {
const struct ssa_result_range left = analyze_expression(alu, 1, ht);
const struct ssa_result_range right = analyze_expression(alu, 2, ht);
/* If either source is a constant load that is not zero, punt. The type
* will always be uint regardless of the actual type. We can't even
* decide if the value is non-zero because -0.0 is 0x80000000, and that
* will (possibly incorrectly) be considered non-zero.
*/
/* FINISHME: We could do better, but it would require having the expected
* FINISHME: type passed in.
*/
if ((nir_src_is_const(alu->src[1].src) && left.range != eq_zero) ||
(nir_src_is_const(alu->src[2].src) && right.range != eq_zero)) {
return (struct ssa_result_range){unknown, false};
}
r.is_integral = left.is_integral && right.is_integral;
/* le_zero: bcsel(<any>, le_zero, lt_zero)
* | bcsel(<any>, eq_zero, lt_zero)
* | bcsel(<any>, le_zero, eq_zero)
* | bcsel(<any>, lt_zero, le_zero)
* | bcsel(<any>, lt_zero, eq_zero)
* | bcsel(<any>, eq_zero, le_zero)
* | bcsel(<any>, le_zero, le_zero)
* ;
*
* lt_zero: bcsel(<any>, lt_zero, lt_zero)
* ;
*
* ge_zero: bcsel(<any>, ge_zero, ge_zero)
* | bcsel(<any>, ge_zero, gt_zero)
* | bcsel(<any>, ge_zero, eq_zero)
* | bcsel(<any>, gt_zero, ge_zero)
* | bcsel(<any>, eq_zero, ge_zero)
* ;
*
* gt_zero: bcsel(<any>, gt_zero, gt_zero)
* ;
*
* ne_zero: bcsel(<any>, ne_zero, gt_zero)
* | bcsel(<any>, ne_zero, lt_zero)
* | bcsel(<any>, gt_zero, lt_zero)
* | bcsel(<any>, gt_zero, ne_zero)
* | bcsel(<any>, lt_zero, ne_zero)
* | bcsel(<any>, lt_zero, gt_zero)
* | bcsel(<any>, ne_zero, ne_zero)
* ;
*
* eq_zero: bcsel(<any>, eq_zero, eq_zero)
* ;
*
* All other cases are 'unknown'.
*
* The ranges could be tightened if the range of the first source is
* known. However, opt_algebraic will (eventually) elminiate the bcsel
* if the condition is known.
*/
static const enum ssa_ranges table[last_range + 1][last_range + 1] = {
/* left\right unknown lt_zero le_zero gt_zero ge_zero ne_zero eq_zero */
/* unknown */ { _______, _______, _______, _______, _______, _______, _______ },
/* lt_zero */ { _______, lt_zero, le_zero, ne_zero, _______, ne_zero, le_zero },
/* le_zero */ { _______, le_zero, le_zero, _______, _______, _______, le_zero },
/* gt_zero */ { _______, ne_zero, _______, gt_zero, ge_zero, ne_zero, ge_zero },
/* ge_zero */ { _______, _______, _______, ge_zero, ge_zero, _______, ge_zero },
/* ne_zero */ { _______, ne_zero, _______, ne_zero, _______, ne_zero, _______ },
/* eq_zero */ { _______, le_zero, le_zero, ge_zero, ge_zero, _______, eq_zero },
};
ASSERT_TABLE_IS_COMMUTATIVE(table);
ASSERT_TABLE_IS_DIAGONAL(table);
r.range = table[left.range][right.range];
break;
}
case nir_op_i2f32: case nir_op_i2f32:
case nir_op_u2f32: case nir_op_u2f32:
r = analyze_expression(alu, 0, ht); r = analyze_expression(alu, 0, ht);