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.
  • [Bug rtl-optimization/128570] ... lzy20000419 at outlook dot com via Gcc-bugs

Reply via email to