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
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
};
}
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: {
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) {
if (floor(v) != v)
r.is_integral = false;
+ if (isnan(v))
+ r.is_a_number = false;
+
if (!isfinite(v))
r.is_finite = false;
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);
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};
}
}
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
*
*
* 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: {
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)
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)
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;
}
/* Various cases can result in NaN, so assume the worst. */
r.is_finite = false;
+ r.is_a_number = false;
break;
}
*/
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
* ;
*/
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
* ;
} 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;
}
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;
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) {
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: {
/* 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)
/* 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)
/* 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)
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: {
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;
}
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)) {
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]];
}
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;
}