2014-04-17 14 views
12

Vorrei sapere se sto rompendo le rigide regole di aliasing con questo snippet. (Credo di sì dal momento che è un dereferenziazione-pointer punned, ma è fatto in una sola espressione e/parete non piange.)Sto violando rigide regole di aliasing?

inline double plop() const // member function 
{ 
    __m128d x = _mm_load_pd(v); 
    ... // some stuff 
    return *(reinterpret_cast<double*>(&x)); // return the lower double in xmm reg referred to by x. 
} 

Se sì, qual è la soluzione? Usare simultaneamente diverse rappresentazioni sta diventando hardcore una volta che si desidera rispettare le specifiche.

Grazie per le vostre risposte, sto perdendo il mio buon umore cercando di trovare una soluzione.

risposte che non saranno accettate e perché:

"uso mm_store" -> L'ottimizzatore non riesce a rimuoverlo se le seguenti istruzioni richiedono un registro XMM così si genera un carico poco dopo. Store + caricare per niente.

"use a union" -> Violazione della regola di aliasing se si utilizzano i due tipi per lo stesso oggetto. Se ho capito bene l'articolo scritto da Thiago Macieira.

+0

Che dire di semplice vecchia 'memcpy' in un' double'? – Praetorian

+1

È quasi impossibile evitare l'aliasing quando si ha a che fare con SIMD. Idealmente eviti di accedere a singoli elementi come lo sei ora, ma se ne hai assolutamente bisogno, ti consiglio un sindacato per le cose in pila e un cast di puntatori per i puntatori provenienti dai parametri. La punzonatura di tipo unionista è esplicitamente consentita in C99, e tutti i compilatori mainstream lo porteranno anche in C++. Cercare di essere completamente conformi agli standard quando si ha a che fare con un'estensione non standard è in una certa misura contraddittoria in primo luogo. – Mysticial

+0

@Praetorian: non usa simd intrinsics e chiama memcpy in modo paradossale? ^^ – ThiSpawn

risposta

2

C'è solo un intrinseco che "estratti" l'ordine doppio valore inferiore dal registro XMM:

double _mm_cvtsd_f64 (__m128d a) 

si potrebbe usare in questo modo:

return _mm_cvtsd_f64(x); 

V'è una certa contraddizione tra diversi riferimenti . MSDN dice: This intrinsic does not map to any specific machine instruction. Mentre la guida Intel intrinseca cita l'istruzione movsd. In quest'ultimo caso questa ulteriore istruzione viene facilmente eliminata dall'ottimizzatore. Almeno gcc 4.8.1 con il flag -O2 genera codice senza istruzioni aggiuntive.

+0

Intel dice che non mappa :) :) Questo è il modo di fare affidamento su compiler impl per rispettare le regole di aliasing. – ThiSpawn

4

il punto proiettile in grassetto dovrei pensare permettere il cast qui, come possiamo considerare __m128d come aggregato di quattro double unione alla piena registro. Per quanto riguarda il rigoroso aliasing, il compilatore è sempre stato molto conciliante nei confronti dell'unione, dove all'origine era ritenuto valido solo un cast per (char *).

§3.10: Se un programma tenta di accedere al valore memorizzato di un oggetto attraverso un glvalue di diverso uno dei seguenti tipi di comportamento è indefinito (L'intento di questa lista è per specificare tali circostanze in cui un oggetto può o non può essere alias):

  • tipo dinamico dell'oggetto,
  • una versione cv qualificato di tipo dinamico dell'oggetto,
  • un tipo simile (come definito in 4.4) per il tipo dinamico dell'oggetto,
  • un tipo che è il segno o tipo senza segno corrispondente al tipo dinamico dell'oggetto,
  • un tipo che è la firma o tipo senza segno corrispondente ad un CV- versione qualificata di tipo dinamico dell'oggetto,
  • un tipo di aggregato o unione che include uno dei tipi indicati sopra tra i suoi elementi o membri di dati non statici (inclusi, ricorsivamente, un elemento o elemento di dati non statica di un subaggregate o unione contenuta),
  • un tipo che è un tipo di classe base (possibilmente qualificato cv) di il tipo dinamico dell'oggetto,
  • un tipo di carattere char o unsigned.
+0

Davvero una bella risposta Non stavo tenendo conto del fatto che nessun compilatore effettivamente usa questo tipo come parola chiave ma invece come tipo specifico è un tipo typedef esplicitamente compatibile o un sindacato che include una rappresentazione compatibile ... lasciami fare alcuni controlli e Accetterò la tua risposta con tutta la mia gratitudine. – ThiSpawn

+0

Questa è la seconda soluzione migliore, ma richiede il re-wrapping di tutti i tipi di SIMD poiché alcuni compilatori hanno strane rappresentazioni speciali che non includono il doppio array. – ThiSpawn

1

Sì, io credo questo rompe rigorosa aliasing. Tuttavia, in pratica, di solito questo va bene.
(sto scrivendo questo per lo più come una risposta, perché è difficile descrivere bene in un commento)

Ma, si potrebbe invece fare qualcosa di simile:

inline double plop() const // member function 
{ 
    __m128d x = _mm_load_pd(v); 
    ... // some stuff 

    union { 
     unsigned long long i; // 64-bit int 
     double    d; // 64-bit double 
    }; 

    i = _mm_cvtsi128_si64(_mm_castpd_si128(x)); // _mm_castpd_si128 to interpret the register as an int vector, _mm_cvtsi128_si64 to extract the lowest 64-bits 

    return d; // use the union to return the value as a double without breaking strict aliasing 
} 
+0

Secondo lo standard, dopo che un membro di un sindacato è stato assegnato a, il valore di tutti gli altri membri diventa non specificato. I sindacati non possono essere utilizzati come portabili per reinterpretare i pattern di bit. – Sneftel

+0

@ Sneftel: Questo è il comportamento [Implementazione definita] (http://gcc.gnu.org/onlinedocs/gcc/C-Implementation.html), l'implementazione è necessaria per definire il comportamento. Nel caso di GCC (e di ogni compilatore che abbia mai usato) non infrange il rigoroso aliasing. Puoi leggere come viene definito il comportamento [qui] (http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2dpunning). – Apriori

+0

Lo standard lo descrive come "non specificato", non "definito dall'implementazione". Cioè, le implementazioni sono libere di * non * definirlo. – Sneftel

1

Che dire return x.m128d_f64[0];?

+1

Ciò presuppone un'implementazione in cui '__m128d' ha membri a cui è possibile accedere in questo modo. Non tutte le implementazioni fanno. Ad esempio: http://clang.llvm.org/doxygen/emmintrin_8h_source.html, dove '__m128d' è definito come' typedef double __m128d __attribute __ ((__ vector_size __ (16))); ' – bames53

+0

Una macro dipendente dal compilatore potrebbe essere un'idea se tutti i compilatori hanno un modo per esprimere un accesso agli elementi, usando m128d_f64 per msvc, direttamente l'operatore [] su clang etc ... che può essere un'idea per assicurarsi che la parte di ottimizzazione del compilatore non vada persa o impedita fare ottimizzazioni .. – ThiSpawn

Problemi correlati