您的位置:首页 > 编程语言

LLVM Cookbook读书笔记(本书的缺点是直接展示大量Sample代码,对SSA/phi并没有怎么解释,TableGen部分也没讲清楚)

2015-08-26 16:44 639 查看

LLVM Cookbook(Packt,2015)

*重新理解 value --> use(每个IR就是一个value,SSA)
builder.GetInsertBlock(); //Codegen: 先有cfg框架,TDD?
if-then-else及for循环:需用PHI合并?(重点)
优化步(IR层)

$ clang -S -O0 -emit-llvm test.cpp
$ opt -O1 -S test.ll (注意:中间分析结果可以共享,如以文件数据库的形式,或VS里的.pdb)

3层概念:Function::iterator --> BasicBlock::iterator --> i->getOpcodeName()
别名分析(AA)

AliasAnalysis:导出AliasResult、ModRefResult(*)
alias(a,b):--> MustAlias(肯定是)、PartialAlias、MayAlias、NoAlias
getAdjustedAnalysisPointer:当用多继承实现了分析接口?

$ opt

--aa-eval ...
--print-dom-info ... (什么是dominator tree?)
--count-aa -basicaa -licm ...

ImmutablePass --> 转换步

LLVMPassManagerRef PM: unwrap(PM)->add(pass);
例,实现DCE(死代码终止),主要思路:开始假设所有IR为dead,然后从根开始,传播liveness
for(Use& OI : inst->operands()) {
if(Instruction* inst_refed = dyn_cast<Instruction>(OI)){ ... //记录到livelist
} for(Instruction& I : inst_range(F)) { ...//第2遍,过滤;
I.dropAllReferences();
} for(Instruction*& I : deadlist)
I->eraseFromParent();
//注意这里的多遍遍历的处理代码;这让我想起了jQuery作者用正则表达式多遍替换实现的Processing.js

函数调用内联

class MyInliner : public Inliner {
...bool runOnSCC(CallGraphSCC& );

InlineCost: getAlways()/getNever() //这算枚举常量吗
Function* callee = callsite.getCalledFunction();

$ opt -memcpyopt

转换前:call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arr_i8, i8* bitcast([3 x i32] * @cst to i8*), i64 12, i32 4, i1 false);
转换后:call void @llvm.memset.p0i8.i64(..., i8 -1, i64 12, ...);

Combining IR(指令序列的替换,Machine dep.)

高效的模式匹配是关键!

LICM(循环不变式外提)

其他:loop-rotate/-unswitch/-unroll

重结合表达式(注意,这些都是标量变换)
Vectorizing IR

? bool matchFlatReduction(PHINode*, BinaryOp*, *DL);

目标无关的代码生成

SelectionDAG合法化

SDNode: target lowering
--> MachineSDNode(.td -->tablegen .inc)拓扑排序/线性化(‘指令调度’?)

寄存器分配
Code emission

addPassesToEmitFile
AsmPrinter
'llc'
MCStreamer

寄存器分配

* 可视化CFG:GraphViz
TableGen(最难理解的东西就是这个!)

class SAMPLEReg< bits<16> Enc, string n> : Register<n> { //每个实例代表一个寄存器;
foreach i=0-3 in { //注意这里的in,参考了ML的语法?
def R#i : R<i, "r"#i>;
...

*定义指令集:#see X86InstrInfo.td
MachineFunction, MachineBasicBlock, MachineInstr(addOperand/MemOperand,隐式参数??)
实现MachineInstrBuilder

BuildMI*

实现MBB

Predecessors/Successors
SplitCriticalEdge*

实现MF

-ConstantPool, -FrameInfo, -FunctionInfo, -RegisterInfo, ...

编写一个指令选择器
Legalizing SelectionDAG(如i64-->i32)
Optimizing SelectionDAG

DAGCombine.cpp ?

从DAG选择指令:SelectionDAGISel(注意这里诡异的ISel后缀,代表指令选择的意思)
指令调度(线性化):DFS,TopSort
优化机器代码:到这里,仍然是SSA形式?

dce, cse(编译原理里面所谓的‘窥孔优化’?):类似于IR,只是多了约束检查(比如RISC流水线/延迟槽?)
分析live intervals(活跃区间)
分配寄存器/SSA解构:phi -> copy
*插入prologue-epilogue代码
TCO
Sibling CO(C++11里的完美转发?)

*编写LLVM后端(除非是做CPU,否则实际中用不到):a Toy backend with r0~r3 + sp + lr(不能再简单了!)

定义calling convention(cc)
定义指令集
frame lowering(?)
sub target(如ARM Neon,Intel SSE/***X)
lowering到多个指令(汇编语言中的‘伪指’)
注册target

LLVM应用

异常:__cxa_throw/begin_catch/end_catch
santizer:shadow内存(hook malloc/free),--> Valgrind
GC:@llvm.gcroot / .statepoint
toJS
bugpoint(类似于git bisect,用于定位LLVM Pass的实现错误)
LLDB(操作的是LLVM最终生成的目标平台机器指令?)
utility步
ClangStaticAnalyzer:符号执行(all path?);ExplodedGraph
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: