2. 向量加法运算
任务目标
本案例实现两个向量(数组)的逐元素相加,并将结果存储到目标向量中。通过RISC-V向量扩展指令完成核心计算,对比标量循环以验证正确性。
核心实现
#include <stdio.h>
#include <stdint.h>
#include <string.h>
// 向量长度(元素个数)
#define VLEN 128
// 使用RISC-V向量扩展的向量加法
// 内联汇编实现:vle32.v加载,vadd.vv执行加法,vse32.v存储
void vec_add_rvv(const int32_t *a, const int32_t *b, int32_t *c, int n) {
int i = 0;
while (i < n) {
int vl;
// 设置向量长度(每次处理尽可能多的元素)
asm volatile(
"vsetvli %0, %1, e32, m1\n\t" // %0 = vl, %1 = 剩余元素数
: "=r"(vl)
: "r"(n - i)
);
if (vl <= 0) break;
asm volatile(
"vle32.v v0, (%0)\n\t" // 从a加载vl个32位元素到v0
"vle32.v v1, (%1)\n\t" // 从b加载vl个32位元素到v1
"vadd.vv v2, v0, v1\n\t" // v2 = v0 + v1(逐元素)
"vse32.v v2, (%2)\n\t" // 将v2存储到c
:
: "r"(a + i), "r"(b + i), "r"(c + i)
: "memory", "v0", "v1", "v2"
);
i += vl;
}
}
// 标量参考实现(用于验证)
void vec_add_scalar(const int32_t *a, const int32_t *b, int32_t *c, int n) {
for (int i = 0; i < n; i++) {
c[i] = a[i] + b[i];
}
}
int main() {
int32_t a[VLEN], b[VLEN], c_rvv[VLEN], c_ref[VLEN];
// 初始化测试数据
for (int i = 0; i < VLEN; i++) {
a[i] = i * 3 - 100;
b[i] = 200 - i * 2;
}
// 执行向量加法
vec_add_rvv(a, b, c_rvv, VLEN);
vec_add_scalar(a, b, c_ref, VLEN);
// 验证结果
int errors = 0;
for (int i = 0; i < VLEN; i++) {
if (c_rvv[i] != c_ref[i]) {
printf("Mismatch at [%d]: RVV=%d, Ref=%d\n", i, c_rvv[i], c_ref[i]);
errors++;
}
}
if (errors == 0) {
printf("PASS: All %d elements match.\n", VLEN);
} else {
printf("FAIL: %d errors.\n", errors);
}
// 打印前5个结果作为示例
printf("First 5 results: ");
for (int i = 0; i < 5; i++) {
printf("%d ", c_rvv[i]);
}
printf("\n");
return errors ? 1 : 0;
}
编译与运行
# 使用支持向量扩展的RISC-V GCC编译
riscv64-unknown-elf-gcc -march=rv64gcv -mabi=lp64d -O2 -o vec_add vec_add.c
# 在模拟器(如QEMU)中运行
qemu-riscv64 ./vec_add
验证方法
程序内置了标量参考实现,运行后自动对比向量指令结果与标量结果。当输出包含 PASS: All 128 elements match. 时,说明向量加法功能正确。若出现 Mismatch 行,则对应位置的向量计算有误。
关键说明
vsetvli动态设置向量长度,返回实际处理的元素数vl,循环直到处理完所有元素。- 使用
e32, m1表示32位整数元素、单个向量寄存器组,适用于通用RVV 1.0实现。 - 内联汇编中
"memory"约束确保内存访问顺序正确,v0-v2声明为 clobber 防止编译器误用。 - 本案例仅演示整数向量加法,浮点加法只需将加载/存储指令改为
vle32.v/vse32.v配合fadd.vv即可。