2017-04-21 2 views
5

Nous utilisons l'assemblage en ligne pour rendre les instructions SHA disponibles si __SHA__ n'est pas défini. Sous GCC nous utilisons:Travailler autour de l'absence de contrainte de machine Yz sous Clang?

GCC_INLINE __m128i GCC_INLINE_ATTRIB 
MM_SHA256RNDS2_EPU32(__m128i a, const __m128i b, const __m128i c) 
{ 
    asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "Yz" (c)); 
    return a; 
} 

Clang ne consomme pas de GCC Yz constraint (voir Clang 3.2 Issue 13199 et Clang 3.9 Issue 32727), qui est requis par l'instruction sha256rnds2:

Yz 

    First SSE register (%xmm0). 

Nous avons ajouté un mov pour Clang :

asm ("mov %2, %%xmm0; sha256rnds2 %%xmm0, %1, %0" : "+x"(a) : "xm"(b), "x" (c) : "xmm0"); 

La performance est désactivée d'environ 3 cycles par octet. Sur ma machine de test Celeron J3455 2,2 GHz (Goldmont avec extensions SHA), cela représente environ 230 Mio/s. C'est non trivial.

En regardant le démontage, Clang n'optimise autour SHA de k lorsque deux tours sont effectués:

Breakpoint 2, SHA256_SSE_SHA_HashBlocks (state=0xaaa3a0, 
    data=0xaaa340, length=0x40) at sha.cpp:1101 
1101  STATE1 = _mm_loadu_si128((__m128i*) &state[4]); 
(gdb) disass 
Dump of assembler code for function SHA256_SSE_SHA_HashBlocks(unsigned int*, unsigned int const*, unsigned long): 
    0x000000000068cdd0 <+0>:  sub $0x308,%rsp 
    0x000000000068cdd7 <+7>:  movdqu (%rdi),%xmm0 
    0x000000000068cddb <+11>: movdqu 0x10(%rdi),%xmm1 
    ... 
    0x000000000068ce49 <+121>: movq %xmm2,%xmm0 
    0x000000000068ce4d <+125>: sha256rnds2 %xmm0,0x2f0(%rsp),%xmm1 
    0x000000000068ce56 <+134>: pshufd $0xe,%xmm2,%xmm3 
    0x000000000068ce5b <+139>: movdqa %xmm13,%xmm2 
    0x000000000068ce60 <+144>: movaps %xmm1,0x2e0(%rsp) 
    0x000000000068ce68 <+152>: movq %xmm3,%xmm0 
    0x000000000068ce6c <+156>: sha256rnds2 %xmm0,0x2e0(%rsp),%xmm2 
    0x000000000068ce75 <+165>: movdqu 0x10(%rsi),%xmm3 
    0x000000000068ce7a <+170>: pshufb %xmm8,%xmm3 
    0x000000000068ce80 <+176>: movaps %xmm2,0x2d0(%rsp) 
    0x000000000068ce88 <+184>: movdqa %xmm3,%xmm4 
    0x000000000068ce8c <+188>: paddd 0x6729c(%rip),%xmm4  # 0x6f4130 
    0x000000000068ce94 <+196>: movq %xmm4,%xmm0 
    0x000000000068ce98 <+200>: sha256rnds2 %xmm0,0x2d0(%rsp),%xmm1 
    ... 

Par exemple, 0068ce8c si 0068ce98 aurait dû être:

paddd 0x6729c(%rip),%xmm0  # 0x6f4130 
sha256rnds2 %xmm0,0x2d0(%rsp),%xmm1 

Je devine notre choix d'instructions asm en ligne est un peu éteint.

Comment pouvons-nous contourner l'absence de contrainte machine Yz sous Clang? Quel modèle évite le déplacement intermédiaire en code optimisé?


Toute tentative d'utilisation Explicit Register Variable:

const __m128i k asm("xmm0") = c; 
asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k)); 
return a; 

Résultats dans:

In file included from sha.cpp:24: 
./cpu.h:831:22: warning: ignored asm label 'xmm0' on automatic variable 
     const __m128i k asm("xmm0") = c; 
          ^
./cpu.h:833:7: error: invalid operand for instruction 
     asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k)); 
      ^
<inline asm>:1:21: note: instantiated into assembly here 
     sha256rnds2 %xmm1, 752(%rsp), %xmm0 
          ^~~~~~~~~~ 
In file included from sha.cpp:24: 
./cpu.h:833:7: error: invalid operand for instruction 
     asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k)); 
      ^
<inline asm>:1:21: note: instantiated into assembly here 
     sha256rnds2 %xmm3, 736(%rsp), %xmm1 
          ^~~~~~~~~~ 
... 
+0

