Consider the following code (uses Eigen):
#include <Eigen/Dense>
#include <iostream>
template<int rows, int cols, int row, class R, class Rv, int N, class... Rs>
inline typename std::enable_if<sizeof...(Rs)==0>::type
setRow(Eigen::Matrix<R,rows,cols>&)
{}
template<int rows, int cols, int row, class R, class Rv, int N=0, class... Rs>
inline typename std::enable_if<sizeof...(Rs)==cols-N-1>::type
setRow(Eigen::Matrix<R,rows,cols>& m, Rv val, Rs...args)
{
m(row,N)=val;
setRow<rows,cols,row,R,Rv,N+1>(m,args...);
}
template<class T, int R, int C, int CUR_ROW>
class MatrixConstructor
{
Eigen::Matrix<T,R,C> m;
public:
MatrixConstructor(const Eigen::Matrix<T,R,C>& m)
: m(m)
{}
MatrixConstructor()
{}
template<class...Ts>
typename std::enable_if<sizeof...(Ts)==C && CUR_ROW<R-1,
MatrixConstructor<T,R,C,CUR_ROW+1>>::type operator()(Ts... vals)
{
setRow<R,C,CUR_ROW>(m,vals...);
return MatrixConstructor<T,R,C,CUR_ROW+1>(m);
}
template<class...Ts>
typename std::enable_if<sizeof...(Ts)==C && CUR_ROW==R-1,
Eigen::Matrix<T,R,C>>::type operator()(Ts... vals)
{
setRow<R,C,CUR_ROW>(m,vals...);
return m;
}
};
void test()
{
Eigen::Matrix<double,4,3> m=MatrixConstructor<double,4,3,0>()(1,2,3)
(4,5,6)
(7,8,9)
(5,4,3);
std::cout << m;
}
int main()
{ test(); }
I compile it with gcc-4.8 with full optimizations and option to generate assembly listing. Here's the command I use:
g++ minitest.cpp -I/usr/include/eigen3 -std=c++0x -O3 -march=native -S -masm=intel
(My CPU is Intel(R) Xeon(R) CPU E3-1226 v3 running a 64 bit Linux system — hopefully now -march=native
makes sense to the readers.)
What makes me wonder is that some instructions generated with this command line seem to be nonsensical, and even redundant. See e.g. how the test()
function starts after setting up stack (for full code for both test()
and main()
see here):
vmovsd xmm4, QWORD PTR .LC6[rip] # 1.0
lea rsi, [rsp+96]
vmovsd xmm5, QWORD PTR .LC7[rip] # 2.0
vmovsd QWORD PTR [rsp], xmm4
vmovapd xmm3, XMMWORD PTR [rsp+16] # What does it read?!
vmovapd xmm1, XMMWORD PTR [rsp] # And this!
vmovsd QWORD PTR [rsp+32], xmm5
vmovsd xmm0, QWORD PTR .LC8[rip] # 3.0
vmovapd XMMWORD PTR [rsp+304], xmm3 # And now even save this junk?!
vmovapd XMMWORD PTR [rsp+192], xmm1
vmovapd xmm3, XMMWORD PTR [rsp+48]
vmovapd xmm1, XMMWORD PTR [rsp+32]
vmovsd QWORD PTR [rsp+64], xmm0
vmovsd xmm7, QWORD PTR .LC12[rip] # 7.0
vmovapd XMMWORD PTR [rsp+336], xmm3
vmovapd XMMWORD PTR [rsp+224], xmm1
vmovapd xmm3, XMMWORD PTR [rsp+80]
vmovsd QWORD PTR [rsp+304], xmm7 # Even stranger — overwrites the junk
I've stepped through these reads of junk in debugger and confirmed that after them the xmm3
and xmm1
registers do indeed have nonsensical values. Looking at this reading of undefined values, I'm starting to suspect that my program indeed tries to access some memory which should be inaccessible. But why? Have I introduced a UB somewhere?
I've also tried running the program compiled with -fsanitize=address
, but it worked without any crashes.
Your code takes the following steps:
So in step 3 you're copying an Eigen::Matrix object that has only had the first row set. The rest of the object's values are still uninitialized. Since these are all temporary objects they're all allocated in the stack, so it's not surprising you're seeing junk being read from the stack.
Note that this assumes that the Eigen::Matrix() constructor doesn't initialize the object, which from a quick look at the source it doesn't appear to do by default.
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