clang c ++ 17 std :: vector поверх выровненных типов копия элементов SIGSEGV при компиляции с -mavx - PullRequest
0 голосов
/ 24 августа 2018

Согласно этому вопросу Я думал, что в 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;
}

вызывает ошибку при создании arr при его компиляции с использованием clang 6.0.1 и -mavx.Без переключателя -mavx он работает нормально (процессор 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 в libc ++использует функцию 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 Ответ

0 голосов
/ 24 августа 2018

Я обнаружил проблему с скомпилированным кодом, однако пока не нашел решения. Но кажется, что это всего лишь проблема лягушек, и 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 не учитывает alignas и не выравнивает массив на границах 64 байта. g ++ также не выравнивает массив внутри вектора по 32-байтовым границам и не использует инструкции avx, когда также не используется -O [not 0]. Однако g ++ всегда использует 128-битные регистры xmm, которые должны быть выровнены только до 16 байтов, в которые распределитель выравнивает данные с обоими компиляторами.

EDIT:

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

...