对齐、Malloc行为和堆栈分配
@Peter Cordes的评论解决了部分问题。
具体而言,在你的OSX机器上,你总是能够获得正确对齐的内存,这符合生成用于__m256i
数据类型的汇编所需的对齐方式,该类型需要32字节对齐。(我用引号括起来的“总是”是因为这并不能用malloc
保证。你可能只是在OSX的malloc
中运气好。同一程序的多次运行往往会获得相同的对齐方式,而不像在一个程序中重复调用malloc
。实际上,参见下文:OS X上的编译器生成不同的asm)
在Ubuntu上,malloc
返回的内存地址没有适当的对齐方式。(有关原因详见下文)
你把第二段代码片段称作slight change
。
int main()
{
struct Box result[1];
(*result).L=(*result).L;
}
这段代码与使用malloc
的第一个片段实际上有很大的不同,因为编译器(在此处为gcc)在在堆栈上分配内存时意识到了数据类型Box
(以及其扩展的__m256i
)的对齐要求。因此,在这种情况下没有崩溃的风险,因为编译器提供正确的对齐方式。
你可以像这篇帖子中所解释的那样操纵malloc
返回的基指针https://dev59.com/pHVC5IYBdhLWcg3wpSzf#227900。我让您查看详细信息,但简单来说,您可以执行以下操作:
struct Box *result=NULL;
void *mem = malloc(2 * sizeof(struct Box));
result = (struct Box *)((uintptr_t)mem + offset);
其中,offset
可以让你探究对齐和段错误。你可以打印出你用于 result
的指针地址,例如:printf("0x%08" PRIXPTR "\n", (uintptr_t)result);
(同样来自该帖子)。
指令差异
最后,在 Ubuntu 和 OSX 上都可以重现此问题。我实际上看到我的 OSX 的 malloc
调用给出了 16 字节 对齐而不是 32 字节对齐。我在 Ubuntu(在同一硬件上的 VM 中)上也看到了 16 字节对齐,这是导致段错误的原因。当我手动对齐到 32 字节时,段错误消失了。
因此,你的问题的根本原因在于系统没有生成相同的汇编指令。我使用 gcc 的 -S
选项得到了汇编代码。在 OSX 上,我看到了使用 XMM 操作数的 vmovaps
4 次,而在 Ubuntu 上则使用 YMM 操作数两次的 vmovdqa
来移动一个 __m256i
。
vmovdqa
和 vmovaps
要求它们的内存操作数是自然对齐的(即 YMM 为 32B,XMM 为 16B)。因此,在 OSX 上生成的汇编只需要 16 字节对齐,尽管 __m256i
是 32 字节。