nir/range-analysis: Range tracking for bcsel
authorIan Romanick <ian.d.romanick@intel.com>
Wed, 24 Jan 2018 12:23:15 +0000 (20:23 +0800)
committerIan Romanick <ian.d.romanick@intel.com>
Tue, 6 Aug 2019 03:14:13 +0000 (20:14 -0700)
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>
src/compiler/nir/nir_range_analysis.c

index dac40ecb066a0a6f186afbfbcaaf1775bdce6452..9555be3fa13e3ffdb7596e347d67f0a902a0628c 100644 (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};
       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);