Contourner le manque de contrainte de la machine Yz sous Clang?

Nous utilisons un assemblage en ligne pour rendre les instructions SHA disponibles si __SHA__ n’est pas défini. Sous GCC, nous utilisons:

 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 consum pas la contrainte Yz de GCC (voir Clang 3.2 version 13199 et Clang 3.9 version 32727 ), requirejse 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’environ 3 cycles par octet. Sur ma machine de test Celeron J3455 à 2,2 GHz (Goldmont avec extensions SHA), cela correspond à environ 230 MiB / s. C’est non-sortingvial.

En regardant le déassembly, Clang n’optimise pas autour de k de SHA 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 : sub $0x308,%rsp 0x000000000068cdd7 : movdqu (%rdi),%xmm0 0x000000000068cddb : movdqu 0x10(%rdi),%xmm1 ... 0x000000000068ce49 : movq %xmm2,%xmm0 0x000000000068ce4d : sha256rnds2 %xmm0,0x2f0(%rsp),%xmm1 0x000000000068ce56 : pshufd $0xe,%xmm2,%xmm3 0x000000000068ce5b : movdqa %xmm13,%xmm2 0x000000000068ce60 : movaps %xmm1,0x2e0(%rsp) 0x000000000068ce68 : movq %xmm3,%xmm0 0x000000000068ce6c : sha256rnds2 %xmm0,0x2e0(%rsp),%xmm2 0x000000000068ce75 : movdqu 0x10(%rsi),%xmm3 0x000000000068ce7a : pshufb %xmm8,%xmm3 0x000000000068ce80 : movaps %xmm2,0x2d0(%rsp) 0x000000000068ce88 : movdqa %xmm3,%xmm4 0x000000000068ce8c : paddd 0x6729c(%rip),%xmm4 # 0x6f4130 0x000000000068ce94 : movq %xmm4,%xmm0 0x000000000068ce98 : sha256rnds2 %xmm0,0x2d0(%rsp),%xmm1 ... 

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

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

Je suppose que notre choix d’instructions asm en ligne est un peu compliqué.

Comment pouvons-nous contourner le manque de contrainte de la machine Yz sous Clang? Quel modèle évite le déplacement intermédiaire dans un code optimisé?


Tentative d’utilisation de la variable de registre explicite :

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

Résulte en:

 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)); ^ :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)); ^ :1:21: note: instantiated into assembly here sha256rnds2 %xmm3, 736(%rsp), %xmm1 ^~~~~~~~~~ ... 

J’ai créé cette réponse sur la base de l’ inline assembly tag inline assembly sans langage spécifique mentionné. Les modèles d’assemblage étendus supposent 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 via ce que l’on appelle une variable de registre explicite :

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

  register int *foo asm ("r12"); 

Ici, r12 est le nom du registre à utiliser. Notez qu’il s’agit de la même syntaxe que celle utilisée pour définir les variables de registre globales, mais que 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 de registre doit être un nom de registre valide pour la plate-forme cible.

Dans votre cas, vous souhaitez forcer l’utilisation de xmm0 register. 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 de l’ensemble étendu en ligne. C’est l’objective 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 pouvoir fournir certaines optimisations maintenant, car il dispose de plus de connaissances sur la manière dont le registre xmm0 doit être utilisé.

Lorsque vous avez placé mov %2, %%xmm0; dans le modèle CLANG (et GCC ) ne faites aucune optimisation sur les instructions. Les modèles d’assemblage de base et d’assemblage étendu constituent une boîte noire dans laquelle il ne sait faire qu’une substitution de base en fonction des contraintes.


Voici un déassembly 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 ...