func (n *VisitNode) Fn() *ssa.Function {
	if n.blk == nil {
		log.Fatal(ErrBadNode)
	}
	return n.blk.Parent()
}
Fn returns the function which the block belongs to.
package block
import (
	"errors"
	"fmt"
	"log"
	"sync"
	"golang.org/x/tools/go/ssa"
)
var (
	ErrEdgesStackEmpty = errors.New("blockgraph: cannot pop edges: stack empty")
	ErrBadNode         = errors.New("VisitNode does not contain block (or has nil block)")
	ErrBadBlock        = errors.New("internal error: Block is nil")
	ErrBadParentFn     = errors.New("internal error: Block has nil parent Fn")
)
type visitedEdges map[int]map[int]bool
type VisitGraph struct {
	sync.Mutex
	nodes []*VisitNode
	visited map[*ssa.Function]visitedEdges
	reentrant bool
	edgesStack map[*ssa.Function][]visitedEdges
}
func NewVisitGraph(reentrant bool) *VisitGraph {
	if reentrant {
		return &VisitGraph{
			visited:    make(map[*ssa.Function]visitedEdges),
			reentrant:  true,
			edgesStack: make(map[*ssa.Function][]visitedEdges),
		}
	}
	return &VisitGraph{
		visited: make(map[*ssa.Function]visitedEdges),
	}
}
func (g *VisitGraph) pushEdges(fn *ssa.Function) {
	if g.reentrant {
		if edges, ok := g.visited[fn]; ok {
			g.edgesStack[fn] = append(g.edgesStack[fn], edges)
			g.visited[fn] = nil
		}
	}
}
func (g *VisitGraph) popEdges(fn *ssa.Function) error {
	if g.reentrant {
		if len(g.edgesStack[fn]) < 1 {
			return ErrEdgesStackEmpty
		}
		g.visited[fn] = g.edgesStack[fn][len(g.edgesStack[fn])-1]
		g.edgesStack[fn] = g.edgesStack[fn][:len(g.edgesStack[fn])-1]
	}
	return nil
}
func (g *VisitGraph) Visit(n *VisitNode) {
	g.Lock()
	defer g.Unlock()
	g.nodes = append(g.nodes, n)
	if len(g.nodes) > 1 {
		n.Prev = g.nodes[len(g.nodes)-2]
		g.nodes[len(g.nodes)-2].Next = n
	}
	if n.Index() == 0 {
		g.markNewFuncVisit(n)
	} else {
		if n.Prev == nil {
			log.Printf("Visit: there is no previous block")
			return
		}
		g.markVisit(n.Prev, n)
	}
}
func (g *VisitGraph) MarkLast(n *VisitNode) {
	if g.reentrant {
		g.popEdges(n.Fn())
	}
}
func (g *VisitGraph) markVisit(prev, n *VisitNode) {
	if prev.Fn() != n.Fn() {
		log.Printf("markVisit: previous block %q#%d and current block %q#%d are in different function",
			prev.Fn(), prev.Index(), n.Fn(), n.Index())
		return
	}
	if _, ok := g.visited[n.Fn()]; !ok {
		log.Printf("markVisit: Function %q was not visited before but visiting block %d",
			n.Fn(), n.Index())
		g.markNewFuncVisit(n)
	}
	g.visited[n.Fn()][n.Index()][prev.Index()] = true
}
func (g *VisitGraph) markNewFuncVisit(n *VisitNode) {
	if _, ok := g.visited[n.Fn()]; ok {
		if !g.reentrant {
			log.Printf("Function %q was visited", n.Fn())
			return
		}
		g.pushEdges(n.Fn())
	}
	g.visited[n.Fn()] = make(map[int]map[int]bool)
	for _, b := range n.Fn().Blocks {
		g.visited[n.Fn()][b.Index] = make(map[int]bool)
		for _, p := range b.Preds {
			g.visited[n.Fn()][b.Index][p.Index] = false
		}
	}
}
func (g *VisitGraph) VisitFrom(prev, n *VisitNode) {
	g.Lock()
	defer g.Unlock()
	g.nodes = append(g.nodes, n)
	if len(g.nodes) > 1 {
		n.Prev = g.nodes[len(g.nodes)-2]
		g.nodes[len(g.nodes)-2].Next = n
	}
	validPrev := false
PREVCHECK:
	for i := len(g.nodes) - 1; i >= 0; i-- {
		if g.nodes[i].Blk() == prev.Blk() {
			validPrev = true
			break PREVCHECK
		}
	}
	if !validPrev {
		log.Printf("VisitFrom: %v is not a valid existing VisitNode", prev)
	}
	g.markVisit(prev, n)
}
func (g *VisitGraph) LastNode() *VisitNode {
	if len(g.nodes) == 0 {
		log.Printf("VisitGraph.LastNode: empty graph")
		return nil
	}
	return g.nodes[len(g.nodes)-1]
}
func (g *VisitGraph) Size() int {
	return len(g.nodes)
}
func (g *VisitGraph) NodeVisited(toVisit *VisitNode) bool {
	g.Lock()
	defer g.Unlock()
	if toVisit.Blk() == nil {
		log.Println("Block is nil - assume visited = true")
		return true
	}
	if g.nodes == nil {  
		return false
	}
	allVisited := true
	if fn, ok := g.visited[toVisit.Fn()]; ok {  
		inEdges, _ := fn[toVisit.Index()]
		for to := range inEdges {
			allVisited = allVisited && inEdges[to]
		}
		return allVisited
	}
	return false
}
func (g *VisitGraph) EdgeVisited(from, to *VisitNode) bool {
	g.Lock()
	defer g.Unlock()
	if to.Blk() == nil {
		log.Println("Block is nil - assume visited = true")
		return true
	}
	if g.nodes == nil {  
		return false
	}
	if fn, ok := g.visited[to.Fn()]; ok {  
		inEdges, _ := fn[to.Index()]
		return inEdges[from.Index()]
	}
	return false
}
func (g *VisitGraph) NodePartialVisited(toVisit *VisitNode) bool {
	g.Lock()
	defer g.Unlock()
	if toVisit.Blk() == nil {
		log.Println("Block is nil - assume unvisited = false (nothing to visit)")
		return false
	}
	if g.nodes == nil {  
		return true  
	}
	if fn, ok := g.visited[toVisit.Fn()]; ok {  
		inEdges, _ := fn[toVisit.Index()]
		if len(inEdges) == 0 {  
			return true  
		}
		for to := range inEdges {
			if inEdges[to] {  
				return false
			}
		}
		return true
	}
	return true
}
func (g *VisitGraph) VisitedOnce(toVisit *VisitNode) bool {
	g.Lock()
	defer g.Unlock()
	if toVisit.Blk() == nil {
		log.Println("Block is nil - assume visited = true")
		return true
	}
	if g.nodes == nil {  
		return false
	}
	if fn, ok := g.visited[toVisit.Fn()]; ok {  
		inEdges, _ := fn[toVisit.Index()]
		if len(inEdges) == 0 {  
			return true
		}
		for to := range inEdges {
			if inEdges[to] {  
				return true
			}
		}
	}
	return false
}
type VisitNode struct {
	blk *ssa.BasicBlock  
	Prev, Next *VisitNode
}
func NewVisitNode(block *ssa.BasicBlock) *VisitNode {
	return &VisitNode{blk: block}
}
func (n *VisitNode) Blk() *ssa.BasicBlock {
	return n.blk
}