前端主要做的是词法、语法和语义的分析
中端会做相关的优化工作
分析:变量类型和范围
优化:基于节点海的优化
后端的部分就生成了目标代码

TurboFan如何用图做JS编译优化?(上)

优化:基于节点海的优化

方案一:常量提前,简化计算
在常量提前中,比较经典的优化就是常数折叠(constant folding),顾名思义,就是把常数加在一起,比如在下面的第一个例子中,我们把 3+5 的操作直接折叠为 8。在简化计算中,比较有代表性的例子就是强度折减(strength reduction)。比如在下面的第二个例子中,x+0 和 x 没有区别,就可以简化成 x。再比如下面第三个例子中,可以把 x*4 改为 x<<2 这样的移位运算。

方案二:去重计算
在去重计算中,最常见的是值编号(value numbering)。意思就是在系统里把等于相同的值赋予同一个编号。比如当 sin、加法以及 LoadField 的运算的结果值是一样的时候,那么就只记录一次即可。

方案三:控制流优化
针对控制流,也有很多不同的优化方式。下面是几个例子,包含了分支折叠、合并折减、控制折减。通过这几个例子就可以看到,无论流程本身,还是流程中的分支和合并在优化后,都简化了很多。

Lowering:语言的层级
TurboFan 的 Tubolizer 会用不同颜色来表示不同类型的节点。包括代表值的浅蓝色节点、代表流程的黄色节点,以及代表运算的深蓝色语言节点,除了深蓝色以外,还有两种语言的节点,一种是上层的红色的 JavaScript 语言的节点,还有就是更接近底层的机器语言的绿色节点。基本上层级越高,对人来说可读性就越高;层级越低,则越接近机器语言。

我们把这种由高到低的过程,叫做 lowering。所以优化的过程,也会是一个不断 lowering 折减的过程。

后端优化:指令和寄存

指令排序
SoN 节点之海最后排序的结果会被放入一个 CFG(Control Flow Graph)程序控制图。这里有三个概念,第一是控制支配,第二是减少寄存压力,第三是循环优化。
控制支配
首先我们来看第一点,在这个过程中,首先是把固定的节点比如 phi、参数等都放到 CFG 中。


之后会用到支配树来计算支配关系,接着会把剩余的节点按照支配关系从 SoN 节点之海输出,输入到 CFG 程序控制图中。

减少寄存压力
支配树
排序后,节点之海完全变成了一个程序控制图。

循环优化
在这个过程里尽量对循环中的代码做提升,这个过程叫做 Loop Invariant Code Motion,也就是我们常说的 hoisting。
指令选择
指令选择可以用最大吞噬。

但是实际上指令选择的顺序,是从程序控制图中相反的顺序,也就是从基本块儿中自下而上地移动标的位置。

寄存器分配
在分析和优化结束后,在编译过程的后端,要最后生成机器码,这个过程中,一件主要的工作就是寄存器的分配。TurboFan 使用线性扫描分配器和实时范围分割来分配寄存器和插入溢出代码。SSA 形式可以在寄存器分配之前或之后通过显式移动进行解构。

寄存器分配的结果是用真实寄存器代替虚拟寄存器的使用,并在指令之间插入溢出代码。

极客时间《Jvascript进阶实战课》学习笔记 Day19

豪猪
4 声望4 粉丝

undefined