为什么GCC 6假定数据是16字节对齐的?

11

(抱歉,我未能将我的问题简化为一个简单的失败测试案例...)

我在升级到GCC 6.3.0以构建我们的代码库时遇到了问题(相关标志:-O3 -m32)。

具体而言,我的应用程序在结构体析构函数中由于GCC的优化而导致段错误。

在这个析构函数中,GCC使用了movaps

movaps %xmm0,0x30a0(%ebx)

movaps要求操作数必须是16字节对齐的。但在这个时刻,%ebx指向我的对象,该对象不一定是16字节对齐的。来自glibc:

“在GNU系统中,malloc或realloc返回的块地址始终是8的倍数(或64位系统上为16)。”

因此,在使用-O3 -m32构建时会出现“段错误”。

为什么GCC似乎假定分配的对象将是16字节对齐的?我有什么误解吗?

备注:

  • 此结构体没有对齐提示或属性
  • 对象已通过默认的new运算符进行初始化
  • 取决于优化级别:
    • 通过:-m32 -O2
    • 失败:-m32 -O2 -ftree-slp-vectorize
    • 通过:-m32 -O3 -fno-tree-slp-vectorize
    • 失败:-m32 -O3

另一个项目似乎遇到了类似的问题:https://github.com/godotengine/godot/issues/4623

他们的调查指向了-fvect-cost-model=dynamic。我的代码库调查则指向-ftree-slp-vectorize


2
在您的对象上使用 alignof 将告诉您编译器认为它需要什么对齐方式。听起来应该不会是 ≥16,但检查一下也无妨。 - TrentP
1
如果 alignof==16,那么这不会是一个错误,但这意味着您不能使用 glibc 的 malloc。然而,标准限制了实现的 malloc,而不是操作系统的 malloc。GCC 可能需要包装 glibc。(我认为无论如何都应该这样做) - MSalters
alignof返回64alignof(max_align_t)返回8(符合预期)。既然没有用户对对象或其成员的对齐要求,为什么alignof会是64?我没有直接使用malloc,但假设new在使用它。 - Julien Vivenot
嗯,嗯,嗯……在结构体的几个层次深处,我刚刚发现需要将对齐方式设置为缓存行大小。这就是整个过程中 alignof==64 的原因。正如 @TrentP 和 @MSalters 所指出的那样,这也意味着我不能使用 8 字节对齐的 new。以前的版本和较低级别的优化可能会偶然起作用,因为 GCC 没有利用整体对齐性…… - Julien Vivenot
请提供需要翻译的英文内容。 - Julien Vivenot
众所周知,GCC的向量化器偶尔会出现对齐问题。虽然我不知道在6.3版本中它仍然存在问题。 - Mysticial
1个回答

3
编译器有可能认为对象的对齐方式≥16字节。使用C++11中的alignof()运算符可以找出编译器认为的对齐方式。GCC有一个扩展__alignof__,可在C和早期C++版本中使用。
结构体的对齐方式是其内部任何内容的最高对齐方式,递归地计算。内部可能存在比预期更高的对齐方式。
虽然C++11标准保证new返回的内存对于任何对象的“基本对齐要求”都对齐,但这仅适用于标准类型和由它们制作的对象。使用C++11 alignas()__attribute__((aligned(x))) GCC扩展请求更高的对齐方式可能超过new提供的范围。
解决方案是使用std::aligned_alloc()(C++11或更高版本)或posix_memalign()(仅限POSIX但< C++11)获取对齐内存。这可以与new运算符的放置形式一起使用,以在该内存中构造对象,或使用类特定的newdelete运算符重载。

1
我还想指出,在c++17标准中,动态分配将被用来支持过度对齐的数据。http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r1.html - Julien Vivenot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接