我的一个副业项目是一个反编译器,它将原生代码转换为LLVM IR,简化并输出伪C代码。该程序过程的一个关键阶段是无模式控制流结构化, 它可以找到程序中的区域,并将它们转换为结构化的控制流语句(即没有goto语句)。
我不得不自己编写代码来查找区域,因为LLVM的区域不完全符合论文的期望。然而,查找区域需要你拥有后支配树,而LLVM的后支配树构建算法不能为没有结束块的函数(如以无限循环“结束”的函数)创建一个后支配树。
这似乎是因为树构建算法需要一个起点。通常,起点是函数的返回块,因为它支配着每个其他块;但在进入无限循环的函数中没有任何return
或unreachable
终止器,所以它们没有起点。(值得注意的是,LLVM的区域代码也依赖于后支配树,对于无法构建的函数也是无用的。)我认为,即使此算法失败,函数不返回并不意味着您不能为其创建后支配树。1实际上,如果该无限循环具有单个反向边缘(这是我可以确保的),则具有该反向边缘的节点必然支配图中的每个其他节点,因此应该可以创建后支配树。
如果我能找到那个节点,我可能会将其提供给LLVM的后置基础设施,并从中获得合理的后支配树。不幸的是,我不太有想象力,我能想到的唯一直接的方法是识别那个关键节点的方式是“它支配着一切”,这肯定不会帮助我引导后支配树。
找回边并不是特别困难。正如Doug Currie所说,您可以使用简单的DFS来完成,事实上,我的项目正是这样做的。然而,在具有无限循环和嵌套终止循环的函数中,如果没有支配信息,我不知道如何区分内部反向边和外部反向边。(如果有帮助的话,在此过程的这个阶段,保证每个循环都有一个入口节点和至多一个出口节点。)
那么,如果一个函数没有任何返回基本块,我该如何构建后支配树呢?
注:我的编译器和图论背景完全是自学的。这可能不准确。
if (5 == 7) break;
? - 500 - Internal Server Error