https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84084
Bug ID: 84084 Summary: -O2 Early VPR pass wrongly removes "ret" exit basic block causing infinite loop/segfaults Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: romain.geissler at amadeus dot com Target Milestone: --- Hi, It looks like there is a regression in the Early VRP pass with -O2 that affects at the very least gcc 7 and 8, but we could find strange behaviour up to gcc 4.8. cat evrp-bug.c #include <stdio.h> //#include <iostream> int main() { int arr[2] = {1, 2}; for (int i = 0, val; (val = arr[i]), i < 2; ++i) { printf("%i\n", val); // With this line, works with gcc <= 6, fails with gcc >= 7 //std::cout << val << std::endl; //With line line, works with gcc <= 4.7, fails with gcc >= 4.8. } } First the C frontend and the C++ frontend disagrees. The C frontend correctly compiles this, and even correctly find that "val = arr[i]" is UB when "i == 2" (when -Wall is enabled) https://godbolt.org/g/SG9SyU <source>: In function 'main': <source>:8:34: warning: array subscript 2 is above array bounds of 'int[2]' [-Warray-bounds] for (int i = 0, val; (val = arr[i]), i < 2; ++i) ~~~^~~ Compiler returned: 0 Execution correctly prints: 1 2 Now with the C++ frontend, things go wrong. First the UB behaviour is not printed. The executation fails with an infinite loop and eventually a segfault https://godbolt.org/g/9xnRX7 The reason for that is that the function epilog: xor eax, eax add rsp, 8 ret is missing when compiled with the C++ frontend. In our case, since "_start" is placed by the linker just after "main", then each calls to "main" eventually continue into "_start", which calls main again, each time increasing the stack usage, and eventually segfaulting because of stack going out of bound. Please note that depending on whether the inside loop is inlinable or not (printf vs std::cout) then the regression either starts with gcc 4.8 or with gcc 6. This issue seems to be a wrong behavior from the early vrp pass. Note that compiling with "-fno-tree-vrp" correctly works around the issue: https://godbolt.org/g/EjvxQd I am providing tree dumps generated with g++ (GCC) 7.2.1 20171022, gcc trunk output might be slightly different. Pass just before evrp: cat evrp-bug.c.037t.fre1 ;; Function int main() (main, funcdef_no=12, decl_uid=2833, cgraph_uid=12, symbol_order=12) int main() () { int val; int i; int arr[2]; <bb 2> [0.00%]: arr[0] = 1; arr[1] = 2; # DEBUG i => 0 <bb 3> [0.00%]: # i_1 = PHI <0(2), i_12(5)> # DEBUG i => i_1 val_7 = arr[i_1]; # DEBUG val => val_7 if (i_1 <= 1) goto <bb 4>; [0.00%] else goto <bb 6>; [0.00%] <bb 4> [0.00%]: printf ("%i\n", val_7); <bb 5> [0.00%]: i_12 = i_1 + 1; # DEBUG i => i_12 goto <bb 3>; [0.00%] <bb 6> [0.00%]: arr ={v} {CLOBBER}; return 0; <L4> [0.00%]: arr ={v} {CLOBBER}; resx 1 } cat evrp-bug.c.038t.evrp Then the evrp pass, which wrongly removes the exit basic block 6: ;; Function int main() (main, funcdef_no=12, decl_uid=2833, cgraph_uid=12, symbol_order=12) ;; 2 loops found ;; ;; Loop 0 ;; header 0, latch 1 ;; depth 0, outer -1 ;; nodes: 0 1 2 3 4 5 6 7 ;; ;; Loop 1 ;; header 3, latch 5 ;; depth 1, outer 0 ;; nodes: 3 5 4 ;; 2 succs { 3 } ;; 3 succs { 4 6 } ;; 4 succs { 7 5 } ;; 5 succs { 3 } ;; 6 succs { 1 } ;; 7 succs { } Value ranges after Early VRP: i_1: [0, 1] val_7: VARYING i_12: [1, 2] Removing basic block 6 Merging blocks 3 and 4 int main() () { int val; int i; int arr[2]; <bb 2> [0.00%]: arr[0] = 1; arr[1] = 2; # DEBUG i => 0 <bb 3> [0.00%]: # i_1 = PHI <0(2), i_12(4)> # DEBUG i => i_1 val_7 = arr[i_1]; # DEBUG val => val_7 printf ("%i\n", val_7); <bb 4> [0.00%]: i_12 = i_1 + 1; # DEBUG i => i_12 goto <bb 3>; [0.00%] <L4> [0.00%]: arr ={v} {CLOBBER}; resx 1 } Can you please have a look at this regression ? Chees, Romain