我试图用LLVM自动向量化一个减少循环。但即使我在他们的文档中运行相同的示例代码,它也会失败

时间:2016-06-11 13:08:17

标签: llvm vectorization reduction

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类:具有宽度为32的静态数组,类型为uint16_t。所以迭代次数是32。

1 个答案:

答案 0 :(得分:3)

问题中没有足够的信息可以自信地回答。但我向你保证,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来跟踪修复此问题。

但这不是一个真正的减少循环因为它具有恒定的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          $0, %eax
  cmpl          $31, %ecx
  jbe           .LBB0_10
# BB#2:                                 # %min.iters.checked
  xorl          %edi, %edi
  movq          %rcx, %r9
  movl          $0, %eax
  andq          $-32, %r9
  je            .LBB0_10
# BB#3:                                 # %vector.body.preheader
  leaq          -32(%r9), %rsi
  shrq          $5, %rsi
  leal          1(%rsi), %r8d
  andl          $1, %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          $2, %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  $1, %ymm0, %xmm1
  vpaddd        %ymm1, %ymm0, %ymm0
  vpshufd       $78, %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          $1, %rdi
  cmpq          %rcx, %rdi
  jl            .LBB0_10
.LBB0_11:                               # %for.cond.cleanup
  vzeroupper
  retq