Commit e25823ed authored by Robert Griesemer's avatar Robert Griesemer

cmd/compile/internal/gc: add tracing support to debug type checking

The compiler must first be built with the constant enableTrace set
to true (typecheck.go). After that, the -t flag becomes available
which enables tracing output of type-checking functions.

With enableTrace == false, the tracing code becomes dead code
and won't affect the compiler.

Typical output might look like this:

path/y.go:4:6: typecheck 0xc00033e180 DCLTYPE <node DCLTYPE> tc=0
path/y.go:4:6: . typecheck1 0xc00033e180 DCLTYPE <node DCLTYPE> tc=2
path/y.go:4:6: . . typecheck 0xc000331a40 TYPE T tc=1
path/y.go:4:6: . . . typecheck1 0xc000331a40 TYPE T tc=2
path/y.go:4:6: . . . . typecheckdef 0xc000331a40 TYPE T tc=2
path/y.go:4:6: . . . . => 0xc000331a40 TYPE T tc=2 type=*T
path/y.go:4:6: . . . => 0xc000331a40 TYPE T tc=2 type=*T
path/y.go:4:6: . . => 0xc000331a40 TYPE T tc=1 type=*T
path/y.go:4:6: . => 0xc00033e180 DCLTYPE <node DCLTYPE> tc=2 type=<T>
path/y.go:4:6: => 0xc00033e180 DCLTYPE <node DCLTYPE> tc=1 type=<T>

Disabled by default.

