记录下这一年半编译器开发的经验。

  1. 编译通过,但是运行结果错误。

如何检查?

  • 先减小问题规模,整出一个最小可复现用例
  • 检查pipeline,parser结果,ir pass结果,isel结果,reg alloc,stack frame等。
  • verify的重要性,每个不同格式的中间表示转换处需要有verify,保证其流入和流出结果的合法性。
  • 重复上述步骤,直到问题解决。

既然是pipeline模型,那么衔接需要处理好。需重点注意。

  1. 编译器挂掉了,没其他信息。一般是各种空指针解引用导致的,linux里面很好处理。
  2. ISA里面还是需要cmp a,b; cset; jmpcc这样的指令。block数量可以少一些,虽然有隐式状态吧。
  3. phi-node-elimination,简单解法很有效。
  4. 将合法化和优化分隔开。写在一起很难调试。比如说上面的phi消除,用phi-node-elimination + register-coalescer处理合法性和优化问题。
  5. 阶段性测试,必不可少,花费时间写测试很有必要,也是约束项目复杂度的关键措施。想想一命通关的难度。
  6. ir层级表示,4到5层是极限了,每层其实都是一个小解释器。llvm有ast,llvm-ir,mir+MC。算上预处理器就4层了,MC是tablegen送的. 每层都要测试,有自己的结构,层之间的转换。
  7. 小即是多,启动目标要尽可能小,而不是摊大饼。多一个环节,复杂度就会上升一级。
  8. 关于ai,因为经济问题,只能用免费版本的。效果其实不错。如果还是传统的需求分析,设计,编码,测试流程,ai挺有用的。编码还需要盯着具体实现,最好是划定一个小范围,每步都有git commit。git真是ai code的大爹。

还有编译器后端开发一些经验

  1. phi节点处理

    • isel:需要谨慎选择block遍历顺序,RPO是个好选择
    • legalize-type:更推荐增量式更新
    • phi-elim:也是增量式,冗余copy之后让copy colaescing处理
  2. legalize type需要在legalize opcode之前。因为机器支持的opcode+类型是确定的。
    比如在32位机器上运行64位乘法,需要拆成mul-low,mul-high,然后再处理mul32的合法性。

  3. stack frame layout中需要仔细检查下偏移量。多测试下

  4. 寄存器分配,看成一个二维图着色问题。一个是变量数目,一个是指令数量(index)

reg0: [10, 14]
reg1: [2, 12]

考虑不同block块的频率不一样,视为频率热力图,greedy算法里面踢出某个块就更容易理解。

图着色坏在不好调试(相对)。