rdi 和 rsi 调用者保存还是被调用者保存的寄存器?
Posted
技术标签:
【中文标题】rdi 和 rsi 调用者保存还是被调用者保存的寄存器?【英文标题】:Are rdi and rsi caller saved or callee saved registers? 【发布时间】:2019-08-18 15:09:14 【问题描述】:从*** x86 调用约定,它说对于 Microsoft x64 调用约定:
寄存器 RBX、RBP、RDI、RSI、RSP、R12、R13、R14 和 R15 被视为非易失性(被调用方保存)。
但对于 System V AMD64 ABI:
如果被调用者希望使用寄存器 RBX、RBP 和 R12-R15,则必须在将控制权返回给调用者之前恢复它们的原始值。
它没有提到任何关于 rdi 和 rsi 的内容。
我还了解到 %rax、%rcx、%rdx、%rdi、%rsi、%rsp 和 %r8-r11 被认为是调用者保存寄存器 (来自pdf)
我的问题是,不同平台的调用约定是否不同?(我尝试在 asm 中为 unix 环境编写一些 libc 函数)
我找不到任何讨论该主题的文章,该主题的资源也会有所帮助。我想知道这些约定的优缺点。
【问题讨论】:
【参考方案1】:是的,在我知道的所有函数调用约定中,传递参数的寄存器都是调用破坏的。(除了系统调用调用约定,通常所有 regs 都会保留,除了一个返回值,包括参数传递。除了 x86-64 syscall
破坏 RCX 和 R11...)
特别是在 x86-64 System V 中,除 RBX、RBP、RSP 和 R12-R15 之外的所有寄存器都被调用破坏。 (包括 xmm0-15、x87/mmx 寄存器,以及 AVX512 zmm0-31 和 k0-k7 掩码寄存器。)
What registers are preserved through a linux x86-64 function call 显示 ABI 文档中的表格。
调用约定/ABI 将寄存器的状态定义为调用保留或调用破坏。不同的约定可以做出不同的选择。
是的,Microsoft Windows 选择了与其他所有人不同的调用约定:Why does Windows64 use a different calling convention from all other OSes on x86-64? 在 Windows x64 中,RDI 是保留调用的,就像在大多数 32 位调用约定中一样。
但在 x86-64 System V 中,设计人员从头开始选择寄存器,并且(正如我对该链接问题的回答所示)发现将 RDI 和 RSI 用于前 2 个参数可以节省指令(使用早期 x86 构建 SPECint 时) gcc 的 -64 端口)。可能是因为当时 gcc 喜欢使用 rep stosd
内联 memset
或 memcpy
,或者库实现使用了它。
(说 RDI本质上被调用破坏是没有意义的,x86-64 ISA 没有定义它。这取决于每个平台来选择。)
术语:
我讨厌“调用者保存”与“被调用者保存”的术语:从 2 个不同的角度(调用者和被调用者)思考会令人困惑,并且错误地暗示每个寄存器确实都保存在某处每个call
。此外,名称仅相差 1 个字母,因此在阅读时在视觉上不是很明显。
“preserved”或“clobbered”很棒;他们从任何一个角度工作。 (被调用者会对你的 regs 做什么,或者你可以对调用者的 regs 做什么。)此外,它们是不言自明的。
【讨论】:
所以如果我在 unix 环境中覆盖 rdi 并且不恢复它,可以吗? 哦,我刚刚阅读了您的更新答案,保留和破坏的术语听起来很棒,谢谢。 @Sayakura:是的,再次更新了 SysV ABI pdf 中的表格链接,该表格明确记录 RDI 和 RSI 为 call-clobbered。我之前很着急,没有看到你的问题部分,你只发现它们被用于参数传递,但没有确认这意味着它们被调用破坏了。它们本质上不是一回事,但在我见过的所有调用约定中,它们都是。 是的。这里的操作系统不同。 RSI 和 RDI 在 Unix x64 中被破坏,但在 Windows 中没有。有关完整概述,请参阅agner.org/optimize/calling_conventions.pdf以上是关于rdi 和 rsi 调用者保存还是被调用者保存的寄存器?的主要内容,如果未能解决你的问题,请参考以下文章