Skip to content
This repository has been archived by the owner on Jul 8, 2020. It is now read-only.
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"runtime/pprof"
)
// Control flow graph, created once.
type Block struct {
Name int
In []*Block
Out []*Block
}
func (b *Block) String() string {
return fmt.Sprintf("b%d", b.Name)
}
func (b *Block) Dump(w io.Writer) {
fmt.Fprintf(w, "%s: %v %v\n", b, b.In, b.Out)
}
type CFG struct {
Block []*Block
Edge []Edge
}
type Edge struct {
Src, Dst int
}
func (g *CFG) NewBlock() *Block {
b := &Block{Name: len(g.Block)}
g.Block = append(g.Block, b)
return b
}
func (g *CFG) Dump(w io.Writer) {
for _, b := range g.Block {
b.Dump(w)
}
}
func (g *CFG) Connect(src, dst *Block) {
src.Out = append(src.Out, dst)
dst.In = append(dst.In, src)
g.Edge = append(g.Edge, Edge{src.Name, dst.Name})
}
func (g *CFG) Path(from *Block) *Block {
n := g.NewBlock()
g.Connect(from, n)
return n
}
func (g *CFG) Diamond(from *Block) *Block {
x := g.Path(from)
y := g.Path(from)
z := g.Path(x)
g.Connect(y, z)
g.Connect(z, from)
return z
}
func (g *CFG) BaseLoop(from *Block) *Block {
z := g.Path(g.Diamond(g.Path(g.Diamond(g.Path(from)))))
g.Connect(z, from)
return g.Path(z)
}
func buildGraph() *CFG {
g := new(CFG)
n0 := g.NewBlock()
n1 := g.NewBlock()
n2 := g.NewBlock()
g.Connect(n0, n2)
for i := 0; i < 10; i++ {
n := g.NewBlock()
g.Connect(n2, n)
for j := 0; j < 100; j++ {
top := n
n = g.Path(n)
for k := 0; k < 25; k++ {
n = g.BaseLoop(n)
}
bottom := g.Path(n)
g.Connect(n, top)
n = bottom
}
g.Connect(n, n1)
}
return g
}
// Basic representation of loop graph.
type LoopGraph struct {
Root Loop
Loop []*Loop
}
type Loop struct {
Block []*Block
Child []*Loop
Parent *Loop
Head *Block
IsRoot bool
IsReducible bool
Counter int
Nesting int
Depth int
}
var loopCounter = 0
func (g *LoopGraph) Clear() {
g.Root.Child = g.Root.Child[:0]
g.Loop = g.Loop[:0]
}
func (g *LoopGraph) NewLoop(lcap int) *Loop {
// If there's a cached loop, use that.
if n := len(g.Loop); n < cap(g.Loop) && g.Loop[:n+1][n] != nil {
g.Loop = g.Loop[:n+1]
l := g.Loop[n]
l.Block = l.Block[:0]
l.Child = l.Child[:0]
l.Parent = nil
l.Head = nil
l.IsRoot = false
l.IsReducible = false
l.Nesting = 0
l.Depth = 0
return l
}
loopCounter++
l := &Loop{Counter: loopCounter}
g.Loop = append(g.Loop, l)
l.Block = make([]*Block, 0, lcap)
return l
}
func (g *LoopGraph) CalculateNesting() {
for _, l := range g.Loop {
if l==nil {panic("nil l")}
if l.IsRoot {
continue
}
if l.Parent == nil {
l.Parent = &g.Root
g.Root.Child = append(g.Root.Child, l)
}
}
g.calculateNesting(&g.Root, 0)
}
func (g *LoopGraph) calculateNesting(l *Loop, depth int) {
l.Depth = depth
for _, child := range l.Child {
g.calculateNesting(child, depth+1)
if n := child.Nesting + 1; l.Nesting < n {
l.Nesting = n
}
}
}
func (g *LoopGraph) Dump(w io.Writer) {
g.dump(w, &g.Root, 0)
}
func (g *LoopGraph) dump(w io.Writer, l *Loop, indent int) {
l.Dump(w, indent)
for _, child := range l.Child {
g.dump(w, child, indent+1)
}
}
func (l *Loop) String() string {
return fmt.Sprintf("loop-%d", l.Counter)
}
func (l *Loop) Dump(w io.Writer, indent int) {
fmt.Fprintf(w, "%*sloop-%d nest: %d depth %d",
2*indent, l.Counter, l.Nesting, l.Depth)
if !l.IsReducible {
fmt.Fprintf(w, " (Irreducible)")
}
if len(l.Child) > 0 {
fmt.Fprintf(w, " Children: %v", l.Child)
}
if len(l.Block) > 0 {
fmt.Fprintf(w, "(")
sep := ""
for _, b := range l.Block {
fmt.Fprint(w, sep, b)
if b == l.Head {
fmt.Fprint(w, "*")
}
sep = " "
}
fmt.Fprintf(w, ")")
}
fmt.Fprintf(w, "\n")
}
// Loop finding state, generated or reused on each iteration.
type LoopFinder struct {
LoopBlock []LoopBlock
DepthFirst []*LoopBlock
Pool []*LoopBlock
}
const Unvisited = -1
type LoopType int
const (
bbNonHeader LoopType = 1 + iota // a regular BB
bbReducible // reducible loop
bbSelf // single BB loop
bbIrreducible // irreducible loop
bbDead // a dead BB
)
type LoopBlock struct {
Block *Block
Loop *Loop
First int
Last int
Header *LoopBlock
Type LoopType
BackPred []*LoopBlock
NonBackPred []*LoopBlock
Union *LoopBlock // union find
}
func (lb *LoopBlock) Init(b *Block) {
lb.Block = b
lb.Loop = nil
lb.First = Unvisited
lb.Last = Unvisited
lb.Header = nil
lb.Type = bbNonHeader
lb.BackPred = lb.BackPred[:0]
lb.NonBackPred = lb.NonBackPred[:0]
lb.Union = lb
}
func (lb *LoopBlock) Find() *LoopBlock {
if lb.Union != lb {
lb.Union = lb.Union.Find()
}
return lb.Union
}
// Depth first search to number blocks.
func (f *LoopFinder) Search(b *Block) {
lb := &f.LoopBlock[b.Name]
f.DepthFirst = append(f.DepthFirst, lb)
lb.First = len(f.DepthFirst)
for _, out := range b.Out {
if f.LoopBlock[out.Name].First == Unvisited {
f.Search(out)
}
}
lb.Last = len(f.DepthFirst)
}
func (lb *LoopBlock) IsAncestor(p *LoopBlock) bool {
return lb.First <= p.First && p.First <= lb.Last
}
func (f *LoopFinder) FindLoops(g *CFG, lsg *LoopGraph) {
size := len(g.Block)
if size == 0 {
return
}
// Step A: Initialize nodes, depth first numbering, mark dead nodes.
if size <= cap(f.LoopBlock) {
f.LoopBlock = f.LoopBlock[:size]
f.DepthFirst = f.DepthFirst[:0]
} else {
f.LoopBlock = make([]LoopBlock, size)
f.DepthFirst = make([]*LoopBlock, 0, size)
}
for i := range f.LoopBlock {
f.LoopBlock[i].Init(g.Block[i])
}
f.Search(g.Block[0])
for i := range f.LoopBlock {
lb := &f.LoopBlock[i]
if lb.First == Unvisited {
lb.Type = bbDead
}
}
// Step B: Classify back edges as coming from descendents or not.
for _, lb := range f.DepthFirst {
for _, b := range lb.Block.In {
lbb := &f.LoopBlock[b.Name]
if lb.IsAncestor(lbb) {
lb.BackPred = append(lb.BackPred, lbb)
} else {
lb.NonBackPred = append(lb.NonBackPred, lbb)
}
}
}
// Start node is root of all other loops.
f.LoopBlock[0].Header = &f.LoopBlock[0]
// Step C:
//
// The outer loop, unchanged from Tarjan. It does nothing except
// for those nodes which are the destinations of backedges.
// For a header node w, we chase backward from the sources of the
// backedges adding nodes to the set P, representing the body of
// the loop headed by w.
//
// By running through the nodes in reverse of the DFST preorder,
// we ensure that inner loop headers will be processed before the
// headers for surrounding loops.
for i := len(f.DepthFirst) - 1; i >= 0; i-- {
w := f.DepthFirst[i]
pool := f.Pool[:0]
// Step D.
for _, pred := range w.BackPred {
if w == pred {
w.Type = bbSelf
continue
}
pool = append(pool, pred.Find())
}
// Process node pool in order as work list.
for i := 0; i < len(pool); i++ {
x := pool[i]
// Step E:
//
// Step E represents the main difference from Tarjan's method.
// Chasing upwards from the sources of a node w's backedges. If
// there is a node y' that is not a descendant of w, w is marked
// the header of an irreducible loop, there is another entry
// into this loop that avoids w.
for _, y := range x.NonBackPred {
ydash := y.Find()
if !w.IsAncestor(ydash) {
w.Type = bbIrreducible
w.NonBackPred = appendUnique(w.NonBackPred, y)
} else if ydash != w {
pool = appendUnique(pool, ydash)
}
}
}
// Collapse/Unionize nodes in a SCC to a single node
// For every SCC found, create a loop descriptor and link it in.
if len(pool) > 0 || w.Type == bbSelf {
l := lsg.NewLoop(1 + len(pool))
l.Head = w.Block
l.Block = append(l.Block, w.Block)
l.IsReducible = w.Type != bbIrreducible
w.Loop = l
// At this point, one can set attributes to the loop, such as:
//
// the bottom node:
// iter = backPreds[w].begin();
// loop bottom is: nodes[iter].node);
//
// the number of backedges:
// backPreds[w].size()
for _, node := range pool {
// Add nodes to loop descriptor.
node.Header = w
node.Union = w
// Nested loops are not added, but linked together.
if node.Loop != nil {
node.Loop.Parent = l
} else {
l.Block = append(l.Block, node.Block)
}
}
}
f.Pool = pool
}
}
func appendUnique(pool []*LoopBlock, b *LoopBlock) []*LoopBlock {
for _, p := range pool {
if b == p {
return pool
}
}
return append(pool, b)
}
// Main program.
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
var memprofile = flag.String("memprofile", "", "write memory profile to this file")
var reuseLoopGraph = flag.Bool("reuseloopgraph", true, "reuse loop graph memory")
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
var f LoopFinder
g := buildGraph()
lsg := new(LoopGraph)
f.FindLoops(g, lsg)
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
return
}
for i := 0; i < 50; i++ {
if *reuseLoopGraph {
lsg.Clear()
f.FindLoops(g, lsg)
} else {
f.FindLoops(g, new(LoopGraph))
}
}
fmt.Printf("# of loops: %d (including 1 artificial root node)\n", len(lsg.Loop))
lsg.CalculateNesting()
}