LLVM Vectorization Subsystem Summary: Current State & Architecture (2025)
1. 核心矛盾:中端抽象 vs 后端差异
LLVM 中端(Middle-end)向量化的核心难点在于通用 IR 与特异硬件之间的鸿沟。
- 抽象层: LLVM IR 试图保持通用(如
<4 x i32>或<vscale x 4 x i32>)。 - 硬件层: 处理器实现差异巨大(定长 vs 变长,Mask 支持,Gather/Scatter 代价)。
- 桥梁 (TTI): TargetTransformInfo 是中端向后端查询的接口,用于获取合法性(Legality)、代价(Cost Model)和配置(Vector Width)。
2. 向量化主要组件
A. SLP Vectorizer (Superword-Level Parallelism)
- 定位: 基本块 (Basic Block) 级别的向量化。
- 原理: Bottom-Up (自底向上)。
- 种子发现: 寻找连续的 Store 或 Load 指令。
- 树构建: 沿着 Use-Def 链向上回溯,将标量指令打包成向量树。
- 调度: 确保打包不违反数据依赖。
- 适用场景: 手动展开的循环、结构体操作、复数运算、Loop Unroll 后的代码。
- 主要产出: 将多条标量指令合并为单条向量指令(Vectorize instructions distinct from loops)。
B. Loop Vectorizer (LV) - Classic & Modern
- 定位: 循环 (Loop) 级别的向量化。
- 原理: Top-Down (自顶向下),将时间(迭代)转化为空间(向量宽度)。
- 流程:
- Legality: 检查内存依赖 (MemorySSA, Alias Analysis) 和控制流。
- Cost Model: 计算 VF (Vectorization Factor) 和 UF (Unroll Factor)。
- Transform: 生成向量循环主体 + 标量尾部 (Scalar Epilogue)。
C. VPlan (The Modern Engine)
- 定位: Loop Vectorizer 的现代内核,显式的向量化规划模型。
- 核心机制:
- H-CFG: 分层的控制流图,独立于底层 LLVM IR。
- Recipes: 描述最终指令生成的配方(如
VPWidenRecipe)。 - VPlan-to-VPlan Transforms: 在图层面进行优化(Predication, Dead Code Elimination)。
- 现状 (2025): 已接管 LV 的代码生成路径,支持基于 VPlan 的 Cost Model。
3. 变长向量化 (Scalable Vectorization / RVV) 的挑战与解法
A. 核心痛点
- 未知长度: 编译期不知道
vscale(VLEN),打破了传统定长向量化的假设。 - Shuffle 失效: LLVM IR 的
shufflevector指令强制要求 Mask 为编译期常数。- 后果: 无法用标准 IR 表达
Reverse(逆序) 或Stride(跨步) 等操作,因为它们的 Mask 索引依赖运行时长度。
- 后果: 无法用标准 IR 表达
B. VPlan 的解决方案
VPlan 是 RISC-V 高效向量化的救星,它允许在更抽象的层级处理这些问题:
- EVL Tail Folding (显式向量长度):
- 不再生成标量 Epilogue。
- 生成带有
AVL控制的循环,利用 RISC-V 的setvl指令处理尾部。
- 特定的 Recipes (配方):
- 引入
VPWidenIntrinsicRecipe等,绕过shufflevector的限制。 - 将
Reverse模式直接映射为vp.reverseintrinsic。 - 将
Stride模式识别并折叠为vp.strided.load(对应vlse指令)。
- 引入
4. 完整的向量化流水线 (Pipeline)
当开启 -O3 时,LLVM 的处理顺序如下:
- Loop Vectorizer (VPlan-backed):
- 优先尝试循环向量化。
- 若是 RISC-V,尝试构建支持 EVL 的 VPlan。
- 如果代价过高或依赖无法解决,则放弃。
- Loop Unroll:
- 如果 LV 放弃,或者为了进一步优化,展开循环体。
- SLP Vectorizer:
- 作为“扫地僧”进场。
- 扫描展开后的代码或线性代码块,打包剩余的标量操作。
5. 总结结论
- SLP 负责把代码里本就存在的空间并行找出来。
- Loop Vectorizer 负责把循环里的时间并行转换成空间并行。
- VPlan 是为了解决变长向量(RVV/SVE)和复杂控制流而引入的显式规划层,它解决了中端 IR 过于通用而无法描述硬件特性(如 EVL, Predication)的问题。