2012-09-11 7 views
6

Sto usando l'estensione vettoriale GCC SIMD per un progetto, tutto funziona abbastanza bene ma, al contrario, ripristinano semplicemente tutti i componenti di un vettore.Come convertire i vettori int per renderli mobili in GCC?

I manual stati:

E 'possibile lanciare da un tipo di vettore a un altro, a condizione che siano della stessa dimensione (in realtà, si possono anche lanciare vettori da e per altri tipi di dati dello stesso dimensione).

Ecco un semplice esempio:

#include <stdio.h> 

typedef int int4 __attribute__ ((vector_size(sizeof(int) * 4))); 
typedef float float4 __attribute__ ((vector_size(sizeof(float) * 4))); 

int main() 
{ 
    int4 i = { 1 , 2 , 3 , 4 }; 
    float4 f = { 0.1 , 0.2 , 0.3 , 0.4 }; 

    printf("%i %i %i %i\n" , i[0] , i[1] , i[2] , i[3]); 
    printf("%f %f %f %f\n" , f[0] , f[1] , f[2] , f[3]); 

    f = (float4)i; 

    printf("%f %f %f %f\n" , f[0] , f[1] , f[2] , f[3]); 
} 

Compilare con gcc cast.c -O3 -o cast e in esecuzione sulla mia macchina ottengo:

1 2 3 4 
0.100000 0.200000 0.300000 0.400000 
0.000000 0.000000 0.000000 0.000000 <-- no no no 

Io non sono quel guru assembler, ma ho solo vedere alcuni movimenti di byte qui:

 
[...] 
400454:  f2 0f 10 1d 1c 02 00 movsd 0x21c(%rip),%xmm3 
40045b:  00 
40045c:  bf 49 06 40 00   mov $0x400649,%edi 
400461:  f2 0f 10 15 17 02 00 movsd 0x217(%rip),%xmm2 
400468:  00 
400469:  b8 04 00 00 00   mov $0x4,%eax 
40046e:  f2 0f 10 0d 12 02 00 movsd 0x212(%rip),%xmm1 
400475:  00 
400476:  f2 0f 10 05 12 02 00 movsd 0x212(%rip),%xmm0 
40047d:  00 
40047e:  48 83 c4 08    add $0x8,%rsp 
400482:  e9 59 ff ff ff   jmpq 4003e0 

I s utilizzare l'equivalente vettoriale dello scalare:

*(int *)&float_value = int_value; 

Come si può spiegare questo comportamento?

+3

Sì, questo è quello che sembra che sta accadendo - una conversione bit per bit. (o meglio, nessuna conversione) Quindi ottieni 4 float denormalizzati invece di una conversione di valore reale. – Mysticial

+1

