RISC-V 向量扩展 RVV

FreeGuideOnline 最新 2026-07-11

assembly

带掩码的向量加法,仅处理 v0 中位为1 的元素

vadd.vv v1, v2, v3, v0.t


---

## 4. RVV 指令集概览

RVV 指令可以分为以下几大类:

### 4.1 向量配置指令
| 指令                | 描述                                       |
| ------------------- | ------------------------------------------ |
| `vsetvli rd, rs1, vtypei` | 根据 `rs1` 中的需求元素数,计算 `vl` 并写入 `rd`,同时设置 `vtype` |
| `vsetivli rd, uimm, vtypei` | 立即数版本 |
| `vsetvl  rd, rs1, rs2` | 使用 `rs2` 中的 `vtype` 值 |

### 4.2 向量访存指令
支持多种寻址模式:

- **单元步长** (unit-stride):`vle8.v`, `vle16.v`, `vle32.v`, `vle64.v` 以及对应的 `vse`
- **常数步长** (strided):`vlse8.v`, `vsse8.v` 等
- **索引型** (indexed):gather 用 `vluxei8.v`, `vloxei8.v`;scatter 用 `vsuxei8.v`, `vsoxei8.v`
- **段式** (segment):一次加载多个连续字段,适合结构数组 (AoS) 访问

### 4.3 向量算术指令
支持整数、定点、浮点运算,并且可以使用不同的宽度组合(如加宽、缩小)。

- 整数:`vadd.vv`, `vsub.vv`, `vmul.vv`, `vdiv.vv`, `vrem.vv`
- 浮点:`vfadd.vv`, `vfsub.vv`, `vfmul.vv`, `vfdiv.vv`, `vfsqrt.v`
- 比较:`vmseq.vv`, `vmslt.vv` 等,结果写入掩码寄存器 `vd`(通常是 `v0`)
- 归约:`vredsum.vs`, `vredmax.vs` 将向量归约为标量
- 乘加:`vmacc.vv`, `vnmsac.vv` 等

### 4.4 向量排列指令
用于元素重组:

- `vmv.v.v`:复制整个向量(受 `vl` 控制)
- `vmerge.vvm`:按掩码合并两个向量
- `vslideup.vi`, `vslidedown.vi`:向量滑动
- `vrgather.vv`:从源向量按索引收集元素
- `vcompress.vm`:按掩码压缩元素

### 4.5 固定点和扩展整数指令
包括饱和算术、平均、绝对差等,适合多媒体处理。

---

## 5. RVV 编程模型

### 5.1 汇编编程
使用 RVV 汇编时,核心流程为:

1. 用 `vsetvli` 设置 `vtype` 和 `vl`;
2. 执行向量访存 / 运算;
3. 处理剩余元素(循环尾部)。

示例:两个数组的单精度浮点加法,数组长度为 `n`(在 `a0` 中)。

```assembly
# a0 = n, a1 = 数组A地址, a2 = 数组B地址, a3 = 结果数组C地址
loop:
    vsetvli t0, a0, e32, m1, ta, ma    # 设置 SEW=32, LMUL=1, 尾部无关, 掩码未屏蔽
    vle32.v v1, (a1)                    # 加载A
    vle32.v v2, (a2)                    # 加载B
    vfadd.vv v3, v1, v2                 # v3 = v1 + v2
    vse32.v v3, (a3)                    # 存回C
    add a1, a1, t0, slli 2             # 移动指针(每个元素4字节)
    add a2, a2, t0, slli 2
    add a3, a3, t0, slli 2
    sub a0, a0, t0
    bnez a0, loop
    ret

vsetvli 返回的 t0 即为本次循环实际处理的元素数量,循环每次处理 vl 个元素直到剩余不足。这是典型的 strip‑mining 循环结构。

5.2 C/C++ Intrinsics

编译器(如 GCC、LLVM)为 RVV 提供了内建函数(intrinsics),无需手写汇编即可使用向量指令。需要包含 <riscv_vector.h>

上面的向量加法用 intrinsics 实现:

#include <riscv_vector.h>

void vector_add(float *c, float *a, float *b, size_t n) {
    size_t vl;
    for (size_t i = 0; i < n; i += vl) {
        vl = vsetvl_e32m1(n - i);          // 设置 SEW=32, LMUL=1
        vfloat32m1_t va = vle32_v_f32m1(a + i, vl);
        vfloat32m1_t vb = vle32_v_f32m1(b + i, vl);
        vfloat32m1_t vc = vfadd_vv_f32m1(va, vb, vl);
        vse32_v_f32m1(c + i, vc, vl);
    }
}