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)
> >

Reply via email to