编译器优化:何为SLP矢量化( 四 )


文章插图
 
(1)SLPVectorizer代码行数:6178 ~ 6228
该Pass是个function pass,以function为单位进行优化,意味着用的资源也是function级别的 。addRequired指的是该PASS中用到的分析结果,addPreserved指的是该pass执行后相应的analysis pass的分析结果仍然有效 。
(2)runImpl()代码行数:6254~ 6323
该Pass的核心功能在此函数中管理,用到了两个容器 Stores和GEPs,定义在头文件:
using StoreList = SmallVector<StoreInst *, 8>;using StoreListMap = MapVector<Value *, StoreList>;using GEPList = SmallVector<GetElementPtrInst *, 8>;using GEPListMap = MapVector<Value *, GEPList>;/// The store instructions in a basic block organized by base pointer.StoreListMap Stores;/// The getelementptr instructions in a basic block organized by base pointer.GEPListMap GEPs;可以理解成两个map,以base pointer为key,instructions为 value 。
开始优化前,先做两个无法SLP的判断:(1)判断架构是否有矢量化寄存器;(2)判断function attribute是否包含NoImplicitFloat,如果包含则不做 。
然后先使用 bottom-up SLP 类从store开始构建从store开始的的指令链 。
之后调用DT->updateDFSNumbers(); 来排序(/// updateDFSNumbers - Assign In and Out numbers to the nodes while walking dominator tree in dfs order.)
接着使用post order(后序)遍历当前function中所有BB块,在遍历中尝试去矢量化,三个场景,(1)Vectorize trees that end at stores.(2)Vectorize trees that end at reductions.(3)vectorize the index computations of getelementptr instructions.
如果矢量化成功了,那么做收尾的调整 。
/// Perform LICM and CSE on the newly generated gather sequences.void optimizeGatherSequence();(3)BoUpSLP代码行数:550 ~ 2448
声明成员函数和结构类型,具体可以参考
https://llvm.org/doxygen/classllvm_1_1slpvectorizer_1_1BoUpSLP.html 。
(4)collectSeedInstructions代码行数:6468 ~ 6501
【编译器优化:何为SLP矢量化】遍历BB块,寻找两样东西,符合条件的store和GEP 。Stores和GEPs是两个map,访问同一个基地址的操作放进同一个key的value中 。
store 条件1:
bool isSimple() const { return !isAtomic() && !isVolatile(); }store 条件2:
isValidElementType(SI->getValueOperand()->getType())GEP条件1:
!(GEP->getNumIndices() > 1 || isa<Constant>(Idx))GEP条件2:
isValidElementType(Idx->getType())GEP条件3:
!(GEP->getType()->isVectorTy())符合以上条件的store或GEP可以做为seed 。
(5) vectorizeStoreChains// Vectorize trees that end at stores.代码行数:10423 ~ 10511
(这部分和llvm-12差异较大,引入了一个函数模板tryToVectorizeSequence)
遍历Stores,如果一个base pointer相关的指令不少于两条,就尝试矢量化,调用函数 vectorizeStores
代码行数:8442 ~ 8573
定义了两个比较器StoreSorter 和 AreCompatibleStores, 对Stores中的store进行排序(///Sort by type, base pointers and values operand) 。以及limit,获取最小的VF 。
以上三个辅助函数给函数 tryToVectorizeSequence 用 。
(6)vectorizeChainsInBlock
// Vectorize trees that end at reductions.// Ran into an instruction without users, like terminator, or function callwith ignored return value, store代码行数:10089 ~ 10330
对PHI节点下手,将PHI节点作为key 。
(7)vectorizeGEPIndices
// Vectorize the index computations of getelementptr instructions. This// is primarily intended to catch gather-like idioms ending at// non-consecutive loads.代码行数:10331 ~ 10422
(8)vectorizeTree以上(5),(6),(7)三大类矢量化场景,最终都要用到vectorizeTree函数 。
4.总结最后以一个例子来总结,SLP和循环矢量化的差异[6]:

编译器优化:何为SLP矢量化

文章插图
 
SLP与LV差异[6]
本文主要带大家了解了传统SLP矢量化优化的基本思想,以及Loop-Aware SLP的使用场景,并且大致了解了llvm中SLP pass 的源码架构,对于具体实现向量化代码的构造函数以及cost model机制需要各位对SLP感兴趣的读者深入学习,同时llvm作为一个优秀的现代C++项目,其中的数据结构,编程技巧都能启发大家,受益颇多 。
另外,SLP本身作为llvm中自动矢量化中的一部分,可以弥补一部分循环矢量化无法覆盖到的优化场景 。社区中对于SLP的讨论也比较火热,感兴趣的读者也可以到llvm社区参与讨论 https://llvm.org/ 。


推荐阅读