2015-06-26 13 views
6

Si consideri il seguente codice (usa Eigen):Perché il codice generato da GCC legge la posta indesiderata dallo stack?

#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(); } 

compilo con gcc-4.8 con le ottimizzazioni pieno e opzione per generare elenco di assemblaggio. Ecco il comando che uso: (. Il mio CPU è Intel (R) Xeon (R) CPU E3-1226 v3 esegue un sistema Linux a 64 bit - si spera ora -march=native ha un senso per i lettori)

g++ minitest.cpp -I/usr/include/eigen3 -std=c++0x -O3 -march=native -S -masm=intel 

Ciò che mi fa meravigliare è che alcune istruzioni generate con questa linea di comando sembrano essere prive di senso e persino ridondanti. Vedi per es. come la funzione test() inizia dopo la creazione dello stack (per il codice completo per entrambi test() e main() vedere 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 

ho fatto un passo attraverso queste letture di spazzatura nel debugger e ha confermato che dopo di loro i xmm3 e xmm1 registri hanno davvero dei valori privi di senso Guardando questa lettura di valori indefiniti, sto iniziando a sospettare che il mio programma cerchi effettivamente di accedere ad una memoria che dovrebbe essere inaccessibile. Ma perché? Ho introdotto un UB da qualche parte?

Ho anche provato a eseguire il programma compilato con -fsanitize=address, ma ha funzionato senza arresti anomali.

+0

Non è solo questo l'assembly assembly Intel? Cioè, non 'vmovapd xmm3, XMMWORD PTR [rsp + 16]' sposta * in * 'xmm3'? – Barry

+0

@Barry esattamente, e questo è _il mistero: legge junk a '[rsp + 16]' in 'xmm3'! Quindi salvalo in '[rsp + 304]', e poi sovrascrive questo valore salvato con '7.0' da' [rip + .LC12] '. – Ruslan

risposta

4

Il codice prende le seguenti fasi:

  1. crea un oggetto non inizializzato MatrixConstructor con una inizializzata membro Eigen :: Matrix
  2. Imposta una riga della Eigen :: membro Matrix
  3. Crea una nuova MatrixConstructor oggetto il cui membro Eigen :: Matrix viene inizializzato con membro Eigen :: Matrix del vecchio oggetto MatrixConstruction

Quindi, al punto 3 si sta copiando un Eigen :: M oggetto atrix che ha avuto solo il primo set di righe. Il resto dei valori dell'oggetto è ancora non inizializzato. Poiché si tratta di oggetti temporanei, sono tutti allocati nello stack, quindi non sorprende che si stia leggendo la posta indesiderata dalla pila.

Si noti che ciò presuppone che il costruttore Eigen :: Matrix() non inizializzi l'oggetto, che da una rapida occhiata all'origine non sembra eseguire per impostazione predefinita.

+0

Hmm, sembri aver ragione. Proverò a passarlo per riferimento tra costruttori e vedere il risultato. Ora ciò che rimane ancora strano è che le operazioni ridondanti non sono state ottimizzate a livello "-O3". – Ruslan

+0

Infatti, la memorizzazione del riferimento alla matrice che viene costruita invece delle copie porta a un codice più volte più piccolo, che utilizza anche le istruzioni quadruple AVX AVX. – Ruslan

Problemi correlati