为什么 GCC 6 假设数据是 16 字节对齐的?
Why does GCC 6 assume data is 16-byte aligned?
(很抱歉未能将我的问题简化为一个简单的失败测试用例...)
我在升级到 GCC 6.3.0 以构建我们的代码库时遇到了问题(相关标志:-O3 -m32
)。
具体来说,由于 GCC 优化,我的应用程序在 struct ctor 调用中出现段错误。
在这个ctor中,GCC使用了movaps
:
movaps %xmm0,0x30a0(%ebx)
movaps
要求操作数16字节对齐。但是此时,%ebx
指向我的对象,不一定是16字节对齐的。来自 glibc:
“The address of a block returned by malloc or realloc in GNU systems is always a multiple of eight (or sixteen on 64-bit systems).“
因此出现段错误(使用 -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
.
编译器可能有理由认为该对象的对齐长度≥ 16 字节。通过使用 C++11 中的 alignof()
运算符,可以找出编译器认为对齐是什么。 GCC 有一个扩展 __alignof__
,可用于 C 和更早的 C++ 版本。
递归地,结构的对齐方式是其中所有内容的最高对齐方式。那里可能有比预期更高对齐的东西。
虽然 C++11 标准保证 new
返回的内存与任何对象的 "fundamental alignment requirement" 所需的值对齐,但这仅适用于标准类型和由它们构成的对象.使用 C++11 alignas()
或 __attribute__((aligned(x)))
GCC 扩展来请求更高对齐可能会超出 new
提供的范围。
对此的解决方案是使用 std::aligned_alloc()
(C++11 或更高版本)或 posix_memalign()
(POSIX-only 但 < C++11)来获得对齐的内存。这可以与 new
运算符的放置形式相结合以在该内存中构造对象或 class new
和 delete
.
的特定运算符重载
(很抱歉未能将我的问题简化为一个简单的失败测试用例...)
我在升级到 GCC 6.3.0 以构建我们的代码库时遇到了问题(相关标志:-O3 -m32
)。
具体来说,由于 GCC 优化,我的应用程序在 struct ctor 调用中出现段错误。
在这个ctor中,GCC使用了movaps
:
movaps %xmm0,0x30a0(%ebx)
movaps
要求操作数16字节对齐。但是此时,%ebx
指向我的对象,不一定是16字节对齐的。来自 glibc:
“The address of a block returned by malloc or realloc in GNU systems is always a multiple of eight (or sixteen on 64-bit systems).“
因此出现段错误(使用 -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
.
编译器可能有理由认为该对象的对齐长度≥ 16 字节。通过使用 C++11 中的 alignof()
运算符,可以找出编译器认为对齐是什么。 GCC 有一个扩展 __alignof__
,可用于 C 和更早的 C++ 版本。
递归地,结构的对齐方式是其中所有内容的最高对齐方式。那里可能有比预期更高对齐的东西。
虽然 C++11 标准保证 new
返回的内存与任何对象的 "fundamental alignment requirement" 所需的值对齐,但这仅适用于标准类型和由它们构成的对象.使用 C++11 alignas()
或 __attribute__((aligned(x)))
GCC 扩展来请求更高对齐可能会超出 new
提供的范围。
对此的解决方案是使用 std::aligned_alloc()
(C++11 或更高版本)或 posix_memalign()
(POSIX-only 但 < C++11)来获得对齐的内存。这可以与 new
运算符的放置形式相结合以在该内存中构造对象或 class new
和 delete
.