assembly 使用SIMD指令时的堆栈对齐

kupeojn6  于 4个月前  发布在  其他
关注(0)|答案(1)|浏览(68)

在我阅读的一本关于汇编的书中,我们被告知,对于我们编写的任何函数,如果它是一个分支函数,并且将调用其他函数,它必须保持堆栈对齐。这样做是为了让SIMD指令可以被我们自己的函数调用的函数使用。
到目前为止,我一直被告知x86必须为SIMD指令保持16字节的堆栈对齐。对于所有使用SIMD的x86程序,32位和64位,它总是16字节吗?
它是否会根据我们正在构建程序的x86操作系统而改变?

e37o9pze

e37o9pze1#

函数无法知道其他函数在内部会做什么,所以真正重要的是能够将库链接在一起并链接到可执行文件,它们同意调用约定/ ABI,并且ABI为调用者设置要求,为被调用者提供关于堆栈对齐的保证。(以及其他事情。)所以它不是“当使用SIMD指令时”,除非你的意思是“如果任何被调用者实际上确实依赖于ABI保证,例如,通过在其堆栈空间上使用SIMD加载或存储”。如 * glibc scanf Segmentation faults when called from a function that doesn't align RSP *
请参阅Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?了解更多关于我在这个答案中提到的一些事情的细节。

64位模式:始终以16对齐:x86-64 System V和Windows x64 ABI都需要在call之前使用RSP%16 == 0,因此在函数条目上保证RSP % 16 == 8。这对于16字节的向量已经足够了,但是需要alignas(32)或更高的局部变量的函数仍然需要自己完成。
32位模式:非Linux上的4字节对齐。只有Linux上使用的i386 System V ABI版本需要16字节对齐(在调用前为ESP % 16 == 0,在函数入口时为ESP % 16 == 12。)甚至使用SysV ABI的其他操作系统也保留了旧的4字节对齐要求,没有采用该更改(例如 *BSD,也许还有MacOSX,在它只支持64位之前)。Windows上的32位代码也只需要/保证4字节对齐。

如果你(或编译器)想要16字节对齐的局部变量(例如溢出/重新加载__m128),该函数需要额外的指令。(通常将EBP设置为帧指针和and esp, -16,类似于为VLA分配空间。)

**ABI要求在GNU/Linux的32位模式下的所有函数中保持16字节的堆栈对齐,这是GCC的一个意外。**当他们注意到-mpreferred-stack-boundary=4让GCC * 假设 * 对齐并使代码在没有对齐的情况下调用时出错的错误时,有很多二进制文件依赖于它,包括在像RedHat Enterprise Linux(RHEL)这样变化缓慢的主要发行版中。摆脱这种情况的最好方法是改变ABI以要求未来,所以-mpreferred-stack-boundary=4成为ABI的一部分,而不仅仅是一个乐观的性能调整,就像我认为它是想象的那样,当它被设置为默认值时。

这一变化实际上打破了手写的asm,它调用了以前允许的ESP对齐小于16的C函数,但是这样的二进制文件可能会继续由GCC版本的默认值创建,当这一点被注意到时,GCC版本已经广泛使用。因此,改变ABI以匹配GCC的发布版本实际上正在做的事情并不是很好,但可能不那么糟糕。可执行文件将被限制为回调函数,或旧代码调用新代码的其他方式。(新代码调用旧代码是可以的,因为提供给予16字节对齐的调用方满足了较宽松的对齐要求。)
其他操作系统避免了这种破坏旧的二进制文件和手写asm的ABI更改崩溃。
请参阅https://sourceforge.net/p/fbc/bugs/659/了解一些历史,以及我在https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838#c91上的评论,以总结i386 GNU/Linux + GCC如何意外地进入一种情况的不幸历史,在这种情况下,对i386 System V ABI的向后不兼容更改是两害相权取其轻。

相关问题