diff options
| author | Joe Richey joerichey@google.com <joerichey@google.com> | 2018-08-30 02:24:33 -0700 |
|---|---|---|
| committer | Joe Richey joerichey@google.com <joerichey@google.com> | 2018-08-30 02:24:33 -0700 |
| commit | 630c8d1d002286cc26bafec78a7f120b5c0e78af (patch) | |
| tree | 5d453c5b099a2881c74ac49922eb968e235bedfd /vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go | |
| parent | b7f5b0ab3c9b1af9397c557e410473119399f8b6 (diff) | |
Update dependancies to lastest version
Diffstat (limited to 'vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go')
| -rw-r--r-- | vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go | 1049 |
1 files changed, 0 insertions, 1049 deletions
diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go b/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go deleted file mode 100644 index cb17f04..0000000 --- a/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go +++ /dev/null @@ -1,1049 +0,0 @@ -package vrp - -// TODO(dh) widening and narrowing have a lot of code in common. Make -// it reusable. - -import ( - "fmt" - "go/constant" - "go/token" - "go/types" - "math/big" - "sort" - "strings" - - "honnef.co/go/tools/ssa" -) - -type Future interface { - Constraint - Futures() []ssa.Value - Resolve() - IsKnown() bool - MarkUnresolved() - MarkResolved() - IsResolved() bool -} - -type Range interface { - Union(other Range) Range - IsKnown() bool -} - -type Constraint interface { - Y() ssa.Value - isConstraint() - String() string - Eval(*Graph) Range - Operands() []ssa.Value -} - -type aConstraint struct { - y ssa.Value -} - -func NewConstraint(y ssa.Value) aConstraint { - return aConstraint{y} -} - -func (aConstraint) isConstraint() {} -func (c aConstraint) Y() ssa.Value { return c.y } - -type PhiConstraint struct { - aConstraint - Vars []ssa.Value -} - -func NewPhiConstraint(vars []ssa.Value, y ssa.Value) Constraint { - uniqm := map[ssa.Value]struct{}{} - for _, v := range vars { - uniqm[v] = struct{}{} - } - var uniq []ssa.Value - for v := range uniqm { - uniq = append(uniq, v) - } - return &PhiConstraint{ - aConstraint: NewConstraint(y), - Vars: uniq, - } -} - -func (c *PhiConstraint) Operands() []ssa.Value { - return c.Vars -} - -func (c *PhiConstraint) Eval(g *Graph) Range { - i := Range(nil) - for _, v := range c.Vars { - i = g.Range(v).Union(i) - } - return i -} - -func (c *PhiConstraint) String() string { - names := make([]string, len(c.Vars)) - for i, v := range c.Vars { - names[i] = v.Name() - } - return fmt.Sprintf("%s = φ(%s)", c.Y().Name(), strings.Join(names, ", ")) -} - -func isSupportedType(typ types.Type) bool { - switch typ := typ.Underlying().(type) { - case *types.Basic: - switch typ.Kind() { - case types.String, types.UntypedString: - return true - default: - if (typ.Info() & types.IsInteger) == 0 { - return false - } - } - case *types.Chan: - return true - case *types.Slice: - return true - default: - return false - } - return true -} - -func ConstantToZ(c constant.Value) Z { - s := constant.ToInt(c).ExactString() - n := &big.Int{} - n.SetString(s, 10) - return NewBigZ(n) -} - -func sigmaInteger(g *Graph, ins *ssa.Sigma, cond *ssa.BinOp, ops []*ssa.Value) Constraint { - op := cond.Op - if !ins.Branch { - op = (invertToken(op)) - } - - switch op { - case token.EQL, token.GTR, token.GEQ, token.LSS, token.LEQ: - default: - return nil - } - var a, b ssa.Value - if (*ops[0]) == ins.X { - a = *ops[0] - b = *ops[1] - } else { - a = *ops[1] - b = *ops[0] - op = flipToken(op) - } - return NewIntIntersectionConstraint(a, b, op, g.ranges, ins) -} - -func sigmaString(g *Graph, ins *ssa.Sigma, cond *ssa.BinOp, ops []*ssa.Value) Constraint { - op := cond.Op - if !ins.Branch { - op = (invertToken(op)) - } - - switch op { - case token.EQL, token.GTR, token.GEQ, token.LSS, token.LEQ: - default: - return nil - } - - if ((*ops[0]).Type().Underlying().(*types.Basic).Info() & types.IsString) == 0 { - var a, b ssa.Value - call, ok := (*ops[0]).(*ssa.Call) - if ok && call.Common().Args[0] == ins.X { - a = *ops[0] - b = *ops[1] - } else { - a = *ops[1] - b = *ops[0] - op = flipToken(op) - } - return NewStringIntersectionConstraint(a, b, op, g.ranges, ins) - } - var a, b ssa.Value - if (*ops[0]) == ins.X { - a = *ops[0] - b = *ops[1] - } else { - a = *ops[1] - b = *ops[0] - op = flipToken(op) - } - return NewStringIntersectionConstraint(a, b, op, g.ranges, ins) -} - -func sigmaSlice(g *Graph, ins *ssa.Sigma, cond *ssa.BinOp, ops []*ssa.Value) Constraint { - // TODO(dh) sigmaSlice and sigmaString are a lot alike. Can they - // be merged? - // - // XXX support futures - - op := cond.Op - if !ins.Branch { - op = (invertToken(op)) - } - - k, ok := (*ops[1]).(*ssa.Const) - // XXX investigate in what cases this wouldn't be a Const - // - // XXX what if left and right are swapped? - if !ok { - return nil - } - - call, ok := (*ops[0]).(*ssa.Call) - if !ok { - return nil - } - builtin, ok := call.Common().Value.(*ssa.Builtin) - if !ok { - return nil - } - if builtin.Name() != "len" { - return nil - } - callops := call.Operands(nil) - - v := ConstantToZ(k.Value) - c := NewSliceIntersectionConstraint(*callops[1], IntInterval{}, ins).(*SliceIntersectionConstraint) - switch op { - case token.EQL: - c.I = NewIntInterval(v, v) - case token.GTR, token.GEQ: - off := int64(0) - if cond.Op == token.GTR { - off = 1 - } - c.I = NewIntInterval( - v.Add(NewZ(off)), - PInfinity, - ) - case token.LSS, token.LEQ: - off := int64(0) - if cond.Op == token.LSS { - off = -1 - } - c.I = NewIntInterval( - NInfinity, - v.Add(NewZ(off)), - ) - default: - return nil - } - return c -} - -func BuildGraph(f *ssa.Function) *Graph { - g := &Graph{ - Vertices: map[interface{}]*Vertex{}, - ranges: Ranges{}, - } - - var cs []Constraint - - ops := make([]*ssa.Value, 16) - seen := map[ssa.Value]bool{} - for _, block := range f.Blocks { - for _, ins := range block.Instrs { - ops = ins.Operands(ops[:0]) - for _, op := range ops { - if c, ok := (*op).(*ssa.Const); ok { - if seen[c] { - continue - } - seen[c] = true - if c.Value == nil { - switch c.Type().Underlying().(type) { - case *types.Slice: - cs = append(cs, NewSliceIntervalConstraint(NewIntInterval(NewZ(0), NewZ(0)), c)) - } - continue - } - switch c.Value.Kind() { - case constant.Int: - v := ConstantToZ(c.Value) - cs = append(cs, NewIntIntervalConstraint(NewIntInterval(v, v), c)) - case constant.String: - s := constant.StringVal(c.Value) - n := NewZ(int64(len(s))) - cs = append(cs, NewStringIntervalConstraint(NewIntInterval(n, n), c)) - } - } - } - } - } - for _, block := range f.Blocks { - for _, ins := range block.Instrs { - switch ins := ins.(type) { - case *ssa.Convert: - switch v := ins.Type().Underlying().(type) { - case *types.Basic: - if (v.Info() & types.IsInteger) == 0 { - continue - } - cs = append(cs, NewIntConversionConstraint(ins.X, ins)) - } - case *ssa.Call: - if static := ins.Common().StaticCallee(); static != nil { - if fn, ok := static.Object().(*types.Func); ok { - switch fn.FullName() { - case "bytes.Index", "bytes.IndexAny", "bytes.IndexByte", - "bytes.IndexFunc", "bytes.IndexRune", "bytes.LastIndex", - "bytes.LastIndexAny", "bytes.LastIndexByte", "bytes.LastIndexFunc", - "strings.Index", "strings.IndexAny", "strings.IndexByte", - "strings.IndexFunc", "strings.IndexRune", "strings.LastIndex", - "strings.LastIndexAny", "strings.LastIndexByte", "strings.LastIndexFunc": - // TODO(dh): instead of limiting by +∞, - // limit by the upper bound of the passed - // string - cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(-1), PInfinity), ins)) - case "bytes.Title", "bytes.ToLower", "bytes.ToTitle", "bytes.ToUpper", - "strings.Title", "strings.ToLower", "strings.ToTitle", "strings.ToUpper": - cs = append(cs, NewCopyConstraint(ins.Common().Args[0], ins)) - case "bytes.ToLowerSpecial", "bytes.ToTitleSpecial", "bytes.ToUpperSpecial", - "strings.ToLowerSpecial", "strings.ToTitleSpecial", "strings.ToUpperSpecial": - cs = append(cs, NewCopyConstraint(ins.Common().Args[1], ins)) - case "bytes.Compare", "strings.Compare": - cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(-1), NewZ(1)), ins)) - case "bytes.Count", "strings.Count": - // TODO(dh): instead of limiting by +∞, - // limit by the upper bound of the passed - // string. - cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(0), PInfinity), ins)) - case "bytes.Map", "bytes.TrimFunc", "bytes.TrimLeft", "bytes.TrimLeftFunc", - "bytes.TrimRight", "bytes.TrimRightFunc", "bytes.TrimSpace", - "strings.Map", "strings.TrimFunc", "strings.TrimLeft", "strings.TrimLeftFunc", - "strings.TrimRight", "strings.TrimRightFunc", "strings.TrimSpace": - // TODO(dh): lower = 0, upper = upper of passed string - case "bytes.TrimPrefix", "bytes.TrimSuffix", - "strings.TrimPrefix", "strings.TrimSuffix": - // TODO(dh) range between "unmodified" and len(cutset) removed - case "(*bytes.Buffer).Cap", "(*bytes.Buffer).Len", "(*bytes.Reader).Len", "(*bytes.Reader).Size": - cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(0), PInfinity), ins)) - } - } - } - builtin, ok := ins.Common().Value.(*ssa.Builtin) - ops := ins.Operands(nil) - if !ok { - continue - } - switch builtin.Name() { - case "len": - switch op1 := (*ops[1]).Type().Underlying().(type) { - case *types.Basic: - if op1.Kind() == types.String || op1.Kind() == types.UntypedString { - cs = append(cs, NewStringLengthConstraint(*ops[1], ins)) - } - case *types.Slice: - cs = append(cs, NewSliceLengthConstraint(*ops[1], ins)) - } - - case "append": - cs = append(cs, NewSliceAppendConstraint(ins.Common().Args[0], ins.Common().Args[1], ins)) - } - case *ssa.BinOp: - ops := ins.Operands(nil) - basic, ok := (*ops[0]).Type().Underlying().(*types.Basic) - if !ok { - continue - } - switch basic.Kind() { - case types.Int, types.Int8, types.Int16, types.Int32, types.Int64, - types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.UntypedInt: - fns := map[token.Token]func(ssa.Value, ssa.Value, ssa.Value) Constraint{ - token.ADD: NewIntAddConstraint, - token.SUB: NewIntSubConstraint, - token.MUL: NewIntMulConstraint, - // XXX support QUO, REM, SHL, SHR - } - fn, ok := fns[ins.Op] - if ok { - cs = append(cs, fn(*ops[0], *ops[1], ins)) - } - case types.String, types.UntypedString: - if ins.Op == token.ADD { - cs = append(cs, NewStringConcatConstraint(*ops[0], *ops[1], ins)) - } - } - case *ssa.Slice: - typ := ins.X.Type().Underlying() - switch typ := typ.(type) { - case *types.Basic: - cs = append(cs, NewStringSliceConstraint(ins.X, ins.Low, ins.High, ins)) - case *types.Slice: - cs = append(cs, NewSliceSliceConstraint(ins.X, ins.Low, ins.High, ins)) - case *types.Array: - cs = append(cs, NewArraySliceConstraint(ins.X, ins.Low, ins.High, ins)) - case *types.Pointer: - if _, ok := typ.Elem().(*types.Array); !ok { - continue - } - cs = append(cs, NewArraySliceConstraint(ins.X, ins.Low, ins.High, ins)) - } - case *ssa.Phi: - if !isSupportedType(ins.Type()) { - continue - } - ops := ins.Operands(nil) - dops := make([]ssa.Value, len(ops)) - for i, op := range ops { - dops[i] = *op - } - cs = append(cs, NewPhiConstraint(dops, ins)) - case *ssa.Sigma: - pred := ins.Block().Preds[0] - instrs := pred.Instrs - cond, ok := instrs[len(instrs)-1].(*ssa.If).Cond.(*ssa.BinOp) - ops := cond.Operands(nil) - if !ok { - continue - } - switch typ := ins.Type().Underlying().(type) { - case *types.Basic: - var c Constraint - switch typ.Kind() { - case types.Int, types.Int8, types.Int16, types.Int32, types.Int64, - types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.UntypedInt: - c = sigmaInteger(g, ins, cond, ops) - case types.String, types.UntypedString: - c = sigmaString(g, ins, cond, ops) - } - if c != nil { - cs = append(cs, c) - } - case *types.Slice: - c := sigmaSlice(g, ins, cond, ops) - if c != nil { - cs = append(cs, c) - } - default: - //log.Printf("unsupported sigma type %T", typ) // XXX - } - case *ssa.MakeChan: - cs = append(cs, NewMakeChannelConstraint(ins.Size, ins)) - case *ssa.MakeSlice: - cs = append(cs, NewMakeSliceConstraint(ins.Len, ins)) - case *ssa.ChangeType: - switch ins.X.Type().Underlying().(type) { - case *types.Chan: - cs = append(cs, NewChannelChangeTypeConstraint(ins.X, ins)) - } - } - } - } - - for _, c := range cs { - if c == nil { - panic("nil constraint") - } - // If V is used in constraint C, then we create an edge V->C - for _, op := range c.Operands() { - g.AddEdge(op, c, false) - } - if c, ok := c.(Future); ok { - for _, op := range c.Futures() { - g.AddEdge(op, c, true) - } - } - // If constraint C defines variable V, then we create an edge - // C->V - g.AddEdge(c, c.Y(), false) - } - - g.FindSCCs() - g.sccEdges = make([][]Edge, len(g.SCCs)) - g.futures = make([][]Future, len(g.SCCs)) - for _, e := range g.Edges { - g.sccEdges[e.From.SCC] = append(g.sccEdges[e.From.SCC], e) - if !e.control { - continue - } - if c, ok := e.To.Value.(Future); ok { - g.futures[e.From.SCC] = append(g.futures[e.From.SCC], c) - } - } - return g -} - -func (g *Graph) Solve() Ranges { - var consts []Z - off := NewZ(1) - for _, n := range g.Vertices { - if c, ok := n.Value.(*ssa.Const); ok { - basic, ok := c.Type().Underlying().(*types.Basic) - if !ok { - continue - } - if (basic.Info() & types.IsInteger) != 0 { - z := ConstantToZ(c.Value) - consts = append(consts, z) - consts = append(consts, z.Add(off)) - consts = append(consts, z.Sub(off)) - } - } - - } - sort.Sort(Zs(consts)) - - for scc, vertices := range g.SCCs { - n := 0 - n = len(vertices) - if n == 1 { - g.resolveFutures(scc) - v := vertices[0] - if v, ok := v.Value.(ssa.Value); ok { - switch typ := v.Type().Underlying().(type) { - case *types.Basic: - switch typ.Kind() { - case types.String, types.UntypedString: - if !g.Range(v).(StringInterval).IsKnown() { - g.SetRange(v, StringInterval{NewIntInterval(NewZ(0), PInfinity)}) - } - default: - if !g.Range(v).(IntInterval).IsKnown() { - g.SetRange(v, InfinityFor(v)) - } - } - case *types.Chan: - if !g.Range(v).(ChannelInterval).IsKnown() { - g.SetRange(v, ChannelInterval{NewIntInterval(NewZ(0), PInfinity)}) - } - case *types.Slice: - if !g.Range(v).(SliceInterval).IsKnown() { - g.SetRange(v, SliceInterval{NewIntInterval(NewZ(0), PInfinity)}) - } - } - } - if c, ok := v.Value.(Constraint); ok { - g.SetRange(c.Y(), c.Eval(g)) - } - } else { - uses := g.uses(scc) - entries := g.entries(scc) - for len(entries) > 0 { - v := entries[len(entries)-1] - entries = entries[:len(entries)-1] - for _, use := range uses[v] { - if g.widen(use, consts) { - entries = append(entries, use.Y()) - } - } - } - - g.resolveFutures(scc) - - // XXX this seems to be necessary, but shouldn't be. - // removing it leads to nil pointer derefs; investigate - // where we're not setting values correctly. - for _, n := range vertices { - if v, ok := n.Value.(ssa.Value); ok { - i, ok := g.Range(v).(IntInterval) - if !ok { - continue - } - if !i.IsKnown() { - g.SetRange(v, InfinityFor(v)) - } - } - } - - actives := g.actives(scc) - for len(actives) > 0 { - v := actives[len(actives)-1] - actives = actives[:len(actives)-1] - for _, use := range uses[v] { - if g.narrow(use) { - actives = append(actives, use.Y()) - } - } - } - } - // propagate scc - for _, edge := range g.sccEdges[scc] { - if edge.control { - continue - } - if edge.From.SCC == edge.To.SCC { - continue - } - if c, ok := edge.To.Value.(Constraint); ok { - g.SetRange(c.Y(), c.Eval(g)) - } - if c, ok := edge.To.Value.(Future); ok { - if !c.IsKnown() { - c.MarkUnresolved() - } - } - } - } - - for v, r := range g.ranges { - i, ok := r.(IntInterval) - if !ok { - continue - } - if (v.Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) == 0 { - if i.Upper != PInfinity { - s := &types.StdSizes{ - // XXX is it okay to assume the largest word size, or do we - // need to be platform specific? - WordSize: 8, - MaxAlign: 1, - } - bits := (s.Sizeof(v.Type()) * 8) - 1 - n := big.NewInt(1) - n = n.Lsh(n, uint(bits)) - upper, lower := &big.Int{}, &big.Int{} - upper.Sub(n, big.NewInt(1)) - lower.Neg(n) - - if i.Upper.Cmp(NewBigZ(upper)) == 1 { - i = NewIntInterval(NInfinity, PInfinity) - } else if i.Lower.Cmp(NewBigZ(lower)) == -1 { - i = NewIntInterval(NInfinity, PInfinity) - } - } - } - - g.ranges[v] = i - } - - return g.ranges -} - -func VertexString(v *Vertex) string { - switch v := v.Value.(type) { - case Constraint: - return v.String() - case ssa.Value: - return v.Name() - case nil: - return "BUG: nil vertex value" - default: - panic(fmt.Sprintf("unexpected type %T", v)) - } -} - -type Vertex struct { - Value interface{} // one of Constraint or ssa.Value - SCC int - index int - lowlink int - stack bool - - Succs []Edge -} - -type Ranges map[ssa.Value]Range - -func (r Ranges) Get(x ssa.Value) Range { - if x == nil { - return nil - } - i, ok := r[x] - if !ok { - switch x := x.Type().Underlying().(type) { - case *types.Basic: - switch x.Kind() { - case types.String, types.UntypedString: - return StringInterval{} - default: - return IntInterval{} - } - case *types.Chan: - return ChannelInterval{} - case *types.Slice: - return SliceInterval{} - } - } - return i -} - -type Graph struct { - Vertices map[interface{}]*Vertex - Edges []Edge - SCCs [][]*Vertex - ranges Ranges - - // map SCCs to futures - futures [][]Future - // map SCCs to edges - sccEdges [][]Edge -} - -func (g Graph) Graphviz() string { - var lines []string - lines = append(lines, "digraph{") - ids := map[interface{}]int{} - i := 1 - for _, v := range g.Vertices { - ids[v] = i - shape := "box" - if _, ok := v.Value.(ssa.Value); ok { - shape = "oval" - } - lines = append(lines, fmt.Sprintf(`n%d [shape="%s", label=%q, colorscheme=spectral11, style="filled", fillcolor="%d"]`, - i, shape, VertexString(v), (v.SCC%11)+1)) - i++ - } - for _, e := range g.Edges { - style := "solid" - if e.control { - style = "dashed" - } - lines = append(lines, fmt.Sprintf(`n%d -> n%d [style="%s"]`, ids[e.From], ids[e.To], style)) - } - lines = append(lines, "}") - return strings.Join(lines, "\n") -} - -func (g *Graph) SetRange(x ssa.Value, r Range) { - g.ranges[x] = r -} - -func (g *Graph) Range(x ssa.Value) Range { - return g.ranges.Get(x) -} - -func (g *Graph) widen(c Constraint, consts []Z) bool { - setRange := func(i Range) { - g.SetRange(c.Y(), i) - } - widenIntInterval := func(oi, ni IntInterval) (IntInterval, bool) { - if !ni.IsKnown() { - return oi, false - } - nlc := NInfinity - nuc := PInfinity - for _, co := range consts { - if co.Cmp(ni.Lower) <= 0 { - nlc = co - break - } - } - for _, co := range consts { - if co.Cmp(ni.Upper) >= 0 { - nuc = co - break - } - } - - if !oi.IsKnown() { - return ni, true - } - if ni.Lower.Cmp(oi.Lower) == -1 && ni.Upper.Cmp(oi.Upper) == 1 { - return NewIntInterval(nlc, nuc), true - } - if ni.Lower.Cmp(oi.Lower) == -1 { - return NewIntInterval(nlc, oi.Upper), true - } - if ni.Upper.Cmp(oi.Upper) == 1 { - return NewIntInterval(oi.Lower, nuc), true - } - return oi, false - } - switch oi := g.Range(c.Y()).(type) { - case IntInterval: - ni := c.Eval(g).(IntInterval) - si, changed := widenIntInterval(oi, ni) - if changed { - setRange(si) - return true - } - return false - case StringInterval: - ni := c.Eval(g).(StringInterval) - si, changed := widenIntInterval(oi.Length, ni.Length) - if changed { - setRange(StringInterval{si}) - return true - } - return false - case SliceInterval: - ni := c.Eval(g).(SliceInterval) - si, changed := widenIntInterval(oi.Length, ni.Length) - if changed { - setRange(SliceInterval{si}) - return true - } - return false - default: - return false - } -} - -func (g *Graph) narrow(c Constraint) bool { - narrowIntInterval := func(oi, ni IntInterval) (IntInterval, bool) { - oLower := oi.Lower - oUpper := oi.Upper - nLower := ni.Lower - nUpper := ni.Upper - - if oLower == NInfinity && nLower != NInfinity { - return NewIntInterval(nLower, oUpper), true - } - if oUpper == PInfinity && nUpper != PInfinity { - return NewIntInterval(oLower, nUpper), true - } - if oLower.Cmp(nLower) == 1 { - return NewIntInterval(nLower, oUpper), true - } - if oUpper.Cmp(nUpper) == -1 { - return NewIntInterval(oLower, nUpper), true - } - return oi, false - } - switch oi := g.Range(c.Y()).(type) { - case IntInterval: - ni := c.Eval(g).(IntInterval) - si, changed := narrowIntInterval(oi, ni) - if changed { - g.SetRange(c.Y(), si) - return true - } - return false - case StringInterval: - ni := c.Eval(g).(StringInterval) - si, changed := narrowIntInterval(oi.Length, ni.Length) - if changed { - g.SetRange(c.Y(), StringInterval{si}) - return true - } - return false - case SliceInterval: - ni := c.Eval(g).(SliceInterval) - si, changed := narrowIntInterval(oi.Length, ni.Length) - if changed { - g.SetRange(c.Y(), SliceInterval{si}) - return true - } - return false - default: - return false - } -} - -func (g *Graph) resolveFutures(scc int) { - for _, c := range g.futures[scc] { - c.Resolve() - } -} - -func (g *Graph) entries(scc int) []ssa.Value { - var entries []ssa.Value - for _, n := range g.Vertices { - if n.SCC != scc { - continue - } - if v, ok := n.Value.(ssa.Value); ok { - // XXX avoid quadratic runtime - // - // XXX I cannot think of any code where the future and its - // variables aren't in the same SCC, in which case this - // code isn't very useful (the variables won't be resolved - // yet). Before we have a cross-SCC example, however, we - // can't really verify that this code is working - // correctly, or indeed doing anything useful. - for _, on := range g.Vertices { - if c, ok := on.Value.(Future); ok { - if c.Y() == v { - if !c.IsResolved() { - g.SetRange(c.Y(), c.Eval(g)) - c.MarkResolved() - } - break - } - } - } - if g.Range(v).IsKnown() { - entries = append(entries, v) - } - } - } - return entries -} - -func (g *Graph) uses(scc int) map[ssa.Value][]Constraint { - m := map[ssa.Value][]Constraint{} - for _, e := range g.sccEdges[scc] { - if e.control { - continue - } - if v, ok := e.From.Value.(ssa.Value); ok { - c := e.To.Value.(Constraint) - sink := c.Y() - if g.Vertices[sink].SCC == scc { - m[v] = append(m[v], c) - } - } - } - return m -} - -func (g *Graph) actives(scc int) []ssa.Value { - var actives []ssa.Value - for _, n := range g.Vertices { - if n.SCC != scc { - continue - } - if v, ok := n.Value.(ssa.Value); ok { - if _, ok := v.(*ssa.Const); !ok { - actives = append(actives, v) - } - } - } - return actives -} - -func (g *Graph) AddEdge(from, to interface{}, ctrl bool) { - vf, ok := g.Vertices[from] - if !ok { - vf = &Vertex{Value: from} - g.Vertices[from] = vf - } - vt, ok := g.Vertices[to] - if !ok { - vt = &Vertex{Value: to} - g.Vertices[to] = vt - } - e := Edge{From: vf, To: vt, control: ctrl} - g.Edges = append(g.Edges, e) - vf.Succs = append(vf.Succs, e) -} - -type Edge struct { - From, To *Vertex - control bool -} - -func (e Edge) String() string { - return fmt.Sprintf("%s -> %s", VertexString(e.From), VertexString(e.To)) -} - -func (g *Graph) FindSCCs() { - // use Tarjan to find the SCCs - - index := 1 - var s []*Vertex - - scc := 0 - var strongconnect func(v *Vertex) - strongconnect = func(v *Vertex) { - // set the depth index for v to the smallest unused index - v.index = index - v.lowlink = index - index++ - s = append(s, v) - v.stack = true - - for _, e := range v.Succs { - w := e.To - if w.index == 0 { - // successor w has not yet been visited; recurse on it - strongconnect(w) - if w.lowlink < v.lowlink { - v.lowlink = w.lowlink - } - } else if w.stack { - // successor w is in stack s and hence in the current scc - if w.index < v.lowlink { - v.lowlink = w.index - } - } - } - - if v.lowlink == v.index { - for { - w := s[len(s)-1] - s = s[:len(s)-1] - w.stack = false - w.SCC = scc - if w == v { - break - } - } - scc++ - } - } - for _, v := range g.Vertices { - if v.index == 0 { - strongconnect(v) - } - } - - g.SCCs = make([][]*Vertex, scc) - for _, n := range g.Vertices { - n.SCC = scc - n.SCC - 1 - g.SCCs[n.SCC] = append(g.SCCs[n.SCC], n) - } -} - -func invertToken(tok token.Token) token.Token { - switch tok { - case token.LSS: - return token.GEQ - case token.GTR: - return token.LEQ - case token.EQL: - return token.NEQ - case token.NEQ: - return token.EQL - case token.GEQ: - return token.LSS - case token.LEQ: - return token.GTR - default: - panic(fmt.Sprintf("unsupported token %s", tok)) - } -} - -func flipToken(tok token.Token) token.Token { - switch tok { - case token.LSS: - return token.GTR - case token.GTR: - return token.LSS - case token.EQL: - return token.EQL - case token.NEQ: - return token.NEQ - case token.GEQ: - return token.LEQ - case token.LEQ: - return token.GEQ - default: - panic(fmt.Sprintf("unsupported token %s", tok)) - } -} - -type CopyConstraint struct { - aConstraint - X ssa.Value -} - -func (c *CopyConstraint) String() string { - return fmt.Sprintf("%s = copy(%s)", c.Y().Name(), c.X.Name()) -} - -func (c *CopyConstraint) Eval(g *Graph) Range { - return g.Range(c.X) -} - -func (c *CopyConstraint) Operands() []ssa.Value { - return []ssa.Value{c.X} -} - -func NewCopyConstraint(x, y ssa.Value) Constraint { - return &CopyConstraint{ - aConstraint: aConstraint{ - y: y, - }, - X: x, - } -} |