Re: [PATCH] phiopt: Fix up spaceship_replacement [PR126564]

Richard Biener <[email protected]> Tue, 4 Aug 2026 10:14:17 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>

> Am 04.08.2026 um 09:53 schrieb Jakub Jelinek <[email protected]>:
>=20
> =EF=BB=BFHi!
>=20
> spaceship_replacement is for optimization of code like
>    <bb 2> :  // cond3_bb
>    if (a_3(D) =3D=3D b_5(D))
>      goto <bb 6>; [50.00%]
>    else
>      goto <bb 3>; [50.00%]
>=20
>    <bb 3> [local count: 536870913]:  // cond2_bb
>    if (a_3(D) < b_5(D))
>      goto <bb 6>; [50.00%]
>    else
>      goto <bb 4>; [50.00%]
>=20
>    <bb 4> [local count: 268435456]:  // cond_bb
>    if (a_3(D) > b_5(D))
>      goto <bb 6>; [50.00%]
>    else
>      goto <bb 5>; [50.00%]
>=20
>    <bb 5> [local count: 134217728]:  // middle_bb
>=20
>    <bb 6> [local count: 1073741824]:  // phi_bb
>    # SR.27_4 =3D PHI <0(2), -1(3), 1(4), -128(5)>
>    _2 =3D SR.27_4 > 0;
> to a single comparison (i.e. say (a <=3D> b) > 0 in C++) (it handles
> also just 2 comparisons instead of 3, but this bug is about the 3
> comparisons).
> In
>      if (e1->flags & EDGE_TRUE_VALUE)
>        {
>          if (tree_to_shwi (arg0) !=3D -128
>              || absu_hwi (tree_to_shwi (arg1)) !=3D 1
>              || wi::to_widest (arg1) =3D=3D wi::to_widest (arg2))
>            return false;
>        }
>      else if (tree_to_shwi (arg1) !=3D -128
>               || absu_hwi (tree_to_shwi (arg0)) !=3D 1
>               || wi::to_widest (arg0) =3D=3D wi::to_widest (arg2))
>        return false;
> (where e1 is 4->6 edge above, arg0 is -128(5), arg1 is 1(4),
> arg2 is -1(3), cond2_phi_edge is 3->6 edge above) we deal with
> the different cases of whether the TRUE edge goes directly to
> phi_bb or through the empty middle_bb in between.
> Right above the above checks is
>      if ((cond2_phi_edge->flags & EDGE_FALSE_VALUE)
>          && HONOR_NANS (TREE_TYPE (lhs1)))
>        return false;
> so for HONOR_NANS, cond2_phi_edge must be TRUE edge, otherwise
> it can be either.  The problematic check that causes the miscompilation
> of the testcase below wants to verify that the two comparisons
> (cmp1 being code of a_3(D) > b_5(D) and cmp2 a_3(D) < b_5(D)))
> are actually different, not just non-removed useless duplications
> (which is what causes miscompilation of the testcase below).
> The lhs2 =3D=3D lhs1 xored case is whether the 2 comparisons are
> x cmp1 y vs. x cmp2 y or x cmp1 y vs. y cmp2 x (earlier code verifies
> the operands aren't different in other way with the exception of
> integral comparisons and < 4 vs. <=3D 3 etc.).
> For the HONOR_NANS case where we know cond2_phi_edge is TRUE
> the other xor operand is whether both cmp2 and cmp1 are </<=3D or
>> />=3D (note, we can treat LT_EXPR and LE_EXPR the same because
> the optimization requires an equality comparison first, so
> LT_EXPR vs. LE_EXPR doesn't matter).  But for !HONOR_NANS I wrote
> a condition checking both the comparison codes and corresponding
> edge flags.  That is wrong because whether e1 is TRUE or FALSE
> edge has been accounted already in the if (e1->flags & EDGE_TRUE_VALUE)
> code above, all we care about is whether cond2_phi_edge is EDGE_TRUE_VALUE=

> or EDGE_FALSE_VALUE or the comparison codes of the two comparisons
> (and order of their arguments).
> So, instead this xors lhs2 =3D=3D lhs1 with whether cmp{1,2} are the same
> with whether cond2_phi_edge is EDGE_FALSE_VALUE.
> For HONOR_NANS there is no difference because the last term will be false.=

> The pr94589*.c tests already cover quite a lot of different cases that
> should or shouldn't be matched.
>=20
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Ok

Richard=20

> For 15 and older the testcase will need to be tweaked slightly (see the PR=
),
> so that it tests miscompilation in those releases.
>=20
> 2026-08-04  Jakub Jelinek  <[email protected]>
>=20
>    PR tree-optimization/126564
>    * tree-ssa-phiopt.cc (spaceship_replacement): Fix up condition
>    when to punt because of redundant cmp1 with cmp2, xor in
>    lhs1 =3D=3D lhs2 with difference of cmp2 from cmp1 (ignoring
>    LT_EXPR vs. LE_EXPR and GT_EXPR vs. GE_EXPR differences) and
>    1 if cond2_phi_edge is EDGE_FALSE_VALUE.
>=20
>    * gcc.dg/torture/pr126564.c: New test.
>=20
> --- gcc/tree-ssa-phiopt.cc.jj    2026-08-03 11:25:36.517969862 +0200
> +++ gcc/tree-ssa-phiopt.cc    2026-08-03 15:58:02.050898704 +0200
> @@ -2429,15 +2429,9 @@ spaceship_replacement (basic_block cond_
>     must be different for non-swapped operands and same for swapped
>     operands.  */
>       if ((lhs2 =3D=3D lhs1)
> -      ^ (HONOR_NANS (TREE_TYPE (lhs1))
> -         ? ((cmp2 =3D=3D LT_EXPR || cmp2 =3D=3D LE_EXPR)
> -        !=3D (cmp1 =3D=3D LT_EXPR || cmp1 =3D=3D LE_EXPR))
> -         : (((cond2_phi_edge->flags
> -          & ((cmp2 =3D=3D LT_EXPR || cmp2 =3D=3D LE_EXPR)
> -             ? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) !=3D 0)
> -        !=3D ((e1->flags
> -             & ((cmp1 =3D=3D LT_EXPR || cmp1 =3D=3D LE_EXPR)
> -             ? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) !=3D 0))))
> +      ^ ((cmp2 =3D=3D LT_EXPR || cmp2 =3D=3D LE_EXPR)
> +         !=3D (cmp1 =3D=3D LT_EXPR || cmp1 =3D=3D LE_EXPR))
> +      ^ ((cond2_phi_edge->flags & EDGE_FALSE_VALUE) !=3D 0))
>    return false;
>       if (!single_pred_p (cond2_bb) || !cond_only_block_p (cond2_bb))
>    return false;
> --- gcc/testsuite/gcc.dg/torture/pr126564.c.jj    2026-08-03 16:14:35.1674=
89718 +0200
> +++ gcc/testsuite/gcc.dg/torture/pr126564.c    2026-08-03 16:15:02.8771436=
94 +0200
> @@ -0,0 +1,25 @@
> +/* PR tree-optimization/126564 */
> +/* { dg-do run } */
> +
> +[[gnu::noipa]] int
> +foo (int x, int y)
> +{
> +  int c =3D -128;
> +  if (x =3D=3D y)
> +    c =3D 0;
> +  else if (x < y)
> +    c =3D -1;
> +  else if (x <=3D y)
> +    c =3D 1;
> +  return c > 0;
> +}
> +
> +int
> +main ()
> +{
> +  int i, j;
> +  for (i =3D -3; i <=3D 3; i++)
> +    for (j =3D -3; j <=3D 3; j++)
> +      if (foo (i, j) !=3D 0)
> +    __builtin_abort ();
> +}
>=20
>    Jakub
>=20