summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2020-12-06 15:17:05 -0500
committerRuss Cox <rsc@golang.org>2020-12-09 17:05:13 +0000
commit4090af83c57c857de600ada68e7a27dffd37d8b1 (patch)
treeace59009655eeedec2dc31394e767b06621d5db3 /src/cmd
parente2d278bfeb2f0f117efc50b3f0f9dcb086a45ed2 (diff)
downloadgo-git-4090af83c57c857de600ada68e7a27dffd37d8b1.tar.gz
[dev.regabi] cmd/compile: use reflection in ir.Dump
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>
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/compile/internal/ir/fmt.go149
1 files changed, 97 insertions, 52 deletions
diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go
index 68e425bdaa..4bea6e2ae0 100644
--- a/src/cmd/compile/internal/ir/fmt.go
+++ b/src/cmd/compile/internal/ir/fmt.go
@@ -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)
}
}