编译器 - 前端后端

29 architecture compiler-construction

我理解前端和后端编译器的结构.但是,我不确定为什么编译器通常分为前端和后端.我相信你能给我一些原因有很多吗?因为,大多数书籍/网站告诉你它们是什么,但没有告诉你为什么!

谢谢.

use*_*421 65

前端处理语言本身:扫描,解析,解析树.后端处理目标系统:目标代码格式,机器代码本身,......这两件事情彼此之间并没有太多关系,对于便携式编译器,非常希望使用相同的前端有多个后端,每个目标一个.

您可以更进一步,gcc并且具有与语言无关的前端/后端接口,因此您可以使用具有相同后端的不同语言前端.在过去,这被称为MxN问题:您不希望编写具有M种语言和N个目标系统的MxN编译器.这个想法只需编写M + N编译器.

  • @Zephyr,假设您有三种语言(C、C++、Rust)和两种目标架构(x86 和 ARM)。如果每个前端目标每个后端,则 C => x86、C => ARM、C++ => x86、C++ => ARM、Rust => x86 和 Rust => ARM,总共 6 个( MXN)。或者,如果前端针对中间语言,后端针对特定架构,那么您就有 C => IL、C++ => IL、Rust => IL、IL => x86 和 IL => ARM,即总共只有 5 个(M+N)。随着语言和架构数量的增加,节省的成本也会增加。 (2认同)

And*_*per 9

如果你所说的fron-end是解析器来标记源代码,而后端是基于令牌化代码生成可执行代码的位,那么一个很好的理由是:可移植性.

将解析器与可执行代码生成分开使得将编译器从一个处理器体系结构移植到另一个处理器体系结构变得更加容易.


old*_*mer 5

因为您想使用某种内部伪代码或表/数据结构.例如,如果您有一行代码:

a = b + c;
Run Code Online (Sandbox Code Playgroud)

您可能希望将其转换为中间语言或IR(中间表示):

load b
load c
add b + c
store a
Run Code Online (Sandbox Code Playgroud)

作为一个例子 - 有很多解决方案.由于多种原因,中间语言比直接组装特定目标更好:

  • 通过抽象硬件并提供寄存器的"逻辑数",我们独立于寄存器和硬件布局的最终​​"物理数量".例如,本机ADD指令可以是基于堆栈的,取1操作数,取2个操作数,或甚至3个操作数.在更高级别,我们不需要知道或关心较低级别的硬件实现.
  • 如果你有一个优化器,可以优化内部语言
  • 如果您希望定位不同的处理器,则通用性足以用于多个目标.

我不太了解它,但我认为你也有常用的解析器bison/flex,将你归结为某种中间代码/指令集然后你为此编写一个后端.

您也可以受益,例如,您可以使用C和C++以及其他语言前端,而不会影响后端.

您还可以将编译器分解为逻辑模块块,您可以独立于后端开发和测试前端.例如,llvm允许导出和导入中间语言,如果你真的真的想用中间语言编写代码并且在后端有多个目标的好处.