diff --git a/Project-02-03-04/Source.gv.png b/Project-02-03-04/Source.gv.png deleted file mode 100644 index ff30cc4..0000000 Binary files a/Project-02-03-04/Source.gv.png and /dev/null differ diff --git a/Project-02-03-04/cfg_build.py b/Project-02-03-04/cfg_build.py index 1ef59dd..4e7ebfa 100644 --- a/Project-02-03-04/cfg_build.py +++ b/Project-02-03-04/cfg_build.py @@ -9,6 +9,13 @@ import compiler import syntax +# ------------------------------------------------- +# Global function environment +# ------------------------------------------------- + +FUNCTIONS = {} # name -> (func_start, func_end) + + # ------------------------------------------------- # Expressions — NO CFG NODES # ------------------------------------------------- @@ -64,13 +71,21 @@ class SEQ(compiler.SEQ): class IF(compiler.IF): def cfa(self, pred, end): - cond = CFG_DIAMOND(self) + # decision node (condition only) + cond = CFG_DIAMOND(self.cond) pred.add_child(cond) + # explicit branch entries + then_entry = CFG_Node() + else_entry = CFG_Node() + cond.add_child(then_entry) + cond.add_child(else_entry) + + # join node join = CFG_Node() - then_end = self.exp1.cfa(cond, end) - else_end = self.exp2.cfa(cond, end) + then_end = self.exp1.cfa(then_entry, end) + else_end = self.exp2.cfa(else_entry, end) if then_end is not None: then_end.add_child(join) @@ -82,13 +97,19 @@ class IF(compiler.IF): class WHILE(compiler.WHILE): def cfa(self, pred, end): - cond = CFG_DIAMOND(self) + # loop condition + cond = CFG_DIAMOND(self.cond) pred.add_child(cond) - body_end = self.body.cfa(cond, end) - if body_end is not None: - body_end.add_child(cond) + # body entry + body_entry = CFG_Node() + cond.add_child(body_entry) + body_end = self.body.cfa(body_entry, end) + if body_end is not None: + body_end.add_child(cond) # back-edge + + # loop exit after = CFG_Node() cond.add_child(after) @@ -96,21 +117,47 @@ class WHILE(compiler.WHILE): # ------------------------------------------------- -# Functions / calls +# Functions / calls (interprocedural CFG) # ------------------------------------------------- class CALL(compiler.CALL): def cfa(self, pred, end): - n = CFG_CALL(self) - pred.add_child(n) - return n + call = CFG_CALL(self) + pred.add_child(call) + + # continuation after return + cont = CFG_Node() + + if self.f_name not in FUNCTIONS: + raise RuntimeError(f"Call to undefined function '{self.f_name}'") + + f_start, f_end = FUNCTIONS[self.f_name] + + # call → function entry + call.add_child(f_start) + + # function exit → continuation + f_end.add_child(cont) + + return cont class DECL(compiler.DECL): def cfa(self, pred, end): - entry = CFG_Node(self) - pred.add_child(entry) - return self.body.cfa(entry, end) + # function entry / exit + f_start = CFG_Node(self) + f_end = CFG_Node(self) + + # register function + FUNCTIONS[self.f_name] = (f_start, f_end) + + # build function body + body_end = self.body.cfa(f_start, f_end) + if body_end is not None: + body_end.add_child(f_end) + + # function declaration does not alter current control flow + return pred class LET(compiler.LET): @@ -130,5 +177,5 @@ class RETURN(syntax.EXPRESSION): def cfa(self, pred, end): n = CFG_RETURN(self) pred.add_child(n) - n.add_child(end) # direct jump to global END + n.add_child(end) # return to function exit return None # no fallthrough