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 即可。