在字节码编译器中计算跳转地址的智能解决方案?

时间:2012-10-26 20:29:54

标签: compiler-construction bytecode

假设我正在实现一个字节码编译器,类似于Lua的/ Python ......等等。

我正在遍历AST,生成字节码指令,并且在break循环内的if-else内部遇到while

while (cond1)
    if (cond2)
        ...
    else
        break

(我尝试写出等效的字节码,但它看起来并没有太大帮助。)

关键是,该示例中至少有4个“跳转”指令,而我 想要找到一个优雅的解决方案来填充跳转地址,因为我编译AST ... 在我完全完成之前,我不知道while循环或break的跳转地址 “汇编”了内部陈述。

  1. 伪代码解决方案是理想的
  2. 解决方案不应该取决于我是否实现了基于寄存器或堆栈的字节码编译器(我正在使用它们)
  3. 我还没读这本龙书。

    如果我以递归方式编译AST,当我在一些任意数量的循环和break块内到达if-else语句时,编译器应该如何知道要跳转到哪个空标签?我猜测递归AST行走函数外部的某种类型的标签名称堆栈。

1 个答案:

答案 0 :(得分:6)

您需要的原则称为“反向修补”:为正向跳转填充虚拟值,为语句正文生成代码,然后返回并将虚拟值替换为末尾的虚拟值。

e.g。

# start of codegen for 'while'
L1:
  [evaluate cond1]
  jne L2   # forward reference: use a dummy value for now

# start of codegen for 'if ... else'
L3:
  [evaluate cond2]
  jne L4   # another forward reference: use a dummy value for now
  [code in 'if' branch]
  j L5     # another forward reference: use a dummy value for now
L4:
  [code in 'else' branch before 'break']
  j L2
  [code in 'else' branch after 'break']
L5:   # end of 'if ... else'
  # now go back and fill in references to L4, L5 inside the 'if ... else' block
  # end of codegen for 'if ... else'

  # codegen for 'while' continues...
  j L1   # loop
L2:   # end of 'while' loop
  # now go back and fill in references to L2 inside the 'while' block
  # end of codegen for 'while'

关于你的编辑:

  

如果我以递归方式编译AST,当我在一些任意数量的循环和break块内到达if-else语句时,编译器应该如何知道要跳转到哪个空标签?我猜测递归AST行走函数外部的某种类型的标签名称堆栈。

实现break语句的跳转目标是最内层封闭循环的结束;是的,您可以使用显式外部堆栈跟踪它。

但是,如果你有一个递归的AST walk函数,你已经有了一个隐式堆栈 - 递归函数调用的调用框架 - 所以你可能也不需要一个明确的调用框架。

e.g。在

...
while (outer)
    ...
    if (outer_done)
        break
    ...

    while (inner)
        ...
        if (inner_done)
            break
        ...
    [break from inner 'while' ends up here]

    ...
    if (outer_done_2)
        break
    ...
[break from outer 'while' ends up here]
...

内部while循环的整个代码生成发生在外部while循环体的AST的递归遍历中。在这个递归调用中,你只需要关心内部循环,而不是外部循环。

所以,你需要做的就是:

  • 在处理while
  • 开始时保存任何当前的回调状态
  • 开始一个新的空列表,用于跟踪此break
  • 正文中显示的所有while
  • 处理正文(可能会导致递归调用)
  • 应用backpatches
  • 然后在退出前恢复之前的状态。

e.g。有点像这样:

codegen for while:
    save previous break backpatch list
    initialise break backpatch list as empty
    perform codegen for evaluating condition
    perform codegen for body statements
    apply backpatches
    restore previous break backpatch list

当前break backpatch列表必须是某个状态的一部分,该状态将传递给所有codegen函数(break的codegen需要附加到它)。但是保存的列表可以作为while codegen函数的局部变量进行跟踪。