Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 为什么x86-64/AMD64 System V ABI要求16字节堆栈对齐?_Assembly_X86 64_Memory Alignment_Abi - Fatal编程技术网

Assembly 为什么x86-64/AMD64 System V ABI要求16字节堆栈对齐?

Assembly 为什么x86-64/AMD64 System V ABI要求16字节堆栈对齐?,assembly,x86-64,memory-alignment,abi,Assembly,X86 64,Memory Alignment,Abi,我在不同的地方读到过这样做是出于“性能原因”,但我仍然想知道在哪些特殊情况下,通过这种16字节对齐可以提高性能。或者,在任何情况下,选择这样做的原因是什么 编辑:我想我写的问题有误导性。我不是问为什么处理器使用16字节对齐的内存会更快,这在文档中到处都有解释。相反,我想知道的是,强制的16字节对齐比只让程序员在需要时自己对齐堆栈要好得多。我问这个问题是因为根据我在汇编方面的经验,堆栈强制有两个问题:它只对执行的代码中不到1%的代码有用(因此在其他情况下,99%实际上是开销);这也是一个非常常见的

我在不同的地方读到过这样做是出于“性能原因”,但我仍然想知道在哪些特殊情况下,通过这种16字节对齐可以提高性能。或者,在任何情况下,选择这样做的原因是什么


编辑:我想我写的问题有误导性。我不是问为什么处理器使用16字节对齐的内存会更快,这在文档中到处都有解释。相反,我想知道的是,强制的16字节对齐比只让程序员在需要时自己对齐堆栈要好得多。我问这个问题是因为根据我在汇编方面的经验,堆栈强制有两个问题:它只对执行的代码中不到1%的代码有用(因此在其他情况下,99%实际上是开销);这也是一个非常常见的bug来源。所以我想知道它到底是如何得到回报的。虽然我对此仍有疑问,但我接受peter的回答,因为它包含了对我原始问题的最详细的回答。

请注意,Linux上使用的i386 System V ABI的当前版本也需要16字节堆栈对齐。有关i386 GNU/Linux+GCC如何意外地陷入这样一种境地的不幸历史,i386系统V ABI的向后不兼容更改是两个祸害中较小的一个,请参阅以了解一些历史,以及我的评论

Windows x64在调用
之前还需要16字节堆栈对齐,大概是出于与x86-64 System V类似的动机

另外,半相关:x86-64 System V要求16字节和大字节的全局数组按16对齐。对于>=16字节或可变大小的本地数组也是如此,尽管只有当您知道传递给您的是数组开头的地址,而不是指向中间的指针时,该细节才与函数相关。(). 它不允许您对任意的
int*
进行任何额外的假设


SSE2是x86-64的基线,我认为使ABI对
\uu m128
等类型和编译器自动矢量化有效是设计目标之一。ABI必须定义如何将这些参数作为函数参数或通过引用传递

16字节对齐有时对堆栈上的局部变量(特别是数组)很有用,保证16字节对齐意味着编译器可以在任何时候免费获得它,即使源代码没有显式请求它

如果堆栈相对于16字节边界的对齐方式未知,则需要对齐本地的每个函数都需要一个
和rsp,-16
,以及额外的指令,以便在到
rsp
的未知偏移量后保存/恢复
rsp
(要么
0
要么
-8
).
例如,对帧指针使用up
rbp

如果没有AVX,内存源操作数必须是16字节对齐的。e、 g.
padd xmm0,[rsp+rdi]
如果内存操作数未对齐,则会出现故障。因此,如果不知道对齐方式,则必须使用
movupsxmm1[rsp+rdi]
/
paddxmm0、xmm1
,或者编写循环序言/尾声来处理未对齐的元素。对于编译器想要自动矢量化的本地数组,它可以简单地选择按16对齐它们

还要注意的是,早期的x86 CPU(在Nehalem/推土机之前)有一条
movups
指令,它比
movaps
慢,即使指针确实对齐了。(即,对齐数据上未对齐的加载/存储速度非常慢,并防止将加载折叠到ALU指令中)。(有关以上所有内容的更多信息,请参阅。)

这些因素就是为什么保证比“通常”保持堆栈对齐更有用的原因允许在未对齐的堆栈上生成实际出错的代码,这样可以提供更多优化机会。

对齐数组还可以加速矢量化的
memcpy
/
strcmp
/任何不能假定对齐的函数,但可以检查对齐并直接跳到整个向量循环

发件人:

数组使用与其元素相同的对齐方式,但局部或全局对齐方式除外 长度至少为16字节的数组变量或C99可变长度数组变量 始终具有至少16个字节的对齐。4

4校准要求允许在阵列上操作时使用SSE指令。 编译器通常无法计算可变长度数组(VLA)的大小,但这是需要的 大多数VLA至少需要16个字节,因此要求VLA至少有16个字节是合乎逻辑的 至少需要16字节的对齐

这有点激进,主要是当自动向量化的函数可以内联时才有帮助,但通常编译器可以将其他局部变量塞进任何间隙中,这样就不会浪费堆栈空间。只要存在已知的堆栈对齐,就不会浪费指令。(显然,如果ABI设计人员决定不要求16字节堆栈对齐,他们可能会忽略这一点。)


溢出/重新加载
\uuu m128
当然,它可以自由地执行
alignas(16)charbuf[1024]或源请求16字节对齐的其他情况

还有
\uuum128
/
\uuum128d
/
\uuuum128i
局部变量。编译器可能无法将所有向量局部数保留在寄存器中(例如,在函数调用中溢出,或寄存器不足),因此出于上述效率原因,编译器需要能够使用
movaps
溢出/重新加载它们,或将其作为ALU指令的内存源操作数

实际上跨缓存线边界(64字节)拆分的加载/存储具有显著的延迟损失,在现代CPU上也具有较小的吞吐量损失。加载需要来自两个独立缓存线的数据,因此需要