AVX 加载指令在 cygwin 上失败
AVX load instruction fails on cygwin
当我 运行 我的机器上的代码时,程序出现段错误。
#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 上的 cygwin gcc 代码 CPU。
怎么会发生这样的事情?
我做了进一步调试。 _mm_malloc
不是问题,是局部变量的对齐问题。
在第二次 vmovdqa
将向量存储到调用者的指针中时,RAX 不是 32 字节对齐的。 vec
in test2 似乎没有对齐。 (Cygwin/mingw return __m256i
vector by reference with the caller passing a hidden pointer ,不像标准 Windows x64 调用约定 return 按值)。
这是 Mysticial 在评论中链接的已知 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 通常不会产生错误代码,但有时即使是优化代码也会 store/reload 将 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 , %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 , %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
当我 运行 我的机器上的代码时,程序出现段错误。
#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 上的 cygwin gcc 代码 CPU。 怎么会发生这样的事情?
我做了进一步调试。 _mm_malloc
不是问题,是局部变量的对齐问题。
在第二次 vmovdqa
将向量存储到调用者的指针中时,RAX 不是 32 字节对齐的。 vec
in test2 似乎没有对齐。 (Cygwin/mingw return __m256i
vector by reference with the caller passing a hidden pointer ,不像标准 Windows x64 调用约定 return 按值)。
这是 Mysticial 在评论中链接的已知 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 通常不会产生错误代码,但有时即使是优化代码也会 store/reload 将 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 , %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 , %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