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
ただけなので、最悪の場合の反復の実行時間がありました。