Bug 108166

Summary: [12 Regression] Wrong code with -O2 since r12-8078-ga42aa68bf1ad745a
Product: gcc Reporter: Vsevolod Livinskii <vsevolod.livinskiy>
Component: tree-optimizationAssignee: Jakub Jelinek <jakub>
Status: RESOLVED FIXED    
Severity: normal CC: babokin, jakub, jiajing_zheng, marxin, regehr, rguenth, vsevolod.livinskiy
Priority: P2 Keywords: wrong-code
Version: 13.0   
Target Milestone: 12.3   
Host: Target:
Build: Known to work: 11.0
Known to fail: 12.0, 13.0 Last reconfirmed: 2022-12-18 00:00:00
Bug Depends on:    
Bug Blocks: 103035    
Attachments: gcc13-pr108166.patch

Description Vsevolod Livinskii 2022-12-18 19:46:01 UTC
Link to the Compiler Explorer: https://godbolt.org/z/j8coqj375

Reproducer:
#include <stdio.h>
bool a, b;
int d, c;
const int &e(const int &f, const int &g) { return !f ? f : g; }
int main() {
  c = e(b, 0) > ((b ? d : b) ?: 8);
  a = b ? d : b;
  printf("%d\n", a);
  if (a != 0)
    __builtin_abort();
}

Error:
>$ g++ -O2 driver.cpp && ./a.out 
1
Aborted (core dumped)
>$ g++ -O0 driver.cpp && ./a.out 
0

gcc version 13.0.0 20221216 (2fdc8546b5c6cb1fe254e40b5bdd19ed6fbb44da)
Comment 1 Andrew Pinski 2022-12-18 21:08:00 UTC
Confirmed.
  # iftmp.2_17 = PHI <iftmp.2_24(5), 0(4)>
  if (iftmp.2_17 != 0)
    goto <bb 8>; [INV]
  else
    goto <bb 7>; [INV]

  <bb 7> :

  <bb 8> :
  # iftmp.1_16 = PHI <iftmp.2_17(6), 8(7)>
  _7 = _4 > iftmp.1_16;
  _8 = (int) _7;
  c = _8;
  D.3392 ={v} {CLOBBER(eol)};
  D.3393 ={v} {CLOBBER(eol)};
  _11 = iftmp.2_17 != 0;

And then _11 gets replaced with:
  _6 = iftmp.2_10 != 0;

I can't figure out why though.
Comment 2 Martin Liška 2022-12-19 08:34:47 UTC
Started with r12-8078-ga42aa68bf1ad745a.
Comment 3 Jakub Jelinek 2022-12-19 13:13:07 UTC
Testcase without includes:
bool a, b;
int d, c;

const int &
foo (const int &f, const int &g)
{
  return !f ? f : g;
}

__attribute__((noipa)) void
bar (int)
{
}

int
main ()
{
  c = foo (b, 0) > ((b ? d : b) ?: 8);
  a = b ? d : b;
  bar (a);
  if (a != 0)
    __builtin_abort ();
}
Comment 4 Jakub Jelinek 2022-12-21 12:52:33 UTC
The phiopt2 optimization is:
   # iftmp.3_10 = PHI <iftmp.3_14(3), 0(2)>
-  if (iftmp.3_10 != 0)
-    goto <bb 6>; [56.25%]
-  else
-    goto <bb 5>; [43.75%]
-
-  <bb 5> [local count: 536870913]:
-
-  <bb 6> [local count: 1073741824]:
-  # iftmp.2_9 = PHI <iftmp.3_10(4), 8(5)>
-  _4 = iftmp.2_9 < 0;
+  _4 = iftmp.3_10 < 0;

So, we have _4 = (iftmp.3_10 ?: 8) < 0 and the optimization optimizes that to
_4 = iftmp.3_10 < 0.  If iftmp.3_10 is non-zero, then it is the same comparison,
if iftmp.3_10 is zero, then previously we'd set _4 to 8 < 0 and newly set it to
0 < 0, both are false.
The problem is in global ranges I think, previously we had:
  # RANGE [irange] int [-INF, -1][1, +INF]
  # iftmp.2_9 = PHI <iftmp.3_10(4), 8(5)>
which is correct, it was iftmp.3_10 ?: 8 which is always non-zero.
But this rnage is moved to iftmp.3_10 after the optimization:
  # RANGE [irange] int [-INF, -1][1, +INF]
  # iftmp.3_10 = PHI <iftmp.3_14(3), 0(2)>
which is incorrect, we don't know anything about iftmp.3_10 range there because d is VARYING.
Comment 5 Richard Biener 2022-12-21 13:12:57 UTC
But we end up with

 <bb 2> [local count: 1073741824]:
  b.1_1 = b;
  if (b.1_1 != 0)
    goto <bb 3>; [50.00%]
  else
    goto <bb 4>; [50.00%]

  <bb 3> [local count: 536870913]:
  iftmp.3_14 = d;

  <bb 4> [local count: 966367640]:
  # RANGE [irange] int [-INF, -1][1, +INF]
  # iftmp.3_10 = PHI <iftmp.3_14(3), 0(2)>

  <bb 6> [local count: 1073741824]:
  # RANGE [irange] int [-INF, -1][1, +INF]
  # iftmp.2_9 = PHI <iftmp.3_10(4)>
  _4 = iftmp.2_9 < 0;

