Skip to content

RISCV中的SIMD接口移植

Posted on:March 2, 2026 at 04:06 AM

LLVM 中 RISC-V SIMD(以 RVV 为主)代码分析与使用指南

1. 先说结论

在 LLVM/Clang 里,RISC-V 的“SIMD”主要是 V 扩展(RVV, Vector Extension)。 实现是分层的:

  1. Clang 前端:定义/生成 riscv_vector.h 与 builtin,做语义检查;
  2. LLVM IR/中端:使用 scalable vector、RVV intrinsic、VP intrinsic;
  3. LLVM RISC-V 后端:DAG/GISel 降低、RVV pseudo 指令选择、vsetvli 插入优化;
  4. 测试:llvm/test/CodeGen/RISCV/rvv 下大量 .ll/.mir 用例。

另外,RISC-V 也有 P extension(packed-SIMD) 路线,但当前主线 SIMD 能力和生态重点是 RVV。


2. 相关代码“在哪”

2.1 Clang 前端(intrinsics/builtin/头文件)

2.2 生成产物(构建目录)

2.3 LLVM RISC-V 后端(核心)

2.4 文档与测试


3. 代码链路(从 riscv_vector.td 到最终汇编)

  1. riscv_vector.td 定义 intrinsic/builtin 规则;
  2. RISCVVEmitter.cpp 生成 builtin/语义/codegen 的 .inc
  3. Clang 在 SemaRISCV.cpp 检查类型、特性、策略参数;
  4. Clang 在 TargetBuiltins/RISCV.cpp 发射 LLVM IR intrinsic;
  5. LLVM 后端在 RISCVISelLowering.cpp 降低为 VL 节点/伪指令;
  6. RISCVInsertVSETVLI 根据数据流插入最少必要的 vsetvli
  7. 最终由 AsmPrinter 输出 RVV 指令。

4. 关键 commit(里程碑与近期演进)

下面是从仓库 git log 中筛出的高相关提交(按时间与主题整理)。

