我可以使用LLVM jit生成AVX矢量化代码吗?

时间:2014-03-21 01:13:41

标签: x86 llvm jit avx

我知道我可以在EngineBuilder中设置mcpu和mattr来生成矢量化代码。 但我发现clang前端必须涉及使用-mavx的AVX。否则,生成的程序集仅使用xmm寄存器。

有没有办法让LLVM知道8个浮点数可以放在AVX寄存器中而不涉及前端?


我的测试代码只是向量添加:

float a[N], b[N];
float c[N];
// initialize a and b
for (int i = 0; i < N; ++i)
    c[i] = a[i] + b[i];

2 个答案:

答案 0 :(得分:5)

TL; DR :是的。您只需要调用opt并告诉它对您的代码进行矢量化。

你绝对可以在没有铿锵声的情况下做到这一点。矢量化器都是关于LLVM IR的,它们不是铿锵的。

我通过使用没有优化的clang从你的例子中得到了这个IR(是的,我作弊,然后注释了一两点):(数据布局和三元组很重要!)

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.9.0"

define float* @f(i32 %N, float* nocapture readonly %a, float* nocapture readonly %b, float* %c) {
entry:
  %cmp10 = icmp sgt i32 %N, 0   ; check for early exit
  br i1 %cmp10, label %for.body, label %for.end

for.body:                                         ; preds = %entry, %for.body
  %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ]

  %arrayidx = getelementptr inbounds float* %a, i64 %indvars.iv
  %0 = load float* %arrayidx, align 4     ; %0 = a[i]
  %arrayidx2 = getelementptr inbounds float* %b, i64 %indvars.iv
  %1 = load float* %arrayidx2, align 4    ; %1 = a[i]

  %add = fadd float %0, %1                ; %add = %0 + %1

  %arrayidx4 = getelementptr inbounds float* %c, i64 %indvars.iv
  store float %add, float* %arrayidx4, align 4   ; c[i] = %add

  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
  %lftr.wideiv = trunc i64 %indvars.iv.next to i32
  %exitcond = icmp eq i32 %lftr.wideiv, %N       ; test for loop exit
  br i1 %exitcond, label %for.end, label %for.body

for.end:                                          ; preds = %for.body, %entry
  ret float* %c
}

现在您想要对代码进行矢量化。让我们通过循环向量化器运行它,然后。

opt a.ll -S -march=x86-64 -mcpu=btver2 -loop-vectorize

(我用-S运行它来获取控制台的输出)

现在我们使用巨大的vector.body矢量化IR,以及一些检查,预读和其他簿记代码。你会在文件中间看到这个:

  %171 = getelementptr inbounds float* %b, i64 %98
  %172 = insertelement <8 x float*> %170, float* %171, i32 7
  %173 = getelementptr float* %109, i32 0
  %174 = bitcast float* %173 to <8 x float>*
  %wide.load18 = load <8 x float>* %174, align 4
  %175 = getelementptr float* %109, i32 8
  %176 = bitcast float* %175 to <8 x float>*
  %wide.load19 = load <8 x float>* %176, align 4
  %177 = getelementptr float* %109, i32 16
  %178 = bitcast float* %177 to <8 x float>*
  %wide.load20 = load <8 x float>* %178, align 4
  %179 = getelementptr float* %109, i32 24
  %180 = bitcast float* %179 to <8 x float>*
  %wide.load21 = load <8 x float>* %180, align 4
  %181 = fadd <8 x float> %wide.load, %wide.load18
  %182 = fadd <8 x float> %wide.load15, %wide.load19
  %183 = fadd <8 x float> %wide.load16, %wide.load20
  %184 = fadd <8 x float> %wide.load17, %wide.load21
  %185 = getelementptr inbounds float* %c, i64 %5
  %186 = insertelement <8 x float*> undef, float* %185, i32 0

它有点复杂,但大多数浮点加法(fadd)都在那里,并且只在向量上完成。让我们更简单,并使用-O2-O3运行其他优化。这将通过移除和/或折叠不需要或有利可图的部分来使IR更小更简单。

opt a.ll -S -march=x86-64 -mcpu=btver2 -loop-vectorize -O3

嗯......因为我们现在已经有了可以在矢量上运行的IR,我们只需要发射它。让我们采取最后一步,并致电llc

opt a.ll -S -march=x86-64 -mcpu=core-avx2 -loop-vectorize -O3 | llc -mcpu=core-avx2

查看反汇编,你有一个紧密的内循环(如果你有与我相同的名字,这应该是标签LBB0_5),以及一堆簿记代码。

您的代码现在已经过矢量化。

答案 1 :(得分:-1)

没有涉及FE,只有LLVM本身:

llc -mattr=+avx -O3 test.il

PS 您可以通过

从C代码生成test.il.
clang -S -emit-llvm test.c