👾
RISC-VのVector Extensionでメモリをゼロクリアするコード
前回の記事でmemcpyをベクター命令で実行するサンプルコードがありました。
それならということで、メモリをゼロクリアする関数を書いたらベクター命令を使ってどんなコードが生成されるかを試してみました。
memclr関数
memclr.c
#include <stddef.h>
#include <stdint.h>
void memclr(void *s, size_t n)
{
uint8_t *p = (uint8_t*)s;
while (n-- > 0) {
*p++ = 0;
}
}
1回目(失敗)
$ clang-16 -c -march=rv64gcv -target riscv64 -O2 memclr.c
$ llvm-objdump -dr memclr.o
memclr.o: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <.text>:
0: 01 00 nop
0000000000000000: R_RISCV_ALIGN *ABS*+0x2
0000000000000002 <memclr>:
2: 81 c1 beqz a1, 0x2 <memclr>
0000000000000002: R_RISCV_RVC_BRANCH .LBB0_2
4: 2e 86 mv a2, a1
6: 81 45 li a1, 0
8: 17 03 00 00 auipc t1, 0
0000000000000008: R_RISCV_CALL_PLT memset
0000000000000008: R_RISCV_RELAX *ABS*
c: 67 00 03 00 jr t1
0000000000000010 <.LBB0_2>:
10: 82 80 ret
あれ?意外に短いな。
よくみると、これはmemset(s, 0, n);
を呼び出しているだけじゃん ! (笑)
というか、標準Cライブラリのmemsetがそのシステム向けにチューニングされていると仮定すれば、普通に賢いですね。
clang 「そんなん、memsetで一発ですわ。」
2回目
$ clang-16 --help |less
で調べると -ffreestanding
をつければ標準Cライブラリに依存しないコードになりそう。
$ clang-16 -c -ffreestanding -march=rv64gcv -target riscv64 -O2 memclr.c
$ llvm-objdump -dr memclr.o
memclr.o: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <.text>:
0: 01 00 nop
0000000000000000: R_RISCV_ALIGN *ABS*+0x2
0000000000000002 <memclr>:
2: 81 c1 beqz a1, 0x2 <memclr>
0000000000000002: R_RISCV_RVC_BRANCH .LBB0_7
4: 73 28 20 c2 csrr a6, vlenb
8: 93 17 18 00 slli a5, a6, 1
c: 63 f0 f5 00 bgeu a1, a5, 0xc <memclr+0xa>
000000000000000c: R_RISCV_BRANCH .LBB0_3
10: aa 86 mv a3, a0
12: 2e 86 mv a2, a1
14: 01 a0 j 0x14 <memclr+0x12>
0000000000000014: R_RISCV_RVC_JUMP .LBB0_6
0000000000000016 <.LBB0_3>:
16: 13 86 f7 ff addi a2, a5, -1
1a: 6d 8e and a2, a2, a1
1c: 91 8d sub a1, a1, a2
1e: b3 06 b5 00 add a3, a0, a1
22: 57 77 00 0c vsetvli a4, zero, e8, m1, ta, ma
26: 57 34 00 5e vmv.v.i v8, 0
000000000000002a <.LBB0_4>:
2a: 27 04 85 02 vs1r.v v8, (a0)
2e: 33 07 05 01 add a4, a0, a6
32: 27 04 87 02 vs1r.v v8, (a4)
36: 9d 8d sub a1, a1, a5
38: 3e 95 add a0, a0, a5
3a: 81 e1 bnez a1, 0x3a <.LBB0_4+0x10>
000000000000003a: R_RISCV_RVC_BRANCH .LBB0_4
3c: 01 c2 beqz a2, 0x3c <.LBB0_4+0x12>
000000000000003c: R_RISCV_RVC_BRANCH .LBB0_7
000000000000003e <.LBB0_6>:
3e: 7d 16 addi a2, a2, -1
40: 13 85 16 00 addi a0, a3, 1
44: 23 80 06 00 sb zero, 0(a3)
48: aa 86 mv a3, a0
4a: 01 e2 bnez a2, 0x4a <.LBB0_6+0xc>
000000000000004a: R_RISCV_RVC_BRANCH .LBB0_6
000000000000004c <.LBB0_7>:
4c: 82 80 ret
うまくベクター命令使っています。
.LBB0_4
のブロックを見てください。1回のループでベクタレジスタ2つ分をゼロクリアしていますね。
関連
Discussion