4. 向量乘法运算

任务目标

实现两个长度相同的向量逐元素相乘(Hadamard积),将结果存储到第三个向量中。本案例使用RISC-V向量扩展指令完成核心计算,并通过C语言封装调用验证结果正确性。

环境与依赖

  • 支持RISC-V向量扩展(V扩展)的编译器:riscv64-unknown-elf-gcc(需支持-march=rv64gcv)
  • 模拟器或开发板:QEMU(qemu-riscv64)或等效环境

核心实现

以下代码定义向量乘法函数,使用向量加载、乘法、存储指令完成逐元素相乘。向量长度通过vsetvli动态设置,循环处理可能超过向量寄存器长度的数据。

#include <stdint.h>
#include <stddef.h>

// 向量逐元素乘法: dst[i] = src1[i] * src2[i]
void vec_mul_int32(int32_t *dst, const int32_t *src1, const int32_t *src2, size_t n) {
    size_t i = 0;
    while (i < n) {
        size_t vl = __builtin_riscv_vsetvli(n - i, __RISCV_VXRM_RNU, __RISCV_VSEW_32);
        vint32m1_t v1 = __builtin_riscv_vle32_v_i32m1(src1 + i, vl);
        vint32m1_t v2 = __builtin_riscv_vle32_v_i32m1(src2 + i, vl);
        vint32m1_t vres = __builtin_riscv_vmul_vv_i32m1(v1, v2, vl);
        __builtin_riscv_vse32_v_i32m1(dst + i, vres, vl);
        i += vl;
    }
}

验证入口

以下主函数构造固定输入,调用向量乘法并检查结果。预期输出为逐元素乘积,若全部匹配则打印PASS。

#include <stdio.h>

int main() {
    const int32_t a[8] = {1, -2, 3, 4, -5, 6, 7, -8};
    const int32_t b[8] = {2, 3, -1, 5, 2, -4, 1, 3};
    int32_t c[8];
    const int32_t expected[8] = {2, -6, -3, 20, -10, -24, 7, -24};

    vec_mul_int32(c, a, b, 8);

    int ok = 1;
    for (int i = 0; i < 8; i++) {
        if (c[i] != expected[i]) {
            printf("Mismatch at [%d]: got %d, expected %d\n", i, c[i], expected[i]);
            ok = 0;
        }
    }
    if (ok) printf("PASS\n");
    return ok ? 0 : 1;
}

编译与运行

riscv64-unknown-elf-gcc -march=rv64gcv -mabi=lp64d -O2 -o vec_mul vec_mul.c
qemu-riscv64 ./vec_mul

验证方法

  • 程序输出PASS且退出码为0,表示所有8个元素乘积与预期一致。
  • 若输出Mismatch at [i],则定位到对应索引,检查输入数据或向量指令使用是否正确。
  • 可修改数组长度测试非向量寄存器倍数的边界情况(如n=10),验证循环处理逻辑。

关键说明

  • __builtin_riscv_vsetvli动态设置向量长度,返回值是实际处理的元素个数,循环直至处理完所有元素。
  • 本实现使用vint32m1_t(32位整数,单寄存器组),适用于任意长度向量;若需更高吞吐可改用m2/m4/m8,但需保证数据对齐。
  • 乘法指令vmul.vv执行两个向量寄存器逐元素相乘,结果类型与输入一致。