RISC-Vベクトル命令の自動生成がかなりサマになってきた、という話を聞いたので、GCCのAutoVectorizationを見てみることにした。
$ 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バイトずつコピーする方式に切り替えている。
こういう状態が発生するかというと...まあ確かに、厳密にはシーケンシャルに動かすコードとベクトルで動かすコードでは結果が異なる。細かいところを考えてみたら、結構そういうところをケアする必要があるのか。