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

来源:互联网 发布:虚拟服务器和端口转发 编辑:程序博客网 时间:2024/05/17 16:02

LLVM Cookbook(Packt,2015)

  1. *重新理解 value --> use(每个IR就是一个value,SSA)
  2. builder.GetInsertBlock(); //Codegen: 先有cfg框架,TDD?
  3. if-then-else及for循环:需用PHI合并?(重点)
  4. 优化步(IR层)
    1. $ clang -S -O0 -emit-llvm test.cpp
    2. $ opt -O1 -S test.ll (注意:中间分析结果可以共享,如以文件数据库的形式,或VS里的.pdb)
  5. 3层概念:Function::iterator --> BasicBlock::iterator --> i->getOpcodeName()
  6. 别名分析(AA)
    1. AliasAnalysis:导出AliasResult、ModRefResult(*)
    2. alias(a,b):--> MustAlias(肯定是)、PartialAlias、MayAlias、NoAlias
    3. getAdjustedAnalysisPointer:当用多继承实现了分析接口?
  7. $ opt
    1. --aa-eval ...
    2. --print-dom-info ... (什么是dominator tree?)
    3. --count-aa -basicaa -licm ...
  8. ImmutablePass --> 转换步
    1. LLVMPassManagerRef PM: unwrap(PM)->add(pass);
    2. 例,实现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
  9. 函数调用内联
    1. class MyInliner : public Inliner {
      ...
      bool runOnSCC(CallGraphSCC& );
    2. InlineCost: getAlways()/getNever() //这算枚举常量吗
    3. Function* callee = callsite.getCalledFunction();
  10. $ opt -memcpyopt
    1. 转换前:call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arr_i8, i8* bitcast([3 x i32] * @cst to i8*), i64 12, i32 4, i1 false);
    2. 转换后:call void @llvm.memset.p0i8.i64(..., i8 -1, i64 12, ...);
  11. Combining IR(指令序列的替换,Machine dep.)
    1. 高效的模式匹配是关键!
  12. LICM(循环不变式外提)
    1. 其他:loop-rotate/-unswitch/-unroll
  13. 重结合表达式(注意,这些都是标量变换)
  14. Vectorizing IR
    1.  ? bool matchFlatReduction(PHINode*, BinaryOp*, *DL);
  15. 目标无关的代码生成
    1. SelectionDAG合法化
      1. SDNode: target lowering
      2. --> MachineSDNode(.td -->tablegen .inc)拓扑排序/线性化(‘指令调度’?)
    2. 寄存器分配
    3. Code emission
      1. addPassesToEmitFile
      2. AsmPrinter
      3. 'llc'
      4. MCStreamer
    4. 寄存器分配
  16. * 可视化CFG:GraphViz
  17. TableGen(最难理解的东西就是这个!)
    1. class SAMPLEReg< bits<16> Enc, string n> : Register<n> { //每个实例代表一个寄存器;
      foreach i=0-3 in { //注意这里的in,参考了ML的语法?
      def R#i : R<i, "r"#i>;
      ...
  18. *定义指令集:#see X86InstrInfo.td
  19. MachineFunction, MachineBasicBlock, MachineInstr(addOperand/MemOperand,隐式参数??)
  20. 实现MachineInstrBuilder
    1. BuildMI*
  21. 实现MBB
    1. Predecessors/Successors
    2. SplitCriticalEdge*
  22. 实现MF
    1. -ConstantPool, -FrameInfo, -FunctionInfo, -RegisterInfo, ...
  23. 编写一个指令选择器
  24. Legalizing SelectionDAG(如i64-->i32)
  25. Optimizing SelectionDAG
    1. DAGCombine.cpp ?
  26. 从DAG选择指令:SelectionDAGISel(注意这里诡异的ISel后缀,代表指令选择的意思)
  27. 指令调度(线性化):DFS,TopSort
  28. 优化机器代码:到这里,仍然是SSA形式?
    1. dce, cse(编译原理里面所谓的‘窥孔优化’?):类似于IR,只是多了约束检查(比如RISC流水线/延迟槽?)
    2. 分析live intervals(活跃区间)
    3. 分配寄存器/SSA解构:phi -> copy
    4. *插入prologue-epilogue代码
    5. TCO
    6. Sibling CO(C++11里的完美转发?)
  29. *编写LLVM后端(除非是做CPU,否则实际中用不到):a Toy backend with r0~r3 + sp + lr(不能再简单了!)
    1. 定义calling convention(cc)
    2. 定义指令集
    3. frame lowering(?)
    4. sub target(如ARM Neon,Intel SSE/AVX)
    5. lowering到多个指令(汇编语言中的‘伪指’)
    6. 注册target
  30. LLVM应用
    1. 异常:__cxa_throw/begin_catch/end_catch
    2. santizer:shadow内存(hook malloc/free),--> Valgrind
    3. GC:@llvm.gcroot / .statepoint
    4. toJS
    5. bugpoint(类似于git bisect,用于定位LLVM Pass的实现错误)
    6. LLDB(操作的是LLVM最终生成的目标平台机器指令?)
    7. utility步
    8. ClangStaticAnalyzer:符号执行(all path?);ExplodedGraph 
0 0