func loopDetectBounds(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {
	switch ctx.L.State {
	case Enter:
		switch ctx.L.Bound {
		case Unknown:
			phiSelectEdge(instr, infer, ctx)
			loopSetIndex(instr, infer, ctx)
		case Static:
			switch ctx.L.Bound {
			case Static:
				phiSelectEdge(instr, infer, ctx)
				if instr == ctx.L.IndexVar {
					ctx.L.Next()
					infer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+"Increment %s by %s to %s", ctx.L.IndexVar.Name(), fmtLoopHL(ctx.L.Step), fmtLoopHL(ctx.L.Index)))
				}
			default:
				visitSkip(instr, infer, ctx)
			}
		case Dynamic:
			phiSelectEdge(instr, infer, ctx)
			infer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+"(dynamic bound) %s", instr.String()))
		}
	default:
		phiSelectEdge(instr, infer, ctx)
	}
}
loopDetectBounds detects static bounds (or set as dynamic bounds) from based
on loop state machine.
package migoextract
import (
	"fmt"
	"go/constant"
	"go/token"
	"golang.org/x/tools/go/ssa"
)
type Loop struct {
	Parent *Function  
	Bound  LoopBound  
	State  LoopState  
	IndexVar  ssa.Value  
	CondVar   ssa.Value  
	Index     int64      
	Start     int64      
	Step      int64      
	End       int64      
	LoopBlock int        
}
func (l *Loop) SetInit(index ssa.Value, init int64) {
	l.IndexVar = index
	l.Start = init
	l.Index = init
}
func (l *Loop) SetStep(step int64) {
	l.Step = step
}
func (l *Loop) SetCond(cond ssa.Value, max int64) {
	l.CondVar = cond
	l.End = max
}
func (l *Loop) Next() {
	if l.Bound == Static {
		l.Index += l.Step
	}
}
func (l *Loop) HasNext() bool {
	if l.Bound == Static {
		return l.Start <= l.Index && l.Index <= l.End
	}
	return false
}
func (l *Loop) String() string {
	if l.Bound != Unknown && l.State != NonLoop {
		return fmt.Sprintf("%s: bound %s [%d..%d..%d] Step:%d", l.State, l.Bound, l.Start, l.Index, l.End, l.Step)
	}
	return fmt.Sprintf("%s: bound %s", l.State, l.Bound)
}
func loopSetIndex(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {
	if i, ok := instr.Edges[0].(*ssa.Const); ok && !i.IsNil() && i.Value.Kind() == constant.Int {
		ctx.L.SetInit(instr, i.Int64())
		infer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+"%s <= i", fmtLoopHL(ctx.L.Start)))
	}
	if bin, ok := instr.Edges[1].(*ssa.BinOp); ok {
		switch bin.Op {
		case token.ADD:
			if i, ok := bin.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {
				ctx.L.SetStep(i.Int64())
			}
			infer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+"i += %s", fmtLoopHL(ctx.L.Step)))
		case token.SUB:
			if i, ok := bin.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {
				ctx.L.SetStep(-i.Int64())
			}
			infer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+"i -= %s", fmtLoopHL(ctx.L.Step)))
		default:
			infer.Logger.Printf("loop index expression not supported %s", bin)
		}
	}
}