/
cfg.go
142 lines (118 loc) · 3.04 KB
/
cfg.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package cfg
import "github.com/skius/stringlang/ast"
type Node struct {
SuccNotTaken *Node
SuccTaken *Node
PredsNotTaken []*Node
PredsTaken []*Node
FuncSucc *Node // TODO: Fill these
Label int
Expr ast.Expr
}
type CFG struct {
Entry *Node
Exits []*Node
}
type counter struct {
curr int
}
// New returns the CFG of the top-level expressions and a map of FuncDecls to CFGs
func New(prog ast.Program) (*CFG, map[string]*CFG) {
cfg := new(CFG)
ctr := new(counter)
// Block is non-empty, can do this
// TODO: ugly
cfg.Entry = buildNode(prog.Code[0], ctr)
cfg.Exits = fillBlock(cfg.Entry, prog.Code[1:], ctr, false)
fillPreds(cfg)
cfgFuncs := make(map[string]*CFG)
// Reuse ctr so we have globally unique labels
// (will cause problems when if I implement separate compilation units)
for _, fd := range prog.Funcs {
funcCfg := new(CFG)
funcCfg.Entry = buildNode(fd.Code[0], ctr)
funcCfg.Exits = fillBlock(funcCfg.Entry, fd.Code[1:], ctr, false)
fillPreds(funcCfg)
cfgFuncs[fd.Identifier] = funcCfg
}
return cfg, cfgFuncs
}
// Visit runs the given closure over the CFG in DFS preorder
func (cfg *CFG) Visit(f func(*Node)) {
visited := make(map[int]bool)
var dfs func(*Node)
dfs = func(curr *Node) {
if curr == nil || visited[curr.Label] {
return
}
visited[curr.Label] = true
f(curr)
dfs(curr.SuccNotTaken)
dfs(curr.SuccTaken)
}
dfs(cfg.Entry)
}
// Fills in backward edges for CFG which already has forward edges
func fillPreds(cfg *CFG) {
cfg.Visit(func(curr *Node) {
if curr.SuccNotTaken != nil {
succ := curr.SuccNotTaken
succ.PredsNotTaken = append(succ.PredsNotTaken, curr)
}
if curr.SuccTaken != nil {
succ := curr.SuccTaken
succ.PredsTaken = append(succ.PredsTaken, curr)
}
})
}
// Returns exits of block
// Only fills in forward-edges, because backward (pred) edges can be added easily using a visitor
func fillBlock(entryPred *Node, block ast.Block, ctr *counter, isBranch bool) []*Node {
preds := []*Node{}
if entryPred != nil {
preds = []*Node{entryPred}
}
updateSucc := func(succ *Node) {
for _, pred := range preds {
if isBranch {
pred.SuccTaken = succ
} else {
pred.SuccNotTaken = succ
}
}
isBranch = false
}
for _, expr := range block {
switch e := expr.(type) {
case ast.IfElse:
condNode := buildNode(e.Cond, ctr)
updateSucc(condNode)
tExits := fillBlock(condNode, e.Then.(ast.Block), ctr, true)
nExits := fillBlock(condNode, e.Else.(ast.Block), ctr, false)
preds = append(tExits, nExits...)
case ast.While:
condNode := buildNode(e.Cond, ctr)
updateSucc(condNode)
bExits := fillBlock(condNode, e.Body.(ast.Block), ctr, true)
for _, pred := range bExits {
pred.SuccNotTaken = condNode
}
preds = []*Node{condNode}
default:
n := buildNode(expr, ctr)
updateSucc(n)
preds = []*Node{n}
}
}
return preds
}
func buildNode(expr ast.Expr, ctr *counter) *Node {
n := new(Node)
n.Label = ctr.incAndGet()
n.Expr = expr
return n
}
func (c *counter) incAndGet() int {
c.curr++
return c.curr
}