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
純粋なものをextern "C"と定義したのはなぜですか?コンパイラに "C"呼び出し規約に従わせるように指示しているので、これが違いの理由と思われます。 –
このサイト(https://www.cs.uaf.edu/2011/fall/cs301/lecture/10_12_asm_c.html)から推薦されたので、名前のマングリングについて心配する必要はありませんでした。 –
'immintrin.h 'が' __m128i'をどのように定義しているかに依存しない 'pretty'を書く普通の方法は' _mm_set1_epi64x(b) 'です。 gccはstore/'vmovddup'を選択します(レイテンシが悪く、ALUが1つ少なくなります)。clangは' vmovq xmm0、rdi'/'vpbroadcastq xmm0、xmm0'を選択します(Haswellでは2つのポート5 uop)。 –