在不同的编译器中定义汇编函数
Define a function on assembly in different compilers
直到现在,我使用内联 asm
破坏它不是获得良好性能的最佳选择。我从汇编开始,但我在我的机器 (GCC) 中编程,但结果代码是 运行 在其他 (ICC) 中,都是 64 位 (Sandy Bridge & Haswell)。
要调用不带参数的函数,我们可以使用 CALL
,但我不太了解如何调用带参数的函数,因此我尝试使用内联 __asm__
在所有函数里面。是个不错的选择?
我的函数:
void add_N(size_t *cnum, size_t *ap, size_t *bp, long &n, unsigned int &c){
__asm__(
//Insert my code here
);
}
当我看到 反汇编(使用 GCC)时,我有:
add_N(unsigned long*, unsigned long*, unsigned long*, long&, unsigned int&):
0x100001ff0 <+0>: pushq %rbp
0x100001ff1 <+1>: movq %rsp, %rbp
0x100001ff4 <+4>: movq %rdi, -0x8(%rbp)
0x100001ff8 <+8>: movq %rsi, -0x10(%rbp)
0x100001ffc <+12>: movq %rdx, -0x18(%rbp)
0x100002000 <+16>: movq %rcx, -0x20(%rbp)
0x100002004 <+20>: movq %r8, -0x28(%rbp)
0x100002008 <+24>: popq %rbp
0x100002009 <+25>: retq
我明白发生了什么。如果函数签名相同,不同的 compilers/microarchitectures 是否总是关联相同的寄存器地址?
然后在我的函数中放入一些代码(不是 __ASM__
代码),然后反汇编 PUSH
很多寄存器。为什么会这样?为什么我不需要推送%rax
和%rsi
(例如),而需要推送r13
、r14
和r15
?
如果我需要推送 r**
寄存器,我可以在 inline __asm__
中做吗?
0x100001ea0 <+0>: pushq %rbp
0x100001ea1 <+1>: movq %rsp, %rbp
0x100001ea4 <+4>: pushq %r15
0x100001ea6 <+6>: pushq %r14
0x100001ea8 <+8>: pushq %r13
0x100001eaa <+10>: pushq %r12
0x100001eac <+12>: pushq %rbx
0x100001ead <+13>: movq %rdi, -0x30(%rbp)
0x100001eb1 <+17>: movq %rsi, -0x38(%rbp)
0x100001eb5 <+21>: movq %rdx, -0x40(%rbp)
0x100001eb9 <+25>: movq %rcx, -0x48(%rbp)
0x100001ebd <+29>: movq %r8, -0x50(%rbp)
对于最后一个问题 - 是的,只要它们使用相同的 ABI,它就会对参数使用相同的寄存器。 Linux x86_64 ABI 在此处定义:http://www.x86-64.org/documentation/abi.pdf 并且所有编译器都必须符合它。具体来说,您对第 16 页 - 参数传递感兴趣。
Windows 我相信 ABI 略有不同。因此,例如,您不能 运行 在 Linux 和 运行 上编译 Windows 上的程序或库(尽管还有一些其他原因)。
有关 gcc 内联汇编的详细信息,请查看一些现有教程,因为它的主题很长。这是一个好的开始:http://asm.sourceforge.net/articles/rmiyagi-inline-asm.txt
直到现在,我使用内联 asm
破坏它不是获得良好性能的最佳选择。我从汇编开始,但我在我的机器 (GCC) 中编程,但结果代码是 运行 在其他 (ICC) 中,都是 64 位 (Sandy Bridge & Haswell)。
要调用不带参数的函数,我们可以使用 CALL
,但我不太了解如何调用带参数的函数,因此我尝试使用内联 __asm__
在所有函数里面。是个不错的选择?
我的函数:
void add_N(size_t *cnum, size_t *ap, size_t *bp, long &n, unsigned int &c){
__asm__(
//Insert my code here
);
}
当我看到 反汇编(使用 GCC)时,我有:
add_N(unsigned long*, unsigned long*, unsigned long*, long&, unsigned int&):
0x100001ff0 <+0>: pushq %rbp
0x100001ff1 <+1>: movq %rsp, %rbp
0x100001ff4 <+4>: movq %rdi, -0x8(%rbp)
0x100001ff8 <+8>: movq %rsi, -0x10(%rbp)
0x100001ffc <+12>: movq %rdx, -0x18(%rbp)
0x100002000 <+16>: movq %rcx, -0x20(%rbp)
0x100002004 <+20>: movq %r8, -0x28(%rbp)
0x100002008 <+24>: popq %rbp
0x100002009 <+25>: retq
我明白发生了什么。如果函数签名相同,不同的 compilers/microarchitectures 是否总是关联相同的寄存器地址?
然后在我的函数中放入一些代码(不是 __ASM__
代码),然后反汇编 PUSH
很多寄存器。为什么会这样?为什么我不需要推送%rax
和%rsi
(例如),而需要推送r13
、r14
和r15
?
如果我需要推送 r**
寄存器,我可以在 inline __asm__
中做吗?
0x100001ea0 <+0>: pushq %rbp
0x100001ea1 <+1>: movq %rsp, %rbp
0x100001ea4 <+4>: pushq %r15
0x100001ea6 <+6>: pushq %r14
0x100001ea8 <+8>: pushq %r13
0x100001eaa <+10>: pushq %r12
0x100001eac <+12>: pushq %rbx
0x100001ead <+13>: movq %rdi, -0x30(%rbp)
0x100001eb1 <+17>: movq %rsi, -0x38(%rbp)
0x100001eb5 <+21>: movq %rdx, -0x40(%rbp)
0x100001eb9 <+25>: movq %rcx, -0x48(%rbp)
0x100001ebd <+29>: movq %r8, -0x50(%rbp)
对于最后一个问题 - 是的,只要它们使用相同的 ABI,它就会对参数使用相同的寄存器。 Linux x86_64 ABI 在此处定义:http://www.x86-64.org/documentation/abi.pdf 并且所有编译器都必须符合它。具体来说,您对第 16 页 - 参数传递感兴趣。
Windows 我相信 ABI 略有不同。因此,例如,您不能 运行 在 Linux 和 运行 上编译 Windows 上的程序或库(尽管还有一些其他原因)。
有关 gcc 内联汇编的详细信息,请查看一些现有教程,因为它的主题很长。这是一个好的开始:http://asm.sourceforge.net/articles/rmiyagi-inline-asm.txt