1
0
mirror of https://github.com/golang/go synced 2024-09-23 17:10:13 -06:00

[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>
This commit is contained in:
Russ Cox 2020-12-06 15:17:05 -05:00
parent e2d278bfeb
commit 4090af83c5

View 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
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.Op() == OCLOSURE && n.Func() != nil && n.Func().Nname.Sym() != nil {
fmt.Fprintf(w, " fnName %+v", n.Func().Nname.Sym())
}
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)
}
}