Merci Michael. GCC appelle cela [Variables de registre explicites] (https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html#Local-Register-Variables). Clang ne le compilera pas. J'ai essayé un certain nombre de façons différentes en utilisant C++ et C jette. Je pense que cela a quelque chose à voir avec le type '__m128i'. – jww

+1

En regardant votre mise à jour vous manquez le qualificateur de type 'register' sur votre variable temporaire qui est nécessaire pour utiliser un registre explicite. Essayez 'register const __m128i k asm (" xmm0 ") = c;' –

+0

Merci Michael. Le mot-clé 'register' disparaît en C++, nous ne pouvons donc pas l'utiliser. (Nous sommes une bibliothèque C++, et ce code est dans un fichier d'en-tête Peut-être que j'ai besoin de trouver une solution de contournement spécifique au compilateur, comme '__register' ou' GCC_REGISTER'). – jww

Répondre

3

J'ai créé cette réponse basée sur l'étiquette inline assembly sans langage spécifique mentionné. Extended assembly templates suppose déjà l'utilisation d'extensions aux langues.

Si la contrainte Yz n'est pas disponible, vous pouvez essayer de créer une variable temporaire pour indiquer à CLANG quel registre utiliser plutôt qu'une contrainte. Vous pouvez le faire à travers ce qu'on appelle un Explicit Register Variable:

Vous pouvez définir une variable de registre local et l'associer à un registre spécifié comme ceci:

register int *foo asm ("r12"); 

r12 Voici le nom du registre qui Devrait être utilisé. Notez qu'il s'agit de la même syntaxe que celle utilisée pour définir les variables de registre globales, mais pour une variable locale, la déclaration apparaît dans une fonction. Le mot-clé register est obligatoire et ne peut pas être combiné avec static. Le nom du registre doit être un nom de registre valide pour la plateforme cible.

Dans votre cas, vous souhaitez forcer l'utilisation du registre xmm0.Vous pouvez affecter le paramètre c à une variable temporaire à l'aide d'un registre explicite et utiliser ce paramètre temporaire en tant que paramètre pour l'assemblage étendu en ligne. C'est le but principal des registres explicites dans GCC/CLANG.

GCC_INLINE __m128i GCC_INLINE_ATTRIB 
MM_SHA256RNDS2_EPU32(__m128i a, const __m128i b, const __m128i c) 
{ 
    register const __m128i tmpc asm("xmm0") = c; 
    __asm__("sha256rnds2 %2, %1, %0" : "+x"(a) : "x"(b), "x" (tmpc)); 
    return a; 
} 

Le compilateur devrait être en mesure de fournir des optimisations maintenant car il a plus de connaissances sur la façon dont le registre xmm0 doit être utilisé.

Lorsque vous avez placé mov %2, %%xmm0; dans le modèle CLANG (et GCC) ne font pas de optimisations sur les instructions. Les modèles d'assemblage de base et d'assemblage étendu sont une boîte noire dont il sait seulement comment effectuer une substitution de base en fonction des contraintes.


Voici un démontage en utilisant la méthode ci-dessus. Il a été compilé avec clang++ et -std=c++03. Les mouvements supplémentaires ne sont plus présents:

Breakpoint 1, SHA256_SSE_SHA_HashBlocks (state=0x7fffffffae60, 
    data=0x7fffffffae00, length=0x40) at sha.cpp:1101 
1101  STATE1 = _mm_loadu_si128((__m128i*) &state[4]); 
(gdb) disass 
Dump of assembler code for function SHA256_SSE_SHA_HashBlocks(unsigned int*, unsigned int const*, unsigned long): 
    0x000000000068cf60 <+0>:  sub $0x308,%rsp 
    0x000000000068cf67 <+7>:  movdqu (%rdi),%xmm0 
    0x000000000068cf6b <+11>: movdqu 0x10(%rdi),%xmm1 
... 
    0x000000000068cfe6 <+134>: paddd 0x670e2(%rip),%xmm0  # 0x6f40d0 
    0x000000000068cfee <+142>: sha256rnds2 %xmm0,0x2f0(%rsp),%xmm2 
    0x000000000068cff7 <+151>: pshufd $0xe,%xmm0,%xmm1 
    0x000000000068cffc <+156>: movdqa %xmm1,%xmm0 
    0x000000000068d000 <+160>: movaps %xmm2,0x2e0(%rsp) 
    0x000000000068d008 <+168>: sha256rnds2 %xmm0,0x2e0(%rsp),%xmm3 
    0x000000000068d011 <+177>: movdqu 0x10(%rsi),%xmm5 
    0x000000000068d016 <+182>: pshufb %xmm9,%xmm5 
    0x000000000068d01c <+188>: movaps %xmm3,0x2d0(%rsp) 
    0x000000000068d024 <+196>: movdqa %xmm5,%xmm0 
    0x000000000068d028 <+200>: paddd 0x670b0(%rip),%xmm0  # 0x6f40e0 
    0x000000000068d030 <+208>: sha256rnds2 %xmm0,0x2d0(%rsp),%xmm2 
... 
+1

Je ne m'inquiéterais pas trop du C ou du C++. Votre réponse peut aider quelqu'un qui travaille avec Clang, SSE et l'assemblage en ligne. Ce sont les parties importantes. Vous avez mon upvote. – jww

+0

Je pense que la prochaine question serait, jusqu'où pouvons-nous plier les règles. Utilise 'register' dans le contexte de variables de registre explicites qui engagent simplement une extension de sorte que la langue ne soit pas violée. Je vais probablement demander aux développeurs de Clang et/ou de GCC. – jww

+1

@jww bien si vous allez par C++ 17 il se déplace à réservé non seulement obsolète qui fournit un problème plus important. Une meilleure prise en charge des registres SIMD dans l'assemblage en ligne serait préférable tant que les intrinsèques pourraient être en retard sur le développement du compilateur. Soit cela ou quelqu'un va proposer une extension de langage qui ne viole pas la norme et permet des registres explicites. –