FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-V VectorのAuto Vectorizationが生成するアセンブリコードを読み解く (Memory Copy)

RISC-Vベクトル命令の自動生成がかなりサマになってきた、という話を聞いたので、GCCのAutoVectorizationを見てみることにした。

使用したのは、RVVに対応したRISC-V GCCだ。

$ riscv64-unknown-elf-gcc --version
riscv64-unknown-elf-gcc () 12.0.1 20220505 (prerelease)
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

で、以下の単純なmemory copyのコードをコンパイルしてみることにした。

void copy_data_scalar(int8_t *dest_data, int8_t *source_data, const int data_num)
{
  for (int i = 0; i < data_num; i++) {
    dest_data[i] = source_data[i];
  }
}
riscv64-unknown-elf-gcc -march=rv64gcv -DPREALLOCATE=1 -static -mcmodel=medany -std=gnu99 -O3 -ffast-math -fno-common -fno-builtin -fno-builtin-printf -nostdlib -nostartfiles -lm -lgcc -T test.ld crt.S syscalls.c main.c memcpy.S -o test.elf
riscv64-unknown-elf-objdump -D test.elf > test.elf.dmp

出てきたのが以下のアセンブリコード。なるほど、確かにRVV命令が生成されている。それ以外にも余分な命令が出ていそうだぞ?

copy_data_scalar:
        ble     a2,zero,.L11
        addi    a4,a1,1
        csrr    a5,vlenb
        sub     a4,a0,a4
        addi    a3,a5,-2
        bleu    a4,a3,.L13
.L14:
        vsetvli a4,a2,e8,m1,ta,mu
        vle8.v  v24,(a1)
        sub     a2,a2,a4
        vse8.v  v24,(a0)
        add     a1,a1,a5
        add     a0,a0,a5
        bne     a2,zero,.L14
        ret
.L13:
        add     a2,a1,a2
.L16:
        lb      a5,0(a1)
        addi    a1,a1,1
        addi    a0,a0,1
        sb      a5,-1(a0)
        bne     a1,a2,.L16
.L11:
        ret
  • コピーサイズが0以下ならば、何もせずに終了。
        ble     a2,zero,.L11
  • これはおそらくオーバラップを気にしているのではなかろうか
        addi    a4,a1,1
        csrr    a5,vlenb
        sub     a4,a0,a4
        addi    a3,a5,-2
        bleu    a4,a3,.L13

// ジャンプ先のシーケンシャルループ
.L13:
        add     a2,a1,a2
.L16:
        lb      a5,0(a1)
        addi    a1,a1,1
        addi    a0,a0,1
        sb      a5,-1(a0)
        bne     a1,a2,.L16

a4 = dest - (source + 1) というのは、sourceとdestの距離を示していて、vlenb(つまりベクトル長)-2よりも小さければ、1バイトずつコピーする方式に切り替えている。 こういう状態が発生するかというと...まあ確かに、厳密にはシーケンシャルに動かすコードとベクトルで動かすコードでは結果が異なる。細かいところを考えてみたら、結構そういうところをケアする必要があるのか。