before CFG cleanup which does look wrong.  Before the phiopt we have

  <bb 4> [local count: 966367640]:
  # iftmp.3_10 = PHI <iftmp.3_14(3), 0(2)>
  if (iftmp.3_10 != 0)
    goto <bb 6>; [56.25%]
  else 
    goto <bb 5>; [43.75%]
  
  <bb 5> [local count: 536870913]:
  
  <bb 6> [local count: 1073741824]:
  # RANGE [irange] int [-INF, -1][1, +INF]
  # iftmp.2_9 = PHI <iftmp.3_10(4), 8(5)>
  _4 = iftmp.2_9 < 0;

but clearly value-replacing the edge 5->6 with iftmp.3_10 is wrong (its
zero, not 8 on that edge)?
Comment 6 Richard Biener 2022-12-21 13:14:42 UTC
replace_phi_edge_with_variable assumes we replace things with the same value, if the new transform does something different because of the _uses_ of the value then it has to make sure to not copy range info in that function (add
another argument to it?)
Comment 7 Jakub Jelinek 2022-12-21 13:35:24 UTC
Created attachment 54142 [details]
gcc13-pr108166.patch

Untested fix.  I wouldn't change replace_phi_edge_with_variable, because even
without the duplicate_ssa_name_range_info (say if new_tree already has SSA_NAME_RANGE_INFO) we'd be lying to the compiler.
The right thing would be to union the global range of the phi result with the
oarg INTEGER_CST, not sure how hard would that be.
Comment 8 GCC Commits 2022-12-22 11:53:28 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:5c17adfb5d08e34da7a7f234dfc2ed1f0aaadaa9

commit r13-4845-g5c17adfb5d08e34da7a7f234dfc2ed1f0aaadaa9
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Dec 22 12:52:48 2022 +0100

    phiopt: Drop SSA_NAME_RANGE_INFO in maybe equal case [PR108166]
    
    The following place in value_replacement is after proving that
    x == cst1 ? cst2 : x
    phi result is only used in a comparison with constant which doesn't
    care if it compares cst1 or cst2 and replaces it with x.
    The testcase is miscompiled because we have after the replacement
    incorrect range info for the phi result, we would need to
    effectively union the phi result range with cst1 (oarg in the code)
    because previously that constant might be missing in the range, but
    newly it can appear (we've just verified that the single use stmt
    of the phi result doesn't care about that value in particular).
    
    The following patch just resets the info, bootstrapped/regtested
    on x86_64-linux and i686-linux, ok for trunk?
    
    Aldy/Andrew, how would one instead union the SSA_NAME_RANGE_INFO
    with some INTEGER_CST and store it back into SSA_NAME_RANGE_INFO
    (including adjusting non-zero bits and the like)?
    
    2022-12-22  Jakub Jelinek  <jakub@redhat.com>
    
            PR tree-optimization/108166
            * tree-ssa-phiopt.cc (value_replacement): For the maybe_equal_p
            case turned into equal_p reset SSA_NAME_RANGE_INFO of phi result.
    
            * g++.dg/torture/pr108166.C: New test.
Comment 9 Jakub Jelinek 2022-12-22 11:55:00 UTC
Fixed on the trunk so far.
Comment 10 GCC Commits 2023-02-10 17:45:03 UTC
The releases/gcc-12 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:86d252ab555d487aefb616562e770ffa46e05b01

commit r12-9133-g86d252ab555d487aefb616562e770ffa46e05b01
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Dec 22 12:52:48 2022 +0100

    phiopt: Drop SSA_NAME_RANGE_INFO in maybe equal case [PR108166]
    
    The following place in value_replacement is after proving that
    x == cst1 ? cst2 : x
    phi result is only used in a comparison with constant which doesn't
    care if it compares cst1 or cst2 and replaces it with x.
    The testcase is miscompiled because we have after the replacement
    incorrect range info for the phi result, we would need to
    effectively union the phi result range with cst1 (oarg in the code)
    because previously that constant might be missing in the range, but
    newly it can appear (we've just verified that the single use stmt
    of the phi result doesn't care about that value in particular).
    
    The following patch just resets the info, bootstrapped/regtested
    on x86_64-linux and i686-linux, ok for trunk?
    
    Aldy/Andrew, how would one instead union the SSA_NAME_RANGE_INFO
    with some INTEGER_CST and store it back into SSA_NAME_RANGE_INFO
    (including adjusting non-zero bits and the like)?
    
    2022-12-22  Jakub Jelinek  <jakub@redhat.com>
    
            PR tree-optimization/108166
            * tree-ssa-phiopt.cc (value_replacement): For the maybe_equal_p
            case turned into equal_p reset SSA_NAME_RANGE_INFO of phi result.
    
            * g++.dg/torture/pr108166.C: New test.
    
    (cherry picked from commit 5c17adfb5d08e34da7a7f234dfc2ed1f0aaadaa9)
Comment 11 Jakub Jelinek 2023-02-10 17:59:03 UTC
Fixed for gcc 12.3 too.
Comment 12 Andrew Pinski 2024-02-01 22:16:55 UTC
*** Bug 113709 has been marked as a duplicate of this bug. ***