2016-03-30 5 views
1

GCCのasmを使用して実際のアプリケーションを実行するための基本機能をいくつか書いています。関数にラップされたGASインラインアセンブリが、純粋なアセンブリ関数とは異なる呼び出し元の命令を生成する理由

私の関数pretty,wrap、およびpureは、64ビット整数を128ビットベクトルにアンパックするのと同じ命令を生成します。 およびwrapをそれぞれ呼び出すadd1およびadd2も同じ命令を生成します。しかしadd3は、xmm0レジスタを別のxmmレジスタにコピーするのではなく、スタックにプッシュして保存することによって異なります。コンパイラはの詳細を見ることができ、他のxmmレジスタのどれもが詰まっていないことを知ることができるので、私はこれを理解しません。ここで

はC++

#include <immintrin.h> 

__m128i pretty(long long b) { return (__m128i){b,b}; } 

__m128i wrap(long long b) { 
    asm ("mov qword ptr [rsp-0x10], rdi\n" 
     "vmovddup xmm0, qword ptr [rsp-0x10]\n" 
     : 
     : "r"(b) 
     ); 
} 

extern "C" __m128i pure(long long b); 
asm (".text\n.global pure\n\t.type pure, @function\n" 
    "pure:\n\t" 
    "mov qword ptr [rsp-0x10], rdi\n\t" 
    "vmovddup xmm0, qword ptr [rsp-0x10]\n\t" 
    "ret\n\t" 
    ); 

__m128i add1(__m128i in, long long in2) { return in + pretty(in2);} 
__m128i add2(__m128i in, long long in2) { return in + wrap(in2);} 
__m128i add3(__m128i in, long long in2) { return in + pure(in2);} 

g++ -c so.cpp -march=native -masm=intel -O3 -fno-inlineでコンパイルし、objdump -d -M intel so.o | c++filtで分解しています。

so.o:  file format elf64-x86-64 


Disassembly of section .text: 

0000000000000000 <pure>: 
    0: 48 89 7c 24 f0   mov QWORD PTR [rsp-0x10],rdi 
    5: c5 fb 12 44 24 f0  vmovddup xmm0,QWORD PTR [rsp-0x10] 
    b: c3      ret 
    c: 0f 1f 40 00    nop DWORD PTR [rax+0x0] 

0000000000000010 <pretty(long long)>: 
    10: 48 89 7c 24 f0   mov QWORD PTR [rsp-0x10],rdi 
    15: c5 fb 12 44 24 f0  vmovddup xmm0,QWORD PTR [rsp-0x10] 
    1b: c3      ret 
    1c: 0f 1f 40 00    nop DWORD PTR [rax+0x0] 

0000000000000020 <wrap(long long)>: 
    20: 48 89 7c 24 f0   mov QWORD PTR [rsp-0x10],rdi 
    25: c5 fb 12 44 24 f0  vmovddup xmm0,QWORD PTR [rsp-0x10] 
    2b: c3      ret 
    2c: 0f 1f 40 00    nop DWORD PTR [rax+0x0] 

0000000000000030 <add1(long long __vector(2), long long)>: 
    30: c5 f8 28 c8    vmovaps xmm1,xmm0 
    34: 48 83 ec 08    sub rsp,0x8 
    38: e8 00 00 00 00   call 3d <add1(long long __vector(2), long long)+0xd> 
    3d: 48 83 c4 08    add rsp,0x8 
    41: c5 f9 d4 c1    vpaddq xmm0,xmm0,xmm1 
    45: c3      ret 
    46: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 
    4d: 00 00 00 

0000000000000050 <add2(long long __vector(2), long long)>: 
    50: c5 f8 28 c8    vmovaps xmm1,xmm0 
    54: 48 83 ec 08    sub rsp,0x8 
    58: e8 00 00 00 00   call 5d <add2(long long __vector(2), long long)+0xd> 
    5d: 48 83 c4 08    add rsp,0x8 
    61: c5 f9 d4 c1    vpaddq xmm0,xmm0,xmm1 
    65: c3      ret 
    66: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 
    6d: 00 00 00 

0000000000000070 <add3(long long __vector(2), long long)>: 
    70: 48 83 ec 18    sub rsp,0x18 
    74: c5 f8 29 04 24   vmovaps XMMWORD PTR [rsp],xmm0 
    79: e8 00 00 00 00   call 7e <add3(long long __vector(2), long long)+0xe> 
    7e: c5 f9 d4 04 24   vpaddq xmm0,xmm0,XMMWORD PTR [rsp] 
    83: 48 83 c4 18    add rsp,0x18 
    87: c3      ret 
+1

純粋なものをextern "C"と定義したのはなぜですか?コンパイラに "C"呼び出し規約に従わせるように指示しているので、これが違いの理由と思われます。 –

+0

このサイト(https://www.cs.uaf.edu/2011/fall/cs301/lecture/10_12_asm_c.html)から推薦されたので、名前のマングリングについて心配する必要はありませんでした。 –

+0

'immintrin.h 'が' __m128i'をどのように定義しているかに依存しない 'pretty'を書く普通の方法は' _mm_set1_epi64x(b) 'です。 gccはstore/'vmovddup'を選択します(レイテンシが悪く、ALUが1つ少なくなります)。clangは' vmovq xmm0、rdi'/'vpbroadcastq xmm0、xmm0'を選択します(Haswellでは2つのポート5 uop)。 –

答えて

2

GCCはアセンブリ言語を理解していません。

pureは外部関数であるため、変更するレジスタを決定することはできません。したがって、ABIはxmmのレジスタがすべて変更されたと仮定する必要があります。

wrapasm文が切り詰めとして未定義の動作を有しxmm0及び切り詰め又は(またはbに依存してもしなくてもよい値)出力、および機能が全くreturnステートメントを持っていないように記載されていない[rsp-0x10]

編集:ABIはインラインアセンブリには適用されません。コマンドラインから-fno-inlineを削除すると、プログラムが機能しなくなることが予想されます。

+0

「xmm0」はOKだったが、今私はそれについて考えると思う。私は '[rsp-0x10]'に該当する 'red zone'は、関数呼び出し間の一貫性についての保証がないと考えました。 returnステートメントの欠如は、戻り値レジスタとして 'xmm0'を指定する呼び出し規約によってカバーされないでしょうか?関数の宣言で記述された値を取り出すために 'wrap'の呼び出し元がどこに行くのでしょうか? –

+0

'xmm0'と' memory'をクローバーリストに追加しました(それは修正されますか?)唯一の変更は 'vmovddup'の直後に' vzeroupper'命令を追加したことです。 –

+0

@chewsocks:gccにあなたがしたいと言っているようにも見えないので、あなたは[inline asmから赤いゾーンを壊さないでください](http://stackoverflow.com/a/34522750/224132)そうする。あなたが 'vmovddup'のためにメモリに値を入れたいなら' asm( "vmovddup%[result]、%[src]":[result] "= x"(出力):[src] "m" (b));出力を返す; '。次に、gccは使用するメモリを決定し、スタック以外の場所から直接ロードすることができます。値が既にメモリに入っていれば、gccにロード/ストア/リロードを強制しません。 [godboltの実例](https://godbolt.org/g/n9uEaO)。 –

関連する問題