Reputation: 16129
Take this toy code:
void f(const int& a, int* dat) {
for (int i=0; i<a; ++i )
dat[i] += a;
}
Observe that the compiler is afraid that dat[i]
might alias with a
, i.e. writing to dat[i]
might change the value of a
- and so it feels obliged to re-load a
on every loop iteration (that's the 'movslq (%rdi), %rax' line at the link).
This could be solved with strict-aliasing, by changing the type of dat:
void f(const int& a, long* dat) {
...
The generated code seems Indeed longer, but that's due to vectorization. It doesn't re-load a
on every iteration.
I'm surprised this doesn't work for my own custom type!
struct myInt {
int i;
myInt& operator+=(int rhs) { i += rhs; return *this;}
};
void f(const int& a, myInt* dat) {
for (int i=0; i<a; ++i )
dat[i] += a;
}
Here the compiler returns to re-loading the loop boundary on every iteration. Both clang and gcc do.
Looks like the compiler's alias-analysis treats myInt
as a regular int
- which is reasonable in many aspects, but causes this optimization to be missed. Could this be a compiler bug? Or is there something I'm missing about strict-aliasing?
Upvotes: 1
Views: 115
Reputation: 29965
If you are sure no aliasing happens, most compilers support restrict from C99:
void f(const int& __restrict a, int* dat) {
for (int i = 0; i < a; ++i) dat[i] += a;
}
Note that this is dangerous code. A better option would be to take a
by value:
void f(const int a, int* dat) {
for (int i = 0; i < a; ++i) dat[i] += a;
}
Upvotes: 1
Reputation: 11400
Imagine the following:
struct myInt {
int i;
myInt& operator+=(int rhs) { i += rhs; return *this;}
};
void f(const int& a, myInt* dat)
{
for (int i=0; i<a; ++i)
dat[i] += a;
}
int main()
{
myInt foo{ 1 };
f(foo.i, &foo);
}
In this program, a
and dat.i
actually alias. They are the same variable. So the compiler actually needs to reload one after a write to the other.
Upvotes: 4