https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119314
Bug ID: 119314 Summary: Possibly wrong code generation for branch after tail function call Product: gcc Version: 14.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: root at hsnovel dot net Target Milestone: --- gcc -v output Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.1/lto-wrapper Target: x86_64-pc-linux-gnu Configured with: /build/gcc/src/gcc/configure --enable-languages=ada,c,c++,d,fortran,go,lto,m2,objc,obj-c++,rust --enable-bootstrap --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://gitlab.archlinux.org/archlinux/packaging/packages/gcc/-/issues --with-build-config=bootstrap-lto --with-linker-hash-style=gnu --with-system-zlib --enable-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-clocale=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-function --enable-gnu-unique-object --enable-libstdcxx-backtrace --enable-link-serialization=1 --enable-linker-build-id --enable-lto --enable-multilib --enable-plugin --enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch --disable-werror Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 14.2.1 20250207 (GCC) The code is a part of a much larger proprietary codebase, so it's not possible for me to share the exact code that reproduces it. I am writing this report to inform you, as I unfortunutely I cannot share the code. These are the flags: -fno-optimize-sibling-calls -fwrapv -std=c11 -fno-strict-aliasing -Wstrict-prototypes -Wall -Wextra -Wfloat-conversion -Wreturn-local-addr -Wshadow -Wformat -Wcast-align -Wmisleading-indentation -msse4.1 -O2 -DNDEBUG -fomit-frame-pointer This is the the snippet, (part of a much bigger function): STRING CachedName = tctx_lookupTable->playerNames[playerHandle.slot].string; DEBUG_LOG_INFO("2 Data %p\n", dest.Data); DEBUG_LOG_INFO("2 Size %d\n", dest.Size); if (dest.Data) { DEBUG_LOG_INFO("3 Data %p\n", dest.Data); U32 CopySize = Min(CachedName.Size, dest.Size); memcpy(dest.Data, CachedName.Data, CopySize); return CopySize; } else { return CachedName.Size; } where DEBUG_LOG_INFO is: #define log_info(...) log_log(Log_Info, __VA_ARGS__) #define DEBUG_LOG_INFO(...) log_info(__VA_ARGS__) static const char* log_level_colors[] = { [Log_Err] = "\x1b[31m", [Log_Warn] = "\x1b[33m", [Log_Info] = "\x1b[36m", [Log_Enum_Count] = "\x1b[0m" }; static const char* log_level_string[] = { [Log_Err] = "[ERROR]: ", [Log_Warn] = "[WARN] : ", [Log_Info] = "[INFO] : ", }; void log_log(enum log_level level, const char *args, ...) { va_list va; FILE *fd; if (level == Log_Err) fflush(stdout); va_start(va, args); fd = (level == Log_Err) ? stderr : stdout; fprintf(fd, "%s", log_level_colors[level]); fprintf(fd, "%s", log_level_string[level]); vfprintf(fd, args, va); if (level == Log_Err) fflush(stderr); fprintf(fd, "%s", log_level_colors[Log_Enum_Count]); va_end(va); } and STRING is: typedef struct { char *Data; unsigned int Size; } STRING; When the code snippet executes, it runs: [INFO] : 2 Data (nil) [INFO] : 2 Size 0 [INFO] : 3 Data (nil) It enters the if branch and prints 3 Data (nil), even tough it prints the pointer as (nil) in "2 Data", right before entering the branch. This is the assembly that got generated for that snippet: ; CODE XREF from sym.IGNORE_THIS_ITS_FUNCTION_NAME @ 0x8bcc(x) ; 0x28f58 lea rdi, section..got call fcn.00006260;[ob] ; int64_t arg3 mov rdx, rbp ; int64_t arg1 mov edi, 2 ; int64_t arg2 ; 0x234e6 ; "2 Data %p\n" lea rsi, str.2_Data__p_n mov rax, qword [rax] add rbx, qword [rax + 0x10] xor eax, eax mov r13, qword [rbx + 8] mov ebx, dword [rbx + 0x10] call sym.log_log;[oa] xor eax, eax ; int64_t arg3 mov edx, r14d ; int64_t arg1 mov edi, 2 ; int64_t arg2 ; 0x234f1 ; "2 Size %d\n" lea rsi, str.2_Size__d_n call sym.log_log;[oa] test rbp, rbp je 0x8a98 Both -fno-strict-aliasing -fwrapv does not make a differance, I removed them, and the only part that did gave a warning about it wasn't related to this piece, and the code gen was the same. I also don't know why, but this code was generated on arch linux yesterday, today I compiled it on a different machine, on manjaro (with the exact same gcc version) and it generated correct code. I am not sure if the code generation is non deterministic, because when we were testing this, different code got generated in the middle.(mainly the code that takes the success part of branches, were switched with the failures, altough I am not exactly sure if we changed the compiler flags meanwhile, so don't rely much on that information) The the surrounding code doesn't make use of any libraries, in fact, there is no libraries that are used in the entire project, including glibc. It's statically linked to musl, and we don't link to single library dynamically. The code did compile correctly with the same flags and same the configuration verbatim on clang. It did produce the correct output. This bug also never happened in the previous versions of gcc. It's this exact version that produces the bug. As I have said, I unfortunutely cannot share much information about the surronding code as it is proprietary, I am merely writing this to inform you about the situation.