图的结构图是由边(edge)连接的节点(vertax),任何二分关系都可以用图表示。编译过程:中间码IR,即中间码(IntermediateRepresentation,有时也称为IntermediateCode,IC)从概念上看,IR可分为HIR(HigherIR)、MIR(MiddleIR)和LIR(LowerIR),分别是高、中、低中间码形式。前端主要做词法、语法、语义分析,中端做相关的优化工作,后端部分生成目标代码。关键任务是寄存器分配和指令选择和排序。Medium:High:HIR主要用于在源语言的基础上进行一些分析和转换。一个典型的例子就是用于语义分析的AST(AbstractSyntaxParser)语法树。中:MIR独立于源语言和CPU架构做一些分析和优化,典型的例子有三地址码TAC(ThreeAddressCode)和程序依赖图PDG(ProgramDependencyGraph),主要用于分析和部分优化算法低:LIR层依赖CPU架构进行优化和代码生成。一个典型的例子就是有向无环图DAG(DirectedAyclicGraph)。它的主要目的是帮助生成目标代码。在这一段的编译过程分析中,一个重要的分析是控制流分析(CFA,ControlFlowAnalysis)和数据流分析(DFA,DataFlowAnalysis)。编译过程中的节点海(SoN,Seaof??Node)在这一段优化中,还有几个重要的概念,分别是通用优化、对象优化和函数优化。例如,循环优化属于一般优化;内联和转义属于对象优化。对于函数式编程来说,更重要的是我们之前讲迭代和递归时提到的循环和尾递归的优化。中端优化:分析优化从IR的结构上,或许可以看出“中间代表”可能是比“中间代码”更准确的描述。因为IR更多的是以“数据结构”的形式来表达,即线性表、树、图等数据结构,而不是“代码”。节点海(SoN,Seaof??Node)TurboFan使用的SoN结构:格式和表达方式整个SoN只由节点和边组成。除了值之外,所有操作也由节点表示。在TurboFan的可视化工具Turbolizer中,使用不同的颜色来区分不同类型的节点。SoN的格式:staticsingleassignmentSoN符合静态单一赋值(SSA,staticsingleassignment)格式。静态单一赋值:每个变量只有一个赋值。例如,在下面的示例中,第一个操作是x等于3乘以7,然后我们将3添加到x。此时,x变量出现了两次。因此,TurboFan在创建图时会重命名局部变量。修改后,值为3的节点有两个运算符同时指向它,一个是乘以7的结果,一个是加3的结果。SoN中没有局部变量的概念,所以变量被节点取代。数据流在数据流中表示,节点用于表示所有操作,包括常量、参数、数学运算、加载、存储、调用等。关于数据流的边缘,有两个核心概念。一是实线边缘表示数据流向,决定了输入输出关系。下面的实线表示计算输出的结果。二是虚线影响数据流中相关数据的读写状态,下面的虚线表示操作读写状态的指定。控制流表示从数据流的角度来看,节点和边代表一切。在控制流中,这个说法也是成立的。控制中的节点包括开始、分支、循环、合并和结束等,均以节点表示。在Turbolizer的可视化图中,黄色节点代表控制流的节点。下面举几个简单看数据流的例子。我们可以看到,从最简单的只包含开始和结束的直线程序,到分支和循环,都可以通过节点海来表达。为了区分不同类型的边,黑色实线、灰色实线和虚边分别表示控制流、数据流和影响关系。结合合并,可以看到一个更完整的例子。在这里你会看到一个phi指令,这个指令是干什么的?分支判断后根据实际情况确定x的值。因为我们说过在SSA(staticsingleassignment)规则中,变量只能被赋值一次,那么我们在遇到分支后合并的时候一定要用phi。分析:变量类型和范围中间代码的重要作用是分析和优化。为什么要分析变量类型和范围呢?这里需要解决三个主要问题。首先,我们说JavaScript是动态类型而不是静态类型的,因此虽然值具有固定的数据类型,但可变变量却没有。比如我们可以说vara=25,25本身是一个不可变的数字类型,但是如果我们说a+"yearsold",那么变量a的类型就变成了字符串。其次,所有的数学运算都是基于64位的。但是在实际应用中,大多数程序并不会用到那么多的位,大部分都小于32位,还有很多如NaN、Infinity、-Infinity、-0.0等特殊值。三是支持asm.js等JS子集中的注解,将上一点提到的NaN、Infinity等特殊值截断为0整数。极客时间《Jvascript进阶实战课》学习笔记Day18
