1
0
mirror of https://github.com/golang/go synced 2024-07-08 20:29:48 +00:00

[dev.regabi] cmd/compile: generalize ir/mknode.go

This CL generalizes ir/mknode.go to get rid of most of almost all of
its special cases for node field types. The only remaining speciale
case now is Field, which doesn't implement Node any more, but perhaps
should.

To help with removing special cases, node fields can now be tagged
with `mknode:"-"` so that mknode ignores them when generating its
helper methods. Further, to simplify skipping all of the orig fields,
a new origNode helper type is added which declares an orig field
marked as `mknode:"-"` and also provides the Orig and SetOrig methods
needed to implement the OrigNode interface.

Passes toolstash -cmp.

Change-Id: Ic68d4f0a9d2ef6e57e9fe87cdc641e5c4859830b
Reviewed-on: https://go-review.googlesource.com/c/go/+/280674
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
Matthew Dempsky 2020-12-29 15:36:48 -08:00
parent 82ab3d1448
commit 499851bac8
9 changed files with 1071 additions and 706 deletions

View File

@ -25,6 +25,14 @@ type OrigNode interface {
SetOrig(Node)
}
// origNode may be embedded into a Node to make it implement OrigNode.
type origNode struct {
orig Node `mknode:"-"`
}
func (n *origNode) Orig() Node { return n.orig }
func (n *origNode) SetOrig(o Node) { n.orig = o }
// Orig returns the “original” node for n.
// If n implements OrigNode, Orig returns n.Orig().
// Otherwise Orig returns n itself.

View File

