https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128570
Bug ID: 128570
Summary: wrong code at -O1: loop-invariant motion hoists a
non-volatile asm with a "memory" clobber out of a loop
that stores to memory
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: inline-asm, wrong-code
Severity: normal
Priority: P3
Component: rtl-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: lzy20000419 at outlook dot com
Target Milestone: ---
The GCC manual says a "memory" clobber means the asm may read or write any
memory not listed in the operands, so it cannot be moved across the store
`g++`. The asm is not volatile, so loop-invariant motion may move it in
principle, but here it is moved because the pass thinks it does not depend on
memory.
int g;
__attribute__((noipa)) int f (int n)
{
int s = 0;
for (int i = 0; i < n; i++)
{
int v;
__asm__ ("movl g(%%rip), %0" : "=r" (v) : : "memory");
s += v;
g++;
}
return s;
}
int main (void)
{
if (f (3) != 3) /* 0 + 1 + 2 */
__builtin_abort ();
return 0;
}
$ gcc -O0 t.c && ./a.out # ok
$ gcc -O1 t.c && ./a.out # Aborted (f returns 0)
$ gcc -O2 -fno-move-loop-invariants t.c && ./a.out # ok
$ clang -O2 t.c && ./a.out # ok
Fails at -O1, -O2 and -O3 with trunk (gcc 17.0.0 20261108, bcf14085d827) and
with 6.3, 7.5, 13.1, 14.2, 15.1, 15.3 and 16.2 at -O1. -Os is ok for this
for-loop form, but a do-while version fails at -Os too. Godbolt:
https://godbolt.org/z/eYvqW7YP1
At -O1/-O2 the asm is in the preheader (loop2_invariant dump: "Set in insn ...
is invariant" / "Decided to move invariant"). find_invariant_insn
(loop-invariant.cc) uses single_set on the asm PARALLEL and only checks
check_maybe_invariant on SET_SRC; check_maybe_invariant accepts ASM_OPERANDS
unless MEM_VOLATILE_P and walks only its inputs. check_dependencies walks DF
uses. The (clobber (mem:BLK (scratch))) that encodes the memory clobber is
never looked at, so stores in the loop are not seen. An ordinary MEM read is
rejected unless MEM_READONLY_P, and an asm with an "m" input is rejected
because its inputs are walked; only the implicit read through the memory
clobber slips through.
Possible fix (not tested): treat an asm with a memory clobber like a MEM read
(not invariant unless the loop has no stores, or simply not invariant).
Related: PR111901 (cselib, same representation issue, different pass and no
intervening store), PR121253 (fwprop dropped asm clobbers, fixed), PR102150
(conditionally executed asms; does not cover the memory clobber).
gcc -O0 -fsanitize=undefined,address runs clean.
Disclaimer: This test case was generated by a fuzzer, and the report was
written with LLM assistance.