代码生成(编译器)

计算中,代码生成编译器的过程链的一部分,并将源代码中间表示形式转换为一个表单(例如,机器代码),该表单可以由目标系统容易执行。

复杂的编译器通常对各种中间形式进行多次通过。之所以使用此多阶段过程,是因为许多用于代码优化的算法一次更易于应用,或者是因为一个优化的输入取决于另一个优化执行的完整处理。该组织还促进了一个可以针对多个体系结构的单个编译器的创建,因为只有代码生成阶段(后端)的最后一个需要从目标变为目标。 (有关编译器设计的更多信息,请参见编译器。)

代码生成器的输入通常由解析树抽象语法树组成。将树转换为线性指令序列,通常使用中间语言,例如三个地址代码。汇编的进一步阶段可能被称为“代码生成”,具体取决于它们是否涉及该程序的表示形式的重大变化。 (例如,尽管代码生成器可能会包含窥视孔优化通行证,但窥视孔优化通行证不太可能被称为“代码生成”。)

主要任务

除了从中间表示形式转换为机器指令的线性序列外,典型的代码生成器试图以某种方式优化生成的代码。

通常是复杂编译器“代码生成”阶段的任务包括:

指令选择通常是通过在抽象语法树上进行递归的后订单遍历来进行的,从而匹配针对模板的特定树配置;例如,树W := ADD(X,MUL(Y,Z))可以通过递归生成的序列来转化为指令的线性序列t1 := Xt2 := MUL(Y,Z),然后发出指令ADD W, t1, t2.

在使用中间语言的编译器中,可能有两个指令选择阶段 - 一个将解析树转换为中间代码,而第二阶段则是在很久以后将中间代码转换为目标计算机指令集中的指令。第二阶段不需要树遍历。它可以线性地完成,通常涉及与相应的Opcodes的中间语言操作的简单替换。但是,如果编译器实际上是语言翻译器(例如,将Java转换为C ++ ),则第二个代码生成阶段可能涉及从线性中间代码构建树。

运行时代码生成

当在运行时发生代码生成时,就像在及时汇编(JIT)一样,重要的是,整个过程在空间和时间方面都要有效。例如,当解释正则表达式并用于在运行时生成代码时,通常会生成非确定性的有限状态机,而不是确定性的机器,因为通常可以更快地创建前者,并且比后者更少的内存空间。尽管JIT代码生成通常会生成效率较低的代码,但可以利用仅在运行时可用的分析信息。

相关概念

以一种语言进行输入并以非平淡无奇的语言产生输出的基本任务可以从正式语言理论的核心变革性运作来理解。因此,一些最初用于编译器的技术也已经以其他方式使用。例如, YACC (另一个编译器兼容器)以Backus-Naur形式输入,并将其转换为C中的解析器。尽管最初是为编译器自动生成解析器的,但YACC通常也用于自动化编写代码,该代码需要修改每个时规格。

许多集成的开发环境(IDE)都支持某种形式的自动源代码生成,通常使用与编译器代码生成器共有的算法,尽管通常不那么复杂。 (另请参阅:程序转换数据转换。)

反射

通常,语法和语义分析仪试图从源代码中检索程序的结构,而代码生成器则使用此结构信息(例如,数据类型)生成代码。换句话说,前者添加了信息,而后者则丢失了一些信息。信息丢失的结果之一是反射变得困难甚至不可能。为了解决这个问题,除了执行所需的代码外,代码生成器除了嵌入句法和语义信息。

也可以看看