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:
@@ -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};
|
||||
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_u2f32:
|
||||
r = analyze_expression(alu, 0, ht);
|
||||
|
Reference in New Issue
Block a user