I compiled the following code with gcc at -O1/2/3/s, and it produces the wrong code. The correct output result should be -1560359471, but -486617677 was output under -O1/2/3/s. I found this bug was introduced starting from gcc-13.1 Compiler explorer: https://godbolt.org/z/s7K9z5166 ```c $ cat test.c int printf(const char *, ...); int a; void b() { long c = 3; unsigned int d = 50253292; int e = 2147483648; for (; a < 5; a++) do { e += 4; d -= c; } while (e < 20); printf("%d\n", d); } int main() { b(); } $ $ gcc-tk test.c -O0; ./test.c -1560359471 $ gcc-tk test.c -O1; ./test.c -486617677 $ gcc-tk test.c -O2; ./test.c -486617677 $ gcc-tk test.c -O3; ./test.c -486617677 $ gcc-tk test.c -Os; ./test.c -486617677 $ ccomp test.c -O1; ./a.out -1560359471 $ $ gcc-tk --version gcc (GCC) 14.0.0 20231028 (experimental) [master r14-4987-g7f974c5fd4] Copyright (C) 2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ $ ccomp --version The CompCert C verified compiler, version 3.12 ```
Confirmed, -ftree-scev-cprop seems like is introducing the "undefined" signed integer overflow code ...
For -O2, the bug has been present since at least 9.1.0. -O1 started to expose it in GCC 12 though. -fwrapv works around the issue. -fno-tree-scev-cprop does too.
Just mention that -fsanitize=undefined is clean with GCC and clang too.
final value replacement introduces <bb 5> [local count: 118111600]: - # e_14 = PHI <e_10(4)> - # d_23 = PHI <d_11(4)> + _7 = e_20 + 4; + _12 = e_20 <= 19; + _25 = (unsigned int) e_20; + _26 = 19 - _25; + _27 = _26 / 4; + _28 = (int) _27; + _29 = _28 * 4; + _30 = _12 ? _29 : 0; + e_14 = _7 + _30; + _31 = e_20 <= 19; + _32 = (unsigned int) e_20; + _33 = 19 - _32; + _34 = _33 / 4; + _35 = _34 * 4294967293; + _36 = _31 ? _35 : 0; + _37 = d_19 + _36; + d_23 = _37 + 4294967293; which is e_20 + 4 + (e_20 <= 19 ? 4 * (int)((19 - (unsigned int) e_20) / 4): 0) and ((e_20 <= 19 ? ((19 - (unsigned int) e_20) / 4) * 4294967293 : 0) + d_19) + 4294967293 I think there's another bug noting that final value replacement with COND_EXPRs should eventually materialize control-flow. I have a patch to do more rewriting to unsigned instead.
The master branch has been updated by Richard Biener <rguenth@gcc.gnu.org>: https://gcc.gnu.org/g:e3da1d7bb288c8c864f0284bc4bc5877b466a2f7 commit r14-5032-ge3da1d7bb288c8c864f0284bc4bc5877b466a2f7 Author: Richard Biener <rguenther@suse.de> Date: Tue Oct 31 10:13:13 2023 +0100 tree-optimization/112305 - SCEV cprop and conditional undefined overflow The following adjusts final value replacement to also rewrite the replacement to defined overflow behavior if there's conditionally evaluated stmts (with possibly undefined overflow), not only when we "folded casts". The patch hooks into expression_expensive for this. PR tree-optimization/112305 * tree-scalar-evolution.h (expression_expensive): Adjust. * tree-scalar-evolution.cc (expression_expensive): Record when we see a COND_EXPR. (final_value_replacement_loop): When the replacement contains a COND_EXPR, rewrite it to defined overflow. * tree-ssa-loop-ivopts.cc (may_eliminate_iv): Adjust. * gcc.dg/torture/pr112305.c: New testcase.
GCC 13.3 is being released, retargeting bugs to GCC 13.4.