使用 simd 指令时的堆栈对齐

问题描述 投票:0回答:1

在我正在读的关于汇编的书中,我们被告知对于我们编写的任何函数,如果它是一个分支函数并且将调用其他函数,它必须保持堆栈对齐。这样做是为了让我们自己的函数调用的函数可以使用 SIMD 指令。

到目前为止,我被告知对于 x86,我们必须为 SIMD 指令保留 16 字节堆栈对齐。对于所有使用 SIMD 的 x86 程序(32 位和 64 位)来说,它总是 16 字节吗? 它会根据我们为其构建程序的 x86 操作系统而改变吗?

assembly x86 32bit-64bit sse memory-alignment
1个回答
0
投票

函数无法知道其他函数在内部会做什么,因此能够将库链接到一起并链接到可执行文件真正重要的是它们同意调用约定/ABI,并且 ABI 为调用者设定要求,从而为被调用者提供保证关于堆栈对齐。 (以及其他事情。)所以它不是“当使用 SIMD 指令时”,除非您的意思是“如果任何被调用者实际上确实依赖于 ABI 保证,例如通过在其堆栈空间上使用 SIMD 加载或存储”。如 glibc scanf 当从未对齐 RSP 的函数调用时出现分段错误

请参阅为什么 x86-64 / AMD64 System V ABI 强制执行 16 字节堆栈对齐?了解有关我在本答案中提到的一些内容的更多详细信息。

64 位模式:始终按 16 对齐:x86-64 System V 和 Windows x64 ABI 都需要

RSP%16 == 0
call
之前,从而保证函数输入时的
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,也许还有 Mac OS X,然后才变为仅限 64 位)。 Windows 上的 32 位代码也只需要/保证 4 字节对齐。

如果您(或编译器)想要 16 字节对齐的局部变量(例如溢出/重新加载

__m128
),则该函数需要额外的指令。 (通常将 EBP 设置为帧指针和
and esp, -16
,类似于为 VLA 分配空间时。)

GNU/Linux 上 32 位模式下所有函数中维护 16 字节堆栈对齐的 ABI 要求是 GCC 的一个意外。 当他们注意到

-mpreferred-stack-boundary=4
让 GCC assume 对齐的错误时并编写在没有这种对齐方式的情况下调用时会出错的代码,因此存在依赖它的二进制文件,包括像 RedHat Enterprise Linux (RHEL) 这样变化缓慢的主要发行版。摆脱这种情况的最不坏的方法是更改 ABI 以要求继续进行下去,因此
-mpreferred-stack-boundary=4
成为 ABI 的一部分,而不仅仅是像我认为在默认设置时想象的那样乐观的性能调整。

此更改实际上破坏了手写汇编,该汇编调用以前允许的 ESP 对齐方式小于 16 的 C 函数,但此类二进制文件可能会继续由 GCC 版本的默认值创建,而当注意到这一点时,GCC 版本已广泛使用。因此,更改 ABI 以匹配已发布版本的 GCC 实际执行的操作虽然不是很好,但可能没那么糟糕。在实践中,具有新可执行文件的旧库的破坏将仅限于回调函数或旧代码调用新代码的其他方式。 (新代码调用旧代码很好,因为提供 16 字节对齐的调用方可以满足更宽松的对齐要求。)

其他操作系统避免了这种 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 的向后不兼容更改是两害相权取其轻的不幸历史。

© www.soinside.com 2019 - 2024. All rights reserved.