Module: Mesa Branch: master Commit: aa5d38decde5c8851988d80ecb407b91de8342c7 URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=aa5d38decde5c8851988d80ecb407b91de8342c7
Author: Ian Romanick <[email protected]> Date: Thu Jul 2 18:18:09 2020 -0700 nir/range_analysis: Add "is a number" range analysis tracking This commit is necessary to support "nir/range_analysis: Fix analysis of fmin and fmax with NaN". No shader-db or fossil-db changes on any Intel platform. v2: Pack and unpack is_a_number. v3: Don't set is_a_number of integer constants. The bit pattern might be NaN. v4: Update handling of b2i32. intBitsToFloat(int(true)) is 1.401298464324817e-45. Return a value consistent with that. Fixes: 405de7ccb6c ("nir/range-analysis: Rudimentary value range analysis pass") Reviewed-by: Rhys Perry <[email protected]> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9108> --- src/compiler/nir/nir_range_analysis.c | 96 ++++++++++++++++++++++++++++++----- src/compiler/nir/nir_range_analysis.h | 3 ++ src/compiler/nir/nir_search_helpers.h | 8 +++ 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/compiler/nir/nir_range_analysis.c b/src/compiler/nir/nir_range_analysis.c index 3045bd7c7a5..26011f73696 100644 --- a/src/compiler/nir/nir_range_analysis.c +++ b/src/compiler/nir/nir_range_analysis.c @@ -37,10 +37,17 @@ is_not_negative(enum ssa_ranges r) return r == gt_zero || r == ge_zero || r == eq_zero; } +static bool +is_not_zero(enum ssa_ranges r) +{ + return r == gt_zero || r == lt_zero || r == ne_zero; +} + static void * pack_data(const struct ssa_result_range r) { - return (void *)(uintptr_t)(r.range | r.is_integral << 8 | r.is_finite << 9); + return (void *)(uintptr_t)(r.range | r.is_integral << 8 | r.is_finite << 9 | + r.is_a_number << 10); } static struct ssa_result_range @@ -51,7 +58,8 @@ unpack_data(const void *p) return (struct ssa_result_range){ .range = v & 0xff, .is_integral = (v & 0x00100) != 0, - .is_finite = (v & 0x00200) != 0 + .is_finite = (v & 0x00200) != 0, + .is_a_number = (v & 0x00400) != 0 }; } @@ -107,7 +115,7 @@ analyze_constant(const struct nir_alu_instr *instr, unsigned src, const nir_load_const_instr *const load = nir_instr_as_load_const(instr->src[src].src.ssa->parent_instr); - struct ssa_result_range r = { unknown, false, false }; + struct ssa_result_range r = { unknown, false, false, false }; switch (nir_alu_type_get_base_type(use_type)) { case nir_type_float: { @@ -117,6 +125,7 @@ analyze_constant(const struct nir_alu_instr *instr, unsigned src, bool all_zero = true; r.is_integral = true; + r.is_a_number = true; r.is_finite = true; for (unsigned i = 0; i < num_components; ++i) { @@ -126,6 +135,9 @@ analyze_constant(const struct nir_alu_instr *instr, unsigned src, if (floor(v) != v) r.is_integral = false; + if (isnan(v)) + r.is_a_number = false; + if (!isfinite(v)) r.is_finite = false; @@ -420,13 +432,13 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, STATIC_ASSERT(last_range + 1 == 7); if (!instr->src[src].src.is_ssa) - return (struct ssa_result_range){unknown, false, false}; + return (struct ssa_result_range){unknown, false, false, false}; if (nir_src_is_const(instr->src[src].src)) return analyze_constant(instr, src, use_type); if (instr->src[src].src.ssa->parent_instr->type != nir_instr_type_alu) - return (struct ssa_result_range){unknown, false, false}; + return (struct ssa_result_range){unknown, false, false, false}; const struct nir_alu_instr *const alu = nir_instr_as_alu(instr->src[src].src.ssa->parent_instr); @@ -445,7 +457,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, if (use_base_type != src_base_type && (use_base_type == nir_type_float || src_base_type == nir_type_float)) { - return (struct ssa_result_range){unknown, false, false}; + return (struct ssa_result_range){unknown, false, false, false}; } } @@ -453,7 +465,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, if (he != NULL) return unpack_data(he->data); - struct ssa_result_range r = {unknown, false, false}; + struct ssa_result_range r = {unknown, false, false, false}; /* ge_zero: ge_zero + ge_zero * @@ -560,9 +572,10 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, * * b2i32 will generate either 0x00000000 or 0x00000001. When those bit * patterns are interpreted as floating point, they are 0.0 and - * 1.401298464324817e-45. The latter is subnormal, but it is finite. + * 1.401298464324817e-45. The latter is subnormal, but it is finite and + * a number. */ - r = (struct ssa_result_range){ge_zero, alu->op == nir_op_b2f32, true}; + r = (struct ssa_result_range){ge_zero, alu->op == nir_op_b2f32, true, true}; break; case nir_op_bcsel: { @@ -572,6 +585,20 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, analyze_expression(alu, 2, ht, use_type); r.is_integral = left.is_integral && right.is_integral; + + /* This could be better, but it would require a lot of work. For + * example, the result of the following is a number: + * + * bcsel(a > 0.0, a, 38.6) + * + * If the result of 'a > 0.0' is true, then the use of 'a' in the true + * part of the bcsel must be a number. + * + * Other cases are even more challenging. + * + * bcsel(a > 0.5, a - 0.5, 0.0) + */ + r.is_a_number = left.is_a_number && right.is_a_number; r.is_finite = left.is_finite && right.is_finite; /* le_zero: bcsel(<any>, le_zero, lt_zero) @@ -638,6 +665,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r = analyze_expression(alu, 0, ht, nir_alu_src_type(alu, 0)); r.is_integral = true; + r.is_a_number = true; r.is_finite = true; if (r.range == unknown && alu->op == nir_op_u2f32) @@ -675,6 +703,13 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r.is_integral = left.is_integral && right.is_integral; r.range = fadd_table[left.range][right.range]; + + /* X + Y is NaN if either operand is NaN or if one operand is +Inf and + * the other is -Inf. If neither operand is NaN and at least one of the + * operands is finite, then the result cannot be NaN. + */ + r.is_a_number = left.is_a_number && right.is_a_number && + (left.is_finite || right.is_finite); break; } @@ -698,6 +733,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, /* Various cases can result in NaN, so assume the worst. */ r.is_finite = false; + r.is_a_number = false; break; } @@ -716,6 +752,9 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, */ r.is_finite = left.is_finite && right.is_finite; + /* If one source is NaN, fmax always picks the other source. */ + r.is_a_number = left.is_a_number || right.is_a_number; + /* gt_zero: fmax(gt_zero, *) * | fmax(*, gt_zero) # Treat fmax as commutative * ; @@ -788,6 +827,9 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, */ r.is_finite = left.is_finite && right.is_finite; + /* If one source is NaN, fmin always picks the other source. */ + r.is_a_number = left.is_a_number || right.is_a_number; + /* lt_zero: fmin(lt_zero, *) * | fmin(*, lt_zero) # Treat fmin as commutative * ; @@ -865,6 +907,15 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, } else r.range = fmul_table[left.range][right.range]; + /* Mulitpliation produces NaN for X * NaN and for 0 * ±Inf. If both + * operands are numbers and either both are finite or one is finite and + * the other cannot be zero, then the result must be a number. + */ + r.is_a_number = (left.is_a_number && right.is_a_number) && + ((left.is_finite && right.is_finite) || + (!is_not_zero(left.range) && right.is_finite) || + (left.is_finite && !is_not_zero(right.range))); + break; } @@ -872,7 +923,8 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r = (struct ssa_result_range){ analyze_expression(alu, 0, ht, nir_alu_src_type(alu, 0)).range, false, - false /* Various cases can result in NaN, so assume the worst. */ + false, /* Various cases can result in NaN, so assume the worst. */ + false /* " " " " " " " " " " */ }; break; @@ -891,6 +943,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, analyze_expression(alu, 0, ht, nir_alu_src_type(alu, 0)); /* fsat(NaN) = 0. */ + r.is_a_number = true; r.is_finite = true; switch (left.range) { @@ -922,13 +975,14 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r = (struct ssa_result_range){ analyze_expression(alu, 0, ht, nir_alu_src_type(alu, 0)).range, true, + true, /* fsign is -1, 0, or 1, even for NaN, so it must be a number. */ true /* fsign is -1, 0, or 1, even for NaN, so it must be finite. */ }; break; case nir_op_fsqrt: case nir_op_frsq: - r = (struct ssa_result_range){ge_zero, false, false}; + r = (struct ssa_result_range){ge_zero, false, false, false}; break; case nir_op_ffloor: { @@ -940,6 +994,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, /* In IEEE 754, floor(NaN) is NaN, and floor(±Inf) is ±Inf. See * https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/functions/floor.html */ + r.is_a_number = left.is_a_number; r.is_finite = left.is_finite; if (left.is_integral || left.range == le_zero || left.range == lt_zero) @@ -961,6 +1016,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, /* In IEEE 754, ceil(NaN) is NaN, and ceil(±Inf) is ±Inf. See * https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/functions/ceil.html */ + r.is_a_number = left.is_a_number; r.is_finite = left.is_finite; if (left.is_integral || left.range == ge_zero || left.range == gt_zero) @@ -982,6 +1038,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, /* In IEEE 754, trunc(NaN) is NaN, and trunc(±Inf) is ±Inf. See * https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/functions/trunc.html */ + r.is_a_number = left.is_a_number; r.is_finite = left.is_finite; if (left.is_integral) @@ -1007,7 +1064,7 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, case nir_op_ult: case nir_op_uge: /* Boolean results are 0 or -1. */ - r = (struct ssa_result_range){le_zero, false, false}; + r = (struct ssa_result_range){le_zero, false, true, false}; break; case nir_op_fpow: { @@ -1072,6 +1129,10 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r.is_integral = left.is_integral && right.is_integral && is_not_negative(right.range); r.range = table[left.range][right.range]; + + /* Various cases can result in NaN, so assume the worst. */ + r.is_a_number = false; + break; } @@ -1086,6 +1147,9 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r.is_integral = first.is_integral && second.is_integral && third.is_integral; + /* Various cases can result in NaN, so assume the worst. */ + r.is_a_number = false; + enum ssa_ranges fmul_range; if (first.range != eq_zero && nir_alu_srcs_equal(alu, alu, 0, 1)) { @@ -1114,6 +1178,9 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, r.is_integral = first.is_integral && second.is_integral && third.is_integral; + /* Various cases can result in NaN, so assume the worst. */ + r.is_a_number = false; + /* Decompose the flrp to first + third * (second + -first) */ const enum ssa_ranges inner_fadd_range = fadd_table[second.range][fneg_table[first.range]]; @@ -1126,13 +1193,16 @@ analyze_expression(const nir_alu_instr *instr, unsigned src, } default: - r = (struct ssa_result_range){unknown, false, false}; + r = (struct ssa_result_range){unknown, false, false, false}; break; } if (r.range == eq_zero) r.is_integral = true; + /* Just like isfinite(), the is_finite flag implies the value is a number. */ + assert((int) r.is_finite <= (int) r.is_a_number); + _mesa_hash_table_insert(ht, pack_key(alu, use_type), pack_data(r)); return r; } diff --git a/src/compiler/nir/nir_range_analysis.h b/src/compiler/nir/nir_range_analysis.h index c8aee9ff839..e30910d2411 100644 --- a/src/compiler/nir/nir_range_analysis.h +++ b/src/compiler/nir/nir_range_analysis.h @@ -40,6 +40,9 @@ struct ssa_result_range { /** A floating-point value that can only have integer values. */ bool is_integral; + /** A floating-point value that cannot be NaN. */ + bool is_a_number; + /** Is the value known to be a finite number? */ bool is_finite; }; diff --git a/src/compiler/nir/nir_search_helpers.h b/src/compiler/nir/nir_search_helpers.h index 9488c7eaf9d..5de812f1e4c 100644 --- a/src/compiler/nir/nir_search_helpers.h +++ b/src/compiler/nir/nir_search_helpers.h @@ -494,4 +494,12 @@ is_not_zero(struct hash_table *ht, const nir_alu_instr *instr, unsigned src, return v.range == lt_zero || v.range == gt_zero || v.range == ne_zero; } +static inline bool +is_a_number(struct hash_table *ht, const nir_alu_instr *instr, unsigned src, + UNUSED unsigned num_components, UNUSED const uint8_t *swizzle) +{ + const struct ssa_result_range v = nir_analyze_range(ht, instr, src); + return v.is_a_number; +} + #endif /* _NIR_SEARCH_ */ _______________________________________________ mesa-commit mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/mesa-commit
