2017-04-21 6 views
5

__SHA__이 정의되지 않은 경우 인라인 어셈블리를 사용하여 SHA 명령어를 사용할 수 있도록합니다. GCC에서 우리는 사용Clang에서 Yz 기계 제약이 부족한 곳에서 작업 하시겠습니까?

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; 
} 

연타 소비하지 않는 GCC의 Yz constraint (Clang 3.2 Issue 13199Clang 3.9 Issue 32727를 참조) sha256rnds2 명령에 의해 요구되는 :

Yz 

    First SSE register (%xmm0). 

우리는 연타에 대한 mov 추가 :

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

성능이 바이트 당 약 3 사이클 씩 감소합니다. 내 2.2 GHz Celeron J3455 테스트 머신 (SHA 확장 기능이있는 Goldmont)은 약 230 MiB/s입니다. 그것의 사소한. 해체 보면

는 연타 주변에 최적화되지 않은 SHA의 k 두 라운드 수행 할 때 :

예를 들어
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 
    ... 

, 0068ce8c0068ce98 불구하고 있었어야 : 나는 추측하고있어

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

우리가 선택한 인라인 asm 명령어는 약간 떨어져 있습니다.

우리는 어떻게기계 제약 조건의 부족을 해결하기 위해 Clang? 최적화 된 코드에서 중간 이동을 피하는 패턴은 무엇입니까? Explicit Register Variable를 사용하려고


:

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

결과 :

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

감사합니다. GCC는 [Explicit Register Variables] (https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html#Local-Register-Variables)를 호출합니다. Clang은 컴파일하지 않습니다. C++과 C 캐스트를 사용하여 여러 가지 방법을 시도했습니다. 나는 그것이'__m128i' 타입과 관련이 있다고 생각합니다. – jww

+1

업데이트를 보면 명시 적 레지스터를 사용해야하는 임시 변수에 'register' 유형 한정자가 없습니다. 'register const __m128i k asm ("xmm0") = c;'시도해보십시오. –

+0

Michael 감사합니다. 'register' 키워드는 C++에서 없어져서 사용할 수 없습니다. (우리는 C++ 라이브러리이며,이 코드는 헤더 파일에 있습니다. 아마도 __register 또는 GCC_REGISTER와 같은 컴파일러 특정 해결 방법을 찾아야 할 수도 있습니다). – jww

답변

3

내가 언급없이 특정 언어와 태그 inline assembly에 따라이 대답을 만들었습니다. Extended assembly templates은 이미 언어에 대한 확장을 사용한다고 가정합니다.

제약 조건을 사용할 수없는 경우 임시 변수를 만들어 CLANG 제약 조건 대신 사용할 수 있습니다. 당신은 소위를 통해이 작업을 수행 할 수있는 Explicit Register Variable :

당신은 로컬 레지스터 변수를 정의하고이 같은 지정된 레지스터와 연결할 수 있습니다

register int *foo asm ("r12"); 
다음

R12는 레지스터의 이름입니다 사용되어야한다. 이것은 전역 레지스터 변수를 정의하는 데 사용되는 구문과 동일하지만 로컬 변수의 경우에는 함수 내에 선언이 나타납니다. register 키워드는 필수이며 정적 키워드와 결합 할 수 없습니다. 레지스터 이름은 대상 플랫폼에 유효한 레지스터 이름이어야합니다.

귀하의 경우에는 xmm0 등록을 강요하고 싶습니다.명시 적 레지스터를 사용하여 c 매개 변수를 임시 변수에 할당하고 해당 임시 변수를 확장 인라인 어셈블리의 매개 변수로 사용할 수 있습니다. 이것이 명시적인 레지스터의 주 목적입니다. 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; 
} 

컴파일러는 xmm0 레지스터를 사용하는 방법에 대한 더 많은 지식을 가지고 있기 때문에 지금은 몇 가지 최적화를 제공 할 수 있어야한다.

당신의 지시에 어떤 최적화를하지 않는 템플릿 (및 GCC)에 mov %2, %%xmm0;를 배치합니다. 기본 어셈블리 및 확장 어셈블리 템플릿은 제약 조건을 기반으로 기본 대체를 수행하는 방법 만 알고있는 블랙 박스입니다.


위의 방법을 사용한 디스 어셈블리입니다. clang++-std=c++03으로 컴파일되었습니다. 추가 이동은 더 이상 존재하지 않습니다.

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

나는 C 나 C++에 대해 너무 걱정하지 않을 것이다. 귀하의 대답은 Clang, SSE 및 인라인 어셈블리를 다루는 사람들을 도울 것입니다. 그것들은 중요한 부분입니다. 너 내 업보트있어. – jww

+0

다음 질문은 얼마나 될지, 우리는 규칙을 어떻게 구부릴 수 있을지 생각합니다. 명시 적 레지스터 변수의 컨텍스트에서 'register'를 사용하여 언어가 위반되지 않도록 확장 프로그램을 사용합니다. 아마도 Clang이나 GCC 개발자에게 물어볼 것입니다. – jww

+1

@jww 만약 당신이 C++ 17로 간다면, 더 중요한 문제를 제공하는 것뿐만 아니라 더 이상 사용되지 않을 것이기 때문에 예약 된 것으로 이동합니다. 내장 함수가 컴파일러 개발에 뒤쳐져있는 한, 인라인 어셈블리에서 SIMD 레지스터를 더 잘 지원할 수 있습니다. 그 또는 누군가는 표준을 위반하지 않고 명시 적 레지스터를 허용하는 언어 확장을 제안 할 것이다. –