From 2c3c69465157db21434e42b403db9111124ab6a9 Mon Sep 17 00:00:00 2001 From: HorizonChaser Date: Mon, 17 Jun 2024 19:54:24 +0800 Subject: [PATCH] upd HorizonChaser report --- .../2024-01/HorizonChaser__RVV_on_GCC_13.md | 293 ++++++++++++++++++ ...zonchaser__rvv_intrinsics_naming_rule.webp | Bin 0 -> 18064 bytes 2 files changed, 293 insertions(+) create mode 100644 src/content/posts/2024-01/HorizonChaser__RVV_on_GCC_13.md create mode 100755 src/content/posts/2024-01/imgs/horizonchaser__rvv_intrinsics_naming_rule.webp diff --git a/src/content/posts/2024-01/HorizonChaser__RVV_on_GCC_13.md b/src/content/posts/2024-01/HorizonChaser__RVV_on_GCC_13.md new file mode 100644 index 0000000..2638d35 --- /dev/null +++ b/src/content/posts/2024-01/HorizonChaser__RVV_on_GCC_13.md @@ -0,0 +1,293 @@ +--- +title: 使用 GCC 13 编译包含 RVV Intrinsic 的程序 +author: 侯文轩 +pubDate: 2024-06-17 +categories: ['2024 年第一期'] +description: '使用 GCC 13 配置 RVV 环境, 并进行简单分析' +--- + +## 0. 总结 + +1. GCC 13 仅支持 RVV 0.11 (但相对容易获取), GCC 14 则支持完整的 RVV 1.0 +2. GCC 的 RVV Intrinsic 是通过编译器实现的 + - `riscv_vector.h` 中没有相关 Intrinsic 与数据类型的声明, 这导致 Intellisense 等代码补全无法直接工作 +3. GCC 的 RVV Intrinsic 需要 `__riscv` 前缀 +4. Linux 内核已经合并了对 RVV 的支持 + +## 1. 获取 GCC 13 + +目前 Debian 12 源中的 `gcc-riscv64-unknown-elf` 版本还在 `12.2.0`, 不支持 RVV Intrinsic. 所以我们需要从 `riscv-collab/riscv-gnu-toolchain` 根据自己的系统版本下载[最新的 nightly release](https://github.com/riscv-collab/riscv-gnu-toolchain/releases/tag/2024.04.12). 下载后解压并将路径添加到 PATH, 再看看我们拿到的 GCC 版本: + +``` +horizon@horizon-vm-ubuntu20:~$ riscv64-unknown-elf-gcc --version +riscv64-unknown-elf-gcc () 13.2.0 +Copyright (C) 2023 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. +``` + +GCC 13 仅支持 RVV 0.11, [GCC 14 则支持 RVV 1.0](https://gcc.gnu.org/onlinedocs/gcc-14.1.0/gcc/RISC-V-Options.html) . 这里我们出于简便起见不再自行编译 GCC (Debian sid 中 `gcc-riscv64-unknown-elf` 版本也只有 `13.2.0`; Arch 上有 [riscv64-elf-gcc 14.1.0](https://archlinux.org/packages/extra/x86_64/riscv64-elf-gcc/)). + +## 2. RVV Intrinsics + +目前最新的 RVV Intrinsic 规范的版本是 `1.0 RC0`, 可以在[这里](https://github.com/riscv-non-isa/rvv-intrinsic-doc/releases)获取. 每个 Intrinsic 函数名称可以分为如下部分: + +![rvv intrinsics nameing rule](imgs/horizonchaser__rvv_intrinsics_naming_rule.webp) + +- 前缀: `__risc_v` +- 操作 +- 操作数: `v` 表示向量, `x` 表示标量 +- 元素格式: `i` 整数, `f` IEEE-754 浮点数 +- `LMUL` 寄存器设定 + - `LMUL` 决定了将若干个向量寄存器视为一个整体, 例如 `LMUL=2` 时, v0 将表示 v0v1, v2 表示 v2v3; `LMUL=4` 时, v0 将表示 v0v1v2v3, v4 表示 v4v5v6v7. +- 可选的掩码或后缀 + +更多关于命名规则的信息可见 [Chapter 6. Naming scheme](https://github.com/riscv-non-isa/rvv-intrinsic-doc/releases/download/draft-20240612-a7127a7371caac3a6c76094fe4efb48cb560340a/v-intrinsic-spec.pdf#[{"num"%3A113%2C"gen"%3A0}%2C{"name"%3A"XYZ"}%2C0%2C841.89%2Cnull]). + +通过 `__riscv_v_intrinsic` 宏, 可以检测编译器对 RVV 的支持情况. 查看 `riscv_vector.h`, 可以看到这样一段: + +```c +/* NOTE: This implementation of riscv_vector.h is intentionally short. It does + not define the RVV types and intrinsic functions directly in C and C++ + code, but instead uses the following pragma to tell GCC to insert the + necessary type and function definitions itself. The net effect is the + same, and the file is a complete implementation of riscv_vector.h. */ +#pragma riscv intrinsic "vector" +``` + +由于不包含 intrinsic 函数以及相应的数据类型, 因此代码提示与补全不能生效 :( + +## 3. 编写一个 RVV demo 并在 qemu 中测试 + +### Vector Add + +参考张宇轩同学在[RVV C Intrinsic 配置教程](https://learning.eulixos.com/posts/2024-01/zyx_01_config/)中的实例代码, 增加了对拍: + +> **注意这里的 intrinsic 有 `__risc_v` 前缀, 如果你按照张宇轩同学的方法配置的环境的话, 可能需要去掉这些前缀** + +```c +// Reference: https://pages.dogdog.run/toolchain/riscv_vector_extension.html +// #define __riscv_vector // make VSC happy when reading riscv_vector.h +#include +#include + +int x[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; +int y[10] = {0, 9, 8, 7, 6, 5, 4, 3, 2, 1}; +int z[10], z_real[10]; + +void vec_add_rvv(int* dst, int* lhs, int* rhs, size_t avl) { + vint32m2_t vlhs, vrhs, vres; + for (size_t vl; (vl = __riscv_vsetvl_e32m2(avl)); + avl -= vl, lhs += vl, rhs += vl, dst += vl) { + vlhs = __riscv_vle32_v_i32m2(lhs, vl); + vrhs = __riscv_vle32_v_i32m2(rhs, vl); + vres = __riscv_vadd_vv_i32m2(vlhs, vrhs, vl); + __riscv_vse32_v_i32m2(dst, vres, vl); + } +} + +void vec_add_real(int* dest, int* lhs, int* rhs, size_t vlen) { + for (size_t i = 0; i < vlen; i++) { + dest[i] = lhs[i] + rhs[i]; + } +} + +void print_vec(int* v, size_t vlen, char* msg) { + printf("%s={ ", msg); + for (size_t i = 0; i < vlen; i++) { + printf("%d, ", v[i]); + } + + printf("}\n"); +} + +int main(int argc, char const* argv[]) { + +// check RVV support +#ifndef __riscv_v_intrinsic + printf("RVV NOT supported in this compiler\n"); + return 0; +#endif + + vec_add_rvv(z, x, y, 10); + vec_add_real(z_real, x, y, 10); + + print_vec(x, 10, "x[10]"); + print_vec(y, 10, "y[10]"); + + for (size_t i = 0; i < 10; i++) { + if (z[i] != z_real[i]) { + printf("==========\nTest FAILED: pos %d mismatch\n", i); + print_vec(z, 10, "z[10]"); + print_vec(z_real, 10, "z_real[10]"); + + return -1; + } + } + + print_vec(z, 10, "z[10]"); + printf("==========\nTest PASSED\n"); + + return 0; +} +``` + +使用 `riscv64-unknown-elf-gcc -O -march=rv64gcv vadd_test.c -o vadd_out` 编译, `qemu-riscv64 -cpu rv64,v=true,vlen=128,vext_spec=v1.0 ./vadd_out` 运行: + +```text +horizon@horizon-vm-ubuntu20:~$ qemu-riscv64 -cpu rv64,v=true,vlen=128,vext_spec=v1.0 ./rvv_out +x[10]={ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, } +y[10]={ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, } +z[10]={ 1, 11, 11, 11, 11, 11, 11, 11, 11, 1, } +========== +Test PASSED +``` + +可以看到 RVV Intrinsic 成功发挥了作用, 并且计算结果与我们手动计算完全相同~ + +#### One Step Further + +在 [这里](https://godbolt.org/z/T9qaebW1P), 我们可以看到 `vec_add_rvv` 函数反汇编后 (或者说汇编前? 总之是它对应的 RISC-V 汇编) 的结果. + +```ASM +vector_add: + beq a3,zero,.L1 +.L3: + vsetvli a5,a3,e32,m1,ta,ma + vle32.v v24,0(a1) + vle32.v v25,0(a2) + vfadd.vv v24,v24,v25 + vse32.v v24,0(a0) + sub a3,a3,a5 + slli a5,a5,2 + add a1,a1,a5 + add a2,a2,a5 + add a0,a0,a5 + bne a3,zero,.L3 +.L1: + ret +``` + +`beq` 对应 `for` 循环的初值边界: 若向量长度等于零, 则直接返回. + +`vsetvli a5, a3, e32, m1, ta, ma` 设置向量相关的配置: a3 是剩余的元素数,e32 表示每个元素是 32 位, m1 表示每个向量寄存器的长度是 1 倍的标准长度 (也就是说每个寄存器里面都是一个向量), ta 和 ma 分别表示尾部与掩码都是不可知的. + +两条 `vle32.v` 分别从 `a1` 与 `a2` 寄存器指向的地址加载一个 32 位的向量到对应的向量寄存器. `vfadd.vv` 进行两个向量之间的加法, `vse32.v` 将 `v24` 中的结果保存到 `a0` 指向的地址. 之后更新各个地址的值, 进行下一轮加法, 直到所有元素都被计算. + +### Vector Multiply + +类似地, 我们可以使用向量乘法, 这里使用 32 位浮点数, 每个向量由 8 个元素构成. + +> **注意这里的 intrinsic 有 `__risc_v` 前缀, 如果你按照张宇轩同学的方法配置的环境的话, 可能需要去掉这些前缀** + +```c +#include +#include + +void vector_mul_rvv(const float *a, const float *b, float *dest, size_t vlen) { + size_t vl; + size_t i = 0; + + for (; i < vlen; i += vl) { + // set vector length + vl = __riscv_vsetvl_e32m1(vlen - i); + + vfloat32m1_t va = __riscv_vle32_v_f32m1(&a[i], vl); + vfloat32m1_t vb = __riscv_vle32_v_f32m1(&b[i], vl); + + vfloat32m1_t vres = __riscv_vfmul_vv_f32m1(va, vb, vl); + + __riscv_vse32_v_f32m1(&dest[i], vres, vl); + } +} + +void vector_mul_real(const float *a, const float *b, float *dest, size_t vlen) { + for (size_t i = 0; i < vlen; i++) { + dest[i] = a[i] * b[i]; + } +} + +void print_vec(float *v, size_t vlen, char *msg) { + printf("%s={ ", msg); + for (size_t i = 0; i < vlen; i++) { + printf("%f, ", v[i]); + } + + printf("}\n"); +} + +int main() { + // check RVV support +#ifndef __riscv_v_intrinsic + printf("RVV NOT supported in this compiler\n"); + return 0; +#endif + + float a[8] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}; + float b[8] = {8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}; + float c[8], c_real[8]; + + vector_mul_rvv(a, b, c, 8); + vector_mul_real(a, b, c_real, 8); + + print_vec(a, 8, "a[8]"); + print_vec(b, 8, "b[8]"); + + for (size_t i = 0; i < 8; i++) { + if (c[i] != c_real[i]) { + printf("==========\nTest FAILED: pos %d mismatch\n", i); + print_vec(c, 8, "c[10]"); + print_vec(c_real, 8, "c_real[10]"); + + return -1; + } + } + + print_vec(c, 8, "c[10]"); + printf("==========\nTest PASSED\n"); + + return 0; +} +``` + +编译: `riscv64-unknown-elf-gcc -O -march=rv64gcv rvv_mul.c -o rvv_mul` + +运行: ` qemu-riscv64 -cpu rv64,v=true,vlen=128,vext_spec=v1.0 ./rvv_mul` + +结果: +```text +horizon@horizon-vm-ubuntu20:~$ qemu-riscv64 -cpu rv64,v=true,vlen=128,vext_spec=v1.0 ./rvv_mul +a[8]={ 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000, 8.000000, } +b[8]={ 8.000000, 7.000000, 6.000000, 5.000000, 4.000000, 3.000000, 2.000000, 1.000000, } +c[10]={ 8.000000, 14.000000, 18.000000, 20.000000, 20.000000, 18.000000, 14.000000, 8.000000, } +========== +Test PASSED +``` + +## 4. Linux 内核目前对 RVV 的支持 + +RVV 1.0 的支持 [已经被合并到内核](https://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git/commit/?h=for-next&id=d5e45e810e0e08114035d31d88049544c038e6fc). + +内核通过 [`bool has_vector()`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/vector.h#L38) 检测对 RVV 的支持: +- [`has_vector()`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/vector.h#L38) -> +- [`riscv_has_extension_unlikely(RISCV_ISA_EXT_v)`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/cpufeature.h#L109) -> +- [`__riscv_isa_extension_available(NULL, ext)`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/kernel/cpufeature.c#L64) -> +- [`test_bit(bit, bmap) ? true : false`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/kernel/cpufeature.c#L71), 最终还是以检测特定比特位的形式完成. + +### 4.1 RVV 任务切换 + +在 [arch/riscv/include/asm/switch_to.h#L102](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/switch_to.h#L102) 中, 可以看到首先判断是否存在 RVV 支持, 若存在, 则调用 [`__switch_to_vector(__prev, __next)`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/vector.h#L240) 完成 RVV 上下文的切换: + +1. 判断当前任务状态 +2. 调用 [`__riscv_v_vstate_save`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/vector.h#L103) 以 8 个一组保存 v0~v31 的值 +3. 判断下一个任务的状态 +4. 调用 [`riscv_v_vstate_set_restore`](https://github.com/torvalds/linux/blob/6ba59ff4227927d3a8530fc2973b80e94b54d58f/arch/riscv/include/asm/vector.h#L126), 同样分组恢复各向量寄存器的值. + +## 99. 更多关于 RVV 的材料 + +- [RVV Intrinsic Viewer](https://dzaima.github.io/intrinsics-viewer): 可以看到 RVV Intrinsics 的定义与功能分析 +- [RISC-V Vector Programming in C with Intrinsics +](https://fprox.substack.com/p/risc-v-vector-programming-in-c-with): 一份很好的 RVV 教程, 包括直接用汇编以及使用 intrinsics +- [RISC-V Vector Intrinsic Document](https://github.com/riscv-non-isa/rvv-intrinsic-doc): RVV Intrinsic 标准 + + diff --git a/src/content/posts/2024-01/imgs/horizonchaser__rvv_intrinsics_naming_rule.webp b/src/content/posts/2024-01/imgs/horizonchaser__rvv_intrinsics_naming_rule.webp new file mode 100755 index 0000000000000000000000000000000000000000..594d80556ea2e712ef62b9126364bf505592bb90 GIT binary patch literal 18064 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