clang c ++ 17 std :: vector在使用-mavx编译时,对齐类型的元素SIGSEGV副本

Max*_*Max 11 c++ clang c++17

根据这个问题,我认为在C++ 17中,带有默认分配器的std :: vector应该处理对齐的类型.但是,以下代码

#include <iostream>
#include <iterator>
#include <array>
#include <vector>

template<typename T, size_t N, size_t Alignment>
struct alignas(Alignment) AlignedArray : public std::array<T, N>
{
    friend std::ostream& operator<<(std::ostream& o, const AlignedArray& a)
    {
        std::copy(a.cbegin(), a.cend(), std::ostream_iterator<T>(o, " "));
        return o;
    }
};

int main()
{
    using Array = AlignedArray<double, 24, 64>;
    std::vector<Array> v(10);
    for(const auto& e : v)
    {
        auto arr(e);
        std::cout << arr << std::endl;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

arr我用clang 6.0.1编译它时创建的段错误-mavx.没有-mavx开关它运行正常(CPU是E5-2697 v2).我编译了它 clang++ -I<path_to_libcxx>/include/c++/v1 -g -mavx -std=c++17 main.cpp -stdlib=libc++ -lc++abi -o alignastest -L<path_to_libcxx>/lib -L<path_to_libcxxabi>/lib.我在旧的RHEL 6.9上运行它,我编译了clang 6.0.1和libcxx,libcxxabi.我在另一个系统(Ubuntu 18.10,gcc 8)上进行了测试,它没有任何问题.

子问题

关于对齐,我发现std::aligned_alloclibc ++ 中的实现依赖于C11功能,该功能仅在最近的glibc版本(__config.h)中启用:

#if __GLIBC_PREREQ(2, 17)
#define _LIBCPP_HAS_C11_FEATURES
#endif
Run Code Online (Sandbox Code Playgroud)

不幸的是RHEL 6.9只ldd (GNU libc) 2.12安装了.难道alignas还取决于glibc的版本?

Ale*_*aum 3

我发现了编译代码的问题,但是,我还没有找到解决方案。但看起来,这只是一个 clang 问题,使用 g++ 可以修复它。

通过显示一些生成的汇编代码可以最好地说明该问题。代码auto arr(e);行被编译为一些移动指令,以将数据从向量复制到堆栈,clang 使用(使用 -mavx 编译时)avx2 指令,如下所示(AT&T 语法):

vmovaps 0xa0(%rax),%ymm0
vmovaps %ymm0,0x120(%rsp)
...
Run Code Online (Sandbox Code Playgroud)

其中 %rax 是向量中当前数组的地址。目标 arr 位于 0x80(%rsp)。该程序将以 32 字节块(256 位 avx2 指令)的形式进行复制。

然而,当查看这些值时,问题就变得清晰了:%rax = 0x55555556be70在我的调试测试中。问题是,vmovaps(移动对齐打包单精度)到 256 位 avx2 寄存器期望目标和源在 256 位或 32 字节(0x20)边界对齐,但是 %rax 仅 16 字节对齐。当不使用alignas进行编译时,clang使用vmovups(相同的指令,但不需要对齐数据)。

所以问题是,std::vector 的分配器不尊重对齐方式,也不在 64 字节边界对齐数组。g++ 也不会将向量内的数组与 32 字节边界对齐,并且在不使用 -O[not 0] 时不使用 avx 指令。然而g++总是使用128位xmm寄存器,它只需要对齐到16字节,分配器将数据与两个编译器对齐。

编辑:

我刚刚意识到,我忘记使用 -std=c++17 进行编译。有了这个标志,它对我来说适用于 clang++。代码看起来相同,但分配器在 64 字节边界正确对齐代码。所以我猜这与一个旧图书馆有关。也许您可以将您的二进制文件发送给我,然后我可以更详细地查看它。