4.1 起步阶段(基础设施)

  1. 5baef6353e88 (2020-11-30) [RISCV] Initial infrastructure for code generation of the RISC-V V-extension
  2. a6805a0e02c9 (2020-12-11) 定义 vadd/vsub/vrsub intrinsic 并 lowering 到 V 指令
  3. c1d6d461aa77 (2020-12-15) 定义 vle/vse intrinsic
  4. d6a0560bf258 (2021-03-05) Clang 增加 RVV 专用 TableGen backend(RISCVVEmitter
  5. 95c0125f2bc6 (2021-02-25) Clang 增加 vsetvl/vsetvlmax intrinsic

4.2 RVV 1.0 与前端生态扩展

  1. 0649dfebbab7 (2021-11-04) RVV 1.0 命名更新
  2. 09058654f68d (2023-12-18) Vector Crypto 扩展去 experimental
  3. 239127d731e6 (2024-08-31) LLVM IR 支持 RISC-V vector tuple type
  4. 0727e7a8a9fd (2025-10-18) 后端支持 Zvfbfa codegen
  5. 8c5a0f74a12d (2025-12-24) 支持 Zvfofp8min llvm intrinsic 与 codegen

4.3 近期(2026)优化/新扩展

  1. 67601a43b57a (2026-01-12) 增加 RISCVVSETVLIInfoAnalysis
  2. 79a1b80af856 (2026-01-12) 按兼容 vtype/vl 优先调度 RVV 指令
  3. 0d11f68a8aa2 (2026-02-03) VLOptimizer 提前到 ISel 后运行
  4. 972e73b812cb / e99259334168 (2026-02-09) Zvabd lowering 与 codegen
  5. 5c1426737479 (2026-02-14) Clang 增加 Zvabd intrinsics
  6. a59b4fca866a (2026-02-26) RISCVVLOptimizer 使用 elementsDependOnVL 改造

4.4 与 P-extension SIMD 的一个提示


5. 如何使用(开发者视角)

5.1 直接写 RVV C intrinsic

最常见方式:#include <riscv_vector.h>

编译示例(RV64 + GCV):

clang --target=riscv64 -march=rv64gcv -mabi=lp64d -O2 test.c -S -o test.s

如果要开具体向量子扩展(示例):

clang --target=riscv64 -march=rv64gcv_zvbb_zvbc -mabi=lp64d -O2 test.c -S -o test.s

5.2 让自动向量化走 RVV

clang --target=riscv64 -march=rv64gcv -mabi=lp64d -O3 test.c -S -o test.s

可结合 remark 看向量化情况:

clang --target=riscv64 -march=rv64gcv -mabi=lp64d -O3 \
  -Rpass=loop-vectorize -Rpass-missed=loop-vectorize test.c -c

5.3 固定向量长度相关参数(后端文档里常见)

llvm/docs/RISCV/RISCVVectorExtension.rst 中提到可用:

-mrvv-vector-bits=zvl

或:

-mllvm -riscv-v-vector-bits-max=<VLEN>

用于需要固定/已知 VLEN 场景(例如某些 fixed-length vector 降低路径)。

5.4 验证是否生成 RVV 指令

clang --target=riscv64 -march=rv64gcv -mabi=lp64d -O2 test.c -S -o - | rg "vsetvli|vle|vse|vadd|vm"

如果有 vsetvliv* 指令,通常说明已走 RVV 路径。


6. C++ 示例:从 intrinsic 到后端 SIMD

下面是一个最小链路示例(vle -> vadd -> vse):

// rvv_add.cpp
#include <cstddef>
#include <cstdint>
#include <riscv_vector.h>

extern "C" void add_i32_rvv(const int32_t *a, const int32_t *b, int32_t *c, size_t n) {
  size_t i = 0;
  while (i < n) {
    size_t vl = __riscv_vsetvl_e32m1(n - i);
    vint32m1_t va = __riscv_vle32_v_i32m1(a + i, vl);
    vint32m1_t vb = __riscv_vle32_v_i32m1(b + i, vl);
    vint32m1_t vc = __riscv_vadd_vv_i32m1(va, vb, vl);
    __riscv_vse32_v_i32m1(c + i, vc, vl);
    i += vl;
  }
}

观察链路的命令:

# 1) 前端到 LLVM IR(会看到 llvm.riscv.* intrinsic)
clang --target=riscv64 -march=rv64gcv -mabi=lp64d -O2 -S -emit-llvm rvv_add.cpp -o rvv_add.ll
rg "llvm.riscv|vsetvl|vadd|vle|vse" rvv_add.ll

# 2) LLVM IR 到 RISC-V 汇编
llc -mtriple=riscv64 -mattr=+v rvv_add.ll -o rvv_add.s

# 3) 确认后端确实生成 RVV SIMD 指令
rg "vsetvli|vle32\\.v|vadd\\.vv|vse32\\.v" rvv_add.s

你会看到:

  1. C++ intrinsic 调用;
  2. IR 中 llvm.riscv.*(目标相关 intrinsic);
  3. 后端输出 RVV 指令(含 vsetvli)。

7. 想移植一个 intrinsic,可参考哪些 commit

下面是“新增/移植 intrinsic”最有参考价值的一组提交(从简单到复杂):

  1. 95c0125f2bc6 (2021-02-25) [Clang][RISCV] Add rvv vsetvl and vsetvlmax intrinsic functions. 适合看最基础 builtin/intrinsic 接入。
  2. be947aded019 (2021-03-17) [RISCV][Clang] Add RVV vle/vse intrinsic functions. 典型 load/store intrinsic。
  3. 66c05609e0d5 (2021-03-29) [RISCV][Clang] Add some RVV Integer intrinsic functions. 典型算术 intrinsic。
  4. 593bf9b4ded3 (2021-05-25) [Clang][RISCV] Implement vlseg and vlsegff. 分段 load/store(更复杂签名)。
  5. 239127d731e6 (2024-08-31) [llvm][RISCV] Support RISCV vector tuple type in llvm IR tuple 类型链路关键点。
  6. 0727e7a8a9fd (2025-10-18) [RISCV] Support Zvfbfa codegen 子扩展 codegen 接入案例。
  7. 8c5a0f74a12d (2025-12-24) [RISCV][llvm] Support Zvfofp8min llvm intrinsics and codegen 新子扩展 + LLVM intrinsic + codegen 联动。
  8. 5c1426737479 (2026-02-14) [Clang][RISCV] Add Zvabd intrinsics 较新的前端 intrinsic 扩展案例。
  9. 972e73b812cb / e99259334168 (2026-02-09) Zvabd 的后端 lowering 与 codegen 对应提交。 适合配合上面的 clang 提交一起看“前后端打通”。

7.1 移植时建议按这个顺序改

  1. clang/include/clang/Basic/riscv_vector*.td 增加定义;
  2. 确认生成 .incriscv_vector.h(TableGen/构建产物);
  3. 必要时补 SemaRISCV.cpp 的特性与参数检查;
  4. 在 LLVM 后端 .td/.cpp 增加 intrinsic/lowering/pattern;
  5. 补 clang/llvm 双侧测试(clang/test + llvm/test/CodeGen/RISCV/rvv)。

8. 常用定位命令(维护/二次分析)

# 找 RVV 后端实现
rg -n "RVV|VSETVLI|VVL|VPseudo|Scalable" llvm/lib/Target/RISCV

# 找 Clang RVV 前端入口
rg -n "riscv_vector|RVV|__builtin_rvv" clang/lib clang/include/clang

# 看 RVV 测试
ls llvm/test/CodeGen/RISCV/rvv | head

# 看最近 RVV 提交
git log --oneline -- llvm/lib/Target/RISCV clang/include/clang/Basic/riscv_vector.td | rg -i "rvv|vector|vsetvli|zv"

9. 一个实用阅读顺序

  1. 先读 llvm/docs/RISCV/RISCVVectorExtension.rst(架构全景);
  2. 再看 clang/include/clang/Basic/riscv_vector.td(intrinsic 定义);
  3. 跟到 clang/lib/Sema/SemaRISCV.cppclang/lib/CodeGen/TargetBuiltins/RISCV.cpp
  4. 最后看 RISCVISelLowering.cppRISCVInstrInfoVVLPatterns.tdRISCVInsertVSETVLI.cpp