【问题标题】:Converting SSA to stack machine将 SSA 转换为堆栈机
【发布时间】:2018-07-14 14:33:35
【问题描述】:

众所周知,如何将代码从 SSA 表示形式转换为寄存器机器。 (基本上,图形着色寄存器分配是这种转换的核心。)

但是从 SSA 转换为堆栈计算机的一般方法是什么? (CIL 字节码,在我正在查看的情况下。)鉴于不需要寄存器分配,我希望它更简单?

【问题讨论】:

  • 也许这可能更适合 CS.se。

标签: compiler-construction cil ssa stack-machine


【解决方案1】:

我参与编译器构建已经超过 15 年了,所以我可能不记得所有细节。

基本上,当退出 SSA 时,您需要在所有块的末尾生成加载/存储指令到虚拟寄存器中,从而导致后续块中的 phi 节点。这将导致生成许多虚拟寄存器,这些虚拟寄存器通常高于实际机器上的可用寄存器。因此,您将寄存器分配应用于局部变量以得出真正的寄存器,将那些不适合的值溢出到堆栈中。

对于基于堆栈的机器,不要执行最后一步。您最终会得到与编译函数中的 phi 节点大致相同数量的虚拟寄存器(该算法实际上并不简单,一个好的起点是 Ron Cytron 的论文 Efficiently Computing Single Static Assignment Form and the Control Dependence Graph,Jeane Ferrante 等人)这些虚拟寄存器将成为您的局部变量。

当从虚拟寄存器(局部变量)中读取一个值以供操作使用时,首先使用一条指令将其压入堆栈。 Java VM iload index 指令就是这样一个例子:它在索引处加载局部变量并将其值压入堆栈。 (见https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iload) 将值写入局部变量时,将其从堆栈中弹出。参见 Java VM istore index 指令(参见 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.istore)。

例如,如果离开SSA后需要编码

local 5 = MUL local[2], local[4]

那么你需要生成这样的东西:

ILOAD 4 ILOAD 2 MUL ISTORE 5

对于 CIL 字节码,您有等效的 ldargstarg 操作。

当然,还有很大的优化空间,以避免多余的加载/存储。

【讨论】:

    【解决方案2】:

    SSA 基本上是一组“逻辑”门,每个门都有多个输入,通常有一个输出。

    所以本质上,您需要将每个门视为一组用于输入的堆栈推送,然后是一个零操作数运算符,将堆栈值组合到该门的结果中。例如,作为 SSA 的 a + b * c 使用乘法和累加运算符对 a、b、c 进行 3 次推送,然后是 MAC_TOS 运算符。

    如果有一个这样的门链,您可以获取较早的门的输出,该门已经在堆栈上,并且就像它已被推送一样。

    因此,SSA 计算看起来像一棵 n 叉树,其输出来自根部。

    您可以按固定顺序遍历树,推送尚未推送的操作数,并在计算完所有操作数后生成门的运算符。

    所以SSA图(树):

    a 
      \
       * 
    b /  \
          +
    c     /
      \  /
       -
      /
    d
    

    可用于生产

    push a
    push b
    times
    push c
    push d
    subtract
    times
    

    【讨论】:

    • 对,如果只需要翻译表达式就足够了。但一般来说,SSA ab 等可能是任意顺序的,并且可能是具有副作用的函数调用,因此需要保留顺序,因此您需要对每个基本块进行前向传递才能在给定的顺序。您如何将其与上述按后缀顺序反向链接的场景相协调?
    • 您的问题被表述为基本见解,所以我的回答处于同一水平:SSA 基本上是 only 表达式;它是您的代码块的功能等价物。真正的代码生成器过着更复杂的生活。如果您有多个具有共享子表达式的表达式(例如,具有多个结果的 DAG),您希望首先(递归地)评估共享子表达式,以便您可以保持堆栈兼容的计算顺序;您可能需要一个内存位置来保存共享结果。
    • 您会注意到 phi 节点增加了复杂性,因为它们的值可能来自非常不同的计算。他们也可能需要一个临时位置。好消息:您可以使用寄存器着色来分配临时对象:-}。如果您有运算符的副作用,那么您将不得不向 SSA 节点添加额外的排序弧,以便在需要排序的情况下建模,并在代码生成过程中遵守.
    • 这是一种非常清晰的解释方式,干得好。
    • @IraBaxter 谢谢,启发!如果没有(堆)内存位置溢出共享子表达式的结果,但在 FORTH 中有适当的堆栈运算符,如 dupdropswaprot,该怎么办?由于支持的堆栈操作集因实现而异,解决方案可能类似于优化编译或查询优化?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 2013-11-08
    • 2016-06-02
    相关资源
    最近更新 更多