首页 > 解决方案 > LLVM/Clang 生成看起来无用的指令

问题描述

如果我写这段代码:

void loop1(int N, double* R, double* A, double* B) {
    for (int i = 0; i < N; i += 1) {
        R[i] = A[i] + B[i];
    }
}

Clang ( -O3) 生成以下 x64 ASM 作为循环的展开版本(编译器资源管理器)的一部分:

.LBB0_14:
    movupd  xmm0, xmmword ptr [rdx + 8*rax]
    movupd  xmm1, xmmword ptr [rdx + 8*rax + 16]
    movupd  xmm2, xmmword ptr [rcx + 8*rax]
    addpd   xmm2, xmm0
    movupd  xmm0, xmmword ptr [rcx + 8*rax + 16]
    addpd   xmm0, xmm1
    movupd  xmmword ptr [rsi + 8*rax], xmm2
    movupd  xmmword ptr [rsi + 8*rax + 16], xmm0

rdxrcx持有我的输入指针(A/ B),rsi是输出(R),并且rax是一个偏移计数器。所以它一次加载两对输入/输出,使用 SIMD 指令添加它们,并将它们写入输出 - 到目前为止一切都很好。

相反,如果我写以下内容:

void loop2(int N, double* R, double* A, double* B) {
    for (int i = 0; i < N; i += 2) {
        R[i] = A[i] + B[i];
        R[i + 1] = A[i + 1] + B[i + 1];
    }
}

LLVM 生成以下内容(编译器资源管理器):

.LBB0_13:
    movupd  xmm0, xmmword ptr [rdx + 8*rdi]
    movupd  xmm1, xmmword ptr [rdx + 8*rdi + 16]
    movupd  xmm2, xmmword ptr [rcx + 8*rdi]
    addpd   xmm2, xmm0
    movupd  xmm0, xmmword ptr [rcx + 8*rdi + 16]
    addpd   xmm0, xmm1

    movapd  xmm1, xmm2
    unpckhpd        xmm1, xmm0      # xmm1 = xmm1[1],xmm0[1]
    unpcklpd        xmm2, xmm0      # xmm2 = xmm2[0],xmm0[0]
    movapd  xmm0, xmm2
    unpcklpd        xmm0, xmm1      # xmm0 = xmm0[0],xmm1[0]
    unpckhpd        xmm2, xmm1      # xmm2 = xmm2[1],xmm1[1]

    movupd  xmmword ptr [rsi + 8*rdi + 16], xmm2
    movupd  xmmword ptr [rsi + 8*rdi], xmm0

为清楚起见,添加了间距,因为中间部分与unpckhpdetc. 让我感到困惑。据我所知,这 6 条指令的整体效果只是交换xmm0and xmm2,这似乎是在浪费时间。

知道为什么要这样做吗?有没有办法阻止它?:p


编辑:我编辑了 ASMloop2()以删除所有类似的块(并在随后的写入中交换寄存器),它似乎运行正确并且速度与loop1()(约快 40%)相同

标签: cassemblycompilationx86-64llvm-clang

解决方案


推荐阅读