+/**
+ * If the \p s is an SSA value that was generated by a negation instruction,
+ * that instruction is returned as a \c nir_alu_instr. Otherwise \c NULL is
+ * returned.
+ */
+static nir_alu_instr *
+get_neg_instr(nir_src s)
+{
+ nir_alu_instr *alu = nir_src_as_alu_instr(s);
+
+ return alu != NULL && (alu->op == nir_op_fneg || alu->op == nir_op_ineg)
+ ? alu : NULL;
+}
+
+bool
+nir_const_value_negative_equal(const nir_const_value *c1,
+ const nir_const_value *c2,
+ unsigned components,
+ nir_alu_type base_type,
+ unsigned bits)
+{
+ assert(base_type == nir_alu_type_get_base_type(base_type));
+ assert(base_type != nir_type_invalid);
+
+ /* This can occur for 1-bit Boolean values. */
+ if (bits == 1)
+ return false;
+
+ switch (base_type) {
+ case nir_type_float:
+ switch (bits) {
+ case 16:
+ for (unsigned i = 0; i < components; i++) {
+ if (_mesa_half_to_float(c1[i].u16) !=
+ -_mesa_half_to_float(c2[i].u16)) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case 32:
+ for (unsigned i = 0; i < components; i++) {
+ if (c1[i].f32 != -c2[i].f32)
+ return false;
+ }
+
+ return true;
+
+ case 64:
+ for (unsigned i = 0; i < components; i++) {
+ if (c1[i].f64 != -c2[i].f64)
+ return false;
+ }
+
+ return true;
+
+ default:
+ unreachable("unknown bit size");
+ }
+
+ break;
+
+ case nir_type_int:
+ case nir_type_uint:
+ switch (bits) {
+ case 8:
+ for (unsigned i = 0; i < components; i++) {
+ if (c1[i].i8 != -c2[i].i8)
+ return false;
+ }
+
+ return true;
+
+ case 16:
+ for (unsigned i = 0; i < components; i++) {
+ if (c1[i].i16 != -c2[i].i16)
+ return false;
+ }
+
+ return true;
+ break;
+
+ case 32:
+ for (unsigned i = 0; i < components; i++) {
+ if (c1[i].i32 != -c2[i].i32)
+ return false;
+ }
+
+ return true;
+
+ case 64:
+ for (unsigned i = 0; i < components; i++) {
+ if (c1[i].i64 != -c2[i].i64)
+ return false;
+ }
+
+ return true;
+
+ default:
+ unreachable("unknown bit size");
+ }
+
+ break;
+
+ case nir_type_bool:
+ return false;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * Shallow compare of ALU srcs to determine if one is the negation of the other
+ *
+ * This function detects cases where \p alu1 is a constant and \p alu2 is a
+ * constant that is its negation. It will also detect cases where \p alu2 is
+ * an SSA value that is a \c nir_op_fneg applied to \p alu1 (and vice versa).
+ *
+ * This function does not detect the general case when \p alu1 and \p alu2 are
+ * SSA values that are the negations of each other (e.g., \p alu1 represents
+ * (a * b) and \p alu2 represents (-a * b)).
+ */
+bool
+nir_alu_srcs_negative_equal(const nir_alu_instr *alu1,
+ const nir_alu_instr *alu2,
+ unsigned src1, unsigned src2)
+{
+ if (alu1->src[src1].abs != alu2->src[src2].abs)
+ return false;
+
+ bool parity = alu1->src[src1].negate != alu2->src[src2].negate;
+
+ /* Handling load_const instructions is tricky. */
+
+ const nir_const_value *const const1 =
+ nir_src_as_const_value(alu1->src[src1].src);
+
+ if (const1 != NULL) {
+ /* Assume that constant folding will eliminate source mods and unary
+ * ops.
+ */
+ if (parity)
+ return false;
+
+ const nir_const_value *const const2 =
+ nir_src_as_const_value(alu2->src[src2].src);
+
+ if (const2 == NULL)
+ return false;
+
+ /* FINISHME: Apply the swizzle? */
+ return nir_const_value_negative_equal(const1,
+ const2,
+ nir_ssa_alu_instr_src_components(alu1, src1),
+ nir_op_infos[alu1->op].input_types[src1],
+ alu1->dest.dest.ssa.bit_size);
+ }
+
+ uint8_t alu1_swizzle[4] = {0};
+ nir_src alu1_actual_src;
+ nir_alu_instr *neg1 = get_neg_instr(alu1->src[src1].src);
+
+ if (neg1) {
+ parity = !parity;
+ alu1_actual_src = neg1->src[0].src;
+
+ for (unsigned i = 0; i < nir_ssa_alu_instr_src_components(neg1, 0); i++)
+ alu1_swizzle[i] = neg1->src[0].swizzle[i];
+ } else {
+ alu1_actual_src = alu1->src[src1].src;
+
+ for (unsigned i = 0; i < nir_ssa_alu_instr_src_components(alu1, src1); i++)
+ alu1_swizzle[i] = i;
+ }
+
+ uint8_t alu2_swizzle[4] = {0};
+ nir_src alu2_actual_src;
+ nir_alu_instr *neg2 = get_neg_instr(alu2->src[src2].src);
+
+ if (neg2) {
+ parity = !parity;
+ alu2_actual_src = neg2->src[0].src;
+
+ for (unsigned i = 0; i < nir_ssa_alu_instr_src_components(neg2, 0); i++)
+ alu2_swizzle[i] = neg2->src[0].swizzle[i];
+ } else {
+ alu2_actual_src = alu2->src[src2].src;
+
+ for (unsigned i = 0; i < nir_ssa_alu_instr_src_components(alu2, src2); i++)
+ alu2_swizzle[i] = i;
+ }
+
+ for (unsigned i = 0; i < nir_ssa_alu_instr_src_components(alu1, src1); i++) {
+ if (alu1_swizzle[alu1->src[src1].swizzle[i]] !=
+ alu2_swizzle[alu2->src[src2].swizzle[i]])
+ return false;
+ }
+
+ return parity && nir_srcs_equal(alu1_actual_src, alu2_actual_src);
+}
+
+bool