@ -14,27 +14,6 @@ import (
"go/token"
)
func maybeDo(x Node, err error, do func(Node) error) error {
if x != nil && err == nil {
err = do(x)
}
return err
}
func maybeDoList(x Nodes, err error, do func(Node) error) error {
if err == nil {
err = DoList(x, do)
}
return err
}
func maybeEdit(x Node, edit func(Node) Node) Node {
if x == nil {
return x
}
return edit(x)
}
// An Expr is a Node that can appear as an expression.
type Expr interface {
Node
@ -77,16 +56,6 @@ func (n *miniExpr) Init() Nodes { return n.init }
func (n *miniExpr) PtrInit() *Nodes { return &n.init }
func (n *miniExpr) SetInit(x Nodes) { n.init = x }
func toNtype(x Node) Ntype {
if x == nil {
return nil
}
if _, ok := x.(Ntype); !ok {
Dump("not Ntype", x)
}
return x.(Ntype)
}
// An AddStringExpr is a string concatenation Expr[0] + Exprs[1] + ... + Expr[len(Expr)-1].
type AddStringExpr struct {
miniExpr
@ -189,7 +158,7 @@ const (
// A CallExpr is a function call X(Args).
type CallExpr struct {
miniExpr
orig Node
origNode
X Node
Args Nodes
Rargs Nodes // TODO(rsc): Delete.
@ -210,9 +179,6 @@ func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {
func (*CallExpr) isStmt() {}
func (n *CallExpr) Orig() Node { return n.orig }
func (n *CallExpr) SetOrig(x Node) { n.orig = x }
func (n *CallExpr) SetOp(op Op) {
switch op {
default:
@ -226,7 +192,7 @@ func (n *CallExpr) SetOp(op Op) {
// A ClosureExpr is a function literal expression.
type ClosureExpr struct {
miniExpr
Func *Func
Func *Func `mknode:"-"`
Prealloc *Name
}
@ -254,7 +220,7 @@ func NewClosureRead(typ *types.Type, offset int64) *ClosureReadExpr {
// Before type-checking, the type is Ntype.
type CompLitExpr struct {
miniExpr
orig Node
origNode
Ntype Ntype
List Nodes // initialized values
Prealloc *Name
@ -270,8 +236,6 @@ func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr {
return n
}
func (n *CompLitExpr) Orig() Node { return n.orig }
func (n *CompLitExpr) SetOrig(x Node) { n.orig = x }
func (n *CompLitExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
@ -286,14 +250,15 @@ func (n *CompLitExpr) SetOp(op Op) {
type ConstExpr struct {
miniExpr
val constant.Value
orig Node
origNode
val constant.Value
}
func NewConstExpr(val constant.Value, orig Node) Node {
n := &ConstExpr{orig: orig, val: val}
n := &ConstExpr{val: val}
n.op = OLITERAL
n.pos = orig.Pos()
n.orig = orig
n.SetType(orig.Type())
n.SetTypecheck(orig.Typecheck())
n.SetDiag(orig.Diag())
@ -301,8 +266,6 @@ func NewConstExpr(val constant.Value, orig Node) Node {
}
func (n *ConstExpr) Sym() *types.Sym { return n.orig.Sym() }
func (n *ConstExpr) Orig() Node { return n.orig }
func (n *ConstExpr) SetOrig(orig Node) { panic(n.no("SetOrig")) }
func (n *ConstExpr) Val() constant.Value { return n.val }
// A ConvExpr is a conversion Type(X).
@ -664,9 +627,9 @@ type TypeAssertExpr struct {
// Runtime type information provided by walkDotType.
// Caution: These aren't always populated; see walkDotType.
SrcType *AddrExpr // *runtime._type for X's type
DstType *AddrExpr // *runtime._type for Type
Itab *AddrExpr // *runtime.itab for Type implementing X's type
SrcType *AddrExpr `mknode:"-"` // *runtime._type for X's type
DstType *AddrExpr `mknode:"-"` // *runtime._type for Type
Itab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type
}
func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr {

View File

@ -115,6 +115,10 @@ func NewFunc(pos src.XPos) *Func {
func (f *Func) isStmt() {}
func (n *Func) copy() Node { panic(n.no("copy")) }
func (n *Func) doChildren(do func(Node) error) error { return doNodes(n.Body, do) }
func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
func (f *Func) Type() *types.Type { return f.Nname.Type() }
func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }
func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }

View File

@ -13,11 +13,16 @@ import (
"go/types"
"io/ioutil"
"log"
"reflect"
"sort"
"strings"
"golang.org/x/tools/go/packages"
)
var irPkg *types.Package
var buf bytes.Buffer
func main() {
cfg := &packages.Config{
Mode: packages.NeedSyntax | packages.NeedTypes,
@ -26,44 +31,26 @@ func main() {
if err != nil {
log.Fatal(err)
}
irPkg = pkgs[0].Types
pkg := pkgs[0].Types
scope := pkg.Scope()
lookup := func(name string) *types.Named {
return scope.Lookup(name).(*types.TypeName).Type().(*types.Named)
}
nodeType := lookup("Node")
ptrNameType := types.NewPointer(lookup("Name"))
ntypeType := lookup("Ntype")
nodesType := lookup("Nodes")
slicePtrCaseClauseType := types.NewSlice(types.NewPointer(lookup("CaseClause")))
slicePtrCommClauseType := types.NewSlice(types.NewPointer(lookup("CommClause")))
ptrFieldType := types.NewPointer(lookup("Field"))
slicePtrFieldType := types.NewSlice(ptrFieldType)
ptrIdentType := types.NewPointer(lookup("Ident"))
var buf bytes.Buffer
fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.")
fmt.Fprintln(&buf)
fmt.Fprintln(&buf, "package ir")
fmt.Fprintln(&buf)
fmt.Fprintln(&buf, `import "fmt"`)
scope := irPkg.Scope()
for _, name := range scope.Names() {
if strings.HasPrefix(name, "mini") {
continue
}
obj, ok := scope.Lookup(name).(*types.TypeName)
if !ok {
continue
}
typName := obj.Name()
typ, ok := obj.Type().(*types.Named).Underlying().(*types.Struct)
if !ok {
continue
}
if strings.HasPrefix(typName, "mini") || !hasMiniNode(typ) {
typ := obj.Type().(*types.Named)
if !implementsNode(types.NewPointer(typ)) {
continue
}
@ -71,77 +58,31 @@ func main() {
fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name)
switch name {
case "Name":
fmt.Fprintf(&buf, "func (n *%s) copy() Node {panic(\"%s.copy\")}\n", name, name)
default:
fmt.Fprintf(&buf, "func (n *%s) copy() Node { c := *n\n", name)
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
switch {
case is(nodesType):
fmt.Fprintf(&buf, "c.%s = c.%s.Copy()\n", name, name)
case is(slicePtrCaseClauseType):
fmt.Fprintf(&buf, "c.%s = copyCases(c.%s)\n", name, name)
case is(slicePtrCommClauseType):
fmt.Fprintf(&buf, "c.%s = copyComms(c.%s)\n", name, name)
case is(ptrFieldType):
fmt.Fprintf(&buf, "if c.%s != nil { c.%s = c.%s.copy() }\n", name, name, name)
case is(slicePtrFieldType):
fmt.Fprintf(&buf, "c.%s = copyFields(c.%s)\n", name, name)
}
})
fmt.Fprintf(&buf, "return &c }\n")
case "Name", "Func":
// Too specialized to automate.
continue
}
fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) error) error { var err error\n", name)
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
switch {
case is(ptrIdentType), is(ptrNameType):
fmt.Fprintf(&buf, "if n.%s != nil { err = maybeDo(n.%s, err, do) }\n", name, name)
case is(nodeType), is(ntypeType):
fmt.Fprintf(&buf, "err = maybeDo(n.%s, err, do)\n", name)
case is(nodesType):
fmt.Fprintf(&buf, "err = maybeDoList(n.%s, err, do)\n", name)
case is(slicePtrCaseClauseType):
fmt.Fprintf(&buf, "err = maybeDoCases(n.%s, err, do)\n", name)
case is(slicePtrCommClauseType):
fmt.Fprintf(&buf, "err = maybeDoComms(n.%s, err, do)\n", name)
case is(ptrFieldType):
fmt.Fprintf(&buf, "err = maybeDoField(n.%s, err, do)\n", name)
case is(slicePtrFieldType):
fmt.Fprintf(&buf, "err = maybeDoFields(n.%s, err, do)\n", name)
}
})
fmt.Fprintf(&buf, "return err }\n")
forNodeFields(typ,
"func (n *%[1]s) copy() Node { c := *n\n",
"",
"c.%[1]s = copy%[2]s(c.%[1]s)",
"return &c }\n")
fmt.Fprintf(&buf, "func (n *%s) editChildren(edit func(Node) Node) {\n", name)
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
switch {
case is(ptrIdentType):
fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Ident) }\n", name, name, name)
case is(ptrNameType):
fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Name) }\n", name, name, name)
case is(nodeType):
fmt.Fprintf(&buf, "n.%s = maybeEdit(n.%s, edit)\n", name, name)
case is(ntypeType):
fmt.Fprintf(&buf, "n.%s = toNtype(maybeEdit(n.%s, edit))\n", name, name)
case is(nodesType):
fmt.Fprintf(&buf, "editList(n.%s, edit)\n", name)
case is(slicePtrCaseClauseType):
fmt.Fprintf(&buf, "editCases(n.%s, edit)\n", name)
case is(slicePtrCommClauseType):
fmt.Fprintf(&buf, "editComms(n.%s, edit)\n", name)
case is(ptrFieldType):
fmt.Fprintf(&buf, "editField(n.%s, edit)\n", name)
case is(slicePtrFieldType):
fmt.Fprintf(&buf, "editFields(n.%s, edit)\n", name)
}
})
fmt.Fprintf(&buf, "}\n")
forNodeFields(typ,
"func (n *%[1]s) doChildren(do func(Node) error) error {\n",
"if n.%[1]s != nil { if err := do(n.%[1]s); err != nil { return err } }",
"if err := do%[2]s(n.%[1]s, do); err != nil { return err }",
"return nil }\n")
forNodeFields(typ,
"func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
"if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }",
"edit%[2]s(n.%[1]s, edit)",
"}\n")
}
for _, name := range []string{"CaseClause", "CommClause"} {
sliceHelper(&buf, name)
}
makeHelpers()
out, err := format.Source(buf.Bytes())
if err != nil {
@ -155,20 +96,32 @@ func main() {
}
}
func sliceHelper(buf *bytes.Buffer, name string) {
tmpl := fmt.Sprintf(`
func copy%[1]ss(list []*%[2]s) []*%[2]s {
// needHelper maps needed slice helpers from their base name to their
// respective slice-element type.
var needHelper = map[string]string{}
func makeHelpers() {
var names []string
for name := range needHelper {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name])
}
}
const sliceHelperTmpl = `
func copy%[1]s(list []%[2]s) []%[2]s {
if list == nil {
return nil
}
c := make([]*%[2]s, len(list))
c := make([]%[2]s, len(list))
copy(c, list)
return c
}
func maybeDo%[1]ss(list []*%[2]s, err error, do func(Node) error) error {
if err != nil {
return err
}
func do%[1]s(list []%[2]s, do func(Node) error) error {
for _, x := range list {
if x != nil {
if err := do(x); err != nil {
@ -178,51 +131,98 @@ func maybeDo%[1]ss(list []*%[2]s, err error, do func(Node) error) error {
}
return nil
}
func edit%[1]ss(list []*%[2]s, edit func(Node) Node) {
func edit%[1]s(list []%[2]s, edit func(Node) Node) {
for i, x := range list {
if x != nil {
list[i] = edit(x).(*%[2]s)
list[i] = edit(x).(%[2]s)
}
}
}
`, strings.TrimSuffix(name, "Clause"), name)
fmt.Fprintln(buf, tmpl)
`
func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) {
fmt.Fprintf(&buf, prologue, named.Obj().Name())
anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool {
if f.Embedded() {
return false
}
name, typ := f.Name(), f.Type()
slice, _ := typ.Underlying().(*types.Slice)
if slice != nil {
typ = slice.Elem()
}
tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg))
if implementsNode(typ) {
if slice != nil {
helper := strings.TrimPrefix(what, "*") + "s"
needHelper[helper] = what
tmpl, what = sliceTmpl, helper
}
} else if what == "*Field" {
// Special case for *Field.
tmpl = sliceTmpl
if slice != nil {
what = "Fields"
} else {
what = "Field"
}
} else {
return false
}
if tmpl == "" {
return false
}
// Allow template to not use all arguments without
// upsetting fmt.Printf.
s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what)
fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")])
return false
})
fmt.Fprintf(&buf, epilogue)
}
func forNodeFields(typName string, typ *types.Struct, f func(name string, is func(types.Type) bool)) {
for i, n := 0, typ.NumFields(); i < n; i++ {
v := typ.Field(i)
if v.Embedded() {
if typ, ok := v.Type().Underlying().(*types.Struct); ok {
forNodeFields(typName, typ, f)
continue
}
}
switch typName {
case "Func":
if strings.ToLower(strings.TrimSuffix(v.Name(), "_")) != "body" {
continue
}
case "Name":
continue
}
switch v.Name() {
case "orig":
continue
}
f(v.Name(), func(t types.Type) bool { return types.Identical(t, v.Type()) })
func implementsNode(typ types.Type) bool {
if _, ok := typ.Underlying().(*types.Interface); ok {
// TODO(mdempsky): Check the interface implements Node.
// Worst case, node_gen.go will fail to compile if we're wrong.
return true
}
if ptr, ok := typ.(*types.Pointer); ok {
if str, ok := ptr.Elem().Underlying().(*types.Struct); ok {
return anyField(str, func(f *types.Var) bool {
return f.Embedded() && f.Name() == "miniNode"
})
}
}
return false
}
func hasMiniNode(typ *types.Struct) bool {
func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool {
for i, n := 0, typ.NumFields(); i < n; i++ {
v := typ.Field(i)
if v.Name() == "miniNode" {
if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok {
if value != "-" {
panic(fmt.Sprintf("unexpected tag value: %q", value))
}
continue
}
f := typ.Field(i)
if pred(f) {
return true
}
if v.Embedded() {
if typ, ok := v.Type().Underlying().(*types.Struct); ok && hasMiniNode(typ) {
return true
if f.Embedded() {
if typ, ok := f.Type().Underlying().(*types.Struct); ok {
if anyField(typ, pred) {
return true
}
}
}
}

View File

@ -143,6 +143,10 @@ type Name struct {
func (n *Name) isExpr() {}
func (n *Name) copy() Node { panic(n.no("copy")) }
func (n *Name) doChildren(do func(Node) error) error { return nil }
func (n *Name) editChildren(edit func(Node) Node) {}
// CloneName makes a cloned copy of the name.
// It's not ir.Copy(n) because in general that operation is a mistake on names,
// which uniquely identify variables.

File diff suppressed because it is too large Load Diff

View File

@ -322,8 +322,8 @@ func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
// A ReturnStmt is a return statement.
type ReturnStmt struct {
miniStmt
orig Node // for typecheckargs rewrite
Results Nodes // return list
origNode // for typecheckargs rewrite
Results Nodes // return list
}
func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
@ -335,9 +335,6 @@ func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
return n
}
func (n *ReturnStmt) Orig() Node { return n.orig }
func (n *ReturnStmt) SetOrig(x Node) { n.orig = x }
// A SelectStmt is a block: { Cases }.
type SelectStmt struct {
miniStmt

View File

@ -185,45 +185,32 @@ func (f *Field) String() string {
return typ
}
func (f *Field) copy() *Field {
// TODO(mdempsky): Make Field a Node again so these can be generated?
// Fields are Nodes in go/ast and cmd/compile/internal/syntax.
func copyField(f *Field) *Field {
if f == nil {
return nil
}
c := *f
return &c
}
func copyFields(list []*Field) []*Field {
out := make([]*Field, len(list))
copy(out, list)
for i, f := range out {
out[i] = f.copy()
func doField(f *Field, do func(Node) error) error {
if f == nil {
return nil
}
return out
}
func maybeDoField(f *Field, err error, do func(Node) error) error {
if f != nil {
if err == nil && f.Decl != nil {
err = do(f.Decl)
}
if err == nil && f.Ntype != nil {
err = do(f.Ntype)
}
}
return err
}
func maybeDoFields(list []*Field, err error, do func(Node) error) error {
if err != nil {
return err
}
for _, f := range list {
err = maybeDoField(f, err, do)
if err != nil {
if f.Decl != nil {
if err := do(f.Decl); err != nil {
return err
}
}
return err
if f.Ntype != nil {
if err := do(f.Ntype); err != nil {
return err
}
}
return nil
}
func editField(f *Field, edit func(Node) Node) {
if f == nil {
return
@ -232,10 +219,25 @@ func editField(f *Field, edit func(Node) Node) {
f.Decl = edit(f.Decl).(*Name)
}
if f.Ntype != nil {
f.Ntype = toNtype(edit(f.Ntype))
f.Ntype = edit(f.Ntype).(Ntype)
}
}
func copyFields(list []*Field) []*Field {
out := make([]*Field, len(list))
for i, f := range list {
out[i] = copyField(f)
}
return out
}
func doFields(list []*Field, do func(Node) error) error {
for _, x := range list {
if err := doField(x, do); err != nil {
return err
}
}
return nil
}
func editFields(list []*Field, edit func(Node) Node) {
for _, f := range list {
editField(f, edit)

View File

@ -106,14 +106,7 @@ func DoChildren(n Node, do func(Node) error) error {
// Note that DoList only calls do on the nodes in the list, not their children.
// If x's children should be processed, do(x) must call DoChildren(x, do) itself.
func DoList(list Nodes, do func(Node) error) error {
for _, x := range list {
if x != nil {
if err := do(x); err != nil {
return err
}
}
}
return nil
return doNodes(list, do)
}
// Visit visits each non-nil node x in the IR tree rooted at n
@ -210,16 +203,3 @@ func EditChildren(n Node, edit func(Node) Node) {
}
n.editChildren(edit)
}
// editList calls edit on each non-nil node x in the list,
// saving the result of edit back into the list.
//
// Note that editList only calls edit on the nodes in the list, not their children.
// If x's children should be processed, edit(x) must call EditChildren(x, edit) itself.
func editList(list Nodes, edit func(Node) Node) {
for i, x := range list {
if x != nil {
list[i] = edit(x)
}
}
}