Инструкция загрузки AVX не работает на Cygwin - PullRequest
2 голосов
/ 08 марта 2019

Когда я запускаю код на моей машине, в программе происходит ошибка сегментации.

#include <immintrin.h>
#include <stdint.h>

static inline __m256i load_vector(__m256i const * addr){
    __m256i res = _mm256_load_si256(addr);
    return res;
}
void test2(){
    int32_t *src;
    src = _mm_malloc(sizeof(__m256i), 32);
    __m256i vec = load_vector((__m256i const * )src);
    _mm_free(src);
}

int main(int argc,char *argv[]){
    test2();
    return 0;
}

Я попытался отладить это с помощью gdb, и она вызывает ошибку сегментации при вызове _mm256_load_si256.

Я запускаю код на Cygwin GCC на AMD 2990WX CPU.Как могут происходить такие вещи?

1 Ответ

3 голосов
/ 10 марта 2019

Я сделал дальнейшую отладку._mm_malloc не было проблемой, это было выравнивание локальных переменных.

Во второй vmovdqa для сохранения вектора в указателе вызывающей стороны RAX не был выровнен по 32 байта.vec в test2, кажется, не выровнен. (Cygwin / mingw возвращает вектор __m256i по ссылке, когда вызывающая сторона передает скрытый указатель, в отличие от стандартного соглашения о вызовах Windows x64, которое возвращает его по значению).

Это известная ошибка Cygwin (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412), на которую Mysticial ссылается в комментариях: Cygwin GCC не может безопасно использовать AVX, потому что он неправильно выравнивает стек для __m256i локальных пользователей, которые получаютхранится в памяти. (Cygwin / MinGW gcc будет правильно выровнять alignas(32) int arr[8] = {0};, но они делают это путем выравнивания отдельного указателя, а не RSP или RBP. Очевидно, есть некоторые ограничения SEH для манипулирования кадрами стека)

Clang, MSVC и ICC все должным образом поддерживают __m256i.

При включенной оптимизации gcc часто не создает ошибочный код, но иногда даже оптимизированный код сохраняет / перезагружает 32-байтовый вектор встек.

_ZL11load_vectorPKDv4_x:
.LFB3671:
    .file 2 "min_case.c"
    .loc 2 4 0
    .cfi_startproc
    pushq   %rbp
    .seh_pushreg    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    .seh_stackalloc 16
    .seh_endprologue
    movq    %rcx, 16(%rbp)
    movq    %rdx, 24(%rbp)
    movq    24(%rbp), %rax
    movq    %rax, -8(%rbp)
.LBB4:
.LBB5:
    .file 3 "/usr/lib/gcc/x86_64-pc-cygwin/7.4.0/include/avxintrin.h"
    .loc 3 909 0
    movq    -8(%rbp), %rax
    vmovdqa (%rax), %ymm0
.LBE5:
.LBE4:
    .loc 2 5 0
    movq    16(%rbp), %rax
    vmovdqa %ymm0, (%rax)
    .loc 2 6 0
    movq    16(%rbp), %rax
    addq    $16, %rsp
    popq    %rbp
    .cfi_restore 6
    .cfi_def_cfa 7, 8
    ret

__m256i не был выровнен в этом тестовом примере :

#include <immintrin.h>
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

const char* check_alignment(const void *ptr, uintptr_t alignment){
    return (((uintptr_t)ptr) & (alignment - 1)) == 0 ? "aligned" : "NOT aligned";
}

static inline __m256i load_vector(__m256i const * addr){
    printf("addr:%s\n", check_alignment(addr, 32));
    __m256i res;
    printf("&res:%s\n", check_alignment(&res, 32));
    res = _mm256_load_si256(addr);
    return res;
}
void test2(){
    int32_t *src;
    src = (int32_t *)_mm_malloc(sizeof(__m256i), 32);
    src[0] = 0; src[0] = 1; src[2] = 2; src[3] = 3;
    src[4] = 4; src[5] = 5; src[6] = 6; src[7] = 7;
    __m256i vec = load_vector((__m256i const * )src);
    _mm_free(src);
}

int main(int argc,char *argv[]){
    test2();
    return 0;
}

// results
// addr:aligned
// &res:NOT aligned
// Segmentation fault
...