]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: use reflection in ir.Dump
authorRuss Cox <rsc@golang.org>
Sun, 6 Dec 2020 20:17:05 +0000 (15:17 -0500)
committerRuss Cox <rsc@golang.org>
Wed, 9 Dec 2020 17:05:13 +0000 (17:05 +0000)
ir.Dump is the final (I think!) piece of the compiler that was walking
nodes using Left, Right etc without knowing what they meant.
This CL uses reflection to walk nodes without knowing what they mean instead.
One benefit is that we can print actual meanings (field names).

While we are here, I could not resist fixing a long-standing mental TODO:
make the line number more clearly a line number. I've forgotten where the
line number is in the dumps far too many times in the last decade.

As a small example, here is a fragment of go tool compile -W test/235.go:

.   FOR l(28) tc(1)
.   .   LT-init
.   .   .   AS l(28) tc(1)
.   .   .   .   NAME-main..autotmp_4 l(28) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   .   .   LEN l(28) tc(1) int
.   .   .   .   .   NAME-main.xs g(2) l(26) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64
.   .   LT l(28) tc(1) hascall bool
.   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int
.   .   .   NAME-main..autotmp_4 l(28) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   BLOCK l(28)
.   .   BLOCK-list
.   .   .   ASOP-ADD l(28) tc(1) implicit(true) int
.   .   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int
.   .   .   .   LITERAL-1 l(28) tc(1) int
.   FOR-body
.   .   VARKILL l(28) tc(1)
.   .   .   NAME-main..autotmp_4 l(28) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   IF l(29) tc(1)
.   .   .   LT l(29) tc(1) bool
.   .   .   .   INDEX l(29) tc(1) uint64
.   .   .   .   .   NAME-main.xs g(2) l(26) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64
.   .   .   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int
.   .   .   .   NAME-main.m g(3) l(27) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64
.   .   IF-body
.   .   .   AS l(30) tc(1)
.   .   .   .   NAME-main.m g(3) l(27) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64
.   .   .   .   INDEX l(30) tc(1) uint64
.   .   .   .   .   NAME-main.xs g(2) l(26) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64
.   .   .   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int

and here it is after this CL:

.   FOR tc(1) # 235.go:28
.   FOR-Cond
.   .   LT-init
.   .   .   AS tc(1) # 235.go:28
.   .   .   .   NAME-main..autotmp_4 x(0) class(PAUTO) esc(N) tc(1) assigned used int # 235.go:28
.   .   .   .   LEN tc(1) int # 235.go:28 int
.   .   .   .   .   NAME-main.xs g(2) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64 # 235.go:26
.   .   LT tc(1) hascall bool # 235.go:28 bool
.   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28
.   .   .   NAME-main..autotmp_4 x(0) class(PAUTO) esc(N) tc(1) assigned used int # 235.go:28
.   FOR-Post
.   .   BLOCK # 235.go:28
.   .   BLOCK-List
.   .   .   ASOP-ADD tc(1) implicit(true) int # 235.go:28 int
.   .   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28
.   .   .   .   LITERAL-1 tc(1) int # 235.go:28
.   FOR-Body
.   .   VARKILL tc(1) # 235.go:28
.   .   .   NAME-main..autotmp_4 x(0) class(PAUTO) esc(N) tc(1) assigned used int # 235.go:28
.   .   IF tc(1) # 235.go:29
.   .   IF-Cond
.   .   .   LT tc(1) bool # 235.go:29 bool
.   .   .   .   INDEX tc(1) uint64 # 235.go:29 uint64
.   .   .   .   .   NAME-main.xs g(2) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64 # 235.go:26
.   .   .   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28
.   .   .   .   NAME-main.m g(3) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64 # 235.go:27
.   .   IF-Body
.   .   .   AS tc(1) # 235.go:30
.   .   .   .   NAME-main.m g(3) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64 # 235.go:27
.   .   .   .   INDEX tc(1) uint64 # 235.go:30 uint64
.   .   .   .   .   NAME-main.xs g(2) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64 # 235.go:26
.   .   .   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28

