diff options
Diffstat (limited to 'src/compiler/nir')
-rw-r--r-- | src/compiler/nir/nir_range_analysis.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/compiler/nir/nir_range_analysis.c b/src/compiler/nir/nir_range_analysis.c index dac40ecb066..9555be3fa13 100644 --- a/src/compiler/nir/nir_range_analysis.c +++ b/src/compiler/nir/nir_range_analysis.c @@ -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); |