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.

Reply via email to