Compiler construction 编译器:针对复杂分支/跳转的寄存器分配

Compiler construction 编译器:针对复杂分支/跳转的寄存器分配,compiler-construction,compiler-theory,Compiler Construction,Compiler Theory,我对优化器和它们的工作方式有一点兴趣,特别是在寄存器分配方面。我在编写高级解释器方面有一定的背景,这些解释器不需要费心生成高效的机器代码,因此围绕编译器构造的与解析、构造AST等相关的部分对我来说相当简单 作为一个学习项目,我一直在尝试一个玩具编译器,它的级别仅略高于机器级别,主要区别在于它使用变量而不是寄存器 我非常困惑的是低级优化器部分,特别是关于IR的寄存器分配以及分支/跳转如何影响这一部分,即使最基本的启发式算法不包括高级主题,如SSA和phi节点 基本示例: a = ... b = .

我对优化器和它们的工作方式有一点兴趣,特别是在寄存器分配方面。我在编写高级解释器方面有一定的背景,这些解释器不需要费心生成高效的机器代码,因此围绕编译器构造的与解析、构造AST等相关的部分对我来说相当简单

作为一个学习项目,我一直在尝试一个玩具编译器,它的级别仅略高于机器级别,主要区别在于它使用变量而不是寄存器

我非常困惑的是低级优化器部分,特别是关于IR的寄存器分配以及分支/跳转如何影响这一部分,即使最基本的启发式算法不包括高级主题,如SSA和phi节点

基本示例:

a = ...
b = ...
c = ...
d = ...
e = ...
f = ...
g = ...

jump_if x == 1, section1
jump_if x == 2, section2
jump_if x == 3, section3
etc

a = a + b - c * 2
jump end

section1:
; all kinds of stuff happens here with some of the above variables
jump end

section2:
; all kinds of stuff happens here with some of the above variables
jump end

section3:
; all kinds of stuff happens here with some of the above variables
jump section1 ; tricky branch!!!

end:
也许我们可以加入循环逻辑和其他各种分支,使这个例子更加复杂

我不理解的是,如果我们把这些变量单独考虑在一起,而不是单独的每个路径,那么所有这些分支路径都有可能使上面所有的变量都变为“活”。 在我看来,缺少的是某种基于堆栈的块结构,因此我们可以有嵌套块,其中寄存器分配可以考虑最内层块及其外部块引用的变量,并在每个块/分支路径上分别执行寄存器分配启发式

在更基于块的更高级别的IR中,似乎更容易推断分支路径,因为分支将被限制在块内,但是对于只稍微抽象到机器级别之上的低级别IR,它具有完全不受约束的分支,您可以到处跳转/分支,这又如何呢

我所看到的大多数IR示例都是机器代码的低级抽象,因此它们常常让我们在执行分支(例如:跳转表)时变得非常混乱,这可能会使我们很难推断出这样的块/节/路径


人们通常如何处理这个问题?有没有一种算法和干净的组织/代码设计,可以分解所有可能的分支路径,因为这样的低级代码允许如此灵活的分支/跳转?

寄存器分配是一个长期存在的问题。在这方面已经有许多研究论文。该领域最近流行的算法是SSA和线性扫描寄存器分配

静态单一赋值形式(SSA)在帮助寄存器分配方面得到了一些普及。其思想是将一个程序逻辑转换成一个只分配一次的变量,每个变量在使用前都需要分配。通常假定可以使用的寄存器数量不限

一旦程序转换成SSA形式,它就可以更容易地转换成寄存器分配。这可以通过多项式时间内的图着色来实现(常用的编译器是LLVM)。这是一个非常复杂的话题。我建议你读几篇这方面的论文

  • 由Ron Cytron等人撰写。他是SSA领域的先驱之一

  • Marc M.Brandis等人

  • Keith D.Cooper等人


  • 如果你不想把SSA作为中间步骤来处理,你可能想看看马西米利亚诺·波莱托的作品。这种贪婪算法用于许多非基于LLVM的编译器,包括v8和JVM

    我一般不喜欢SO关于什么是好答案的政策,但这个答案实际上是“仅链接”的,如果链接消失了,它就完全没有用了。鉴于这些链接似乎与某个特定机构的某个与论文作者无关的人有关,我猜当这个人离开时,这种链接就会消失。我认为你应该在回答中包括论文的名字和参考文献。(我认为这些链接指向非常好的论文)。这些都很有趣,尽管SSA目前似乎有点让我不知所措(或者是吗?编译器设计新手应该从SSA开始还是到此结束?)我一直在学习Andrew Appel用C语言实现的现代编译器,他的IR不是高级代码,而是非常低级的类似机器的代码(尽管对于硬件独立性来说有些抽象)。在那些论文中,用
    while
    repeat
    if
    等关键字推断控制流似乎比这些允许非常任意分支的低级构造要简单一些…@Ike:Cytron论文真的很漂亮。他会回答你的问题,让你成为一个信徒。非常非常推荐。(一个关键想法:具有副作用的程序很难推理。SSA形式本质上将程序转换为函数形式:所有副作用都消失了[实际上,它们都是显式的]。这使得进行复杂推理变得更容易。)关于寄存器分配:如果你有一个SSA图,可以通过直接着色来构建图着色寄存器分配器。@Ike,低级别IR实际上更容易构建分支路径。基本上,每个路径都成为流程图中的一条边。如果是这样,我建议您使用LLVM。LLVM已经实现了Phi节点。你需要做的只是拿出一个SSA IR代码。