x86 머신 코드 (MMX / SSE1), 26 바이트 (4x int16_t)
x86 머신 코드 (SSE4.1), 28 바이트 (4x int32_t 또는 uint32_t)
x86 머신 코드 (SSE2), 24 바이트 (4x float32) 또는 27B-cvt int32
int32를 float로 변환하는 마지막 버전은 동일한 float로 반올림하는 큰 정수에 대해서는 완벽하게 정확하지 않습니다. float 입력의 경우 반올림은 호출자의 문제이며 NaN이 없으면이 함수는 올바르게 작동하여 ==를 비교하는 float을 식별합니다. 정수 버전은 모든 입력에 대해 작동하며 부호있는 2의 보수로 처리합니다.)
이들 모두는 동일한 머신 코드를 사용하여 16/32/64 비트 모드에서 작동합니다.
스택 인수 호출 규칙을 사용하면 args를 두 번 반복 (최대 값 찾기 및 비교) 할 수 있으므로 더 작은 구현을 제공 할 수는 있지만 그 방법을 시도하지는 않았습니다.
x86 SIMD에는 벡터-> 정수 비트 맵이 단일 명령어 ( pmovmskb
또는 movmskps
pd)로 포함되어 있으므로 MMX / SSE 명령어의 길이가 3 바이트 이상이지만 자연 스럽습니다. SSSE3 이상 명령어는 SSE2보다 길며 MMX / SSE1 명령어가 가장 짧습니다. pmax*
부호있는 단어 (16 비트) 및 부호없는 바이트 만 갖는 SSE1 (mmx regs) 및 SSE2 (xmm regs)와 함께 다른 버전의 (packed-integer vertical max)가 다른 시간에 도입되었습니다.
( pshufw
및 pmaxsw
MMX에 등록 그래서 정말 그들이 SSE1뿐 아니라 MMX CPU 기능 비트를 필요 케트 마이 펜티엄 III와 새로운입니다.)
이는 i에서 arg unsigned max4_mmx(__m64)
를 전달하는 i386 System V ABI와 마찬가지로 C에서 호출 할 수 있습니다 . (전달되지 않음 - 64 시스템 V, 에 !)__m64
mm0
__m64
xmm0
line code bytes
num addr
1 global max4_mmx
2 ;; Input 4x int16_t in mm0
3 ;; output: bitmap in EAX
4 ;; clobbers: mm1, mm2
5 max4_mmx:
6 00000000 0F70C8B1 pshufw mm1, mm0, 0b10110001 ; swap adjacent pairs
7 00000004 0FEEC8 pmaxsw mm1, mm0
8
9 00000007 0F70D14E pshufw mm2, mm1, 0b01001110 ; swap high/low halves
10 0000000B 0FEECA pmaxsw mm1, mm2
11
12 0000000E 0F75C8 pcmpeqw mm1, mm0 ; 0 / -1
13 00000011 0F63C9 packsswb mm1, mm1 ; squish word elements to bytes, preserving sign bit
14
15 00000014 0FD7C1 pmovmskb eax, mm1 ; extract the high bit of each byte
16 00000017 240F and al, 0x0F ; zero out the 2nd copy of the bitmap in the high nibble
17 00000019 C3 ret
size = 0x1A = 26 bytes
이 있었다면 pmovmskw
어떤 일이 저장된 것 packsswb
과 and
(3 + 2 바이트). MMX 레지스터에서 이미 상위 바이트를 0으로 만들기 and eax, 0x0f
때문에 필요하지 않습니다 pmovmskb
. MMX 레지스터는 폭이 8 바이트에 불과하므로 8 비트 AL은 0이 아닌 모든 비트를 포함합니다.
입력이 음수가 아닌 것을 알고 있다면packsswb mm1, mm0
의 상위 4 바이트에서 음이 아닌 부호있는 바이트를 생성 할 수mm1
있으므로 and
after 의 필요성을 피할 수 pmovmskb
있습니다. 따라서 24 바이트입니다.
부호있는 채도를 가진 x86 팩은 입력 및 출력을 부호있는 것으로 취급하므로 항상 부호 비트를 유지합니다. ( https://www.felixcloutier.com/x86/packsswb:packssdw ). 재미있는 사실 : 서명되지 않은 채도를 가진 x86 팩은 여전히 입력 을 서명 된 것으로 취급합니다 . 이것이 PACKUSDW
SSE4.1까지 소개되지 않은 이유 일 수 있지만 MMX / SSE2 이후로 크기와 서명의 다른 3 가지 조합이 존재했습니다.
또는 XMM 레지스터 (및 pshufd
대신 pshufw
) 에 32 비트 정수를 사용 movmskps
하면 팩 / 및 교체를 제외하고 모든 명령에 접두어 바이트가 하나 더 필요합니다 . 그러나 pmaxsd
/ pmaxud
여분의 여분의 바이트가 필요합니다 ...
같은 C에서 호출unsigned max4_sse4(__m128i);
- 64 V 시스템 또는 MSVC의 vectorcall (로 -Gv
패스 둘) __m128i
/ __m128d
/ __m128
XMM의 REGS의 인수로 시작 xmm0
.
20 global max4_sse4
21 ;; Input 4x int32_t in xmm0
22 ;; output: bitmap in EAX
23 ;; clobbers: xmm1, xmm2
24 max4_sse4:
25 00000020 660F70C8B1 pshufd xmm1, xmm0, 0b10110001 ; swap adjacent pairs
26 00000025 660F383DC8 pmaxsd xmm1, xmm0
27
28 0000002A 660F70D14E pshufd xmm2, xmm1, 0b01001110 ; swap high/low halves
29 0000002F 660F383DCA pmaxsd xmm1, xmm2
30
31 00000034 660F76C8 pcmpeqd xmm1, xmm0 ; 0 / -1
32
33 00000038 0F50C1 movmskps eax, xmm1 ; extract the high bit of each dword
34 0000003B C3 ret
size = 0x3C - 0x20 = 28 bytes
또는로 입력을 수락 float
하면 SSE1 명령어를 사용할 수 있습니다. float
포맷은 정수 값의 넓은 범위를 나타낼 수있다 ..
또는 규칙이 너무 많이 구부러진다고 생각되면 0F 5B C0 cvtdq2ps xmm0, xmm0
변환하기 위해 3 바이트 로 시작하여 IEEE binary32로 정확하게 표현할 수있는 모든 정수 float
와 일부 입력이있는 여러 입력 조합에서 작동하는 27 바이트 함수를 작성 하십시오 변환하는 동안 2, 4, 8의 배수로 올림합니다. (따라서 SSE4.1 버전보다 1 바이트 작으며 SSE2 만있는 모든 x86-64에서 작동합니다.)
플로트 입력 중 하나가 NaN의 메모를하는 경우가 maxps a,b
정확하게 구현 (a<b) ? a : b
, 정렬되지 않은에서 두번째 피연산자에서 요소를 유지 . 따라서 입력 위치에 따라 일부 NaN이 입력에 포함되어 있어도 0이 아닌 비트 맵으로 반환 될 수 있습니다.
unsigned max4_sse2(__m128);
37 global max4_sse2
38 ;; Input 4x float32 in xmm0
39 ;; output: bitmap in EAX
40 ;; clobbers: xmm1, xmm2
41 max4_sse2:
42 ; cvtdq2ps xmm0, xmm0
43 00000040 660F70C8B1 pshufd xmm1, xmm0, 0b10110001 ; swap adjacent pairs
44 00000045 0F5FC8 maxps xmm1, xmm0
45
46 00000048 660F70D14E pshufd xmm2, xmm1, 0b01001110 ; swap high/low halves
47 0000004D 0F5FCA maxps xmm1, xmm2
48
49 00000050 0FC2C800 cmpeqps xmm1, xmm0 ; 0 / -1
50
51 00000054 0F50C1 movmskps eax, xmm1 ; extract the high bit of each dword
52 00000057 C3 ret
size = 0x58 - 0x40 = 24 bytes
와의 복사 및 셔플 pshufd
은 여전히 최선의 방법 입니다 . 에서의shufps dst,src,imm8
절반에 대한 입력을 읽습니다 . 또한 비파괴 복사 및 셔플이 두 번 필요하므로 3 바이트 와 / pd가 모두 나옵니다. 스칼라 최대 값으로 좁히는 경우이를 사용할 수 있지만 모든 요소에 최대 값이없는 경우 비교하기 전에 브로드 캐스트하는 데 다른 명령이 필요합니다.dst
dst
movhlps
unpckhps
관련 : SSE4.1 phminposuw
은 uint16_t
XMM 레지스터에서 최소값의 위치와 값을 찾을 수 있습니다 . 최대로 사용하기 위해 65535에서 빼는 것이 승리라고 생각하지 않지만 최대 바이트 또는 부호있는 정수 에 사용하는 것에 대한 SO 답변을 참조하십시오 .