2010-01-06 9 views
11

J'essaie d'implémenter un assembleur en ligne (en code C/C++) pour tirer parti de SSE. J'aimerais copier et dupliquer des valeurs (à partir d'un registre XMM ou de la mémoire) vers un autre registre XMM. Par exemple, supposons que j'ai des valeurs {1, 2, 3, 4} en mémoire. Je voudrais copier ces valeurs de sorte que xmm1 soit rempli avec {1, 1, 1, 1}, xmm2 avec {2, 2, 2, 2} et ainsi de suite.Comment peupler un registre XMM x86 avec 4 flottants identiques provenant d'une autre entrée de registre XMM?

En parcourant les manuels de référence Intel, je n'ai pas trouvé d'instruction pour cela. Dois-je juste utiliser une combinaison de MOVSS répété et de rotation (via PSHUFD?)?

Répondre

14

Il y a deux façons:

  1. Utilisez exclusivement shufps:

    __m128 first = ...; 
    __m128 xxxx = _mm_shuffle_ps(first, first, 0x00); // _MM_SHUFFLE(0, 0, 0, 0) 
    __m128 yyyy = _mm_shuffle_ps(first, first, 0x55); // _MM_SHUFFLE(1, 1, 1, 1) 
    __m128 zzzz = _mm_shuffle_ps(first, first, 0xAA); // _MM_SHUFFLE(2, 2, 2, 2) 
    __m128 wwww = _mm_shuffle_ps(first, first, 0xFF); // _MM_SHUFFLE(3, 3, 3, 3) 
    
  2. Let le compilateur choisit la meilleure façon d'utiliser _mm_set1_ps et _mm_cvtss_f32:

    __m128 first = ...; 
    __m128 xxxx = _mm_set1_ps(_mm_cvtss_f32(first)); 
    

Notez que la 2ème méthode produira un code horrible MSVC, as discussed here, et uniquement des produits 'xxxx' comme résultat, contrairement à la première option.

Je suis en train de mettre en œuvre une assembleur en ligne (en code C/C++) de prendre avantage de l'ESS

Ceci est très peu portable. Utiliser des intrinsèques.

+0

C'est un très bon point sur la portabilité. Je n'y avais pas vraiment pensé puisque c'est surtout un exercice d'apprentissage pour moi. Votre article semble également très intéressant à première vue. Je suis impatient de passer plus de temps avec ça. – jbl

+1

La méthode intrinsèque montrée dans cette réponse est meilleure que inline asm car les intrinsèques permettent au compilateur de faire beaucoup plus d'optimisations qui ne sont pas effectuées sur asm: assignation de registre, déroulement de boucle, entrelacement d'instruction, levage d'invariants de boucles, etc. était ASM parce que c'est ce que la question initiale demandait, mais si j'allais utiliser le code moi-même, je l'écrirais avec Intrinsics pour PERFORMANCE _AND_ PORTABILITY. – Adisak

+0

Adisak: ce que vous avez dit est vrai pour tout sauf MSVC - il gère très mal les intrinsèques (voir mon article). dans MSVC, l'assemblage écrit à la main est meilleur si les performances viennent avant la portabilité et la maintenabilité (rarement).Je voudrais juste suggérer de changer de compilateur si :). – LiraNuna

5

Déplacez la source vers le registre dest. Utilisez 'shufps' et utilisez simplement deux fois le nouveau registre de dest puis sélectionnez le masque approprié.

L'exemple suivant diffuse les valeurs de XMM2.x à XMM0.xyzw

MOVAPS XMM0, XMM2 
SHUFPS XMM0, XMM0, 0x00 
1

Si vos valeurs sont 16 octets alignés dans la mémoire:

movdqa (mem), %xmm1 
pshufd $0xff, %xmm1, %xmm4 
pshufd $0xaa, %xmm1, %xmm3 
pshufd $0x55, %xmm1, %xmm2 
pshufd $0x00, %xmm1, %xmm1 

Sinon, vous pouvez faire une charge non alignée, ou quatre charges scalaires. Sur les plates-formes plus récentes, la charge non alignée devrait être plus rapide; sur les plates-formes plus anciennes, les charges scalaires peuvent gagner.

Comme d'autres l'ont noté, vous pouvez également utiliser shufps.

+0

Remarque: 'pshufd' est une instruction SSE2. – LiraNuna

+0

@LiraNuna: J'ai utilisé le terme "SSE" pour désigner un sous-ensemble non spécifié de SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, etc. Comme pratiquement tout le matériel x86 a été supporté par SSE2 pour un certain nombre des années maintenant, il semblait assez sûr de supposer que le questionneur ne voulait pas le proscrire. –

+0

C'est une note générale - il n'était pas destiné à être contre votre réponse en aucune façon. – LiraNuna