nir/range_analysis: Add "is a number" range analysis tracking
authorIan Romanick <ian.d.romanick@intel.com>
Fri, 3 Jul 2020 01:18:09 +0000 (18:18 -0700)
committerMarge Bot <eric+marge@anholt.net>
Thu, 11 Mar 2021 22:00:30 +0000 (22:00 +0000)
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 <pendingchaos02@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9108>

src/compiler/nir/nir_range_analysis.c
src/compiler/nir/nir_range_analysis.h
src/compiler/nir/nir_search_helpers.h

index 3045bd7..26011f7 100644 (file)
@@ -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;
 }
index c8aee9f..e30910d 100644 (file)
@@ -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;
 };
index 9488c7e..5de812f 100644 (file)
@@ -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_ */