web-dev-qa-db-ja.com

128ビットのイミディエートをXMMレジスタに移動する方法

これにはすでに 質問 がありますが、「あいまい」として閉じられたので、新しいものを開きます-答えを見つけました。おそらく他の人にも役立つでしょう。

問題は、XMMレジスタを128ビットの即時(定数)値で初期化するための一連のアセンブリコードをどのように記述するかということです。

24
Virgil

Agner FogのマニュアルのAssemblyを使用したさまざまな定数の生成について読むことができることを追加したかっただけです アセンブリ言語でのサブルーチンの最適化 、定数の生成、セクション13.8、134ページ。

18
Norbert P.

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

10
Paul R

それを行う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
7
Pierre

命令ストリームに定数を埋め込むには複数の方法があります。

  1. 即値オペランドを使用する
  2. pC相対アドレスからロードする

したがって、即時ロード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によって提供された最初のバージョンは、入手できる最速です。

6
FrankH.

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
6
Virgil