Questo è ciò che i cast di vettore sono definiti da fare (qualsiasi altra cosa sarebbe completamente disonesta, e renderebbe molto penoso scrivere degli idiomi di programmazione vettoriali standard). Se si vuole effettivamente ottenere una conversione, probabilmente si vorrà usare un intrinseco di qualche tipo, come '_mm_cvtepi32_ps' (questo rompe la bella indipendenza architettonica del codice vettoriale, naturalmente, che è anche fastidioso; un approccio comune è utilizzare un'intestazione di traduzione che definisce un set portatile di "intrinseci"). –

+0

Posso vedere il tuo punto, ma la domanda diventa: quando questo _cast_ sarebbe utile? – cYrus

risposta

8

Questo è ciò che i cast di vettore sono definiti da fare (qualsiasi altra cosa sarebbe completamente disonesta, e renderebbe molto penoso scrivere gli idiomi di programmazione vettoriale standard). Se vuoi davvero ottenere una conversione, probabilmente vorrai usare un intrinseco di qualche tipo, come _mm_cvtepi32_ps (questo rompe la bella indipendenza architettonica del tuo codice vettoriale, ovviamente, che è anche noioso, un approccio comune è usare un'intestazione di traduzione che definisce un set portatile di "intrinseci").

Perché è utile? Una serie di motivi, ma ecco la più grande:

Nel codice vettoriale, non si vuole quasi mai ramificarsi. Invece, se devi fare qualcosa in modo condizionale, valuti entrambi i lati della condizione e usa una maschera per selezionare il risultato appropriato corsia per corsia. Questi vettori di maschera "naturalmente" hanno un tipo intero, mentre i vettori di dati sono spesso a virgola mobile; vuoi combinare i due utilizzando le operazioni logiche. Questo idioma estremamente comune è molto naturale se i calchi vettoriali semplicemente reinterpretano i bit.

Certo, è possibile aggirare questo caso, o uno qualsiasi degli altri comuni idiomi vettoriali, ma la vista "il vettore è un sacco di bit" è estremamente comune e riflette il modo in cui la maggior parte dei programmatori di vettori pensano.

2

In effetti, nel tuo caso non viene nemmeno generata un'istruzione vettoriale singola e non viene eseguito nemmeno un typecast in fase di esecuzione. È tutto fatto in fase di compilazione a causa dello switch -O3. Le quattro istruzioni MOVSD stanno attualmente caricando gli argomenti preconversati su printf. Infatti, secondo l'ABI SysV AMD64, gli argomenti in virgola mobile vengono passati nei registri XMM.La sezione che avete smontato è (codice assembly ottenuto dalla compilazione con -S):

movsd .LC6(%rip), %xmm3 
    movl $.LC5, %edi 
    movsd .LC7(%rip), %xmm2 
    movl $4, %eax 
    movsd .LC8(%rip), %xmm1 
    movsd .LC9(%rip), %xmm0 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    jmp  printf 
    .cfi_endproc 

.LC5 etichette stringa di formato:

.LC5: 
    .string "%f %f %f %f\n" 

Il puntatore alla stringa di formato è di classe INTEGER e così è passato nel registro RDI (essendo da qualche parte nei primi 4 GiB dello spazio VA, alcuni byte di codice vengono salvati emettendo uno spostamento a 32 bit nella parte inferiore di RDI). Il registro RAX (EAX utilizzato per salvare byte di codice) viene caricato con il numero di argomenti passati nei registri XMM (sempre in base all'ABI SysV AMD64 per le chiamate alle funzioni con numero variabile di argomenti). Tutti i quattro MOVSD (MOVe Scalar Double-precision) spostano gli argomenti corrispondenti nei registri XMM. .LC9 ad esempio etichette due doubleword:

.align 8 
.LC9: 
    .long 0 
    .long 916455424 

Quei due forma l'quadword 64 bit 0x36A0000000000000 che risulta essere 2 -149 a 64 bit IEEE 754 rappresentazione. In IEEE 754 a 32 bit denormalizzati sembra 0x00000001, quindi in effetti non è una conversione dell'intero numero 1 (ma dal momento che printf si aspetta che gli argomenti double siano ancora precostvertiti con precisione doppia). Il secondo argomento è:

.align 8 
.LC8: 
    .long 0 
    .long 917504000 

Questo è 0x36B0000000000000 o 2 -148 a 64 bit IEEE 754 e 0x00000002 in denormalizzato 32 bit IEEE 754. Va sulla stessa per gli altri due argomenti.

Si noti che il codice precedente non utilizza una variabile a pila singola - funziona solo con costanti precalcolate. Ciò risulta dall'utilizzo di un livello di ottimizzazione molto elevato (-O3). Una conversione di runtime effettiva si verifica se si compila con un livello di ottimizzazione inferiore (-O2 o inferiore). Il codice seguente viene quindi emessa effettuare il typecast:

movaps -16(%rbp), %xmm0 
    movaps %xmm0, -32(%rbp) 

Questo sposta solo i quattro valori interi nelle fessure corrispondenti del punto vettore galleggiante, quindi alcuna conversione di sorta. Poi per ogni elemento un po 'SSE Mumbo-Jumbo viene eseguita al fine di convertire da singola precisione a doppia precisione (come previsto dal printf):

movss -20(%rbp), %xmm0 
    unpcklps  %xmm0, %xmm0 
    cvtps2pd  %xmm0, %xmm3 

(perché non basta usare CVTSS2SD va oltre la mia comprensione delle istruzioni SSE imposta)

+0

Grazie per il chiarimento! – cYrus

+0

Prego! –

1

È possibile lanciare da int galleggiare loop sugli elementi direttamente

float4 cast(int4 x) { 
    float4 y; 
    for(int i=0; i<4; i++) y[i] = x[i]; 
    return y; 
} 

GCC, Clang, e ICC tutto generare un'istruzione cvtdq2ps xmm0, xmm0 per questo.

https://godbolt.org/g/KU1aPg