This patch is my proposed fix for PR target/94871, a missed optimization
for simplifying vector comparisons in the RTL optimizers (combine) that
includes both middle-end and i386 backend changes.
The testcase from the original bugzilla PR is:
typedef long long int64_t;
typedef signed char int8_t;
typedef double v2df __attribute__((vector_size(16)));
typedef int64_t v2di __attribute__((vector_size(16)));
typedef int8_t v16qi __attribute__((vector_size(16)));
inline v2di set1_epi8(int8_t a)
{
return (v2di)(v16qi){a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a};
}
v2di foo(v2df a, v2df b)
{
return ((v2di)__builtin_ia32_cmpeqpd(a, b) ^ set1_epi8(0xFF));
}
where GCC with -O2 currently generates:
foo: cmpeqpd %xmm1, %xmm0
pcmpeqd %xmm1, %xmm1
pxor %xmm1, %xmm0
ret
where with this pair of changes, we instead generate:
foo: cmpneqpd %xmm1, %xmm0
ret
Basically, the usual optimizations (not (eq x y)) -> (ne x y) and
(not (ne x y)) -> (eq x y), but applied to vector mode comparisons
(and in this case floating point vector comparisons).
The first (i386 backend) part is to observe that the middle-end
RTL optimizers enjoy canonicalizing floating point vector comparisons
to return integer vector results. Hence -fdump-rtl-combine-all
contains the lines:
Failed to match this instruction:
(set (reg:V2DI 105)
(eq:V2DI (reg:V2DF 106 [ aD.2972 ])
(reg:V2DF 107 [ bD.2973 ])))
Currently sse.md contains patterns for matching vector comparisons
for comparing V2DFs returning a V2DF, but not the equivalent patterns
for comparing V2DFs but returning a V2DI. Very easily handled by
cloning the existing define_insn but substituting <sseintvecmode> for
the comparison result mode.
The second (simplify-rtx.cc) part is to tweak the current
(not (eq X Y)) -> (ne X Y) transformation in
simplify_unary_operation_1 to allow/handle vector modes.
In fact, this is a (latent) bug fix, as the current logic
checks STORE_FLAG_VALUE even for modes where this isn't
relevant. The target's STORE_FLAG_VALUE is applicable for
SCALAR_INT_MODE_P comparisons; VECTOR_STORE_FLAG_VALUE
should be used for VECTOR modes, and FLOAT_STORE_FLAG_VALUE
for comparisons that yield floating point results. In this
patch I've split the logic into a helper function,
comparison_all_ones_result_p to make things clearer.
I notice aarch64 returns a vector of 1s, not -1s, for vector
mode comparisons, so in theory similar RTL simplifications should
be possible there for (vx == vy) ^ 1 -> vx != vy, and likewise
1 - (vx == yy) -> vx != vy.
This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures. Ok for mainline (from middle-end and i386
backend maintainers)?
2026-06-23 Roger Sayle <[email protected]>
gcc/ChangeLog
PR target/94871
* config/i386/sse.md (*<sse>_imaskcmp<mode>3_comm): Clone
of *<sse>_maskcmp<mode>3_comm pattern with integer vector
result.
* simplify-rtx.cc (comparison_all_ones_result_p): New
helper function to check that the true result from a
comparison with result mode MODE is a mask of all ones.
(simplify_unary_operation_1): Use it when reversing
comparisons, i.e. (not (eq X Y)) -> (ne X Y).
gcc/testsuite/ChangeLog
PR target/94871
* gcc.target/i386/pr94871.c: New test case.
Thanks in advance (to you both),
Roger
--
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 6ca3e34b2c7..636406aeae6 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -4611,6 +4611,23 @@
(set_attr "prefix" "orig,vex")
(set_attr "mode" "<MODE>")])
+(define_insn "*<sse>_imaskcmp<mode>3_comm"
+ [(set (match_operand:<sseintvecmode> 0 "register_operand" "=x,x")
+ (match_operator:<sseintvecmode> 3 "sse_comparison_operator"
+ [(match_operand:VF_128_256 1 "register_operand" "%0,x")
+ (match_operand:VF_128_256 2 "vector_operand" "xBm,xjm")]))]
+ "TARGET_SSE
+ && GET_RTX_CLASS (GET_CODE (operands[3])) == RTX_COMM_COMPARE"
+ "@
+ cmp%D3<ssemodesuffix>\t{%2, %0|%0, %2}
+ vcmp%D3<ssemodesuffix>\t{%2, %1, %0|%0, %1, %2}"
+ [(set_attr "isa" "noavx,avx")
+ (set_attr "addr" "*,gpr16")
+ (set_attr "type" "ssecmp")
+ (set_attr "length_immediate" "1")
+ (set_attr "prefix" "orig,vex")
+ (set_attr "mode" "<MODE>")])
+
(define_insn "<sse>_maskcmp<mode>3"
[(set (match_operand:VF_128_256 0 "register_operand" "=x,x")
(match_operator:VF_128_256 3 "sse_comparison_operator"
diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 872ae032869..a6919698a28 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -921,6 +921,25 @@ exact_int_to_float_conversion_p (const_rtx op)
return in_bits <= out_bits;
}
+/* Test whether the result of a comparison in mode MODE is all ones. */
+static bool
+comparison_all_ones_result_p (machine_mode mode)
+{
+ if (mode == BImode)
+ return true;
+ if (SCALAR_INT_MODE_P (mode) && STORE_FLAG_VALUE == -1)
+ return true;
+#ifdef VECTOR_STORE_FLAG_VALUE
+ if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
+ && VECTOR_STORE_FLAG_VALUE (mode) == constm1_rtx)
+ return true;
+#else
+ if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+ return true;
+#endif
+ return false;
+}
+
/* Perform some simplifications we can do even if the operands
aren't constant. */
rtx
@@ -941,7 +960,7 @@ simplify_context::simplify_unary_operation_1 (rtx_code
code, machine_mode mode,
/* (not (eq X Y)) == (ne X Y), etc. if BImode or the result of the
comparison is all ones. */
if (COMPARISON_P (op)
- && (mode == BImode || STORE_FLAG_VALUE == -1)
+ && comparison_all_ones_result_p (mode)
&& ((reversed = reversed_comparison_code (op, NULL)) != UNKNOWN))
return simplify_gen_relational (reversed, mode, VOIDmode,
XEXP (op, 0), XEXP (op, 1));
diff --git a/gcc/testsuite/gcc.target/i386/pr94871.c
b/gcc/testsuite/gcc.target/i386/pr94871.c
new file mode 100644
index 00000000000..f218198647c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr94871.c
@@ -0,0 +1,26 @@
+/* PR target/94871 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx2 -march=skylake" } */
+
+typedef double v2df __attribute__((vector_size(16)));
+typedef long long v2di __attribute__((vector_size(16)));
+typedef char v16qi __attribute__((vector_size(16)));
+
+v2df p, q;
+v2di out;
+
+void foo()
+{
+ signed char a = 0xff;
+ v2di tmp = (v2di)(v16qi){a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a};
+ out = ((v2di)__builtin_ia32_cmpeqpd(p, q) ^ tmp);
+}
+
+void bar()
+{
+ signed char a = 0xff;
+ v2di tmp = (v2di)(v16qi){a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a};
+ out = ((v2di)__builtin_ia32_cmpneqpd(p, q) ^ tmp);
+}
+
+/* { dg-final { scan-assembler-not "xor" } } */