关于如何将SSA表示形式转换为堆栈计算机有很多问题,但我对逆运算感兴趣。
问题
考虑具有条件/无条件跳转的基于堆栈的VM,其中每个操作码都有固定数量的消耗和产生的堆栈元素。
LLVM框架中是否有工具/方法可以从字节码输出中重建SSA形式。这本质上是一种拆卸形式。
答案 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
不同,它表示那时的堆栈。
从b1
和entry
的末尾的堆栈计算出块b1
的入口堆栈。您可以在每个基本块的开头为堆栈上的每个值生成一个phi节点;挑战在于,每个phi节点的类型取决于前面各块末尾堆栈上的类型。在这种情况下,entry
末尾的堆栈只有一个条目a1
,而在b1
的后半部分的堆栈只有一个a2
。因此,stack.0.b1
的类型取决于a2
的类型,而stack.0.b1
的类型又取决于{{1}}。您需要对此进行认真考虑,特别是如果您的语言包括隐式类型提升或强制转换(i32到i64,字符串到对象等)的话。
(我本可以从类似ruby的类型系统和代码开始,而不是从类似c的代码开始;我认为最终的问题将是相同的,只是您的解决方案有所不同。)