我正在尝试使用 LLVM 自动矢量化缩减循环。但是即使我 运行 在他们的文档中给出了相同的示例代码,它也会失败

I am trying to auto vectorize a reduction loop with LLVM. But it fails even when I run the same example code given in their documentation

static void iadd(int &R, Vector &A) {
     unsigned sum = 0;
     int a;

     for (int i=0; i<A.vector_elements_16; i++) {
          a = static_cast<int>(A.data_16[i]);
          sum += a ;

      }

     R=static_cast<int>(sum);

    }

Vector class:具有宽度为 32 且类型为 uint16_t 的静态数组。所以迭代次数是 32.

问题中没有足够的信息来自信地回答它。但我向你保证,LLVM 和 Clang 至少会在树的顶部对缩减循环进行矢量化处理(我还没有检查旧版本的功能)。

第一个问题是矢量化确实取决于架构。我将在示例中使用 x86-64 和 Haswell 微体系结构(支持 AVX2),因为您没有列出特定的体系结构。如果您指定,我很乐意更新我对其他架构的回答。

下一个问题是您的描述听起来不像是真正的缩减循环。首先,如果数组是 static,那么我真的不知道这是关于什么的——那是一个全局数组。但是假设你的意思是一个长度为 32 的成员数组,那应该等同于以下(稍微简化的)完整并编译的代码:

struct V {
  static constexpr int length = 32;
  unsigned short data[32];
};

int reduce(V &v) {
  int sum = 0;
  for (int i = 0; i < v.length; ++i)
    sum += static_cast<int>(v.data[i]);
  return sum;
}

尽管给出这段代码,因为长度是一个常量,LLVM 完全展开了循环,这意味着没有循环矢量化将发挥作用。可悲的是,我们实际上为展开的循环生成了非常糟糕的代码——32 次加载和 32 次添加。我们可以做得更好,更好。我已提交 http://llvm.org/PR28090 以跟踪修复此问题。

但这不再是真正的缩减 loop 因为它具有恒定的 rip 计数并展开。如果你真的有一个循环,比如下面的代码:

struct V {
  int length;
  unsigned short *data;
};

int reduce(V &v) {
  int sum = 0;
  for (int i = 0; i < v.length; ++i)
    sum += static_cast<int>(v.data[i]);
  return sum;
}

然后 LLVM 实际上会为 Haswell 很好地矢量化它。它将 8 个元素加载到一个向量中,将它们扩展为 32 位值,然后对它们求和。它还一次处理其中的 4 个向量,以充分利用架构的带宽。在此处查看代码:

_Z6reduceR1V:                           # @_Z6reduceR1V
  .cfi_startproc
# BB#0:                                 # %entry
  movslq        (%rdi), %rcx
  xorl          %eax, %eax
  testq         %rcx, %rcx
  jle           .LBB0_11
# BB#1:                                 # %for.body.lr.ph
  movq          8(%rdi), %rdx
  xorl          %edi, %edi
  movl          [=12=], %eax
  cmpl          , %ecx
  jbe           .LBB0_10
# BB#2:                                 # %min.iters.checked
  xorl          %edi, %edi
  movq          %rcx, %r9
  movl          [=12=], %eax
  andq          $-32, %r9
  je            .LBB0_10
# BB#3:                                 # %vector.body.preheader
  leaq          -32(%r9), %rsi
  shrq          , %rsi
  leal          1(%rsi), %r8d
  andl          , %r8d
  xorl          %eax, %eax
  testq         %rsi, %rsi
  je            .LBB0_4
# BB#5:                                 # %vector.body.preheader.new
  leaq          -1(%r8), %rdi
  subq          %rsi, %rdi
  vpxor         %ymm0, %ymm0, %ymm0
  xorl          %eax, %eax
  vpxor         %ymm1, %ymm1, %ymm1
  vpxor         %ymm2, %ymm2, %ymm2
  vpxor         %ymm3, %ymm3, %ymm3
  .p2align      4, 0x90
.LBB0_6:                                # %vector.body
                                        # =>This Inner Loop Header: Depth=1
  vpmovzxwd     (%rdx,%rax,2), %ymm4    # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpmovzxwd     16(%rdx,%rax,2), %ymm5  # ymm5 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpmovzxwd     32(%rdx,%rax,2), %ymm6  # ymm6 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpmovzxwd     48(%rdx,%rax,2), %ymm7  # ymm7 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpaddd        %ymm0, %ymm4, %ymm0
  vpaddd        %ymm1, %ymm5, %ymm1
  vpaddd        %ymm2, %ymm6, %ymm2
  vpaddd        %ymm3, %ymm7, %ymm3
  vpmovzxwd     64(%rdx,%rax,2), %ymm4  # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpmovzxwd     80(%rdx,%rax,2), %ymm5  # ymm5 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpmovzxwd     96(%rdx,%rax,2), %ymm6  # ymm6 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpmovzxwd     112(%rdx,%rax,2), %ymm7 # ymm7 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  leaq          64(%rax), %rax
  vpaddd        %ymm0, %ymm4, %ymm0
  vpaddd        %ymm1, %ymm5, %ymm1
  vpaddd        %ymm2, %ymm6, %ymm2
  vpaddd        %ymm3, %ymm7, %ymm3
  addq          , %rdi
  jne           .LBB0_6
  jmp           .LBB0_7
.LBB0_4:
  vpxor         %ymm0, %ymm0, %ymm0
  vpxor         %ymm1, %ymm1, %ymm1
  vpxor         %ymm2, %ymm2, %ymm2
  vpxor         %ymm3, %ymm3, %ymm3
.LBB0_7:                                # %middle.block.unr-lcssa
  testq         %r8, %r8
  je            .LBB0_9
# BB#8:                                 # %middle.block.epilog-lcssa
  vpmovzxwd     48(%rdx,%rax,2), %ymm4  # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpaddd        %ymm3, %ymm4, %ymm3
  vpmovzxwd     32(%rdx,%rax,2), %ymm4  # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpaddd        %ymm2, %ymm4, %ymm2
  vpmovzxwd     16(%rdx,%rax,2), %ymm4  # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpaddd        %ymm1, %ymm4, %ymm1
  vpmovzxwd     (%rdx,%rax,2), %ymm4    # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
  vpaddd        %ymm0, %ymm4, %ymm0
.LBB0_9:                                # %middle.block
  vpaddd        %ymm0, %ymm1, %ymm0
  vpaddd        %ymm0, %ymm2, %ymm0
  vpaddd        %ymm0, %ymm3, %ymm0
  vextracti128  , %ymm0, %xmm1
  vpaddd        %ymm1, %ymm0, %ymm0
  vpshufd       , %xmm0, %xmm1       # xmm1 = xmm0[2,3,0,1]
  vpaddd        %ymm1, %ymm0, %ymm0
  vphaddd       %ymm0, %ymm0, %ymm0
  vmovd         %xmm0, %eax
  movq          %r9, %rdi
  cmpq          %r9, %rcx
  je            .LBB0_11
  .p2align      4, 0x90
.LBB0_10:                               # %for.body
                                        # =>This Inner Loop Header: Depth=1
  movzwl        (%rdx,%rdi,2), %esi
  addl          %esi, %eax
  addq          , %rdi
  cmpq          %rcx, %rdi
  jl            .LBB0_10
.LBB0_11:                               # %for.cond.cleanup
  vzeroupper
  retq