我正在编写一个从 .NET CIL 代码到某种高级语言的编译器。过程类似于反编译。我做了一些控制流分析——检测循环、ifs 等等。在数据流分析方面,我通过模拟一些涉及评估堆栈的指令完成了简单的表达式传播 - 我将评估堆栈视为隐藏变量,将更复杂的表达式推送到其上,并且每当有对某个变量的任何赋值指令(例如starg
或stloc
) - 我弹出并将传播的表达式从堆栈分配给这个变量,并将这个表达式转换为高级语言代码中的语句。当然,现在它是如此简单,以至于它会产生故障。考虑一个用 C# 编写的函数:
int GCD(int n1, int n2)
{
while(n2 != 0)
{
int c = n1 % n2;
n1 = n2;
n2 = c;
}
return n1;
}
此函数编译为 IL:
.method private hidebysig
instance int32 GCD (
int32 n1,
int32 n2
) cil managed
{
.maxstack 8
IL_0000: br.s IL_000a
IL_0002: ldarg.1 // load n1 on eval stack
IL_0003: ldarg.2 // load n2 on eval stack
IL_0004: rem // pop n1 and n2 from stack, compute n1 % n2 and push it on stack
IL_0005: ldarg.2 // load n2 on stack
IL_0006: starg.s n1 // pop n2 from stack and store it in n1
IL_0008: starg.s n2 // pop n1 % n2 from stack and store it in n2
IL_000a: ldarg.2
IL_000b: brtrue.s IL_0002
IL_000d: ldarg.1
IL_000e: ret
}
通过这个简单的传播,我们n1 % n2
压栈,然后加载n2
栈,然后我们有starg
指令,所以我们从栈中弹出表达式并将赋值转换为语句。然后我们再次弹出,并执行相同的操作。结果代码如下所示:
GCD(n1, n2) {
while (n2) {
n1 = n2;
n2 = (n1 % n2);
}
return n1;
}
这表明我必须做一些与消除死代码相反的事情,可能被称为“必要的代码引入”。我搜索了一些有关在反编译中引入新变量的方法的资料,但没有找到。你有什么想法?