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?
CodePudding user response:
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.
CodePudding user response:
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;
}
