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

时间:2009-03-04 21:12:41

标签: macos stack alignment calling-convention abi

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

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

  1. 操作系统的PPC,x86和x64版本之间的一致性
  2. 似乎GCC codegen现在始终执行子esp,xxx然后将数据“移动”到堆栈而不是简单地执行“推送”指令。在某些硬件上,这实际上可能更快。
  3. 虽然这会使调用站点变得复杂,但是当使用默认的“cdecl”约定时,调用者清理堆栈时会产生很少的额外开销。
  4. 我对最后一项的问题是,对于依赖于被调用者清理堆栈的调用约定,上述要求真的“uglifies”codegen。例如,某些编译器决定为自己的内部使用实现更快的基于寄存器的调用样式(即任何不打算从其他语言或源调用的代码)?这种堆栈对齐可能会通过在寄存器中传递一些参数来抵消一些性能提升。

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

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

    相关

10 个答案:

答案 0 :(得分:29)

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

“为了获得最佳性能,Streaming SIMD Extensions和Streaming SIMD Extensions 2要求其内存操作数与16字节边界对齐。与对齐数据相比,未对齐数据可能会导致严重的性能损失。”

来自附录D:

“在函数输入时确保堆栈帧与16字节边界对齐非常重要,以保持本地__m128数据,参数和XMM寄存器溢出位置在整个函数调用中保持一致。”

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

答案 1 :(得分:6)

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

答案 2 :(得分:5)

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

答案 3 :(得分:3)

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

只有在调用系统函数时才需要堆栈对齐,因为许多系统库使用SSE或Altivec扩展,这需要16字节对齐。我在libgmalloc MAN page中找到了明确的引用。

您可以按照自己的方式完美地处理堆栈帧,但如果您尝试使用未对齐的堆栈调用系统函数,则最终会出现 misaligned_stack_error 消息。

修改 对于记录,您可以使用mstack-realign选项在使用GCC进行编译时消除对齐问题。

答案 4 :(得分:2)

这是一个效率问题。

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

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

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

答案 5 :(得分:2)

我的猜测是Apple认为每个人都只使用XCode(gcc)来为你调整堆栈。因此,要求堆栈对齐,因此内核不需要只是一个微优化。

答案 6 :(得分:1)

嗯,没有OS X ABI也做有趣的RISC就像在寄存器中传递小结构一样吗?

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

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

答案 7 :(得分:1)

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

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

关于ABI,请特别注意:

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

希望这很有用。

答案 8 :(得分:0)

为了保持内核的一致性。这允许在没有修改的情况下在多个体系结构上引导相同的内核。

答案 9 :(得分:0)

不确定为什么没有人考虑过基于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位PowerPC函数调用约定”,最后:

  

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

     

电源对齐模式源自使用的对齐规则   用于AIX操作系统的IBM XLC编译器。这是默认值   AIX上使用的PowerPC架构版GCC的对齐模式   和Mac OS X.因为这种模式最有可能兼容   在不同供应商的PowerPC架构编译器之间,它是   通常用于不同之间共享的数据结构   程序“。

鉴于基于PowerPC的传统OSX背景,可移植性是一个主要考虑因素 - 它要求遵循惯例一直回到AIX的XLC编译器。如果您认为需要确保所有工具和应用程序能够以最少的返工工作,我认为尽可能坚持使用相同的传统ABI非常重要。

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

  

被调用的函数负责分配   它自己的堆栈帧,确保保留16字节对齐   堆。这个操作是由一段代码来完成的   prolog,编译器放在子例程的主体之前。   在子程序的主体之后,编译器将一个epilog放到   将处理器恢复到子程序之前的状态   调用