编译器中间阶段代码优化的目的是什么?
What is the purpose of code optimization at intermediate phase in compiler?
对中间代码进行了一些代码优化,因为
- 它们增强了编译器到目标处理器的可移植性
- 程序分析在中间代码上比在机器代码上更准确
- 数据流分析的信息不能用于优化
- 前端的信息不能用于优化
IMO:中间代码是机器独立代码。因此,中间代码可用于代码优化,因为给定的源代码可以转换为目标机器代码。因此,选项 (1),但某处解释的选项 (2) 也是正确的。
代码优化的目的是什么,它在编译器的中间阶段有什么好处?
编译器优化可移植性并不是对中间代码进行很多优化的原因。但是,我们因此免费获得了一个优势。你说的其他三点含糊不清。反正不用讨论了。
为了回答你的问题,我先把一个典型的Ahead-Of-Time (AOT) compiler的操作说一遍(这个问题只适用于这种类型的编译器)。在编译期间,编译器通常会处理源代码的五种表示形式:
- 文本表示(这是您编写的代码)。
- 解析器生成的 concrete syntax tree。
- 语法分析器生成的abstract syntax tree。
- IR 代码生成器生成的中间表示 (IR)。这将是前端执行的最后一个操作。
- 二进制代码生成器生成的二进制表示(由目标 ISA 指定)。
现在让我们看看哪种表示最适合执行优化。使用前三种表示中的任何一种都将导致编译器极其缓慢,因为几乎任何优化都需要广泛分析和修改输入表示。我说几乎是因为前端执行的优化很少(通常在 AST 上)。一个常见的例子是常量折叠。在此级别执行此类优化的原因是它们所做的所有修改都是本地的(在表达式内)。因此,它们很便宜。它们还使生成的 IR 代码更清晰,更适合 further analysis。另一方面,AST 非常适合执行语义分析,因此编译器可以尽快发现任何错误,并在发现任何错误时中止进一步处理。
大多数编译器优化接受 IR 代码作为输入并生成(希望经过优化的)IR 代码作为输出(好吧,一些编译器可能会逐渐降低 IR 直到一个优化发出二进制代码)。可以轻松修改的 intermediate language is designed specifically to apply optimizations. First, it has a sequential representation(类似于二进制代码)。其次,IR 保留了 AST 中可用的大部分信息。这包括全局、局部和临时变量定义和类型。这种表现力使编译器能够更有效地优化代码。第三,它是低级的,因此它的指令是原始的,只有一个或几个连续的 IL 指令被映射到几个目标 ISA 指令。这有助于代码生成器快速实现其目的。
对二进制代码执行的优化很少。这些包括第一遍或第二遍指令调度和第二遍寄存器分配。
所有这一切之后,链接器(如果需要)开始工作,其中可能包括一些其他优化。
请注意,大多数编译器优化也可以对二进制代码执行(尽管效果不佳)。这种类型的优化称为动态二进制优化,它们用于动态二进制转换和检测。
关于可移植性,我想多说几句。 IL 使我们能够为多种源语言使用相同的后端。然而,即使我们确定只会支持一种语言,正如我刚才解释的那样,IL 仍然非常重要。也很少有极其重要的优化依赖于目标 ISA。有许多优化可以将代码从 IR 转换为 IR。这些显然与目标无关。这些优化确实是可移植的,可以在不同目标架构的后端之间共享。
对中间代码进行了一些代码优化,因为
- 它们增强了编译器到目标处理器的可移植性
- 程序分析在中间代码上比在机器代码上更准确
- 数据流分析的信息不能用于优化
- 前端的信息不能用于优化
IMO:中间代码是机器独立代码。因此,中间代码可用于代码优化,因为给定的源代码可以转换为目标机器代码。因此,选项 (1),但某处解释的选项 (2) 也是正确的。
代码优化的目的是什么,它在编译器的中间阶段有什么好处?
编译器优化可移植性并不是对中间代码进行很多优化的原因。但是,我们因此免费获得了一个优势。你说的其他三点含糊不清。反正不用讨论了。
为了回答你的问题,我先把一个典型的Ahead-Of-Time (AOT) compiler的操作说一遍(这个问题只适用于这种类型的编译器)。在编译期间,编译器通常会处理源代码的五种表示形式:
- 文本表示(这是您编写的代码)。
- 解析器生成的 concrete syntax tree。
- 语法分析器生成的abstract syntax tree。
- IR 代码生成器生成的中间表示 (IR)。这将是前端执行的最后一个操作。
- 二进制代码生成器生成的二进制表示(由目标 ISA 指定)。
现在让我们看看哪种表示最适合执行优化。使用前三种表示中的任何一种都将导致编译器极其缓慢,因为几乎任何优化都需要广泛分析和修改输入表示。我说几乎是因为前端执行的优化很少(通常在 AST 上)。一个常见的例子是常量折叠。在此级别执行此类优化的原因是它们所做的所有修改都是本地的(在表达式内)。因此,它们很便宜。它们还使生成的 IR 代码更清晰,更适合 further analysis。另一方面,AST 非常适合执行语义分析,因此编译器可以尽快发现任何错误,并在发现任何错误时中止进一步处理。
大多数编译器优化接受 IR 代码作为输入并生成(希望经过优化的)IR 代码作为输出(好吧,一些编译器可能会逐渐降低 IR 直到一个优化发出二进制代码)。可以轻松修改的 intermediate language is designed specifically to apply optimizations. First, it has a sequential representation(类似于二进制代码)。其次,IR 保留了 AST 中可用的大部分信息。这包括全局、局部和临时变量定义和类型。这种表现力使编译器能够更有效地优化代码。第三,它是低级的,因此它的指令是原始的,只有一个或几个连续的 IL 指令被映射到几个目标 ISA 指令。这有助于代码生成器快速实现其目的。
对二进制代码执行的优化很少。这些包括第一遍或第二遍指令调度和第二遍寄存器分配。
所有这一切之后,链接器(如果需要)开始工作,其中可能包括一些其他优化。
请注意,大多数编译器优化也可以对二进制代码执行(尽管效果不佳)。这种类型的优化称为动态二进制优化,它们用于动态二进制转换和检测。
关于可移植性,我想多说几句。 IL 使我们能够为多种源语言使用相同的后端。然而,即使我们确定只会支持一种语言,正如我刚才解释的那样,IL 仍然非常重要。也很少有极其重要的优化依赖于目标 ISA。有许多优化可以将代码从 IR 转换为 IR。这些显然与目标无关。这些优化确实是可移植的,可以在不同目标架构的后端之间共享。