clang С++ 17 std :: vector over aligned types копия элементов SIGSEGV при компиляции с -mavx

В соответствии с этим вопросом я думал, что в 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;
}

segfaults о создании 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_alloc в lib c++ основана на функции C11, которая включена только с последней версией glibc (__config.h):

#if __GLIBC_PREREQ(2, 17)
#define _LIBCPP_HAS_C11_FEATURES
#endif

К сожалению, у RHEL 6.9 установлен только ldd (GNU libc) 2.12. Является ли alignas также в зависимости от версии glibc?

Ответы

Ответ 1

Я нашел проблему с скомпилированным кодом, однако я еще не нашел решение. Но это швы, что это всего лишь проблема clang, и использование g++ исправляет это.

Проблема лучше всего проиллюстрирована, показывая некоторые из полученного кода сборки. auto arr(e); строка кода скомпилирована для некоторых команд перемещения для копирования данных из вектора в стек, clang использует (при компиляции с -mavx) инструкциями avx2, такими как следующие (синтаксис AT & T):

vmovaps 0xa0(%rax),%ymm0
vmovaps %ymm0,0x120(%rsp)
...

Где% 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 байт и не использует команды avx, когда не используется -O [not 0]. Однако g++ всегда использует 128-битные xmm-регистры, для которых требуется только выравнивание до 16 байтов, к которым распределитель выравнивает данные с обоими компиляторами.

РЕДАКТИРОВАТЬ:

Я просто понял, что забыл скомпилировать с -std = С++ 17. с этим флагом он работает для меня с кланом g++. Код выглядит одинаково, но распределитель правильно выравнивает код на границе 64 байта. Поэтому я предполагаю, что это связано со старой библиотекой. Может быть, вы можете отправить мне свой двоичный код, тогда я мог бы более подробно рассмотреть его.