Created attachment 52926 [details] compressed preprocessed source file that produces the bug Operating system and compiler versions: Ubuntu 20.04: tested with g++ version 7.5.0, 8.4.0, 9.4.0, 10.3.0, 11.1.0, and 13.0 (compiled from git repository this week-end with default options). Also in 32-bit cygwin with 11.2.0. All produce the same problem. Step to reproduce the problem: The program works well when compiling without -flto: $ g++ -Wall -Wextra -Wno-unused-parameter -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations -O3 -fsanitize=unreachable -o repro repro.ii $ ./repro -> Yeah, no bug! adding -flto makes it crash: $ g++ -Wall -Wextra -Wno-unused-parameter -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations -O3 -flto -fsanitize=unreachable -o repro repro.ii $ ./repro -> repro.cpp:1345:24: runtime error: execution reached an unreachable program point I added some trace to the code, that shows a little more information. I tried to reduce the size of the code as much pas possible, but it was very difficult because many changes can make the problem appear or disappear. In particular, not catching exceptions in main makes the bug disappear. I searched the web for this error message, and found this interesting link: https://deathandthepenguinblog.wordpress.com/2021/02/13/undefined-behaviour-and-nasal-demons-or-do-not-meddle-in-the-affairs-of-optimizers/ What is described there feels similar to what is happening to me. So I searched hard for undefined behaviour in my code. I humbly admit that this crash may very well be a bug in my code instead of a bug in g++, but after several days of struggle, I still really can make no sense of what is happening. If g++ detected undefined behavior in my code and is using this to remove some part of the code or perform optimizations, then I feel it should tell me. I want to know, because undefined behavior is much more likely to be a bug that needs fixing rather than a good optimization opportunity.
I can reproduce what you observe with -O3 -fwhole-program -fsanitize=unreachable (without the sanitize it gets into an infinite loop). -fno-ipa-cp-clone avoids the issue (whatever it is exactly). Can you attach un-preprocessed source as well? You seem to be not using any non-standard header files.
(In reply to Richard Biener from comment #1) > I can reproduce what you observe with -O3 -fwhole-program > -fsanitize=unreachable > (without the sanitize it gets into an infinite loop). -fno-ipa-cp-clone > avoids > the issue (whatever it is exactly). > > Can you attach un-preprocessed source as well? You seem to be not using any > non-standard header files. Thanks for checking my code. The code is available on github: https://github.com/Remi-Coulom/joedb Necessary files to replicate the bug can be found there: https://github.com/Remi-Coulom/joedb/tree/master/doc/source/tutorial/gcc_bug repro.cpp and all_includes.h should be enough. I produced repro.cpp by preprocessing bug.cpp and pruning a lot of code. I suppose repro.cpp should be enough for your purpose. If you ever wish to compile bug.cpp you'll have to compile joedb first by running "./generate.sh" in doc/source/tutorial.
Reducing that right now..
-fno-devirtualize also makes it go away. Without that, the __builtin_unreachable() call in question is created in: #0 0x00007ffff7ad4424 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6 #1 0x0000000000fee86e in ggc_internal_alloc (size=144, f=0x0, s=0, n=1) at ../../gcc/ggc-page.cc:1400 #2 0x00000000012bf869 in ggc_internal_cleared_alloc (size=144, f=0x0, s=0, n=1) at ../../gcc/ggc-common.cc:114 #3 0x0000000000c9f446 in ggc_internal_cleared_alloc (s=144) at ../../gcc/ggc.h:147 #4 0x00000000012d69e5 in ggc_alloc_cleared_gimple_statement_stat (s=144) at ../../gcc/ggc.h:329 #5 0x00000000012cc892 in gimple_alloc (code=GIMPLE_CALL, num_ops=5) at ../../gcc/gimple.cc:156 #6 0x00000000012d00ae in gimple_copy (stmt=<gimple_call 0x7fffe888af30>) at ../../gcc/gimple.cc:1913 #7 0x000000000183e098 in remap_gimple_stmt (stmt=<gimple_call 0x7fffe888af30>, id=0x7fffffffd1e0) at ../../gcc/tree-inline.cc:1862 #8 0x000000000183e996 in copy_bb (id=0x7fffffffd1e0, bb=<basic_block 0x7fffe74a6548 (5)>, num=..., den=...) at ../../gcc/tree-inline.cc:2051 #9 0x0000000001841ee7 in copy_cfg_body (id=0x7fffffffd1e0, entry_block_map=<basic_block 0x7fffe6c40af8 (21)>, exit_block_map=<basic_block 0x7fffe71bd548 (212)>, new_entry=<basic_block 0x0>) at ../../gcc/tree-inline.cc:3085 #10 0x0000000001842b2a in copy_body (id=0x7fffffffd1e0, entry_block_map=<basic_block 0x7fffe6c40af8 (21)>, exit_block_map=<basic_block 0x7fffe71bd548 (212)>, new_entry=<basic_block 0x0>) at ../../gcc/tree-inline.cc:3338 #11 0x0000000001847993 in expand_call_inline (bb=<basic_block 0x7fffe6c40af8 (21)>, stmt=<gimple_call 0x7fffe83ea870>, id=0x7fffffffd1e0, to_purge=0x7fffffffd1c0) at ../../gcc/tree-inline.cc:5123 when copying the OBJ_TYPE_REF(_9;(struct Writable)writable_7(D)->10B) (writable_7(D), 0); call (line 1345 in repro.cpp) and later on this one is replaced by __builtin_unreachable: #0 gimple_call_set_fndecl (gs=0x7fffe8621090, decl=<function_decl 0x7fffea34ec00 __builtin_unreachable>) at ../../gcc/gimple.h:3198 #1 0x00000000010b7521 in cgraph_edge::redirect_call_stmt_to_callee ( e=<cgraph_edge* 0x7fffe74a6750 (<cgraph_node * 0x7fffe7ae6000 "local_concurrency"/1538> -> <cgraph_node * 0x7fffe737faa0 "__builtin_unreachable"/2408>)>) at ../../gcc/cgraph.cc:1555 #2 0x0000000001841af2 in redirect_all_calls (id=0x7fffffffd1e0, bb=<basic_block 0x7fffe74a68f0 (220)>) at ../../gcc/tree-inline.cc:3002 #3 0x00000000018423c7 in copy_cfg_body (id=0x7fffffffd1e0, entry_block_map=<basic_block 0x7fffe6c40af8 (21)>, exit_block_map=<basic_block 0x7fffe71bd548 (212)>, new_entry=<basic_block 0x0>) at ../../gcc/tree-inline.cc:3173 #4 0x0000000001842b2a in copy_body (id=0x7fffffffd1e0, entry_block_map=<basic_block 0x7fffe6c40af8 (21)>, exit_block_map=<basic_block 0x7fffe71bd548 (212)>, new_entry=<basic_block 0x0>) at ../../gcc/tree-inline.cc:3338 #5 0x0000000001847993 in expand_call_inline (bb=<basic_block 0x7fffe6c40af8 (21)>, stmt=<gimple_call 0x7fffe83ea870>, id=0x7fffffffd1e0, to_purge=0x7fffffffd1c0) at ../../gcc/tree-inline.cc:5123 #6 0x000000000184874c in gimple_expand_calls_inline (bb=<basic_block 0x7fffe6c40af8 (21)>, id=0x7fffffffd1e0, to_purge=0x7fffffffd1c0) at ../../gcc/tree-inline.cc:5318 #7 0x0000000001848f1c in optimize_inline_calls (fn=<function_decl 0x7fffe7adca00 local_concurrency>) at ../../gcc/tree-inline.cc:5490 #8 0x000000000141b1ee in inline_transform (node=<cgraph_node * 0x7fffe7ae6000 "local_concurrency"/1538>) at ../../gcc/ipa-inline-transform.cc:790
Reduced test-case: namespace std { template <class> struct char_traits; template <typename _CharT, typename = char_traits<_CharT>> class basic_ostream; struct ios_base { struct Init { Init(); }; }; template <typename, typename> struct basic_ostream { void operator<<(const void *); }; extern basic_ostream<char> cerr; ios_base::Init __ioinit; } // namespace std enum Commit_Level { no_commit }; struct Writable { virtual void drop_fieldtimestampvalid_data() {} virtual void checkpoint(Commit_Level) {} }; struct Posix_File { Posix_File(); ~Posix_File(); }; Posix_File::Posix_File() {} Posix_File::~Posix_File() {} struct Readonly_Journal { Readonly_Journal(); void play_until_checkpoint(Writable &writable) { writable.checkpoint(no_commit); std::cerr << ""; } }; Readonly_Journal::Readonly_Journal() {} struct Writable_Journal : Readonly_Journal { Writable_Journal(); ~Writable_Journal(); }; Writable_Journal::Writable_Journal() {} Writable_Journal::~Writable_Journal() {} struct Connection { virtual void push(Readonly_Journal &, long, bool) = 0; }; template <typename Client_Data> struct Trans_NS_joedb_Client { Connection &connection; Client_Data data; Trans_NS_joedb_Client(Connection &, Posix_File file) : connection(connection), data(connection, file) { std::cerr << &data; data.update(); } }; struct Local_Connection : Connection { Posix_File file; void push(Readonly_Journal &, long, bool) {} }; struct Generic_File_Database : Writable { Writable_Journal journal; Generic_File_Database(); void checkpoint(Commit_Level) {} }; struct Client_Data : Generic_File_Database { Client_Data(Connection &, Posix_File) {} void update() { journal.play_until_checkpoint(*this); } }; struct Client : Trans_NS_joedb_Client<Client_Data> { Posix_File Client_local_file; Client() : Trans_NS_joedb_Client(connection, Client_local_file) {} }; Generic_File_Database::Generic_File_Database() {} struct Buggy_Client : Local_Connection, Client {}; int main() { Buggy_Client(); }
g++ repro.ii -fsanitize=undefined -O3 -fwhole-program && ./a.out 0x7fffffffdb38repro.ii:29:24: runtime error: execution reached an unreachable program point #0 0x401300 in main (/home/marxin/Programming/testcases/a.out+0x401300) #1 0x7ffff751f62f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #2 0x7ffff751f6ef in __libc_start_main_impl ../csu/libc-start.c:392 #3 0x401384 in _start (/home/marxin/Programming/testcases/a.out+0x401384)
Backtrace with debuginfo: $ g++ repro.ii -fsanitize=undefined -O3 -fwhole-program -g && ./a.out 0x7fffffffdb38repro.ii:29:24: runtime error: execution reached an unreachable program point #0 0x401300 in Readonly_Journal::play_until_checkpoint(Writable&) /home/marxin/Programming/testcases/repro.ii:29 #1 0x401300 in Client_Data::update() /home/marxin/Programming/testcases/repro.ii:63 #2 0x401300 in Trans_NS_joedb_Client<Client_Data>::Trans_NS_joedb_Client(Connection&, Posix_File) /home/marxin/Programming/testcases/repro.ii:49 #3 0x401300 in Client::Client() /home/marxin/Programming/testcases/repro.ii:67 #4 0x401300 in Buggy_Client::Buggy_Client() /home/marxin/Programming/testcases/repro.ii:70 #5 0x401300 in main /home/marxin/Programming/testcases/repro.ii:72 #6 0x7ffff751f62f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #7 0x7ffff751f6ef in __libc_start_main_impl ../csu/libc-start.c:392 #8 0x401384 in _start (/home/marxin/Programming/testcases/a.out+0x401384)
The original testcase has void lock() override = 0; void unlock() override = 0; void lock() final override void unlock() final override and a few more override. The reduced one has nothing like that (but maybe it's misreduced). Removing 'final' doesn't avoid the issue though. It might be this is a bad interaction of speculative devirt and inlining, -fno-devirtualize-speculatively is enough to prevent the bug.
It's the 1st devirtualization that happens: Polymorphic call context combine: Outer type:struct Buggy_Client offset 192 With context: Outer type (dynamic):struct Trans_NS_joedb_Client (maybe in construction) offset 64 Speculative outer type:struct Client_Data at offset 0 Second type is base of first First context does not permit base -> invalid No devirtualization target in play_until_checkpoint.constprop/106 Introduced new external node (void __builtin_unreachable()/107). ***dbgcnt: lower limit 1 reached for devirt.*** ***dbgcnt: upper limit 1 reached for devirt.***
Maybe interacts with IPA CP -fno-ipa-cp helps here.
Likely started with r5-7027-g0b986c6ac777aa4e, it is looping before the revision.
On the original testcase I see First context does not permit base -> invalid No devirtualization target in void joedb::Readonly_Journal::play_until_checkpoint.constprop(joedb::Writable&)/2704 It is unclear why play_until_checkpoint would be cloned, it doesn't have any args that would be IPA-CP propagated. The checkpoint virtual call has, but until it is devirtualized we can't optimize it.
(In reply to Martin Liška from comment #11) > Likely started with r5-7027-g0b986c6ac777aa4e, it is looping before the > revision. No, that revision just caused -fsanitize=unreachable to catch it (see PR 61591).
Thanks for investigating the bug. Can you recommend a workaround at this point? I don't want to tell users of the library to change their compilation options, so I tried this in class Client_Data: #ifdef __GNUC__ // Workaround for gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105469 __attribute__ ((noinline)) #endif void update() { ... } It seems to be working OK for the moment, but all kinds of random changes to the code seemed to make the bug appear or disappear in the past, so I am not completely satisfied with this situation. Can you explain the bug better? Do you have any recommendation? Thanks.
Re-confirmed on todays trunk. IPA folks - can you please have a look here?
Can't reproduce the __builtin_unreachable on the trunk. Bisection shows that cc1plus -quiet -fsanitize=undefined -O3 -fwhole-program -fdump-tree-optimized pr105469.C; grep unreachable pr105469.C.* stopped printing anything with r13-4686-g095a13eda2caf6842096a3ab78b2081c50fe8799 Martin, is that a real fix for this or it just went latent?
(In reply to Jakub Jelinek from comment #16) > Can't reproduce the __builtin_unreachable on the trunk. With the https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105469#c5 testcase, that is. Haven't tried the original.
It should just make any bug to go latent. It surprises me it makes any difference given that things not cloned by ipa-cp should be all handled by ipa-sra.
(In reply to Jakub Jelinek from comment #16) > Martin, is that a real fix for this or it just went latent? Not a fix, the bug mst be latent. But it is surprising so I'll have a look what happened too.
GCC 10 branch is being closed.
GCC 11 branch is being closed.
GCC 12 branch is being closed.