使用clang / g ++时,__ m256i数组的分段错误

时间:2015-12-23 17:29:12

标签: c++ clang avx icc

我正在尝试生成__m256i的数组,以便在另一个计算中重用。当我尝试这样做时(即使使用最小的测试用例),我也会遇到分段错误 - 但前提是代码是用g ++或clang编译的。如果我使用英特尔编译器(版本16.0)编译代码,则不会发生分段错误。这是我创建的测试用例:

int main() {
    __m256i *table = new __m256i[10000];
    __m256i zeroes = _mm256_set_epi64x(0, 0, 0, 0);
    table[99] = zeroes;
}

使用clang 3.6和g ++ 4.8编译上述内容时,会发生分段错误。

以下是英特尔编译器生成的程序集(来自https://gcc.godbolt.org/,icc 13.0):

pushq     %rbx                                          #3.12
movq      %rsp, %rbx                                    #3.12
andq      $-32, %rsp                                    #3.12
pushq     %rbp                                          #3.12
pushq     %rbp                                          #3.12
movq      8(%rbx), %rbp                                 #3.12
movq      %rbp, 8(%rsp)                                 #3.12
movq      %rsp, %rbp                                    #3.12
subq      $112, %rsp                                    #3.12
movl      $3200, %eax                                   #4.38
vzeroupper                                              #4.38
movq      %rax, %rdi                                    #4.38
call      operator new[](unsigned long)                 #4.38
movq      %rax, -112(%rbp)                              #4.38
movq      -112(%rbp), %rax                              #4.38
movq      %rax, -104(%rbp)                              #4.20
vxorps    %ymm0, %ymm0, %ymm0                           #5.22
vmovdqu   %ymm0, -80(%rbp)                              #5.22
vmovdqu   -80(%rbp), %ymm0                              #5.22
vmovdqu   %ymm0, -48(%rbp)                              #5.20
movl      $3168, %eax                                   #6.17
addq      -104(%rbp), %rax                              #6.5
vmovdqu   -48(%rbp), %ymm0                              #6.17
vmovdqu   %ymm0, (%rax)                                 #6.5
movl      $0, %eax                                      #7.1
vzeroupper                                              #7.1
leave                                                   #7.1
movq      %rbx, %rsp                                    #7.1
popq      %rbx                                          #7.1
ret                                                     #7.1

这是来自clang 3.7:

pushq   %rbp
movq    %rsp, %rbp
andq    $-32, %rsp
subq    $192, %rsp
xorl    %eax, %eax
movl    $3200, %ecx             # imm = 0xC80
movl    %ecx, %edi
movl    %eax, 28(%rsp)          # 4-byte Spill
callq   operator new[](unsigned long)
movq    %rax, 88(%rsp)
movq    $0, 168(%rsp)
movq    $0, 160(%rsp)
movq    $0, 152(%rsp)
movq    $0, 144(%rsp)
vmovq   168(%rsp), %xmm0        # xmm0 = mem[0],zero
vmovq   160(%rsp), %xmm1        # xmm1 = mem[0],zero
vpunpcklqdq     %xmm0, %xmm1, %xmm0 # xmm0 = xmm1[0],xmm0[0]
vmovq   152(%rsp), %xmm1        # xmm1 = mem[0],zero
vpslldq $8, %xmm1, %xmm1        # xmm1 = zero,zero,zero,zero,zero,zero,zero,zero,xmm1[0,1,2,3,4,5,6,7]
vmovaps %xmm1, %xmm2
vinserti128     $1, %xmm0, %ymm2, %ymm2
vmovaps %ymm2, 96(%rsp)
vmovaps %ymm2, 32(%rsp)
movq    88(%rsp), %rax
vmovaps %ymm2, 3168(%rax)
movl    28(%rsp), %eax          # 4-byte Reload
movq    %rbp, %rsp
popq    %rbp
vzeroupper
retq

我是否遇到了clang / g ++中的编译器错误?或者我只是做错了什么?

2 个答案:

答案 0 :(得分:3)

我之前已多次说隐式SIMD加载/存储是一个坏主意。停止使用它们。使用像这样的显式加载/存储

int64_t* table = new int64_t[4*10000];
__m256i zeroes = _mm256_set_epi64x(0, 0, 0, 0);
_mm256_storeu_si256((__m256i*)&table[4*99], zeroes);

或者因为这是POD使用the cross-compiler/OS function _mm_malloc

int64_t* table = (int64_t*)_mm_malloc(sizeof(int64_t)*4*10000, 32);
__m256i zeroes = _mm256_set_epi64x(0, 0, 0, 0);
_mm256_store_si256((__m256i*)&table[4*99], zeroes);

您可以使用_mm256_setzero_si256()代替_mm256_set_epi64x(0, 0, 0, 0)(请注意_mm256_set_epi64x在某些版本的MSVC上无法在32位模式下运行)但是GCC和Clang足够聪明地知道它们是一回事。

由于您使用的是不属于C / C ++规范then some rules such as strict aliasing may be overlooked的内在函数。

答案 1 :(得分:2)

我猜这个问题与错误的内存对齐有关。 vmovaps要求内存位置以32字节边界开始,而vmovdqu则不需要。这就是英特尔版本运行的原因,而clang / g ++代码崩溃了。我不知道这是否是编译器错误,但你可能还是想要对齐。

以下代码应该有效,尽管它比C ++更多C。

int main() {
  __m256i *table = (__m256i*) memalign( 32, 10000 * sizeof(__m256i) );
  __m256i zeroes = _mm256_set_epi64x(0, 0, 0, 0);
  table[99] = zeroes;
}