Note in particular the clear marking of FOR-Cond, FOR-Post, FOR-Body compared to the original.

The only changes to a few test files are the improved field name lines, and of course the line numbers.

Passes buildall w/ toolstash -cmp.

Change-Id: I5b654d9d8ee898976d4c387742ea688a082bac78
Reviewed-on: https://go-review.googlesource.com/c/go/+/275785
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/ir/fmt.go

index 68e425bdaa287f44e7a55a80f10c5aa76c40bdc6..4bea6e2ae0938a1f901f981d6539ac4f6bf04674 100644 (file)
@@ -10,6 +10,9 @@ import (
        "go/constant"
        "io"
        "os"
+       "path/filepath"
+       "reflect"
+       "strings"
 
        "unicode/utf8"
 
@@ -957,17 +960,6 @@ func dumpNodeHeader(w io.Writer, n Node) {
                fmt.Fprintf(w, " defn(%p)", n.Name().Defn)
        }
 
-       if n.Pos().IsKnown() {
-               pfx := ""
-               switch n.Pos().IsStmt() {
-               case src.PosNotStmt:
-                       pfx = "_" // "-" would be confusing
-               case src.PosIsStmt:
-                       pfx = "+"
-               }
-               fmt.Fprintf(w, " l(%s%d)", pfx, n.Pos().Line())
-       }
-
        if n.Offset() != types.BADWIDTH {
                fmt.Fprintf(w, " x(%d)", n.Offset())
        }
@@ -1029,6 +1021,32 @@ func dumpNodeHeader(w io.Writer, n Node) {
        if n.Name() != nil && n.Name().Used() {
                fmt.Fprint(w, " used")
        }
+
+       if n.Op() == OCLOSURE {
+               if fn := n.Func(); fn != nil && fn.Nname.Sym() != nil {
+                       fmt.Fprintf(w, " fnName(%+v)", fn.Nname.Sym())
+               }
+       }
+
+       if n.Type() != nil {
+               if n.Op() == OTYPE {
+                       fmt.Fprintf(w, " type")
+               }
+               fmt.Fprintf(w, " %+v", n.Type())
+       }
+
+       if n.Pos().IsKnown() {
+               pfx := ""
+               switch n.Pos().IsStmt() {
+               case src.PosNotStmt:
+                       pfx = "_" // "-" would be confusing
+               case src.PosIsStmt:
+                       pfx = "+"
+               }
+               pos := base.Ctxt.PosTable.Pos(n.Pos())
+               file := filepath.Base(pos.Filename())
+               fmt.Fprintf(w, " # %s%s:%d", pfx, file, pos.Line())
+       }
 }
 
 func dumpNode(w io.Writer, n Node, depth int) {
@@ -1052,6 +1070,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
        case OLITERAL:
                fmt.Fprintf(w, "%+v-%v", n.Op(), n.Val())
                dumpNodeHeader(w, n)
+               return
 
        case ONAME, ONONAME, OMETHEXPR:
                if n.Sym() != nil {
@@ -1065,6 +1084,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
                        fmt.Fprintf(w, "%+v-ntype", n.Op())
                        dumpNode(w, n.Name().Ntype, depth+1)
                }
+               return
 
        case OASOP:
                fmt.Fprintf(w, "%+v-%+v", n.Op(), n.SubOp())
@@ -1073,61 +1093,86 @@ func dumpNode(w io.Writer, n Node, depth int) {
        case OTYPE:
                fmt.Fprintf(w, "%+v %+v", n.Op(), n.Sym())
                dumpNodeHeader(w, n)
-               fmt.Fprintf(w, " type=%+v", n.Type())
                if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil {
                        indent(w, depth)
                        fmt.Fprintf(w, "%+v-ntype", n.Op())
                        dumpNode(w, n.Name().Ntype, depth+1)
                }
-       }
+               return
 
-       if n.Op() == OCLOSURE && n.Func() != nil && n.Func().Nname.Sym() != nil {
-               fmt.Fprintf(w, " fnName %+v", n.Func().Nname.Sym())
+       case OCLOSURE:
+               fmt.Fprintf(w, "%+v", n.Op())
+               dumpNodeHeader(w, n)
+
+       case ODCLFUNC:
+               // Func has many fields we don't want to print.
+               // Bypass reflection and just print what we want.
+               fmt.Fprintf(w, "%+v", n.Op())
+               dumpNodeHeader(w, n)
+               fn := n.Func()
+               if len(fn.Dcl) > 0 {
+                       indent(w, depth)
+                       fmt.Fprintf(w, "%+v-Dcl", n.Op())
+                       for _, dcl := range n.Func().Dcl {
+                               dumpNode(w, dcl, depth+1)
+                       }
+               }
+               if fn.Body().Len() > 0 {
+                       indent(w, depth)
+                       fmt.Fprintf(w, "%+v-body", n.Op())
+                       dumpNodes(w, n.Body(), depth+1)
+               }
+               return
        }
-       if n.Sym() != nil && n.Op() != ONAME {
+
+       if n.Sym() != nil {
                fmt.Fprintf(w, " %+v", n.Sym())
        }
-
        if n.Type() != nil {
                fmt.Fprintf(w, " %+v", n.Type())
        }
 
-       if n.Left() != nil {
-               dumpNode(w, n.Left(), depth+1)
-       }
-       if n.Right() != nil {
-               dumpNode(w, n.Right(), depth+1)
-       }
-       if n.Op() == OCLOSURE && n.Func() != nil && n.Func().Body().Len() != 0 {
-               indent(w, depth)
-               // The function associated with a closure
-               fmt.Fprintf(w, "%+v-clofunc", n.Op())
-               dumpNode(w, n.Func(), depth+1)
-       }
-       if n.Op() == ODCLFUNC && n.Func() != nil && n.Func().Dcl != nil && len(n.Func().Dcl) != 0 {
-               indent(w, depth)
-               // The dcls for a func or closure
-               fmt.Fprintf(w, "%+v-dcl", n.Op())
-               for _, dcl := range n.Func().Dcl {
-                       dumpNode(w, dcl, depth+1)
+       v := reflect.ValueOf(n).Elem()
+       t := reflect.TypeOf(n).Elem()
+       nf := t.NumField()
+       for i := 0; i < nf; i++ {
+               tf := t.Field(i)
+               vf := v.Field(i)
+               if tf.PkgPath != "" {
+                       // skip unexported field - Interface will fail
+                       continue
+               }
+               switch tf.Type.Kind() {
+               case reflect.Interface, reflect.Ptr, reflect.Slice:
+                       if vf.IsNil() {
+                               continue
+                       }
+               }
+               name := strings.TrimSuffix(tf.Name, "_")
+               // Do not bother with field name header lines for the
+               // most common positional arguments: unary, binary expr,
+               // index expr, send stmt, go and defer call expression.
+               switch name {
+               case "X", "Y", "Index", "Chan", "Value", "Call":
+                       name = ""
+               }
+               switch val := vf.Interface().(type) {
+               case Node:
+                       if name != "" {
+                               indent(w, depth)
+                               fmt.Fprintf(w, "%+v-%s", n.Op(), name)
+                       }
+                       dumpNode(w, val, depth+1)
+               case Nodes:
+                       if val.Len() == 0 {
+                               continue
+                       }
+                       if name != "" {
+                               indent(w, depth)
+                               fmt.Fprintf(w, "%+v-%s", n.Op(), name)
+                       }
+                       dumpNodes(w, val, depth+1)
                }
-       }
-       if n.List().Len() != 0 {
-               indent(w, depth)
-               fmt.Fprintf(w, "%+v-list", n.Op())
-               dumpNodes(w, n.List(), depth+1)
-       }
-
-       if n.Rlist().Len() != 0 {
-               indent(w, depth)
-               fmt.Fprintf(w, "%+v-rlist", n.Op())
-               dumpNodes(w, n.Rlist(), depth+1)
-       }
-
-       if n.Body().Len() != 0 {
-               indent(w, depth)
-               fmt.Fprintf(w, "%+v-body", n.Op())
-               dumpNodes(w, n.Body(), depth+1)
        }
 }