On Tue, Sep 29, 2026 at 11:10 AM Andrea Pinski
<[email protected]> wrote:
>
> On Tue, Sep 29, 2026 at 3:40 AM <[email protected]> wrote:
> >
> > From: Kyrylo Tkachov <[email protected]>
> >
> > If-conversion can expose separate comparisons for a predicate and its
> > inverse. Value number selects using an available inverse comparison
> > with exchanged arms. This also exposes equivalent scalar selects to
> > CSE and lets their users simplify in the same VN pass.
> >
> > For example, with finite-math assumptions:
> >
> > p = x < y;
> > a = p ? v1 : v2;
> > q = x >= y;
> > b = q ? v2 : v1;
> > r = a ^ b;
> >
> > becomes:
> >
> > r = 0;
> >
> > Look up the exchanged COND_EXPR in visit_nary_op. If no equivalent
> > select exists, use an available same-block inverse to build its value
> > number. Prefer the lower comparison code for this new expression,
> > including after VN operand canonicalization, to keep the choice stable.
> > Extend the existing insertion code to materialize COND_EXPR after
> > checking availability of all three operands.
> >
> > Use invert_tree_comparison for NaN semantics. Keep a nontrapping guard
> > because EQ/NE inversion can be valid even when signaling NaNs can trap.
> > Reusing a later comparison must not move an earlier trap past a side effect.
> >
> > This removes one comparison and one mask copy from each hot vector loop
> > in miniBUDE 20210901 on Neoverse V2 and Olympus.
> >
> > Bootstrapped and tested on aarch64-none-linux-gnu.
> > Ok for trunk?
> > Thanks,
> > Kyrill
> >
> > gcc/ChangeLog:
> >
> > * tree-ssa-sccvn.cc (visit_nary_op): Value number selects using
> > inverse comparisons and exchanged arms.
> > (eliminate_dom_walker::eliminate_insert): Handle COND_EXPR.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * gcc.dg/tree-ssa/fre-inverse-compare-1.c: New test.
> > * gcc.dg/tree-ssa/fre-inverse-compare-2.c: Likewise.
> > * gcc.dg/tree-ssa/fre-inverse-compare-3.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-1.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-2.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-3.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-4.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-5.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-6.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-7.c: Likewise.
> > * gcc.dg/tree-ssa/ifc-cmp-inverse-8.c: Likewise.
> >
> > Signed-off-by: Kyrylo Tkachov <[email protected]>
> > ---
> > .../gcc.dg/tree-ssa/fre-inverse-compare-1.c | 46 ++++++++++
> > .../gcc.dg/tree-ssa/fre-inverse-compare-2.c | 26 ++++++
> > .../gcc.dg/tree-ssa/fre-inverse-compare-3.c | 65 ++++++++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-1.c | 35 ++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-2.c | 33 +++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-3.c | 34 ++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-4.c | 83 ++++++++++++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-5.c | 60 +++++++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-6.c | 69 +++++++++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-7.c | 57 ++++++++++++
> > .../gcc.dg/tree-ssa/ifc-cmp-inverse-8.c | 62 +++++++++++++
> > gcc/tree-ssa-sccvn.cc | 87 +++++++++++++++++++
> > 12 files changed, 657 insertions(+)
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-2.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-3.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-2.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-3.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-4.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-5.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-6.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-7.c
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-8.c
> >
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-1.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-1.c
> > new file mode 100644
> > index 00000000000..e986bc350f7
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-1.c
> > @@ -0,0 +1,46 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-fgimple -O2 -ffinite-math-only -fno-trapping-math
> > -fdump-tree-fre1 -fdump-tree-optimized" } */
> > +
> > +/* Reuse the available inverse comparison and swap the select arms. */
> > +
> > +int __GIMPLE (ssa,startwith("fre1"))
> > +scalar_select (float x, float y, int a, int b, int c, int d)
> > +{
> > + _Bool p;
> > + _Bool q;
> > + int t;
> > + int u;
> > + int r;
> > +
> > + __BB(2):
> > + p_7 = x_1(D) < y_2(D);
> > + t_8 = p_7 ? a_3(D) : b_4(D);
> > + q_9 = x_1(D) >= y_2(D);
> > + u_10 = q_9 ? c_5(D) : d_6(D);
> > + r_11 = t_8 + u_10;
> > + return r_11;
> > +}
> > +
> > +/* Operand canonicalization must not make each select prefer the other's
> > + comparison. Keep the available comparison with reversed operands. */
> > +
> > +int __GIMPLE (ssa,startwith("fre1"))
> > +reversed_select (float x, float y, int a, int b, int c, int d)
> > +{
> > + _Bool p;
> > + _Bool q;
> > + int t;
> > + int u;
> > + int r;
> > +
> > + __BB(2):
> > + q_7 = y_2(D) > x_1(D);
> > + t_8 = q_7 ? a_3(D) : b_4(D);
> > + p_9 = x_1(D) >= y_2(D);
> > + u_10 = p_9 ? c_5(D) : d_6(D);
> > + r_11 = t_8 + u_10;
> > + return r_11;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {= [pq]_7 \? d_6\(D\) : c_5\(D\);} 2
> > "fre1" } } */
> > +/* { dg-final { scan-tree-dump-not { >= } "optimized" } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-2.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-2.c
> > new file mode 100644
> > index 00000000000..7b72aa3b126
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-2.c
> > @@ -0,0 +1,26 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-fgimple -O2 -ftrapping-math -fsignaling-nans
> > -fdump-tree-fre1" } */
> > +
> > +volatile int marker;
> > +
> > +int __GIMPLE (ssa,startwith("fre1"))
> > +compare (float x, float y, int a, int b, int c, int d)
> > +{
> > + _Bool p;
> > + _Bool q;
> > + int t;
> > + int u;
> > + int r;
> > +
> > + __BB(2):
> > + p_7 = x_1(D) != y_2(D);
> > + marker = 1;
> > + q_8 = x_1(D) == y_2(D);
> > + t_9 = q_8 ? a_3(D) : b_4(D);
> > + u_10 = p_7 ? c_5(D) : d_6(D);
> > + r_11 = t_9 + u_10;
> > + return r_11;
> > +}
> > +
> > +/* The earlier comparison must remain a condition of its select. */
> > +/* { dg-final { scan-tree-dump {u_10 = p_7 \? c_5\(D\) : d_6\(D\);} "fre1"
> > } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-3.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-3.c
> > new file mode 100644
> > index 00000000000..0c6a1a068d8
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/fre-inverse-compare-3.c
> > @@ -0,0 +1,65 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-fgimple -O2 -ffinite-math-only -fno-trapping-math
> > -fdump-tree-fre1" } */
> > +
> > +/* Equivalent selects must have the same value number, so their users
> > + can also be simplified during the same FRE pass. */
> > +
> > +int __GIMPLE (ssa,startwith("fre1"))
> > +equivalent_selects (float x, float y, int a, int b)
> > +{
> > + _Bool p;
> > + _Bool q;
> > + int t;
> > + int u;
> > + int r;
> > +
> > + __BB(2):
> > + p_5 = x_1(D) < y_2(D);
> > + t_6 = p_5 ? a_3(D) : b_4(D);
> > + q_7 = x_1(D) >= y_2(D);
> > + u_8 = q_7 ? b_4(D) : a_3(D);
> > + r_9 = t_6 ^ u_8;
> > + return r_9;
> > +}
> > +
> > +/* The inverse lookup must also accept reversed comparison operands. */
> > +
> > +int __GIMPLE (ssa,startwith("fre1"))
> > +reversed_operands (float x, float y, int a, int b)
> > +{
> > + _Bool p;
> > + _Bool q;
> > + int t;
> > + int u;
> > + int r;
> > +
> > + __BB(2):
> > + p_5 = y_2(D) > x_1(D);
> > + t_6 = p_5 ? a_3(D) : b_4(D);
> > + q_7 = x_1(D) >= y_2(D);
> > + u_8 = q_7 ? b_4(D) : a_3(D);
> > + r_9 = t_6 ^ u_8;
> > + return r_9;
> > +}
> > +
> > +/* Find the same redundancy when the opposite select occurs first. */
> > +
> > +int __GIMPLE (ssa,startwith("fre1"))
> > +reversed_order (float x, float y, int a, int b)
> > +{
> > + _Bool p;
> > + _Bool q;
> > + int t;
> > + int u;
> > + int r;
> > +
> > + __BB(2):
> > + q_5 = x_1(D) >= y_2(D);
> > + t_6 = q_5 ? b_4(D) : a_3(D);
> > + p_7 = y_2(D) > x_1(D);
> > + u_8 = p_7 ? a_3(D) : b_4(D);
> > + r_9 = t_6 ^ u_8;
> > + return r_9;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {return 0;} 3 "fre1" } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-1.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-1.c
> > new file mode 100644
> > index 00000000000..8d291d0b789
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-1.c
> > @@ -0,0 +1,35 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Ofast -fdump-tree-ifcvt" } */
> > +
> > +/* The two merge points are predicated on DISTBB < 0 and on DISTBB >= 0.
> > + With NaNs out of the way the second predicate is the inverse of the
> > + first, so if-conversion has to reuse it with the arms of the select
> > + swapped instead of emitting a second comparison. */
> > +
> > +void
> > +f (float *restrict etot, const float *restrict lx, float radij, float
> > r_radij,
> > + float elcdst, float elcdst1, float distdslv, float r_distdslv,
> > + float chrg_init, float dslv_init, int n)
> > +{
> > + for (int l = 0; l < n; l++)
> > + {
> > + float distij = lx[l];
> > + float distbb = distij - radij;
> > + int zone1 = distbb < 0.f;
> > +
> > + etot[l] += (1.f - distij * r_radij) * (zone1 ? 76.f : 0.f);
> > +
> > + float chrg_e = chrg_init
> > + * ((zone1 ? 1.f : 1.f - distbb * elcdst1)
> > + * (distbb < elcdst ? 1.f : 0.f));
> > + etot[l] += chrg_e * 45.f;
> > +
> > + float coeff = 1.f - distbb * r_distdslv;
> > + float dslv_e = dslv_init * (distbb < distdslv ? 1.f : 0.f);
> > + dslv_e *= distbb >= 0.f ? coeff : 1.f;
> > + etot[l] += dslv_e;
> > + }
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {= \S+ < 0\.0;} 1 "ifcvt" } } */
> > +/* { dg-final { scan-tree-dump-not {= \S+ >= 0\.0;} "ifcvt" } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-2.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-2.c
> > new file mode 100644
> > index 00000000000..76eef5bf586
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-2.c
> > @@ -0,0 +1,33 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Ofast -fno-finite-math-only -fdump-tree-ifcvt" } */
> > +
> > +/* Same kernel as ifc-cmp-inverse-1.c. With NaNs honoured DISTBB >= 0 is
> > + not the inverse of DISTBB < 0, so both comparisons have to stay. */
> > +
> > +void
> > +f (float *restrict etot, const float *restrict lx, float radij, float
> > r_radij,
> > + float elcdst, float elcdst1, float distdslv, float r_distdslv,
> > + float chrg_init, float dslv_init, int n)
> > +{
> > + for (int l = 0; l < n; l++)
> > + {
> > + float distij = lx[l];
> > + float distbb = distij - radij;
> > + int zone1 = distbb < 0.f;
> > +
> > + etot[l] += (1.f - distij * r_radij) * (zone1 ? 76.f : 0.f);
> > +
> > + float chrg_e = chrg_init
> > + * ((zone1 ? 1.f : 1.f - distbb * elcdst1)
> > + * (distbb < elcdst ? 1.f : 0.f));
> > + etot[l] += chrg_e * 45.f;
> > +
> > + float coeff = 1.f - distbb * r_distdslv;
> > + float dslv_e = dslv_init * (distbb < distdslv ? 1.f : 0.f);
> > + dslv_e *= distbb >= 0.f ? coeff : 1.f;
> > + etot[l] += dslv_e;
> > + }
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {= \S+ < 0\.0;} 1 "ifcvt" } } */
> > +/* { dg-final { scan-tree-dump-times {= \S+ >= 0\.0;} 1 "ifcvt" } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-3.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-3.c
> > new file mode 100644
> > index 00000000000..022aa52fd26
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-3.c
> > @@ -0,0 +1,34 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Ofast -fno-finite-math-only -fdump-tree-ifcvt" } */
> > +
> > +/* When the source spells the second test as the arms of a select, GCC
> > + builds the else predicate as DISTBB u>= 0, which is the inverse of
> > + DISTBB < 0 even with NaNs honoured, so one comparison is enough. */
> > +
> > +void
> > +f (float *restrict etot, const float *restrict lx, float radij, float
> > r_radij,
> > + float elcdst, float elcdst1, float distdslv, float r_distdslv,
> > + float chrg_init, float dslv_init, int n)
> > +{
> > + for (int l = 0; l < n; l++)
> > + {
> > + float distij = lx[l];
> > + float distbb = distij - radij;
> > + int zone1 = distbb < 0.f;
> > +
> > + etot[l] += (1.f - distij * r_radij) * (zone1 ? 76.f : 0.f);
> > +
> > + float chrg_e = chrg_init
> > + * ((zone1 ? 1.f : 1.f - distbb * elcdst1)
> > + * (distbb < elcdst ? 1.f : 0.f));
> > + etot[l] += chrg_e * 45.f;
> > +
> > + float coeff = 1.f - distbb * r_distdslv;
> > + float dslv_e = dslv_init * (distbb < distdslv ? 1.f : 0.f);
> > + dslv_e *= zone1 ? 1.f : coeff;
> > + etot[l] += dslv_e;
> > + }
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {= \S+ < 0\.0;} 1 "ifcvt" } } */
> > +/* { dg-final { scan-tree-dump-not {= \S+ u>= 0\.0;} "ifcvt" } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-4.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-4.c
> > new file mode 100644
> > index 00000000000..867a7381a96
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-4.c
> > @@ -0,0 +1,83 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-Ofast" } */
> > +
> > +/* Swapping the arms of a select and testing the inverse predicate has to
> > + pick the same value. Every input and every intermediate value here is a
> > + small integer, so the result does not depend on the order in which the
> > + fast-math flags let the compiler evaluate the expression. */
> > +
> > +#define N 41
> > +
> > +static float lx[N], etot[N], ref[N];
> > +
> > +__attribute__ ((noipa))
> > +static void
> > +kernel (float *restrict etot, const float *restrict lx, float radij,
> > + float r_radij, float elcdst, float elcdst1, float distdslv,
> > + float r_distdslv, float chrg_init, float dslv_init, int n)
> > +{
> > + for (int l = 0; l < n; l++)
> > + {
> > + float distij = lx[l];
> > + float distbb = distij - radij;
> > + int zone1 = distbb < 0.f;
> > +
> > + etot[l] += (1.f - distij * r_radij) * (zone1 ? 76.f : 0.f);
> > +
> > + float chrg_e = chrg_init
> > + * ((zone1 ? 1.f : 1.f - distbb * elcdst1)
> > + * (distbb < elcdst ? 1.f : 0.f));
> > + etot[l] += chrg_e * 45.f;
> > +
> > + float coeff = 1.f - distbb * r_distdslv;
> > + float dslv_e = dslv_init * (distbb < distdslv ? 1.f : 0.f);
> > + dslv_e *= distbb >= 0.f ? coeff : 1.f;
> > + etot[l] += dslv_e;
> > + }
> > +}
> > +
> > +__attribute__ ((noipa, optimize ("O0")))
> > +static void
> > +reference (float *restrict etot, const float *restrict lx, float radij,
> > + float r_radij, float elcdst, float elcdst1, float distdslv,
> > + float r_distdslv, float chrg_init, float dslv_init, int n)
> > +{
> > + for (int l = 0; l < n; l++)
> > + {
> > + float distij = lx[l];
> > + float distbb = distij - radij;
> > + int zone1 = distbb < 0.f;
> > +
> > + etot[l] += (1.f - distij * r_radij) * (zone1 ? 76.f : 0.f);
> > +
> > + float chrg_e = chrg_init
> > + * ((zone1 ? 1.f : 1.f - distbb * elcdst1)
> > + * (distbb < elcdst ? 1.f : 0.f));
> > + etot[l] += chrg_e * 45.f;
> > +
> > + float coeff = 1.f - distbb * r_distdslv;
> > + float dslv_e = dslv_init * (distbb < distdslv ? 1.f : 0.f);
> > + dslv_e *= distbb >= 0.f ? coeff : 1.f;
> > + etot[l] += dslv_e;
> > + }
> > +}
> > +
> > +int
> > +main (void)
> > +{
> > + for (int i = 0; i < N; i++)
> > + {
> > + lx[i] = i - N / 2;
> > + etot[i] = i & 3;
> > + ref[i] = etot[i];
> > + }
> > +
> > + kernel (etot, lx, 3.f, 2.f, 5.f, 1.f, 4.f, 2.f, 3.f, 2.f, N);
> > + reference (ref, lx, 3.f, 2.f, 5.f, 1.f, 4.f, 2.f, 3.f, 2.f, N);
> > +
> > + for (int i = 0; i < N; i++)
> > + if (etot[i] != ref[i])
> > + __builtin_abort ();
> > +
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-5.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-5.c
> > new file mode 100644
> > index 00000000000..2b624516c0f
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-5.c
> > @@ -0,0 +1,60 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O3 -ffast-math -fno-ssa-phiopt" } */
> > +
> > +/* Factoring the first select removes a comparison that can be an inverse
> > + predicate for the second select. */
> > +
> > +__attribute__ ((noipa))
> > +static void
> > +kernel (float *restrict out, const float *restrict in, float z, int n)
> > +{
> > + for (int i = 0; i < n; ++i)
> > + {
> > + float x = in[i * 3];
> > + float y = in[i * 3 + 1];
> > + float w = in[i * 3 + 2];
> > + int p;
> > + if (x < 0)
> > + p = y < z;
> > + else
> > + p = w < z;
> > + out[i * 2] = p;
> > + float q;
> > + if (y >= z)
> > + q = x * 3 + 1;
> > + else
> > + q = x * 2 - 1;
> > + out[i * 2 + 1] = q;
> > + }
> > +}
> > +
> > +__attribute__ ((noipa, optimize ("O0")))
> > +static void
> > +check (const float *out, const float *in, float z, int n)
> > +{
> > + for (int i = 0; i < n; ++i)
> > + {
> > + float x = in[i * 3];
> > + float y = in[i * 3 + 1];
> > + float w = in[i * 3 + 2];
> > + float p = x < 0 ? y < z : w < z;
> > + float q = y >= z ? x * 3 + 1 : x * 2 - 1;
> > + if (out[i * 2] != p || out[i * 2 + 1] != q)
> > + __builtin_abort ();
> > + }
> > +}
> > +
> > +int
> > +main (void)
> > +{
> > + float in[3 * 257], out[2 * 257];
> > + for (int i = 0; i < 3 * 257; ++i)
> > + in[i] = (i * 13 % 41) - 20;
> > + for (int n = 0; n <= 257; ++n)
> > + for (int z = -2; z <= 2; ++z)
> > + {
> > + kernel (out, in, z, n);
> > + check (out, in, z, n);
> > + }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-6.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-6.c
> > new file mode 100644
> > index 00000000000..f1431e88d2d
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-6.c
> > @@ -0,0 +1,69 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O3 -fno-trapping-math -fno-ssa-phiopt" } */
> > +/* { dg-require-effective-target flt_dbl_ldbl_inf_nan } */
> > +
> > +/* Ordered comparisons are not complementary for unordered operands.
> > + Check inverse and reversed comparisons in nested loop predicates. */
> > +
> > +#define N 81
> > +static float a[N], b[N];
> > +static int out[N * 4];
> > +
> > +__attribute__ ((noipa))
> > +static void
> > +kernel (int *restrict out, const float *restrict a,
> > + const float *restrict b, int n)
> > +{
> > + for (int i = 0; i < n; ++i)
> > + {
> > + float x = a[i], y = b[i];
> > + out[4 * i] = x < y ? 11 : 29;
> > + out[4 * i + 1] = x >= y ? 31 : 47;
> > + out[4 * i + 2] = !(x < y) ? 53 : 71;
> > + int r;
> > + if (y <= x)
> > + r = x == y ? 73 : 89;
> > + else
> > + r = x != y ? 97 : 101;
> > + out[4 * i + 3] = r;
> > + }
> > +}
> > +
> > +int
> > +main (void)
> > +{
> > + float values[9] = { -__builtin_inff (), -2.0f, -0.0f, 0.0f, 2.0f,
> > + __builtin_inff (), __builtin_nanf (""),
> > + -__builtin_nanf (""), 1.0f };
> > + /* Equal ranks denote equal values. Rank 4 denotes unordered values. */
> > + int rank[9] = { -3, -2, 0, 0, 2, 3, 4, 4, 1 };
> > + for (int i = 0; i < N; ++i)
> > + {
> > + a[i] = values[i / 9];
> > + b[i] = values[i % 9];
> > + }
> > + for (int n = 0; n <= N; ++n)
> > + {
> > + for (int i = 0; i < N * 4; ++i)
> > + out[i] = -1;
> > + kernel (out, a, b, n);
> > + for (int i = 0; i < n; ++i)
> > + {
> > + int x = rank[i / 9], y = rank[i % 9];
> > + int ordered = x != 4 && y != 4;
> > + int lt = ordered && x < y;
> > + int ge = ordered && x >= y;
> > + int eq = ordered && x == y;
> > + if (out[4 * i] != (lt ? 11 : 29)
> > + || out[4 * i + 1] != (ge ? 31 : 47)
> > + || out[4 * i + 2] != (lt ? 71 : 53)
> > + || out[4 * i + 3] != (ge ? (eq ? 73 : 89)
> > + : (eq ? 101 : 97)))
> > + __builtin_abort ();
> > + }
> > + for (int i = n * 4; i < N * 4; ++i)
> > + if (out[i] != -1)
> > + __builtin_abort ();
> > + }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-7.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-7.c
> > new file mode 100644
> > index 00000000000..7b0b17028f6
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-7.c
> > @@ -0,0 +1,57 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O3 -fno-ssa-phiopt" } */
> > +
> > +/* Check reversed integer comparisons and nested predicates at the type
> > + limits. Each result identifies one path through the conditions. */
> > +
> > +#define N 25
> > +static int a[N], b[N], out[N * 3];
> > +
> > +__attribute__ ((noipa))
> > +static void
> > +kernel (int *restrict out, const int *restrict a,
> > + const int *restrict b, int n)
> > +{
> > + for (int i = 0; i < n; ++i)
> > + {
> > + int x = a[i], y = b[i];
> > + out[3 * i] = x < y ? 11 : 29;
> > + out[3 * i + 1] = y <= x ? 31 : 47;
> > + int r;
> > + if (x >= y)
> > + r = y == x ? 53 : 71;
> > + else
> > + r = y > 0 ? 73 : 89;
> > + out[3 * i + 2] = r;
> > + }
> > +}
> > +
> > +int
> > +main (void)
> > +{
> > + int values[5] = { -__INT_MAX__ - 1, -1, 0, 1, __INT_MAX__ };
> > + for (int i = 0; i < N; ++i)
> > + {
> > + a[i] = values[i / 5];
> > + b[i] = values[i % 5];
> > + }
> > + for (int n = 0; n <= N; ++n)
> > + {
> > + for (int i = 0; i < N * 3; ++i)
> > + out[i] = -1;
> > + kernel (out, a, b, n);
> > + for (int i = 0; i < n; ++i)
> > + {
> > + int x = i / 5, y = i % 5;
> > + if (out[3 * i] != (x < y ? 11 : 29)
> > + || out[3 * i + 1] != (x < y ? 47 : 31)
> > + || out[3 * i + 2] != (x >= y ? (x == y ? 53 : 71)
> > + : (y > 2 ? 73 : 89)))
> > + __builtin_abort ();
> > + }
> > + for (int i = n * 3; i < N * 3; ++i)
> > + if (out[i] != -1)
> > + __builtin_abort ();
> > + }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-8.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-8.c
> > new file mode 100644
> > index 00000000000..cbf12764479
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/ifc-cmp-inverse-8.c
> > @@ -0,0 +1,62 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O3 -fno-ssa-phiopt" } */
> > +
> > +/* Check inverse predicates on conditional stores and reductions.
> > + Unsigned arithmetic permits the reductions to wrap. */
> > +
> > +#define N 25
> > +static unsigned int a[N], b[N], out[N];
> > +
> > +__attribute__ ((noipa))
> > +static unsigned int
> > +kernel (unsigned int *restrict out, const unsigned int *restrict a,
> > + const unsigned int *restrict b, int n)
> > +{
> > + unsigned int sum = 1;
> > + for (int i = 0; i < n; ++i)
> > + {
> > + unsigned int x = a[i], y = b[i];
> > + if (x < y)
> > + sum += y;
> > + if (y <= x)
> > + {
> > + out[i] = x;
> > + sum -= x;
> > + }
> > + }
> > + return sum;
> > +}
> > +
> > +int
> > +main (void)
> > +{
> > + unsigned int values[5] = { 0, 1, __INT_MAX__, __INT_MAX__ + 1U, ~0U };
> > + for (int i = 0; i < N; ++i)
> > + {
> > + a[i] = values[i / 5];
> > + b[i] = values[i % 5];
> > + }
> > + for (int n = 0; n <= N; ++n)
> > + {
> > + for (int i = 0; i < N; ++i)
> > + out[i] = 42;
> > + unsigned int sum = kernel (out, a, b, n);
> > + unsigned int expected = 1;
> > + for (int i = 0; i < n; ++i)
> > + {
> > + int x = i / 5, y = i % 5;
> > + if (x < y)
> > + expected += values[y];
> > + else
> > + expected -= values[x];
> > + if (out[i] != (x < y ? 42 : values[x]))
> > + __builtin_abort ();
> > + }
> > + if (sum != expected)
> > + __builtin_abort ();
> > + for (int i = n; i < N; ++i)
> > + if (out[i] != 42)
> > + __builtin_abort ();
> > + }
> > + return 0;
> > +}
> > diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
> > index c496ff93d11..d9ac15708b2 100644
> > --- a/gcc/tree-ssa-sccvn.cc
> > +++ b/gcc/tree-ssa-sccvn.cc
> > @@ -5709,6 +5709,79 @@ visit_nary_op (tree lhs, gassign *stmt)
> > tree rhs1 = gimple_assign_rhs1 (stmt);
> > switch (code)
> > {
> > + case COND_EXPR:
> > + {
> > + /* Valueization can return a constant even for an SSA selector. */
> > + tree cond = vn_valueize (rhs1);
> > + gassign *cmp = NULL;
> > + if (TREE_CODE (cond) == SSA_NAME)
> > + cmp = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (cond));
> > + if (!cmp
> > + || TREE_CODE_CLASS (gimple_assign_rhs_code (cmp)) !=
> > tcc_comparison
> > + /* invert_tree_comparison allows EQ/NE inversion even when
> > signaling
> > + NaNs can trap. It preserves the trap at the comparison, but
> > + reusing a later comparison must not move an earlier trap past
> > + a side effect. */
> > + || gimple_could_trap_p (cmp))
> > + break;
> > + tree_code cmp_code = gimple_assign_rhs_code (cmp);
> > + tree ops[2] = { gimple_assign_rhs1 (cmp), gimple_assign_rhs2 (cmp)
> > };
> > + tree_code inverse_code
> > + = invert_tree_comparison (cmp_code, HONOR_NANS (ops[0]));
> > + if (inverse_code == ERROR_MARK)
> > + break;
> > + tree inverse = vn_nary_op_lookup_pieces (2, inverse_code,
> > + TREE_TYPE (cond), ops, NULL);
> > + if (!inverse)
> > + break;
> > + tree select_ops[3] = { inverse, gimple_assign_rhs3 (stmt),
> > + gimple_assign_rhs2 (stmt) };
> > + /* Equivalent selects can be found in either direction. */
> > + result = vn_nary_op_lookup_pieces (3, COND_EXPR, type,
> > + select_ops, NULL);
> > + /* Create new selects in only one direction, using the lower
> > comparison
> > + code. A mere inequality would let each polarity replace the
> > other.
> > + The inverse lookup can return a constant, not just an SSA name.
> > */
>
> What does lower comparison code mean? I don't think the comparison
> codes are in any official order at this point. This is why for an
> example combine_comparisons does its own translation to something
> which does have an order and meaning. And even why
> convert_tree_comp_to_rtx (besides the unsignedness) have a switch
> statement.
> This comment here needs to be expanded more.
>
> Actually better yet, why not create a canonicalization form rather
> than what you are doing? That is for NE, always look up the EQ
> version. For LT, always look up the GE. Etc.
Actually, why don't we force the creation of COND_EXPR and their
comparison in a canonical form in the first place?
>
> > + if (!result && inverse_code < cmp_code
> > + && TREE_CODE (inverse) == SSA_NAME)
> > + {
> > + /* An SSA name can have a constant value number or VN_TOP. */
> > + tree value = SSA_VAL (inverse);
> > + vn_avail *av = (TREE_CODE (value) == SSA_NAME
> > + ? VN_INFO (value)->avail : NULL);
> > + /* Require an inverse available in this block. Checking the
> > latest
> > + availability record keeps eliminate_avail on its same-block
> > + fast path, without walking the availability chain. */
> > + tree leader = NULL_TREE;
> > + if (rpo_avail && av && av->location == gimple_bb (stmt)->index)
> > + leader = rpo_avail->eliminate_avail (gimple_bb (stmt),
> > inverse);
> > + gassign *def = NULL;
> > + if (leader && TREE_CODE (leader) == SSA_NAME)
> > + def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (leader));
> > + /* The available leader need not be the comparison returned by
> > the
> > + lookup. Require a comparison in this block and check its
> > code
> > + too: VN can swap operands and thus change the comparison
> > code.
> > + The strict ordering must also hold for this actual leader.
> > */
> > + if (def && gimple_bb (def) == gimple_bb (stmt)
> > + && TREE_CODE_CLASS (gimple_assign_rhs_code (def))
> > + == tcc_comparison
> > + && gimple_assign_rhs_code (def) < cmp_code)
> > + {
> > + gimple_match_op match_op (gimple_match_cond::UNCOND,
> > + COND_EXPR, type, leader,
> > + select_ops[1], select_ops[2]);
> > + result = vn_nary_build_or_lookup (&match_op);
> > + }
> > + }
> > + if (result)
> > + {
> > + bool changed = set_ssa_val_to (lhs, result);
> > + if (TREE_CODE (result) == SSA_NAME)
> > + vn_nary_op_insert_stmt (stmt, result);
> > + return changed;
> > + }
> > + }
> > + break;
> > CASE_CONVERT:
> > /* Match arithmetic done in a different type where we can easily
> > substitute the result from some earlier sign-changed or widened
> > @@ -7203,6 +7276,7 @@ eliminate_dom_walker::eliminate_insert (basic_block
> > bb,
> > || (!CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
> > && gimple_assign_rhs_code (stmt) != VIEW_CONVERT_EXPR
> > && gimple_assign_rhs_code (stmt) != NEGATE_EXPR
> > + && gimple_assign_rhs_code (stmt) != COND_EXPR
> > && gimple_assign_rhs_code (stmt) != BIT_FIELD_REF
> > && (gimple_assign_rhs_code (stmt) != BIT_AND_EXPR
> > || TREE_CODE (gimple_assign_rhs2 (stmt)) != INTEGER_CST)))
> > @@ -7226,6 +7300,19 @@ eliminate_dom_walker::eliminate_insert (basic_block
> > bb,
> > else if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR)
> > res = gimple_build (&stmts, BIT_AND_EXPR,
> > TREE_TYPE (val), leader, gimple_assign_rhs2 (stmt));
> > + else if (gimple_assign_rhs_code (stmt) == COND_EXPR)
> > + {
> > + tree arg1 = gimple_assign_rhs2 (stmt);
> > + tree arg2 = gimple_assign_rhs3 (stmt);
> > + if (TREE_CODE (arg1) == SSA_NAME)
> > + arg1 = eliminate_avail (bb, arg1);
> > + if (TREE_CODE (arg2) == SSA_NAME)
> > + arg2 = eliminate_avail (bb, arg2);
> > + if (!arg1 || !arg2)
> > + return NULL_TREE;
> > + res = gimple_build (&stmts, COND_EXPR, TREE_TYPE (val),
> > + leader, arg1, arg2);
> > + }
> > else
> > res = gimple_build (&stmts, gimple_assign_rhs_code (stmt),
> > TREE_TYPE (val), leader);
> > --
> > 2.50.1 (Apple Git-155)
> >