如何使用LLVM将基于堆栈的虚拟机字节码转换为SSA形式

时间:2019-05-02 06:54:42

标签: compilation llvm disassembly llvm-ir ssa

关于如何将SSA表示形式转换为堆栈计算机有很多问题,但我对逆运算感兴趣。

问题

考虑具有条件/无条件跳转的基于堆栈的VM,其中每个操作码都有固定数量的消耗和产生的堆栈元素。

LLVM框架中是否有工具/方法可以从字节码输出中重建SSA形式。这本质上是一种拆卸形式。

1 个答案:

答案 0 :(得分:1)

LLVM本身没有工具,只是SMoP。我做完了它的某些部分很困难,但无所不包。我将回答而不是发表评论,以使您对最困难的部分有所了解。

堆栈通常是无类型的;堆栈顶部的值具有类型,但“堆栈顶部”则没有。 LLVM Value始终具有类型,并且代码包含循环时这两个系统会发生冲突。考虑以下代码:

int a = b();
while(a<10)
    a++;

a具有类型,并且其所有值均为int(也许在LLVM IR中为i32)。当第一行将返回值从b()推入堆栈时,堆栈顶部将获取int类型。您可能可以设想这些行在堆栈计算机上的外观。应该像这样将其翻译为IR:

entry:
  %a1 = call @b();
  br label %b1
b1:
  %stack.0.b1 = phi i32 [%entry, %a1], [%b1, %a2]
  %a2 = add i32 1, %stack.0.b1
  %done = icmp ult i32 %stack.0.b1, 10
  br i1 %done, label %b1, label %b2
b2:

(对不起语法错误,我没有手工写很多IR。)

您可能会看到,除了phi以外的每条指令都可以从堆栈语言中的一条指令中生成。用您的堆栈语言编写的一条指令可能导致不止一个IR指令,或者没有任何IR指令,例如dup或push-constant-zero,仅修改堆栈。

phi不同,它表示那时的堆栈。

b1entry的末尾的堆栈计算出块b1的入口堆栈。您可以在每个基本块的开头为堆栈上的每个值生成一个phi节点;挑战在于,每个phi节点的类型取决于前面各块末尾堆栈上的类型。在这种情况下,entry末尾的堆栈只有一个条目a1,而在b1的后半部分的堆栈只有一个a2。因此,stack.0.b1的类型取决于a2的类型,而stack.0.b1的类型又取决于{{1}}。您需要对此进行认真考虑,特别是如果您的语言包括隐式类型提升或强制转换(i32到i64,字符串到对象等)的话。

(我本可以从类似ruby的类型系统和代码开始,而不是从类似c的代码开始;我认为最终的问题将是相同的,只是您的解决方案有所不同。)

相关问题