2012-04-02 14 views
12

L'intrinseco _mm_slli_si128 eseguirà uno spostamento logico a sinistra di un registro a 128 bit, ma è limitato ai valori di spostamento immediati e si sposta di byte non di bit.Ricerca dell'operazione di spostamento a 128 bit di sse per il valore di spostamento non immediato

È possibile utilizzare un valore intrinseco come _mm_sll_epi64 o _mm_sll_epi32 per spostare a sinistra un set di valori all'interno del registro __m128i, ma questi non contengono i bit di "overflow".

Per uno spostamento da N bit immaginare che avrei potuto fare una cosa del genere:

  • _mm_sll_epi64
  • _mm_srr_epi64 (per i bit voglio portare: spostarli nell'ordine basso)
  • riordino il risultato srr
  • o questi insieme.

(ma probabilmente anche includere controlli di N relativi a 64).

C'è un modo migliore?

+1

Non penso ci sia un modo migliore. Ho scritto una risposta a un recente duplicato di questa domanda: http://stackoverflow.com/q/34478328/224132. Per i conteggi a tempo costante della compilazione, diventa 4 insns o 2 insns con count> = 64. Con un conteggio variabile, si dirama e deve 'movd' il conteggio e 64-count dal numero intero ai registri vettoriali. '__uint128_t' fa meglio in questo caso, se i dati sono già in registri di numeri interi. –

risposta

4

Non è la soluzione ideale, ma se si desidera ruotare o spostare un registro SSE di un numero di bit multiplo di 8, può essere d'aiuto l'istruzione PSHUFB (e l'interno _mm_shuffle_epi8()). Ci vuole un secondo registro SSE come input; ogni byte nel registro contiene un valore che viene utilizzato per indicizzare i byte nel primo registro di input.

+4

Penso che il PO abbia specificato in particolare che voleva la granularità del bit e non limitato agli immediati. '_mm_shuffle_epi8()' è sia a livello di granularità dei byte che richiede un immediato. – Mysticial

+4

So che voleva la granularità del bit, quindi la prima clausola nella mia risposta. Inoltre, '_mm_shuffle_epi8()' non richiede un immediato; il secondo argomento è un valore '__m128i'. [Vedere la documentazione qui] (http://msdn.microsoft.com/en-us/library/bb531427.aspx). –

+1

Si noti che questa funzione richiede il supporto SSSE3, che potrebbe non essere sufficiente se si desidera eseguire su macchine meno recenti. –

4

Questo è emerso come un problema secondario in un post del blog (del mio) su unusual C preprocessor uses. Per i 127 diversi offset di spostamento, ci sono quattro diverse sequenze ottimali di istruzioni SSE2 per un cambio di bit. Il preprocessore rende ragionevole costruire una funzione di spostamento che equivale a un'istruzione switch a 129 vie. Perdonate il codice raw qui; Non ho familiarità con la pubblicazione del codice direttamente qui. Controlla il post sul blog per una spiegazione di cosa sta succedendo.

#include <emmintrin.h> 

typedef __m128i XMM; 
#define xmbshl(x,n) _mm_slli_si128(x,n) // xm <<= 8*n -- BYTE shift left 
#define xmbshr(x,n) _mm_srli_si128(x,n) // xm >>= 8*n -- BYTE shift right 
#define xmshl64(x,n) _mm_slli_epi64(x,n) // xm.hi <<= n, xm.lo <<= n 
#define xmshr64(x,n) _mm_srli_epi64(x,n) // xm.hi >>= n, xm.lo >>= n 
#define xmand(a,b) _mm_and_si128(a,b) 
#define xmor(a,b) _mm_or_si128(a,b) 
#define xmxor(a,b) _mm_xor_si128(a,b) 
#define xmzero  _mm_setzero_si128() 

XMM xm_shl(XMM x, unsigned nbits) 
{ 
    // These macros generate (1,2,5,6) SSE2 instructions, respectively: 
    #define F1(n) case 8*(n): x = xmbshl(x, n); break; 
    #define F2(n) case n: x = xmshl64(xmbshl(x, (n)>>3), (n)&15); break; 
    #define F5(n) case n: x = xmor(xmshl64(x, n), xmshr64(xmbshl(x, 8), 64-(n))); break; 
    #define F6(n) case n: x = xmor(xmshl64(xmbshl(x, (n)>>3), (n)&15),\ 
            xmshr64(xmbshl(x, 8+((n)>>3)), 64-((n)&155))); break; 
    // These macros expand to 7 or 49 cases each: 
    #define DO_7(f,x) f((x)+1) f((x)+2) f((x)+3) f((x)+4) f((x)+5) f((x)+6) f((x)+7) 
    #define DO_7x7(f,y) DO_7(f,(y)+1*8) DO_7(f,(y)+2*8) DO_7(f,(y)+3*8) DO_7(f,(y)+4*8) \ 
             DO_7(f,(y)+5*8) DO_7(f,(y)+6*8) DO_7(f,(y)+7*8) 
    switch (nbits) { 
    case 0: break; 
    DO_7(F5, 0) // 1..7 
    DO_7(F1, 0) // 8,16,..56 
    DO_7(F1, 7) // 64,72,..120 
    DO_7x7(F6, 0) // 9..15 17..23 ... 57..63 i.e. [9..63]\[16,24,..,56] 
    DO_7x7(F2,56) // 65..71 73..79 ... 121..127 i.e. [65..127]\[64,72,..,120] 
    default: x = xmzero; 
    } 
    return x; 
} 

xm_shr pari a quanto sopra, ma scambiando "SHL" e "shr" ovunque nelle macro F [1256]. HTH.

+2

In realtà, il codice sopra riportato non funziona per circa la metà dei valori di spostamento. L'ho provato rispetto a uno spostamento standard su interi a 128 bit (gcc supporta __uint128_t), e i risultati sono marcatamente diversi.Ad esempio, tutti i turni superiori a 120 azzerano tutti i bit. – seba

+1

Per il conteggio degli spostamenti costante in fase di compilazione, non sono mai necessarie più di 4 istruzioni (o 5 senza AVX: un altro 'movdqa'). Per il conteggio <64, lo spostamento di byte lasciato da 64b, quindi il bit shift che trasporta a destra di 64-count. 'OR' il trasporto con' psllq xmm0, 64'. L'ho scritto con un 'if', e si ricompila bene per un conteggio costante in fase di compilazione: http://goo.gl/O14GhI. Vedi http://stackoverflow.com/a/34482688/224132 –

+0

Per correggere il codice, basta sostituire ogni & 15 o & 155 espressione di & 7. Detto questo, questo codice è molto lento (conosci la ramificazione ?!), e la proposta di Peter Cordes sembra molto più promettente. –

Problemi correlati