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

            Bug ID: 128016
           Summary: Extra pointer increment and copy when updating a
                    reference in a loop
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: adamant.pwn at gmail dot com
  Target Milestone: ---

Created attachment 65896
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65896&action=edit
Decimal parser benchmark: source files and compiler configuration

GCC generates extra pointer arithmetic for sum_direct compared with sum_local.
Both sum a zero-terminated array and leave p at its terminator.

unsigned sum_direct(const unsigned*& p) {
    unsigned sum = 0;
    while (*p) sum += *p++;
    return sum;
}
unsigned sum_local(const unsigned*& p) {
    auto q = p;
    unsigned sum = 0;
    while (*q) sum += *q++;
    p = q;
    return sum;
}

Reproduces on x86-64 with GCC trunk 17.0.0 20261019 (Compiler Explorer,
revision 1f9dc1fbeb06):

g++ -std=c++23 -O2 -S -masm=intel repro.cpp

After loading and testing the first element, sum_direct generates:

        add     rax, 4
        xor     ecx, ecx
.L3:
        mov     rsi, rax
        add     rax, 4
        add     ecx, edx
        mov     edx, DWORD PTR [rax-4]
        test    edx, edx
        jne     .L3
        mov     QWORD PTR [rdi], rsi

sum_local initializes ecx before testing the first element, then uses:

.L9:
        add     rax, 4
        add     ecx, edx
        mov     edx, DWORD PTR [rax]
        test    edx, edx
        jne     .L9
.L8:
        mov     QWORD PTR [rdi], rax

sum_direct could use the same pointer update as sum_local, avoiding the extra
increment before the loop and the copy inside it.

The same difference occurs in the attached decimal parser. With GCC 16.2.1
20260810 on a Ryzen 9 7945HX, using a local cursor reduced parsing time for
five million nine-digit tokens from 19.51 to 18.87 ms (3.3%; medians of 31
interleaved runs). The sum example above showed no meaningful timing
difference.

The attachment contains reader.cpp, benchmark.cpp and local g++ -v output. The
benchmark prints parsing time in milliseconds and checks the result sum and
each final cursor:

g++ -std=c++23 -O2 benchmark.cpp reader.cpp -o direct
g++ -std=c++23 -O2 -DLOCAL benchmark.cpp reader.cpp -o local
taskset -c 12 ./direct 5000000
taskset -c 12 ./local 5000000

Both parser variants passed ASan/UBSan.
  • [Bug tree-optimization/128016] N... adamant.pwn at gmail dot com via Gcc-bugs

Reply via email to