GoPLS Viewer

Home|gopls/go/ssa/sanity.go
1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssa
6
7// An optional pass for sanity-checking invariants of the SSA representation.
8// Currently it checks CFG invariants but little at the instruction level.
9
10import (
11    "fmt"
12    "go/types"
13    "io"
14    "os"
15    "strings"
16)
17
18type sanity struct {
19    reporter io.Writer
20    fn       *Function
21    block    *BasicBlock
22    instrs   map[Instruction]struct{}
23    insane   bool
24}
25
26// sanityCheck performs integrity checking of the SSA representation
27// of the function fn and returns true if it was valid.  Diagnostics
28// are written to reporter if non-nil, os.Stderr otherwise.  Some
29// diagnostics are only warnings and do not imply a negative result.
30//
31// Sanity-checking is intended to facilitate the debugging of code
32// transformation passes.
33func sanityCheck(fn *Functionreporter io.Writerbool {
34    if reporter == nil {
35        reporter = os.Stderr
36    }
37    return (&sanity{reporterreporter}).checkFunction(fn)
38}
39
40// mustSanityCheck is like sanityCheck but panics instead of returning
41// a negative result.
42func mustSanityCheck(fn *Functionreporter io.Writer) {
43    if !sanityCheck(fnreporter) {
44        fn.WriteTo(os.Stderr)
45        panic("SanityCheck failed")
46    }
47}
48
49func (s *sanitydiagnostic(prefixformat stringargs ...interface{}) {
50    fmt.Fprintf(s.reporter"%s: function %s"prefixs.fn)
51    if s.block != nil {
52        fmt.Fprintf(s.reporter", block %s"s.block)
53    }
54    io.WriteString(s.reporter": ")
55    fmt.Fprintf(s.reporterformatargs...)
56    io.WriteString(s.reporter"\n")
57}
58
59func (s *sanityerrorf(format stringargs ...interface{}) {
60    s.insane = true
61    s.diagnostic("Error"formatargs...)
62}
63
64func (s *sanitywarnf(format stringargs ...interface{}) {
65    s.diagnostic("Warning"formatargs...)
66}
67
68// findDuplicate returns an arbitrary basic block that appeared more
69// than once in blocks, or nil if all were unique.
70func findDuplicate(blocks []*BasicBlock) *BasicBlock {
71    if len(blocks) < 2 {
72        return nil
73    }
74    if blocks[0] == blocks[1] {
75        return blocks[0]
76    }
77    // Slow path:
78    m := make(map[*BasicBlock]bool)
79    for _b := range blocks {
80        if m[b] {
81            return b
82        }
83        m[b] = true
84    }
85    return nil
86}
87
88func (s *sanitycheckInstr(idx intinstr Instruction) {
89    switch instr := instr.(type) {
90    case *If, *Jump, *Return, *Panic:
91        s.errorf("control flow instruction not at end of block")
92    case *Phi:
93        if idx == 0 {
94            // It suffices to apply this check to just the first phi node.
95            if dup := findDuplicate(s.block.Preds); dup != nil {
96                s.errorf("phi node in block with duplicate predecessor %s"dup)
97            }
98        } else {
99            prev := s.block.Instrs[idx-1]
100            if _ok := prev.(*Phi); !ok {
101                s.errorf("Phi instruction follows a non-Phi: %T"prev)
102            }
103        }
104        if nenp := len(instr.Edges), len(s.block.Preds); ne != np {
105            s.errorf("phi node has %d edges but %d predecessors"nenp)
106
107        } else {
108            for ie := range instr.Edges {
109                if e == nil {
110                    s.errorf("phi node '%s' has no value for edge #%d from %s"instr.Commentis.block.Preds[i])
111                }
112            }
113        }
114
115    case *Alloc:
116        if !instr.Heap {
117            found := false
118            for _l := range s.fn.Locals {
119                if l == instr {
120                    found = true
121                    break
122                }
123            }
124            if !found {
125                s.errorf("local alloc %s = %s does not appear in Function.Locals"instr.Name(), instr)
126            }
127        }
128
129    case *BinOp:
130    case *Call:
131    case *ChangeInterface:
132    case *ChangeType:
133    case *SliceToArrayPointer:
134    case *Convert:
135        if from := instr.X.Type(); !isBasicConvTypes(typeSetOf(from)) {
136            if to := instr.Type(); !isBasicConvTypes(typeSetOf(to)) {
137                s.errorf("convert %s -> %s: at least one type must be basic (or all basic, []byte, or []rune)"fromto)
138            }
139        }
140
141    case *Defer:
142    case *Extract:
143    case *Field:
144    case *FieldAddr:
145    case *Go:
146    case *Index:
147    case *IndexAddr:
148    case *Lookup:
149    case *MakeChan:
150    case *MakeClosure:
151        numFree := len(instr.Fn.(*Function).FreeVars)
152        numBind := len(instr.Bindings)
153        if numFree != numBind {
154            s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
155                numBindinstr.FnnumFree)
156
157        }
158        if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
159            s.errorf("MakeClosure's type includes receiver %s"recv.Type())
160        }
161
162    case *MakeInterface:
163    case *MakeMap:
164    case *MakeSlice:
165    case *MapUpdate:
166    case *Next:
167    case *Range:
168    case *RunDefers:
169    case *Select:
170    case *Send:
171    case *Slice:
172    case *Store:
173    case *TypeAssert:
174    case *UnOp:
175    case *DebugRef:
176        // TODO(adonovan): implement checks.
177    default:
178        panic(fmt.Sprintf("Unknown instruction type: %T"instr))
179    }
180
181    if callok := instr.(CallInstruction); ok {
182        if call.Common().Signature() == nil {
183            s.errorf("nil signature: %s"call)
184        }
185    }
186
187    // Check that value-defining instructions have valid types
188    // and a valid referrer list.
189    if vok := instr.(Value); ok {
190        t := v.Type()
191        if t == nil {
192            s.errorf("no type: %s = %s"v.Name(), v)
193        } else if t == tRangeIter {
194            // not a proper type; ignore.
195        } else if bok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
196            s.errorf("instruction has 'untyped' result: %s = %s : %s"v.Name(), vt)
197        }
198        s.checkReferrerList(v)
199    }
200
201    // Untyped constants are legal as instruction Operands(),
202    // for example:
203    //   _ = "foo"[0]
204    // or:
205    //   if wordsize==64 {...}
206
207    // All other non-Instruction Values can be found via their
208    // enclosing Function or Package.
209}
210
211func (s *sanitycheckFinalInstr(instr Instruction) {
212    switch instr := instr.(type) {
213    case *If:
214        if nsuccs := len(s.block.Succs); nsuccs != 2 {
215            s.errorf("If-terminated block has %d successors; expected 2"nsuccs)
216            return
217        }
218        if s.block.Succs[0] == s.block.Succs[1] {
219            s.errorf("If-instruction has same True, False target blocks: %s"s.block.Succs[0])
220            return
221        }
222
223    case *Jump:
224        if nsuccs := len(s.block.Succs); nsuccs != 1 {
225            s.errorf("Jump-terminated block has %d successors; expected 1"nsuccs)
226            return
227        }
228
229    case *Return:
230        if nsuccs := len(s.block.Succs); nsuccs != 0 {
231            s.errorf("Return-terminated block has %d successors; expected none"nsuccs)
232            return
233        }
234        if nanf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
235            s.errorf("%d-ary return in %d-ary function"nanf)
236        }
237
238    case *Panic:
239        if nsuccs := len(s.block.Succs); nsuccs != 0 {
240            s.errorf("Panic-terminated block has %d successors; expected none"nsuccs)
241            return
242        }
243
244    default:
245        s.errorf("non-control flow instruction at end of block")
246    }
247}
248
249func (s *sanitycheckBlock(b *BasicBlockindex int) {
250    s.block = b
251
252    if b.Index != index {
253        s.errorf("block has incorrect Index %d"b.Index)
254    }
255    if b.parent != s.fn {
256        s.errorf("block has incorrect parent %s"b.parent)
257    }
258
259    // Check all blocks are reachable.
260    // (The entry block is always implicitly reachable,
261    // as is the Recover block, if any.)
262    if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
263        s.warnf("unreachable block")
264        if b.Instrs == nil {
265            // Since this block is about to be pruned,
266            // tolerating transient problems in it
267            // simplifies other optimizations.
268            return
269        }
270    }
271
272    // Check predecessor and successor relations are dual,
273    // and that all blocks in CFG belong to same function.
274    for _a := range b.Preds {
275        found := false
276        for _bb := range a.Succs {
277            if bb == b {
278                found = true
279                break
280            }
281        }
282        if !found {
283            s.errorf("expected successor edge in predecessor %s; found only: %s"aa.Succs)
284        }
285        if a.parent != s.fn {
286            s.errorf("predecessor %s belongs to different function %s"aa.parent)
287        }
288    }
289    for _c := range b.Succs {
290        found := false
291        for _bb := range c.Preds {
292            if bb == b {
293                found = true
294                break
295            }
296        }
297        if !found {
298            s.errorf("expected predecessor edge in successor %s; found only: %s"cc.Preds)
299        }
300        if c.parent != s.fn {
301            s.errorf("successor %s belongs to different function %s"cc.parent)
302        }
303    }
304
305    // Check each instruction is sane.
306    n := len(b.Instrs)
307    if n == 0 {
308        s.errorf("basic block contains no instructions")
309    }
310    var rands [10]*Value // reuse storage
311    for jinstr := range b.Instrs {
312        if instr == nil {
313            s.errorf("nil instruction at index %d"j)
314            continue
315        }
316        if b2 := instr.Block(); b2 == nil {
317            s.errorf("nil Block() for instruction at index %d"j)
318            continue
319        } else if b2 != b {
320            s.errorf("wrong Block() (%s) for instruction at index %d "b2j)
321            continue
322        }
323        if j < n-1 {
324            s.checkInstr(jinstr)
325        } else {
326            s.checkFinalInstr(instr)
327        }
328
329        // Check Instruction.Operands.
330    operands:
331        for iop := range instr.Operands(rands[:0]) {
332            if op == nil {
333                s.errorf("nil operand pointer %d of %s"iinstr)
334                continue
335            }
336            val := *op
337            if val == nil {
338                continue // a nil operand is ok
339            }
340
341            // Check that "untyped" types only appear on constant operands.
342            if _ok := (*op).(*Const); !ok {
343                if basicok := (*op).Type().(*types.Basic); ok {
344                    if basic.Info()&types.IsUntyped != 0 {
345                        s.errorf("operand #%d of %s is untyped: %s"iinstrbasic)
346                    }
347                }
348            }
349
350            // Check that Operands that are also Instructions belong to same function.
351            // TODO(adonovan): also check their block dominates block b.
352            if valok := val.(Instruction); ok {
353                if val.Block() == nil {
354                    s.errorf("operand %d of %s is an instruction (%s) that belongs to no block"iinstrval)
355                } else if val.Parent() != s.fn {
356                    s.errorf("operand %d of %s is an instruction (%s) from function %s"iinstrvalval.Parent())
357                }
358            }
359
360            // Check that each function-local operand of
361            // instr refers back to instr.  (NB: quadratic)
362            switch val := val.(type) {
363            case *Const, *Global, *Builtin:
364                continue // not local
365            case *Function:
366                if val.parent == nil {
367                    continue // only anon functions are local
368                }
369            }
370
371            // TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
372
373            if refs := val.Referrers(); refs != nil {
374                for _ref := range *refs {
375                    if ref == instr {
376                        continue operands
377                    }
378                }
379                s.errorf("operand %d of %s (%s) does not refer to us"iinstrval)
380            } else {
381                s.errorf("operand %d of %s (%s) has no referrers"iinstrval)
382            }
383        }
384    }
385}
386
387func (s *sanitycheckReferrerList(v Value) {
388    refs := v.Referrers()
389    if refs == nil {
390        s.errorf("%s has missing referrer list"v.Name())
391        return
392    }
393    for iref := range *refs {
394        if _ok := s.instrs[ref]; !ok {
395            s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function"v.Name(), iref)
396        }
397    }
398}
399
400func (s *sanitycheckFunction(fn *Functionbool {
401    // TODO(adonovan): check Function invariants:
402    // - check params match signature
403    // - check transient fields are nil
404    // - warn if any fn.Locals do not appear among block instructions.
405
406    // TODO(taking): Sanity check origin, typeparams, and typeargs.
407    s.fn = fn
408    if fn.Prog == nil {
409        s.errorf("nil Prog")
410    }
411
412    _ = fn.String()               // must not crash
413    _ = fn.RelString(fn.relPkg()) // must not crash
414
415    // All functions have a package, except delegates (which are
416    // shared across packages, or duplicated as weak symbols in a
417    // separate-compilation model), and error.Error.
418    if fn.Pkg == nil {
419        if strings.HasPrefix(fn.Synthetic"wrapper ") ||
420            strings.HasPrefix(fn.Synthetic"bound ") ||
421            strings.HasPrefix(fn.Synthetic"thunk ") ||
422            strings.HasSuffix(fn.name"Error") ||
423            strings.HasPrefix(fn.Synthetic"instance ") ||
424            strings.HasPrefix(fn.Synthetic"instantiation ") ||
425            (fn.parent != nil && len(fn.typeargs) > 0/* anon fun in instance */ {
426            // ok
427        } else {
428            s.errorf("nil Pkg")
429        }
430    }
431    if srcsyn := fn.Synthetic == ""fn.Syntax() != nilsrc != syn {
432        if len(fn.typeargs) > 0 && fn.Prog.mode&InstantiateGenerics != 0 {
433            // ok (instantiation with InstantiateGenerics on)
434        } else if fn.topLevelOrigin != nil && len(fn.typeargs) > 0 {
435            // ok (we always have the syntax set for instantiation)
436        } else {
437            s.errorf("got fromSource=%t, hasSyntax=%t; want same values"srcsyn)
438        }
439    }
440    for il := range fn.Locals {
441        if l.Parent() != fn {
442            s.errorf("Local %s at index %d has wrong parent"l.Name(), i)
443        }
444        if l.Heap {
445            s.errorf("Local %s at index %d has Heap flag set"l.Name(), i)
446        }
447    }
448    // Build the set of valid referrers.
449    s.instrs = make(map[Instruction]struct{})
450    for _b := range fn.Blocks {
451        for _instr := range b.Instrs {
452            s.instrs[instr] = struct{}{}
453        }
454    }
455    for ip := range fn.Params {
456        if p.Parent() != fn {
457            s.errorf("Param %s at index %d has wrong parent"p.Name(), i)
458        }
459        // Check common suffix of Signature and Params match type.
460        if sig := fn.Signaturesig != nil {
461            j := i - len(fn.Params) + sig.Params().Len() // index within sig.Params
462            if j < 0 {
463                continue
464            }
465            if !types.Identical(p.Type(), sig.Params().At(j).Type()) {
466                s.errorf("Param %s at index %d has wrong type (%s, versus %s in Signature)"p.Name(), ip.Type(), sig.Params().At(j).Type())
467
468            }
469        }
470        s.checkReferrerList(p)
471    }
472    for ifv := range fn.FreeVars {
473        if fv.Parent() != fn {
474            s.errorf("FreeVar %s at index %d has wrong parent"fv.Name(), i)
475        }
476        s.checkReferrerList(fv)
477    }
478
479    if fn.Blocks != nil && len(fn.Blocks) == 0 {
480        // Function _had_ blocks (so it's not external) but
481        // they were "optimized" away, even the entry block.
482        s.errorf("Blocks slice is non-nil but empty")
483    }
484    for ib := range fn.Blocks {
485        if b == nil {
486            s.warnf("nil *BasicBlock at f.Blocks[%d]"i)
487            continue
488        }
489        s.checkBlock(bi)
490    }
491    if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
492        s.errorf("Recover block is not in Blocks slice")
493    }
494
495    s.block = nil
496    for ianon := range fn.AnonFuncs {
497        if anon.Parent() != fn {
498            s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s"ianonanonanon.Parent())
499        }
500        if i != int(anon.anonIdx) {
501            s.errorf("AnonFuncs[%d]=%s but %s.anonIdx=%d"ianonanonanon.anonIdx)
502        }
503    }
504    s.fn = nil
505    return !s.insane
506}
507
508// sanityCheckPackage checks invariants of packages upon creation.
509// It does not require that the package is built.
510// Unlike sanityCheck (for functions), it just panics at the first error.
511func sanityCheckPackage(pkg *Package) {
512    if pkg.Pkg == nil {
513        panic(fmt.Sprintf("Package %s has no Object"pkg))
514    }
515    _ = pkg.String() // must not crash
516
517    for namemem := range pkg.Members {
518        if name != mem.Name() {
519            panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
520                pkg.Pkg.Path(), memmem.Name(), name))
521        }
522        obj := mem.Object()
523        if obj == nil {
524            // This check is sound because fields
525            // {Global,Function}.object have type
526            // types.Object.  (If they were declared as
527            // *types.{Var,Func}, we'd have a non-empty
528            // interface containing a nil pointer.)
529
530            continue // not all members have typechecker objects
531        }
532        if obj.Name() != name {
533            if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
534                // Ok.  The name of a declared init function varies between
535                // its types.Func ("init") and its ssa.Function ("init#%d").
536            } else {
537                panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
538                    pkg.Pkg.Path(), memobj.Name(), name))
539            }
540        }
541        if obj.Pos() != mem.Pos() {
542            panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d"memmem.Pos(), obj.Pos()))
543        }
544    }
545}
546
MembersX
sanity.diagnostic.args
sanity.warnf
sanity.checkBlock.RangeStmt_8044.BlockStmt.b2
sanity.checkBlock.RangeStmt_8044.BlockStmt.RangeStmt_8506.op
sanityCheckPackage
findDuplicate.m
sanity.checkInstr.BlockStmt.BlockStmt.found
sanity.checkFunction.RangeStmt_12265.BlockStmt.RangeStmt_12297.instr
sanity.checkFinalInstr.s
sanity.checkBlock.RangeStmt_7544.BlockStmt.found
sanity.checkReferrerList
sanity.checkReferrerList.v
sanity.checkReferrerList.refs
sanity.diagnostic.prefix
findDuplicate
sanity.checkInstr.s
sanity.checkBlock.RangeStmt_8044.BlockStmt.RangeStmt_8506.i
sanity.checkReferrerList.RangeStmt_10305.i
sanityCheckPackage.pkg
sanity.insane
sanity.warnf.args
sanity.checkBlock.RangeStmt_8044.instr
sanity.checkBlock.RangeStmt_8044.BlockStmt.RangeStmt_8506.BlockStmt.BlockStmt.RangeStmt_9893.ref
sanity.checkFunction.RangeStmt_12933.fv
sanityCheckPackage.RangeStmt_14220.name
mustSanityCheck.reporter
sanity.errorf.s
sanity.checkInstr
sanity.checkInstr.BlockStmt.recv
sanity.checkBlock.RangeStmt_7205.a
sanity.checkFunction.s
sanity.checkFunction.RangeStmt_13301.i
sanity.checkBlock
sanity.checkBlock.RangeStmt_8044.BlockStmt.RangeStmt_8506.BlockStmt.refs
sanity.checkFunction.RangeStmt_11971.i
sanity.checkFunction.RangeStmt_12370.i
sanity.checkBlock.index
sanity.block
sanityCheck
sanityCheck.reporter
sanity.diagnostic
findDuplicate.RangeStmt_2154.b
sanity.checkInstr.BlockStmt.BlockStmt.RangeStmt_2940.e
sanity.checkInstr.BlockStmt.numBind
sanity.checkFunction.RangeStmt_11971.l
sanity.checkFunction.RangeStmt_12265.b
sanity.instrs
sanity.checkInstr.BlockStmt.from
sanity.checkFinalInstr.BlockStmt.na
sanity.checkFunction.RangeStmt_12370.p
mustSanityCheck.fn
sanity.diagnostic.format
sanity.warnf.format
sanity.checkInstr.idx
sanity.checkFinalInstr.instr
sanity.checkBlock.s
sanity.checkBlock.RangeStmt_8044.j
sanity.checkFunction.RangeStmt_13576.i
sanity.checkBlock.rands
sanity
sanityCheck.fn
mustSanityCheck
sanity.diagnostic.s
sanity.warnf.s
sanity.checkInstr.instr
sanity.checkInstr.BlockStmt.BlockStmt.RangeStmt_3161.l
sanity.checkFunction.fn
sanityCheckPackage.RangeStmt_14220.BlockStmt.obj
sanity.checkFunction.RangeStmt_12370.BlockStmt.sig
sanity.reporter
sanity.errorf.format
sanity.errorf.args
sanity.checkInstr.BlockStmt.BlockStmt.to
sanity.checkFinalInstr.BlockStmt.nsuccs
sanity.checkBlock.RangeStmt_7205.BlockStmt.found
sanity.checkBlock.RangeStmt_7544.BlockStmt.RangeStmt_7591.bb
sanity.checkFunction.RangeStmt_13301.b
sanityCheckPackage.RangeStmt_14220.mem
sanity.errorf
sanity.checkInstr.BlockStmt.t
sanity.checkFinalInstr
sanity.checkBlock.b
sanity.checkBlock.RangeStmt_8044.BlockStmt.RangeStmt_8506.BlockStmt.val
sanity.checkFunction
sanity.checkFunction.RangeStmt_13576.anon
sanity.checkInstr.BlockStmt.ne
sanity.checkInstr.BlockStmt.BlockStmt.RangeStmt_2940.i
sanity.checkFinalInstr.BlockStmt.nf
sanity.checkBlock.RangeStmt_7544.c
sanity.checkBlock.n
sanity.checkReferrerList.RangeStmt_10305.ref
sanity.checkFunction.RangeStmt_12933.i
sanity.fn
findDuplicate.blocks
sanity.checkInstr.BlockStmt.BlockStmt.dup
sanity.checkInstr.BlockStmt.np
sanity.checkInstr.BlockStmt.numFree
sanity.checkBlock.RangeStmt_7205.BlockStmt.RangeStmt_7252.bb
sanity.checkReferrerList.s
Members
X