2013-12-10 4 views
3

두 개의 SSE 128 비트 레지스터를 16 비트 값으로로드합니다. 값은 다음과 같은 순서에 있습니다SSE 레지스터의 짝수 및 홀수 채널 섞기

src[0] = [E_7, E_6, E_5, E_4, E_3, E_2, E_1, E_0] 
src[1] = [O_7, O_6, O_5, O_4, O_3, O_2, O_1, O_0] 

당신은 SSE의 내장 함수를 사용하여이을 (할 수있는 좋은 방법이 있는지 알고 계십니까 :

src[0] = [E_3, O_3, E_2, O_2, E_1, O_1, E_0, O_0] 
src[1] = [E_7, O_7, E_6, O_6, E_5, O_5, E_4, O_4] 

이 무엇을 달성하고자하는 것은이 같은 순서입니다 SSE 4.2까지)?

128 비트 레지스터의 위쪽과 아래쪽 사이에 16 비트 값을 셔플 할 수 없기 때문에 당분간 막혔습니다.및 _mm_shufflehi_epi16 내장 함수 만 발견되었습니다.

업데이트 : 폴

감사합니다, 나는 16 비트 값에 대한 epi8 내장 함수를 사용하려고 생각했다.

shuffle_split = _mm_set_epi8(15, 14, 11, 10, 7, 6, 3, 2, 13, 12, 9, 8, 5, 4, 1, 0); 

xtmp[0] = _mm_load_si128(src_vec); 
xtmp[1] = _mm_load_si128(src_vec+1); 
xtmp[0] = _mm_shuffle_epi8(xtmp[0], shuffle_split); 
xtmp[1] = _mm_shuffle_epi8(xtmp[1], shuffle_split); 

xsrc[0] = _mm_unpacklo_epi16(xtmp[0], xtmp[1]); 
xsrc[0] = _mm_shuffle_epi8(xsrc[0], shuffle_split); 
xsrc[1] = _mm_unpackhi_epi16(xtmp[0], xtmp[1]); 
xsrc[1] = _mm_shuffle_epi8(xsrc[1], shuffle_split); 

여전히 더 나은 솔루션이 있습니까 :

내 솔루션은 다음과 같다?

+1

이 _mm_and_si128' /'_mm_srli_epi32'와''의 조합이 _mm_packus_epi32'은 그것을의 –

+1

또는 두 개의 응용 프로그램을 할 것입니다 '_mm_shuffle_epi8' 적절한 permute 마스크와 함께. –

+0

16 비트 요소의 경우에는 업데이트에서 제안한 것보다 더 좋은 해결책이 없을 수도 있지만 32 비트 요소의 경우 더 나은 방법이 있습니다. https://stackoverflow.com/q/45376193/3852630 및 https : /를 참조하십시오. /stackoverflow.com/a/45385216/3852630 – Ralf

답변

1

SSE의 순열은 쉽지 않습니다. 다양한 명령어 조합을 사용하여 동일한 결과를 얻는 방법은 여러 가지가 있습니다. 서로 다른 조합에는 다양한 수의 명령어, 레지스터 또는 메모리 액세스가 필요할 수 있습니다. 수동으로 퍼즐을 풀기 위해 고심하는 대신 LLVM 컴파일러가하는 것을보고 싶습니다. 그래서 매우 유연한 벡터 셔플 명령어를 사용하는 LLVM의 중간 언어에서 원하는 순열의 간단한 버전을 작성했습니다 :

llc shuffle_even_odd.ll -o shuffle_even_odd.s 다음과 같은 86 어셈블리 같은 것을 얻을 :

define void @shuffle_even_odd(<8 x i16>* %src0) { 
    %src1 = getelementptr <8 x i16>* %src0, i64 1 
    %a = load <8 x i16>* %src0, align 16 
    %b = load <8 x i16>* %src1, align 16 
    %x = shufflevector <8 x i16> %a, <8 x i16> %b, <8 x i32> <i32 1, i32 3, i32 5, i32 7, i32 9, i32 11, i32 13, i32 15> 
    %y = shufflevector <8 x i16> %a, <8 x i16> %b, <8 x i32> <i32 0, i32 2, i32 4, i32 6, i32 8, i32 10, i32 12, i32 14> 
    store <8 x i16> %x, <8 x i16>* %src0, align 16 
    store <8 x i16> %y, <8 x i16>* %src1, align 16 
    ret void 
} 
이 사용하는 LLVM IR - 투 - ASM 컴파일러를 컴파일 내가 위에서 * LCPIO_에 의해 참조되는 일정 데이터 섹션을 제외 한

movdqa (%rdi), %xmm0 
movdqa 16(%rdi), %xmm1 
movdqa %xmm1, %xmm2 
pshufb LCPI0_0(%rip), %xmm2 
movdqa %xmm0, %xmm3 
pshufb LCPI0_1(%rip), %xmm3 
por %xmm2, %xmm3 
movdqa %xmm3, (%rdi) 
pshufb LCPI0_2(%rip), %xmm1 
pshufb LCPI0_3(%rip), %xmm0 
por %xmm1, %xmm0 
movdqa %xmm0, 16(%rdi) 

을하지만,이 대략 다음 C 코드로 변환됩니다 :

void shuffle_even_odd(__m128i * src) { 
    __m128i shuffle0 = _mm_setr_epi8(128, 128, 128, 128, 128, 128, 128, 128, 2, 3, 6, 7, 10, 11, 14, 15); 
    __m128i shuffle1 = _mm_setr_epi8(2, 3, 6, 7, 10, 11, 14, 15, 128, 128, 128, 128, 128, 128, 128, 128); 
    __m128i shuffle2 = _mm_setr_epi8(128, 128, 128, 128, 128, 128, 128, 128, 0, 1, 4, 5, 8, 9, 12, 13); 
    __m128i shuffle3 = _mm_setr_epi8(0, 1, 4, 5, 8, 9, 12, 13, 128, 128, 128, 128, 128, 128, 128, 128); 
    __m128i a = src[0]; 
    __m128i b = src[1]; 
    src[0] = _mm_or_si128(_mm_shuffle_epi8(b, shuffle0), _mm_shuffle_epi8(a, shuffle1)); 
    src[1] = _mm_or_si128(_mm_shuffle_epi8(b, shuffle2), _mm_shuffle_epi8(a, shuffle3)); 
} 

이것은 셔플과 2 비트 또는 명령에 불과합니다. 나는 bitwise 명령어가 제안 된 언팩 명령어보다 CPU 파이프 라인에서 더 효율적으로 스케줄 될 수 있다고 생각한다.

당신은 LLVM의 다운로드 페이지에서 "연타 진"패키지에서 "LLC"컴파일러를 찾을 수 있습니다 http://www.llvm.org/releases/download.html

관련 문제