Eli*_*mal 6 assembly x86-64 abi cpu-registers calling-convention
我最近做了很多x64汇编编程(在Linux上),用于与我的C/C++程序集成.
由于我主要关心效率,我喜欢尽可能少地使用不同的寄存器/存储器地址,以及尝试不创建任何堆栈帧或保留寄存器(每个周期计数).
根据cdecl r10和r11寄存器不保留,我希望在我的函数中使用它们作为临时变量,最好不要保留.它是否会导致任何编译器出现任何无法解决的问题/错误(到目前为止还没有遇到任何问题,但这是一个问题)?
Pet*_*des 11
x86-64 System V ABI不会调用其调用约定"cdecl".它只是x86-64 SysV调用约定.字符串"cdecl"未出现在ABI doc中.
r11 是一个临时的,名为call-clobbered寄存器.
r10也是一个呼叫破坏寄存器.ABI说"用于传递函数的静态链指针",但C不使用它,gcc和clang生成的代码可以自由使用r10而不保存/恢复它.(gcc确实将其r10作为其trampoline的一部分用于指向GNU C嵌套函数的函数指针,用于指向外部作用域的堆栈帧的指针.堆栈上的机器代码的蹦床是一个hack,但这确实是一个静态链指针;对嵌套函数有适当支持的语言可能让调用者知道它(如lambda/closure),并在r10使用指向嵌套函数的指针时传入一个值.)
r10并且r10不是arg传递寄存器,与其他call-clobbered寄存器不同,因此"wrapper"函数可以使用它们(特别是r10)而不保存/恢复任何东西.
在正常功能中,RBX,RBP和RSP与R12..R15一起被呼叫保留.所有其他人都可能被破坏而不保存/恢复. (包括xmm/ymm0..15和zmm0..31,以及x87堆栈和RFLAGS中的条件代码).
请注意r10,即使使用32位操作数大小(例如r11),也需要REX前缀.如果你有一些64位非指针整数,那么确保将它们保存在r8..r11中,因为无论如何你总是需要一个64位操作数大小的REX前缀.
较小的代码大小通常不会更糟,有时有助于解码和uop-cache密度以及L1i缓存密度.RAX,RCX,RDX,RSI,RDI应该是您的临时注册的首选.(并且除非你需要64位,否则使用32位操作数大小.例如,这r11是将RAX归零的正确方法.Silvermont不认为r8..15是归零的惯用语,所以xor r10d, r10d即使它不能保存代码大小也要使用.)
如果你的低寄存器用完了,理想情况下使用xor eax,eax/ xor r10,r10用于通常与64位操作数大小(或VEX前缀)一起使用的东西.例如,指向64位数据的指针或指向指针的指针. xor r10d,r10d需要REX前缀而不需要r10.但是r11和mov eax, [r10]大小相同.
它很难获得很多,因为你经常需要在不同的组合中使用不同的值,比如最终使用mov eax, [rdi]或者其他什么,但是如果你想全力以赴进行优化,那么考虑一下代码大小.也许还要考虑指令边界的位置以及它如何适应uop缓存.
有关编写高效代码的提示,请参阅x86标记wiki,尤其是http://agner.org/optimize/.