【问题标题】:Compilers: Register Allocation Against Complex Branching/Jumps编译器:针对复杂分支/跳转的寄存器分配
【发布时间】:2015-05-14 04:28:43
【问题描述】:

我对优化器及其工作方式感兴趣,尤其是在寄存器分配方面。我在编写高级解释器方面有一定的背景,这些解释器不会费心生成高效的机器代码,因此与解析、构造 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 示例都是相当低级的机器代码抽象,因此它们似乎经常让我们对如何进行分支(例如:跳转表)变得非常混乱很难推断出这样的块/部分/路径。

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

【问题讨论】:

    标签: compiler-construction compiler-theory


    【解决方案1】:

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

    静态单一分配表 (SSA) 已获得一定程度的普及,以帮助寄存器分配。这个想法是将程序逻辑转换为一个变量,该变量只分配一次,并且每个变量都需要在使用之前进行分配。一般假设可以使用的寄存器数量是无限的。

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

    1. Efficiently Computing Static Single Assignment Form and the Control Dependence Graph Ron Cytron 等人。他是SSA领域的先驱之一。

    2. Single-Pass Generation of Static Single Assignment Form for Structured Languages 由 Marc M. Brandis 等人撰写。

    3. A Simple, Fast Dominance Algorithm Keith D. Cooper 等人。

    如果您不想将 SSA 作为中间步骤处理,您可能需要查看 Massimiliano Poletto 的 Linear Scan Register Allocation。这种贪心算法用于许多非基于 LLVM 的编译器,包括 v8 和 JVM。

    【讨论】:

    • 我通常不喜欢 SO 关于什么是好的答案的政策,但这个答案确实是“仅限链接”,如果链接失效,则完全没用。鉴于这些链接似乎是针对与论文作者无关的特定人,在特定机构,我猜这个链接会在这个人继续前进时消失。我认为您应该在此答案中包含论文的名称和参考文献。 (我认为这些链接指向非常好的论文)。
    • 这些非常有趣,尽管 SSA 目前似乎有点让我头疼(或者是吗?编译器设计的新手应该从 SSA 开始还是结束?) Andrew Appel 和他的 IR 在 C 中的编译器实现不是高级代码,而是非常低级的机器类代码(尽管对于硬件独立性来说有些抽象)。在那些论文中,使用 whilerepeatif 等关键字推断控制流似乎比这些允许非常任意分支的低级构造更简单......
    • @Ike:Cytron 纸真的很漂亮。他会回答你的问题,让你成为一个信徒。强烈推荐,强烈推荐。 (一个关键思想:具有副作用的程序很难推理。SSA 形式本质上将程序转换为函数形式:所有副作用都消失了[实际上,它们都是显式的]。这使得进行复杂推理变得更加容易.) 关于寄存器分配:如果你有一个 SSA 图,你可以通过直接着色来构建一个 graph-coloring register allocator
    • @Ike,低级 IR 实际上更容易构建分支路径。本质上,每条路径都成为流程图中的一条边。
    • 如果是这样的话,我会鼓励你使用 LLVM。 LLVM 已经实现了 Phi 节点。您需要做的只是想出一个 SSA IR 代码。
    猜你喜欢
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多