Why doesn't GCC optimize away memory writes in this summation loop?

Viewed 55

Given the following C code, why does GCC (version 10.1, x86-64, -O3) write to memory inside the loop instead of just using two registers during summation and writing the result to memory only at the end?

void sum(unsigned int l, int *as, int *r) {
    r[0] = 0;
    r[1] = 0;
    for (int i = 0; i < l; i++) {
        r[0] += as[2 * i];
        r[1] += as[2 * i + 1];
    }
}

Generated instructions here.

My guess is that this behavior is related to GCC having concurrency "in mind". You could have another thread for example reading from memory at the same address while the summation is performed.
Is this the real (and only) reason? Is it probably even defined by a C standard?

1 Answers

The compiler does not know whether as and r point into the same array, so it does not know that r[0] += as[2 * i]; does not change the value of as[2 * i + 1], which is needed in r[1] += as[2 * i + 1];, and similarly for other interactions between r[0], r[1] and various elements of as.

You can tell the compiler that the elements referred to via as and r do not overlap by changing the function declaration to void sum(unsigned int l, int * restrict as, int * restrict r).

Related