x86 asm 기능 : 14 바이트의 기계 코드
uint64_t 버전 : 24 바이트
x86-64 SysV 호출 규칙 ( x
in edi
)이지만 동일한 기계 코드도 32 비트 모드에서 작동합니다. ((가) 어디 lea
로서 디코딩한다 lea eax, [edi + eax*2]
, 동일한 결과를 제공한다 ).
0000000000000040 <onemask_even>:
40: 89 f8 mov eax,edi
42: 25 55 55 55 55 and eax,0x55555555
47: 29 c7 sub edi,eax
49: d1 ef shr edi,1
4b: 8d 04 47 lea eax,[rdi+rax*2]
4e: c3 ret
4f: <end>
0x4f - 0x40
= 14 바이트
이것은 xnor의 뛰어난 마스크 원스 아이디어를 반대 방향으로 사용하여 얻은 컴파일러 출력 입니다. (그리고 반대 용어 : 낮은 비트는 비트 0이며, 홀수는 아닙니다.)
unsigned onemask_even(unsigned x) {
unsigned emask = ~0U/3;
unsigned e = (x & emask);
return e*2 + ((x - e) >> 1);
}
컴파일러의 기능에 대한 개선 사항을 찾지 못했습니다. mov eax, 0x555...
/ 로 작성했을 수도 and eax, edi
있지만 길이는 같습니다.
64 비트 정수의 동일한 함수에는 24 바이트가 필요합니다 (godbolt 링크 참조). movabs rax, 0x55...
레지스터에서 마스크를 생성하는 데 10 바이트보다 짧은 방법은 없습니다 . (x86의 div
지시는 어리석기 때문에 서명하지 않은 모든 사람을 3으로 나누는 것은 도움이되지 않습니다.)
마스크를 rax로 생성하는 루프를 만들었지 만 10 바이트입니다 (정확하게는 길이와 동일 mov imm64
).
# since 0x55 has its low bit set, shifting it out the top of RAX will set CF
0000000000000000 <swap_bitpairs64>:
0: 31 c0 xor eax,eax ; old garbage in rax could end the loop early
0000000000000002 <swap_bitpairs64.loop>:
2: 48 c1 e0 08 shl rax,0x8
6: b0 55 mov al,0x55 ; set the low byte
8: 73 f8 jnc 2 <swap_bitpairs64.loop> ; loop until CF is set
000000000000000a <swap_bitpairs64.rest_of_function_as_normal>:
# 10 bytes, same as mov rax, 0x5555555555555555
# rax = 0x5555...
a: 48 21 f8 and rax,rdi
...
기존의 바이트 중 rax
하위 비트 세트 가 없다는 것을 알고 있다면을 건너 뛸 수 xor
있으며 길이는 8 바이트입니다.
이 답변의 이전 버전에는 loop
insn을 사용하여 10 바이트 루프가 있었지만 0xFFFFFFFFFFFFFF08
설정 만했기 때문에 최악의 반복 런타임이있었습니다 cl
.