diff --git a/src/compiler/spirv/vtn_alu.c b/src/compiler/spirv/vtn_alu.c index 54019c37396..a9bdb4c3cf1 100644 --- a/src/compiler/spirv/vtn_alu.c +++ b/src/compiler/spirv/vtn_alu.c @@ -600,7 +600,29 @@ vtn_handle_alu(struct vtn_builder *b, SpvOp opcode, break; } - case SpvOpFUnordEqual: + case SpvOpFUnordEqual: { + const bool save_exact = b->nb.exact; + + b->nb.exact = true; + + /* This could also be implemented as !(a < b || b < a). If one or both + * of the source are numbers, later optimization passes can easily + * eliminate the isnan() checks. This may trim the sequence down to a + * single (a == b) operation. Otherwise, the optimizer can transform + * whatever is left to !(a < b || b < a). Since some applications will + * open-code this sequence, these optimizations are needed anyway. + */ + dest->def = + nir_ior(&b->nb, + nir_feq(&b->nb, src[0], src[1]), + nir_ior(&b->nb, + nir_fneu(&b->nb, src[0], src[0]), + nir_fneu(&b->nb, src[1], src[1]))); + + b->nb.exact = save_exact; + break; + } + case SpvOpFUnordLessThan: case SpvOpFUnordGreaterThan: case SpvOpFUnordLessThanEqual: @@ -623,12 +645,16 @@ vtn_handle_alu(struct vtn_builder *b, SpvOp opcode, b->nb.exact = true; + /* Use the property FUnordLessThan(a, b) ≡ !FOrdGreaterThanEqual(a, b). */ + switch (op) { + case nir_op_fge: op = nir_op_flt; break; + case nir_op_flt: op = nir_op_fge; break; + default: unreachable("Impossible opcode."); + } + dest->def = - nir_ior(&b->nb, - nir_build_alu(&b->nb, op, src[0], src[1], NULL, NULL), - nir_ior(&b->nb, - nir_fneu(&b->nb, src[0], src[0]), - nir_fneu(&b->nb, src[1], src[1]))); + nir_inot(&b->nb, + nir_build_alu(&b->nb, op, src[0], src[1], NULL, NULL)); b->nb.exact = save_exact; break;