LLVM / Clang генерирует бесполезные инструкции - PullRequest
0 голосов
/ 08 февраля 2019

Если я напишу этот код:

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

rdx и rcx содержат мои входные указатели (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

Для ясности добавлен интервал, потому что именно эта средняя часть с unpckhpd и т. Д. Меня смущает.Насколько я вижу, общий эффект этих 6 инструкций состоит в том, чтобы просто поменять местами xmm0 и xmm2, что кажется пустой тратой времени.

Есть идеи, почему он это делает?И есть ли способ остановить это?: p


РЕДАКТИРОВАТЬ : я отредактировал ASM для loop2(), чтобы удалить все похожие блоки (и поменять местами регистры в последующих записях), и он, кажется, работает правильнои с той же скоростью, что и loop1() (~ 40% быстрее)

...