Compilation 从三地址代码到JVM字节码的代码生成

Compilation 从三地址代码到JVM字节码的代码生成,compilation,bytecode,jvm-languages,Compilation,Bytecode,Jvm Languages,我正在为Renjin(JVM的R)开发字节码编译器,并尝试将我们的中间三地址码(TAC)表示转换为字节码。我参考过的所有编译器教科书都讨论了代码生成过程中的寄存器分配,但我没有找到任何用于在基于堆栈的虚拟机(如JVM)上生成代码的资源 简单的TAC指令很难翻译成字节码,但当涉及临时指令时,我会有点不知所措。有人有没有指向描述这一点的资源的指针 下面是一个完整的示例: 原始R代码如下所示: x + sqrt(x * y) aload_x dup aload_y invokestatic r/p

我正在为Renjin(JVM的R)开发字节码编译器,并尝试将我们的中间三地址码(TAC)表示转换为字节码。我参考过的所有编译器教科书都讨论了代码生成过程中的寄存器分配,但我没有找到任何用于在基于堆栈的虚拟机(如JVM)上生成代码的资源

简单的TAC指令很难翻译成字节码,但当涉及临时指令时,我会有点不知所措。有人有没有指向描述这一点的资源的指针

下面是一个完整的示例:

原始R代码如下所示:

x + sqrt(x * y)
aload_x 
dup
aload_y
invokestatic r/primitives/Ops.multiply(Lr/lang/Vector;Lr/lang/Vector;)
invokestatic r/primitives/Ops.sqrt(Lr/lang/Vector;)
invokestatic r/primitives/Ops.plus(Lr/lang/Vector;Lr/lang/Vector;)
areturn
交咨会:

 0:  _t2 := primitive<*>(x, y)
 1:  _t3 := primitive<sqrt>(_t2)
 2:  return primitive<+>(x, _t3)

基本上,在程序的顶部,我已经需要考虑,在到达TAC指令2时,我需要在堆栈的开始处使用局部变量x。我可以手动思考这个问题,但我很难通过一个算法来正确思考这个问题。有指针吗?

将3地址表示转换为堆栈比将堆栈表示转换为3地址更容易

您的顺序应如下所示:

  • 形成基本块
  • 执行SSA转换
  • 在基本块中构建表达式树
  • 执行寄存器调度(同时删除phi),为上一步未消除的寄存器分配局部变量
  • 发出JVM代码-寄存器进入变量,表达式树被简单地扩展为堆栈操作

  • 哇!谢谢,这正是我想要的。问题:我需要在每个基本块内或在整个过程中进行SSA转换吗?您是否有任何指向教程、教科书或其他资源的指针?SSA transform通常适用于整个过程:您只需为每个正在分配变量(具有多个分配位置)的基本块找到支配边界,在那里插入phi节点,然后去除冗余的phi(注意:有些可能具有循环依赖关系)@akbertram,
    LLVM
    在这里可以成为一个有用的灵感来源,您可以安全地在它之后对中间表示进行建模。一些重要的设计决策:不允许将一个寄存器分配给另一个寄存器,也不允许将常量分配给寄存器,始终替换它。表达式树看起来是一个很像最初的AST——当唯一的目标是JVM时,是否值得通过类似TAC的IR进行一次往返,或者另一种IR是否有意义?@akbertram,如果您可以在进行TAC之前填充JVM编译,那么是的,您不需要它,直接生成堆栈代码更容易。否则,如果您只能在existin之上进行堆栈,那么在编译基础设施中,您需要重新构造表达式树。有趣的是,JIT也会这样做。