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