cygwin 上的 AVX 加载指令失败

Posted

技术标签:

【中文标题】cygwin 上的 AVX 加载指令失败【英文标题】:AVX load instruction fails on cygwin 【发布时间】:2019-07-29 23:27:07 【问题描述】:

当我在我的机器上运行代码时,程序出现分段错误。

#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 时出现分段错误。

我在 AMD 2990wx CPU 上的 cygwin gcc 上运行代码。 怎么会发生这样的事情?

【问题讨论】:

在我的机器上工作;我看不出有什么问题。您可以尝试使用 gdb 更仔细地查看问题所在。什么指令产生了段错误? cygwin gcc 的 _mm_malloc 是否损坏且未返回 32 字节对齐的内存? 读取未初始化内存是未定义行为:***.com/a/37184840 @chtz 从技术上讲它是 UB,但我们可以做得更好。我不明白这会如何导致 OP 的段错误。 @OP,因为您使用的是 cygwin,这可能意味着 Windows。您使用的是什么编译器标志?如果是-O0,那么res 可能被放入堆栈。 And GCC has a stack alignment problem that has made AVX unusable on Windows since antiquity. @Mysticial 我同意这不太可能是段错误的原因。因此,我只是将其作为评论发布(当然,我本可以更清楚地表明这可能是无关的)。 【参考方案1】:

我做了进一步的调试。 _mm_malloc 不是问题,是局部变量的对齐。

在第二个vmovdqa 将向量存储到调用者的指针中时,RAX 不是 32 字节对齐的。 test2 中的vec 似乎没有对齐。(Cygwin/mingw 通过引用返回 __m256i 向量,调用者传递隐藏指针,这与标准 Windows x64 调用约定按值返回它不同)。

这是 Mysticial 在 cmets 中链接的已知 Cygwin 错误 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412):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

【讨论】:

以上是关于cygwin 上的 AVX 加载指令失败的主要内容,如果未能解决你的问题,请参考以下文章

在啥情况下,AVX2 收集指令会比单独加载数据更快?

启用 AVX2 的处理器上的非法指令 vgatherdps

使用 Intel Core i7 的 AVX 上的非法指令

在 Cygwin 中构建 Emacs 时,目标“自动加载”的配方失败

如何在Windows中通过Cygwin安装和运行Nmap程序?

使用 AVX 的有符号/无符号整数的最小值