正式构建控制流图

时间:2010-09-01 10:11:07

标签: language-agnostic compiler-construction scala compiler-theory

我正在为大学项目编写一个编译器,我想将我的抽象语法树转换为控制流图(CFG)。

我认为CFG中的节点(V)应该是来自AST的节点。我在算法上知道如何构造边集(G=(V,E))但是我很难正式地编写过程

我创建了这个scala样式模式匹配(Pseudo):

def edges(n:Node)(nestedin_next: Node) : List[(Node,Node)] = 
    n match {
       case (c_1 :: c_2::tl) => (c1,c2) :: edges(c2::tl)(nestedin_next)++
                                   edges(c_1)(c_2)//recurse
       case c_1 :: Nil => (c_1,nestedin_next)::Nil
       case  i@ IF(_,c1,c2) => (i,c1)::(i,c2)::edges(c1)(nestedin_next)++
                                edges(c2)(nestedin_next)
       case _ => Nil
     }

哪个匹配AST结构,如:

( IF(1,
       ASSIGN(x,1), // ia1
       ASSIGN(x,2) // ia2
     ) ::  // i1
  ASSIGN(y,2) ::  // a1
  ASSIGN(z,ADD(x,y)) :: //a2 
  IF(z, 
       RET(z), //i2r1
         assign(z,0):: // i2a1
         ret(z) // i2r2
  ) :://i2
   Nil
)

并提供如下边缘集:

{ i1 -> ia1,
   i1 -> ia2,
   ia1 -> a1,
   ia2 -> a1,
   a1 -> a2,
   a2 -> i2,
   i2 -> i2r1
   i2-> i2a1
   i2a1 -> i2r2
   i2r2 -> _|_
   i2r1 -> _|_ 
}

CFG(dot) DotSrc

任何人都有关于如何比scala“伪代码”更正式地做到这一点的任何提示?

我正在思考一些归纳法:

e[[ IF(_,b1,b2) ]] = (if -> b1) + (if -> b2) \cup e[[ b1 ]] \cup e[[ b2 ]]
e[[ b1, b2 ]] = e[[b1]] \cup e[[b2]]

(上面只会给出一棵树,而不是一张图。例如,没有边缘从then-branch到next语句的边缘)

编辑:

我一直在阅读kiama and dataflows scala,我喜欢他们使用的“succ”和“follow”方法。然而,我很难将其归结为一个更正式的描述,主要是因为漂亮的childAttrs.next隐藏了一些细节,当我试图正式指定它时变得丑陋

EDIT2:

我已经浏览过Dragon Book和“ML中的现代编译器实现”以及来自Learning to write a compiler的一些其他材料以及一些/大多数提到的数据流和控制流程,但从来没有涉及到如何以任何正式的方式创建CFG。

EDIT3:

通过Kiama作者,Associate Professor Dr. Tony Sloane我收到了一些additional book references to look up

据我所知,根据这些书的“方法”是基于程序的“per语句”而不是AST,并且基于Basic Blocks。不过还有很棒的投入!

2 个答案:

答案 0 :(得分:4)

Google's Closure Compiler实现了Control-Flow Analysis,它将JavaScript的JavaScript转换为控制流图。这个实现的想法受到了论文的启发:Declarative Intraprocedural Flow Analysis of Java Source Code

答案 1 :(得分:3)

如果您打算简单地创建看起来更正式的内容,那么您可以使用standard notation将这些匹配操作表达为推理规则。你应该用一个简单的步骤来表达它,而不是递归地表达它,因为只需继续应用这些规则直到不再应用它们就足够了。

也就是说,这个定义基本上与scala代码完全相同。如果你真的想做任何“正式”的事情你需要证明的属性是:

  • 您的CFG翻译算法始终终止
  • 对于给定的AST输入,您的CFG是否最小
  • 对于给定的AST输入,您的算法是否存在可导出的唯一CFG(即,它产生的CFG不是非确定性的。)

我认为你的基本块方法(而不是每个语句方法)也不一定是个坏主意。如果您可以匹配基本块,则可以编写一个规则,根据此匹配的存在,对集合成员资格进行断言,这似乎是完全合理的。看起来你开始草绘的归纳定义可以正常工作。

其他有趣的事情可能是尝试联系(正式)structured operational semantics和你的CFG构建。可能已经有了这个领域的工作,但我只是粗略地进行了谷歌搜索,并没有发现两者之间有任何明确的关系,但直觉上似乎应该存在。