繁体   English   中英

为什么 Mac ABI 对于 x86-32 需要 16 字节堆栈 alignment?

[英]Why does the Mac ABI require 16-byte stack alignment for x86-32?

我可以理解旧 PPC RISC 系统甚至 x86-64 的这一要求,但对于久经考验的旧 x86? 在这种情况下,堆栈只需要在 4 字节边界上对齐。 是的,一些 MMX/SSE 指令需要 16 字节对齐,但如果这是被调用者的要求,那么它应该确保对齐正确。 为什么要让每个调用者都负担这个额外的要求? 这实际上会导致性能下降,因为每个呼叫站点都必须管理此要求。 我错过了什么吗?

更新:在对此进行了更多调查并咨询了一些内部同事之后,我对此有一些理论:

  1. PPC、x86 和 x64 版本操作系统之间的一致性
  2. 似乎 GCC 代码生成器现在始终执行 sub esp,xxx 然后将数据“移动”到堆栈上,而不是简单地执行“推送”指令。 这实际上在某些硬件上可能更快。
  3. 虽然这确实使调用站点有点复杂,但在使用调用者清理堆栈的默认“cdecl”约定时几乎没有额外的开销。

我对最后一项的问题是,对于依赖于被调用方清理堆栈的调用约定,上述要求确实“丑化”了代码生成。 例如,某些编译器决定实现更快的基于寄存器的调用样式以供其内部使用(即任何不打算从其他语言或源调用的代码)? 这种堆栈对齐的事情可能会抵消通过在寄存器中传递一些参数所获得的一些性能提升。

更新:到目前为止,唯一真正的答案是一致性,但对我来说,这有点太容易了。 我在 x86 架构方面拥有超过 20 年的经验,如果一致性,而不是性能或其他具体的东西,真的是原因,那么我恭敬地建议开发人员要求它有点天真。 他们忽略了近三年的工具和支持。 特别是如果他们希望工具供应商能够快速轻松地为他们的平台调整他们的工具(也许不是......它苹果......),而不必跳过几个看似不必要的障碍。

我会在另一天左右给出这个话题然后关闭它......

有关的

来自“英特尔® 64 和 IA-32 架构优化参考手册”,第 4.4.2 节:

“为了获得最佳性能,流式 SIMD 扩展和流式 SIMD 扩展 2 要求其 memory 操作数与 16 字节边界对齐。与对齐的数据相比,未对齐的数据可能会导致显着的性能损失。”

来自附录 D:

“重要的是要确保堆栈帧在 function 条目时与 16 字节边界对齐,以在整个 function 调用中保持本地 __m128 数据、参数和 XMM 寄存器溢出位置对齐。”

http://www.intel.com/Assets/PDF/manual/248966.pdf

我不确定,因为我没有第一手证据,但我相信原因是 SSE。 如果您的缓冲区已经在 16 字节边界上对齐(movps 与 movups),那么 SSE 会快得多,并且任何 x86 对于 mac os x 至少具有 sse2。 它可以由应用程序用户来处理,但成本非常高。 如果在 ABI 中强制执行它的总成本不是太高,那么它可能是值得的。 SSE 在 mac os X 中非常普遍地使用:加速框架等......

我相信这是为了让它与 x86-64 ABI 保持一致。

首先,请注意 16 字节 alignment 是 Apple 引入 System V IA-32 ABI 的一个例外。

只有在调用系统函数时才需要堆栈 alignment,因为许多系统库正在使用需要 16 字节 alignment 的 SSE 或 Altivec 扩展。 我在libgmalloc MAN 页面中找到了一个明确的参考。

您可以按照您想要的方式完美地处理您的堆栈帧,但是如果您尝试使用未对齐的堆栈调用系统 function,您最终会收到一条misaligned_stack_error消息。

编辑:作为记录,您可以使用mstack-realign选项在使用 GCC 编译时摆脱 alignment 问题。

这是一个效率问题。

确保使用新 SSE 指令的每个 function 中的堆栈是 16 字节对齐的,这会增加使用这些指令的大量开销,从而有效地降低性能。

另一方面,始终保持堆栈 16 字节对齐可确保您可以自由使用 SSE 指令而不会降低性能。 这没有成本(至少在说明中衡量的成本)。 它只涉及更改 function 序言中的常数。

浪费堆栈空间很便宜,它可能是缓存中最热的部分。

我的猜测是,Apple 相信每个人都只使用 XCode (gcc) 来为您对齐堆栈。 因此,要求堆栈对齐以便 kernel 不必只是一个微优化。

嗯,OS X ABI 不是也做有趣的 RISC,比如在寄存器中传递小结构吗?

这表明与其他平台理论的一致性。

想想看,FreeBSD 系统调用 api 也对齐 64 位值。 (例如 lseek 和 mmap)

虽然我无法真正回答您的问题,但您可能会发现以下站点上的手册很有用:

http://www.agner.org/optimize/

关于 ABI,请特别查看:

http://www.agner.org/optimize/calling_conventions.pdf

希望这很有用。

为了保持 kernel 中的一致性。 这允许相同的 kernel 无需修改即可在多个架构上启动。

不知道为什么没有人考虑过从传统的基于 PowerPC 的平台轻松移植的可能性?

读这个:

http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/100-32-bit_PowerPC_Function_Calling_Conventions/32bitPowerPC.html#//apple_ref/doc/uid/TP40002438-SW20

然后放大到“32-bit PowerPC Function Calling Conventions”,最后是这个:

“这些是 32 位 PowerPC 环境中可用的嵌入 alignment 模式:

Power alignment 模式源自 IBM XLC 编译器用于 AIX 操作系统的 alignment 规则。 这是在 AIX 和 Mac OS X 上使用的 GCC 的 PowerPC 架构版本的默认 alignment 模式。因为这种模式最有可能在不同供应商的 PowerPC 架构编译器之间兼容,所以它通常与在不同供应商之间共享的数据结构一起使用不同的节目。”

鉴于 OSX 遗留的基于 PowerPC 的背景,可移植性是一个主要的考虑因素——它要求一直遵循 AIX 的 XLC 编译器的约定。 当您考虑需要确保所有工具和应用程序能够以最少的返工协同工作时,我认为尽可能坚持使用相同的旧 ABI 是很重要的。

这给出了哲学,进一步阅读是明确提到的规则(“Prolog and Epilog”):

被调用的 function 负责分配自己的栈帧,确保在栈中保留 16 字节的 alignment。 此操作由一段称为 prolog 的代码完成,编译器将其放在子例程主体之前。 在子例程主体之后,编译器放置一个结语以将处理器恢复到子例程调用之前的 state。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM