You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
9.3 KiB
468 lines
9.3 KiB
package main
|
|
|
|
/*
|
|
@Auth:ShenZ
|
|
@Description:
|
|
*/
|
|
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", "cpu6.prof", "write cpu profile to this file")
|
|
var memprofile = flag.String("memprofile", "mem6.prof", "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()
|
|
}
|