Change-Id: Ifd8385290d1cf0d3fc5e8468b2f4ab84e8eff338
Reviewed-on: https://go-review.googlesource.com/c/146782Reviewed-by: 's avatarMatthew Dempsky <mdempsky@google.com>
parent c21ba09b
...@@ -229,6 +229,9 @@ func Main(archInit func(*Arch)) { ...@@ -229,6 +229,9 @@ func Main(archInit func(*Arch)) {
flag.BoolVar(&flag_race, "race", false, "enable race detector") flag.BoolVar(&flag_race, "race", false, "enable race detector")
} }
objabi.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) objabi.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s'])
if enableTrace {
flag.BoolVar(&trace, "t", false, "trace type-checking")
}
flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity") flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity")
objabi.Flagcount("w", "debug type checking", &Debug['w']) objabi.Flagcount("w", "debug type checking", &Debug['w'])
......
...@@ -12,6 +12,50 @@ import ( ...@@ -12,6 +12,50 @@ import (
"strings" "strings"
) )
// To enable tracing support (-t flag), set enableTrace to true.
const enableTrace = false
var trace bool
var traceIndent []byte
func tracePrint(title string, n *Node) func(np **Node) {
indent := traceIndent
// guard against nil
var pos, op string
var tc uint8
if n != nil {
pos = linestr(n.Pos)
op = n.Op.String()
tc = n.Typecheck()
}
fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc)
traceIndent = append(traceIndent, ". "...)
return func(np **Node) {
traceIndent = traceIndent[:len(traceIndent)-2]
// if we have a result, use that
if np != nil {
n = *np
}
// guard against nil
// use outer pos, op so we don't get empty pos/op if n == nil (nicer output)
var tc uint8
var typ *types.Type
if n != nil {
pos = linestr(n.Pos)
op = n.Op.String()
tc = n.Typecheck()
typ = n.Type
}
fmt.Printf("%s: %s=> %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ)
}
}
const ( const (
Etop = 1 << iota // evaluated at statement level Etop = 1 << iota // evaluated at statement level
Erv // evaluated in value context Erv // evaluated in value context
...@@ -31,11 +75,16 @@ const ( ...@@ -31,11 +75,16 @@ const (
var typecheckdefstack []*Node var typecheckdefstack []*Node
// resolve ONONAME to definition, if any. // resolve ONONAME to definition, if any.
func resolve(n *Node) *Node { func resolve(n *Node) (res *Node) {
if n == nil || n.Op != ONONAME { if n == nil || n.Op != ONONAME {
return n return n
} }
// only trace if there's work to do
if enableTrace && trace {
defer tracePrint("resolve", n)(&res)
}
if n.Sym.Pkg != localpkg { if n.Sym.Pkg != localpkg {
if inimport { if inimport {
Fatalf("recursive inimport") Fatalf("recursive inimport")
...@@ -150,7 +199,7 @@ var typecheck_tcstack []*Node ...@@ -150,7 +199,7 @@ var typecheck_tcstack []*Node
// typecheck type checks node n. // typecheck type checks node n.
// The result of typecheck MUST be assigned back to n, e.g. // The result of typecheck MUST be assigned back to n, e.g.
// n.Left = typecheck(n.Left, top) // n.Left = typecheck(n.Left, top)
func typecheck(n *Node, top int) *Node { func typecheck(n *Node, top int) (res *Node) {
// cannot type check until all the source has been parsed // cannot type check until all the source has been parsed
if !typecheckok { if !typecheckok {
Fatalf("early typecheck") Fatalf("early typecheck")
...@@ -160,6 +209,11 @@ func typecheck(n *Node, top int) *Node { ...@@ -160,6 +209,11 @@ func typecheck(n *Node, top int) *Node {
return nil return nil
} }
// only trace if there's work to do
if enableTrace && trace {
defer tracePrint("typecheck", n)(&res)
}
lno := setlineno(n) lno := setlineno(n)
// Skip over parens. // Skip over parens.
...@@ -294,7 +348,11 @@ func indexlit(n *Node) *Node { ...@@ -294,7 +348,11 @@ func indexlit(n *Node) *Node {
// The result of typecheck1 MUST be assigned back to n, e.g. // The result of typecheck1 MUST be assigned back to n, e.g.
// n.Left = typecheck1(n.Left, top) // n.Left = typecheck1(n.Left, top)
func typecheck1(n *Node, top int) *Node { func typecheck1(n *Node, top int) (res *Node) {
if enableTrace && trace {
defer tracePrint("typecheck1", n)(&res)
}
switch n.Op { switch n.Op {
case OLITERAL, ONAME, ONONAME, OTYPE: case OLITERAL, ONAME, ONONAME, OTYPE:
if n.Sym == nil { if n.Sym == nil {
...@@ -2381,7 +2439,11 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost ...@@ -2381,7 +2439,11 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost
// typecheckMethodExpr checks selector expressions (ODOT) where the // typecheckMethodExpr checks selector expressions (ODOT) where the
// base expression is a type expression (OTYPE). // base expression is a type expression (OTYPE).
func typecheckMethodExpr(n *Node) *Node { func typecheckMethodExpr(n *Node) (res *Node) {
if enableTrace && trace {
defer tracePrint("typecheckMethodExpr", n)(&res)
}
t := n.Left.Type t := n.Left.Type
// Compute the method set for t. // Compute the method set for t.
...@@ -2924,7 +2986,11 @@ func pushtype(n *Node, t *types.Type) { ...@@ -2924,7 +2986,11 @@ func pushtype(n *Node, t *types.Type) {
// The result of typecheckcomplit MUST be assigned back to n, e.g. // The result of typecheckcomplit MUST be assigned back to n, e.g.
// n.Left = typecheckcomplit(n.Left) // n.Left = typecheckcomplit(n.Left)
func typecheckcomplit(n *Node) *Node { func typecheckcomplit(n *Node) (res *Node) {
if enableTrace && trace {
defer tracePrint("typecheckcomplit", n)(&res)
}
lno := lineno lno := lineno
defer func() { defer func() {
lineno = lno lineno = lno
...@@ -3337,6 +3403,10 @@ func samesafeexpr(l *Node, r *Node) bool { ...@@ -3337,6 +3403,10 @@ func samesafeexpr(l *Node, r *Node) bool {
// if this assignment is the definition of a var on the left side, // if this assignment is the definition of a var on the left side,
// fill in the var's type. // fill in the var's type.
func typecheckas(n *Node) { func typecheckas(n *Node) {
if enableTrace && trace {
defer tracePrint("typecheckas", n)(nil)
}
// delicate little dance. // delicate little dance.
// the definition of n may refer to this assignment // the definition of n may refer to this assignment
// as its definition, in which case it will call typecheckas. // as its definition, in which case it will call typecheckas.
...@@ -3393,6 +3463,10 @@ func checkassignto(src *types.Type, dst *Node) { ...@@ -3393,6 +3463,10 @@ func checkassignto(src *types.Type, dst *Node) {
} }
func typecheckas2(n *Node) { func typecheckas2(n *Node) {
if enableTrace && trace {
defer tracePrint("typecheckas2", n)(nil)
}
ls := n.List.Slice() ls := n.List.Slice()
for i1, n1 := range ls { for i1, n1 := range ls {
// delicate little dance. // delicate little dance.
...@@ -3521,6 +3595,10 @@ out: ...@@ -3521,6 +3595,10 @@ out:
// type check function definition // type check function definition
func typecheckfunc(n *Node) { func typecheckfunc(n *Node) {
if enableTrace && trace {
defer tracePrint("typecheckfunc", n)(nil)
}
for _, ln := range n.Func.Dcl { for _, ln := range n.Func.Dcl {
if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) { if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) {
ln.Name.Decldepth = 1 ln.Name.Decldepth = 1
...@@ -3637,6 +3715,10 @@ func copytype(n *Node, t *types.Type) { ...@@ -3637,6 +3715,10 @@ func copytype(n *Node, t *types.Type) {
} }
func typecheckdeftype(n *Node) { func typecheckdeftype(n *Node) {
if enableTrace && trace {
defer tracePrint("typecheckdeftype", n)(nil)
}
n.Type.Sym = n.Sym n.Type.Sym = n.Sym
n.SetTypecheck(1) n.SetTypecheck(1)
n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype) n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype)
...@@ -3654,6 +3736,10 @@ func typecheckdeftype(n *Node) { ...@@ -3654,6 +3736,10 @@ func typecheckdeftype(n *Node) {
} }
func typecheckdef(n *Node) { func typecheckdef(n *Node) {
if enableTrace && trace {
defer tracePrint("typecheckdef", n)(nil)
}
lno := setlineno(n) lno := setlineno(n)
if n.Op == ONONAME { if n.Op == ONONAME {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment