I answered this question, and noticed what I consider as a strange behavior of the compiler.
I first wrote this program (as part of my answer there):
class Vector {
private:
double** ptr;
public:
Vector(double** _ptr): ptr(_ptr) {}
inline double& operator[](const int iIndex) const {
return *ptr[iIndex];
}
};
extern "C" int test(const double a);
int main() {
double a[2] = { 1.0, 2.0 };
Vector va((double**) &a);
double a1 = va[0];
test(a1);
double a2 = va[0];
test(a2);
}
which generates two load instructions when compiled with :
clang -O3 -S -emit-llvm main.cpp -o main.ll
This can be seen in the llvm-IR (and could be seen in the assembly):
define i32 @main() #0 { entry: %a.sroa.0.0.copyload = load double*, double** bitcast ([2 x double]* @_ZZ4mainE1a to double**), align 16 %0 = load double, double* %a.sroa.0.0.copyload, align 8, !tbaa !2 %call1 = tail call i32 @test(double %0) %1 = load double, double* %a.sroa.0.0.copyload, align 8, !tbaa !2 %call3 = tail call i32 @test(double %1) ret i32 0 }
I would expect only one load instruction, since no function with side effect on memory has been called, and I didn't link this object to something with side effects. In fact, when reading the program, I just expect two calls to
test(1.0);
since my array is constant in memory and everything can be inlined properly.
Just to be sure, I replaced the double pointer by a simple pointer:
class Vector {
private:
double* ptr;
public:
Vector(double* _ptr): ptr(_ptr) {}
inline double& operator[](const int iIndex) const {
return ptr[iIndex];
}
};
extern "C" int test(const double a);
int main() {
double a[2] = { 1.0, 2.0 };
Vector va(a);
double a1 = va[0];
test(a1);
double a2 = va[0];
test(a2);
}
Compiled with the same line , I get the expected result:
define i32 @main() #0 {
entry:
%call1 = tail call i32 @test(double 1.000000e+00)
%call3 = tail call i32 @test(double 1.000000e+00)
ret i32 0
}
Which looks like way better optimized :)
My question is therefore:
What reason prevents the compiler to perform the same inlining on the first code sample? Is that double pointers?
The error is in these lines:
double a[2] = { 1.0, 2.0 };
Vector<double> va((double**) &a);
a
is an array of two doubles. It decays to a double *
, but &a
is not a double **
. Arrays and pointers are not same animal.
In fact you have the following: (void *) a == (void *) &a
because the address of an array is the address of its first element.
If you want to build a pointer to pointer you must create explicitely a true pointer:
double a[2] = { 1.0, 2.0 };
double *pt = a; // or &(a[0]) ...
Vector<double> va((double**) &pt);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With