https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122610

            Bug ID: 122610
           Summary: Load after store incorrectly deleted with -O2
                    -fno-strict-aliasing while ipa-modref active
           Product: gcc
           Version: 15.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gcc at alanwu dot email
  Target Milestone: ---

https://godbolt.org/z/Ycvze77an

x86-64 GCC 15.2.1 miscompiles str_buf_cat4() from the following when using
`-std=gnu23 -O2 -fno-strict-aliasing -march=amdfam10`. This code is reduced
from `ruby-3.3.10.tar.gz`.

    // string.c
    char *ptr_global;
    void *ruby_xmalloc2(int);
    enum {
        RUBY_FL_USHIFT = 12,
        RUBY_FL_USER1 = 1 << (RUBY_FL_USHIFT+1),
        RSTRING_NOEMBED = RUBY_FL_USER1,
    };
    struct RBasic {
        long flags;
        long klass;
    };
    struct RString {
        struct RBasic basic;
        long len;
        union {
            struct {
                char *ptr;
                union {
                    long capa;
                    long shared;
                } aux;
            } heap;
            struct {
                char ary[];
            } embed;
        } as;
    };
    long RB_FL_ANY_RAW(const long obj, long flags) {
        return ((struct RString *)obj)->basic.flags & flags;
        //or: return ((struct RBasic *)obj)->flags & flags;
    }
    struct RString inline rbimpl_rstring_getmem(const long str) {
        if (RB_FL_ANY_RAW(str, RSTRING_NOEMBED))
            return *(struct RString *)str;
        struct RString retval;
        retval.as.heap.ptr = ((struct RString *)str)->as.embed.ary;
        return retval;
    }
    char *RSTRING_PTR(const long str) { return
rbimpl_rstring_getmem(str).as.heap.ptr; }
    void str_buf_cat4(const long str, char *ptr, long len) {
        struct RString rstring = *(struct RString *)str;
        ptr_global = RSTRING_PTR(str);
        ((struct RString *)str)->basic.flags |= RSTRING_NOEMBED;
        ((struct RString *)str)->len = rstring.len + len;
        ((struct RString *)str)->as.heap.ptr = ruby_xmalloc2(len);
        ptr_global = RSTRING_PTR(str);
        __builtin_memcpy(ptr_global, ptr, len);
    }
    void rb_str_buf_append(long str, long str2) {
        str_buf_cat4(str, RSTRING_PTR(str2), ((struct RString *)str2)->len);
    }

GCC deletes the second call to RSTRING_PTR() in str_buf_cat4(). Add
-fno-ipa-modref and it puts the call back. Wrong code:

    str_buf_cat4:
        push    r12
        push    rbp
        mov     r12, rdx
        push    rbx
        mov     rbx, rdi
        sub     rsp, 64
        mov     rdx, QWORD PTR [rdi+16]
        mov     QWORD PTR [rsp+8], rsi
        call    RSTRING_PTR
        mov     QWORD PTR ptr_global[rip], rax
        or      QWORD PTR [rbx], 8192
        mov     rbp, rax
        add     rdx, r12
        mov     edi, r12d
        mov     QWORD PTR [rbx+16], rdx
        call    ruby_xmalloc2
        mov     QWORD PTR ptr_global[rip], rbp
        mov     QWORD PTR [rbx+24], rax
        mov     rsi, QWORD PTR [rsp+8]
        add     rsp, 64
        pop     rbx
        mov     rdx, r12
        mov     rdi, rbp
        pop     rbp
        pop     r12
        jmp     memcpy

Commentary and speculation:
 * Code looks fine to me with respect to C strict aliasing rules. And the
   output x86 is the same regardless of -fno-strict-aliasing.
 * ipa-modref and -march=amdfam10 are necessary ingredients here, but
   downstream reports indicate that this can happen for non-x86 targets and
   with other optimization options.
 * The flexible array member in RString is a GNU extension. Ruby's original
   code uses instead `char ary[1];`.

Reduced with C-Vise using hints from downstream reports. Many thanks.

https://bugs.gentoo.org/965095
https://bugs.ruby-lang.org/issues/21655

I ran reduction on gcc version 15.2.1 20250813, using Arch's package version
15.2.1+r22+gc4e96a094636-1

Reply via email to