これにはすでに 質問 がありますが、「あいまい」として閉じられたので、新しいものを開きます-答えを見つけました。おそらく他の人にも役立つでしょう。
問題は、XMMレジスタを128ビットの即時(定数)値で初期化するための一連のアセンブリコードをどのように記述するかということです。
Agner FogのマニュアルのAssemblyを使用したさまざまな定数の生成について読むことができることを追加したかっただけです アセンブリ言語でのサブルーチンの最適化 、定数の生成、セクション13.8、134ページ。
movaps
命令を1つだけ使用して、次のように実行できます。
_.section .rodata # put your constants in the read-only data section
.p2align 4 # align to 16 = 1<<4
LC0:
.long 1082130432
.long 1077936128
.long 1073741824
.long 1065353216
.text
foo:
movaps LC0(%rip), %xmm0
_
データロードを使用してロードすることは、特に必要な命令の数のために、通常、命令ストリームに埋め込むよりも望ましい方法です。これは、CPUが実行するためのいくつかの追加のuopsです。これは、2、3シフトのすべてから生成できない任意の定数の場合です。
簡単な場合は、別のセクションではなく、jitコンパイルする関数の直前または直後に定数を配置できます。ただし、CPUはL1d/L1iキャッシュとTLBを分割しているため、通常、定数を命令とは別にグループ化するのが最善です。
定数の両方の半分が同じである場合は、SSE3でブロードキャストロードできますmovddup (m64), %xmm0
。
それを行う10000の方法の1つとして、SSE4.1 pinsrq
を使用します
mov rax, first half
movq xmm0, rax ; better than pinsrq xmm0,rax,0 for performance and code-size
mov rax, second half
pinsrq xmm0, rax, 1
命令ストリームに定数を埋め込むには複数の方法があります。
したがって、即時ロードをXMM
レジスタに実行する方法はありませんが、「すぐ隣」に格納されている値からPC相対ロード(64ビット)を実行することは可能です。コードが実行される場所。それは次のようなものを作成します:
.align 4
.val:
.long 0x12345678
.long 0x9abcdef0
.long 0xfedbca98
.long 0x76543210
func:
movdqa .val(%rip), %xmm0
分解するとき:
0000000000000000: 0:78 56 34 12 f0 de bc 9a 8:98 ca db fe 10 32 54 76 0000000000000010: 10 :66 0f 6f 05 e8 ff ff movdqa -0x18(%rip)、%xmm0#0
これは完全にコンパクト、23バイトです。
他のオプションは、スタックに値を作成し、そこから再度ロードすることです。 %rip
-相対メモリアクセスがない32ビットx86でも、24バイトでそれを行うことができます(スタックポインタがエントリ時に整列されていると仮定します。そうでない場合は、整列されていないロードが必要です)。
00000000: 0:68 78 56 3412プッシュ$ 0x12345678 5:68 f0 de bc9aプッシュ$ 0x9abcdef0 a:68 98 ca dbfeプッシュ$ 0xfedbca98 f:68 10 32 5476プッシュ$ 0x76543210 14:66 0f 6f 04 24 movdqa(%esp)、%xmm0
64ビット(関数エントリでのスタックポインターの配置はABIによって保証されています)では、27バイトかかります。
0000000000000000: 0:48 b8 f0 de bc 9a 78 56 34 12 movabs $ 0x123456789abcdef0、%rax a:50プッシュ%rax b:48 b8 10 32 54 76 98 ba dc fe movabs $ 0xfedcba9876543210、%rax 15:50プッシュ%rax 16:66 0f 6f 04 24 movdqa(%rsp)、%xmm0
これらのいずれかをMOVLHPS
バージョンと比較すると、最も長いことがわかります。
0000000000000000: 0:48 b8 f0 de bc 9a 78 56 34 12 movabs $ 0x123456789abcdef0、%rax a:66 48 0f 6e c0 movq%rax、%xmm0 f: 48 b8 10 32 54 76 98 ba dc fe movabs $ 0xfedcba9876543210、%rax 19:66 48 0f 6e c8 movq%rax、%xmm1 1e:0f 16 c1 movlhps%xmm1、%xmm0
33バイトで。
命令メモリから直接ロードするもう1つの利点は、movdqa
が以前のものに依存しないことです。おそらく、@ Paul Rによって提供された最初のバージョンは、入手できる最速です。
2つのレジスタ(たとえば、xmm0とxmm1)を即値の2つの64ビット半分で初期化するための最良の解決策(特にSSE2に固執したい場合、つまりAVXの使用を回避したい場合)は、MOVLHPS xmm0、xmm1を実行します。 64ビット値を初期化する最も簡単な解決策は、汎用レジスタ(AXなど)を使用してから、MOVQを使用してその値をXMMレジスタに転送することです。したがって、シーケンスは次のようになります。
MOV RAX, <first_half>
MOVQ XMM0, RAX
MOV RAX, <second_half>
MOVQ XMM1, RAX
MOVLHPS XMM0,XMM1