diff options
| author | Russ Cox <rsc@golang.org> | 2020-11-25 01:11:56 -0500 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2020-11-25 17:30:43 +0000 |
| commit | 41f3af9d04362a56c1af186af134c704a03fa97b (patch) | |
| tree | e67e7c1b67dd5c48a4c18fdb2f973bf789c7b2d1 /src/cmd/compile/internal/gc/closure.go | |
| parent | 4d0d9c2c5c35377b0662f2fd0995867919552251 (diff) | |
| download | go-git-41f3af9d04362a56c1af186af134c704a03fa97b.tar.gz | |
[dev.regabi] cmd/compile: replace *Node type with an interface Node [generated]
The plan is to introduce a Node interface that replaces the old *Node pointer-to-struct.
The previous CL defined an interface INode modeling a *Node.
This CL:
- Changes all references outside internal/ir to use INode,
along with many references inside internal/ir as well.
- Renames Node to node.
- Renames INode to Node
So now ir.Node is an interface implemented by *ir.node, which is otherwise inaccessible,
and the code outside package ir is now (clearly) using only the interface.
The usual rule is never to redefine an existing name with a new meaning,
so that old code that hasn't been updated gets a "unknown name" error
instead of more mysterious errors or silent misbehavior. That rule would
caution against replacing Node-the-struct with Node-the-interface,
as in this CL, because code that says *Node would now be using a pointer
to an interface. But this CL is being landed at the same time as another that
moves Node from gc to ir. So the net effect is to replace *gc.Node with ir.Node,
which does follow the rule: any lingering references to gc.Node will be told
it's gone, not silently start using pointers to interfaces. So the rule is followed
by the CL sequence, just not this specific CL.
Overall, the loss of inlining caused by using interfaces cuts the compiler speed
by about 6%, a not insignificant amount. However, as we convert the representation
to concrete structs that are not the giant Node over the next weeks, that speed
should come back as more of the compiler starts operating directly on concrete types
and the memory taken up by the graph of Nodes drops due to the more precise
structs. Honestly, I was expecting worse.
% benchstat bench.old bench.new
name old time/op new time/op delta
Template 168ms ± 4% 182ms ± 2% +8.34% (p=0.000 n=9+9)
Unicode 72.2ms ±10% 82.5ms ± 6% +14.38% (p=0.000 n=9+9)
GoTypes 563ms ± 8% 598ms ± 2% +6.14% (p=0.006 n=9+9)
Compiler 2.89s ± 4% 3.04s ± 2% +5.37% (p=0.000 n=10+9)
SSA 6.45s ± 4% 7.25s ± 5% +12.41% (p=0.000 n=9+10)
Flate 105ms ± 2% 115ms ± 1% +9.66% (p=0.000 n=10+8)
GoParser 144ms ±10% 152ms ± 2% +5.79% (p=0.011 n=9+8)
Reflect 345ms ± 9% 370ms ± 4% +7.28% (p=0.001 n=10+9)
Tar 149ms ± 9% 161ms ± 5% +8.05% (p=0.001 n=10+9)
XML 190ms ± 3% 209ms ± 2% +9.54% (p=0.000 n=9+8)
LinkCompiler 327ms ± 2% 325ms ± 2% ~ (p=0.382 n=8+8)
ExternalLinkCompiler 1.77s ± 4% 1.73s ± 6% ~ (p=0.113 n=9+10)
LinkWithoutDebugCompiler 214ms ± 4% 211ms ± 2% ~ (p=0.360 n=10+8)
StdCmd 14.8s ± 3% 15.9s ± 1% +6.98% (p=0.000 n=10+9)
[Geo mean] 480ms 510ms +6.31%
name old user-time/op new user-time/op delta
Template 223ms ± 3% 237ms ± 3% +6.16% (p=0.000 n=9+10)
Unicode 103ms ± 6% 113ms ± 3% +9.53% (p=0.000 n=9+9)
GoTypes 758ms ± 8% 800ms ± 2% +5.55% (p=0.003 n=10+9)
Compiler 3.95s ± 2% 4.12s ± 2% +4.34% (p=0.000 n=10+9)
SSA 9.43s ± 1% 9.74s ± 4% +3.25% (p=0.000 n=8+10)
Flate 132ms ± 2% 141ms ± 2% +6.89% (p=0.000 n=9+9)
GoParser 177ms ± 9% 183ms ± 4% ~ (p=0.050 n=9+9)
Reflect 467ms ±10% 495ms ± 7% +6.17% (p=0.029 n=10+10)
Tar 183ms ± 9% 197ms ± 5% +7.92% (p=0.001 n=10+10)
XML 249ms ± 5% 268ms ± 4% +7.82% (p=0.000 n=10+9)
LinkCompiler 544ms ± 5% 544ms ± 6% ~ (p=0.863 n=9+9)
ExternalLinkCompiler 1.79s ± 4% 1.75s ± 6% ~ (p=0.075 n=10+10)
LinkWithoutDebugCompiler 248ms ± 6% 246ms ± 2% ~ (p=0.965 n=10+8)
[Geo mean] 483ms 504ms +4.41%
[git-generate]
cd src/cmd/compile/internal/ir
: # We need to do the conversion in multiple steps, so we introduce
: # a temporary type alias that will start out meaning the pointer-to-struct
: # and then change to mean the interface.
rf '
mv Node OldNode
add node.go \
type Node = *OldNode
'
: # It should work to do this ex in ir, but it misses test files, due to a bug in rf.
: # Run the command in gc to handle gc's tests, and then again in ssa for ssa's tests.
cd ../gc
rf '
ex . ../arm ../riscv64 ../arm64 ../mips64 ../ppc64 ../mips ../wasm {
import "cmd/compile/internal/ir"
*ir.OldNode -> ir.Node
}
'
cd ../ssa
rf '
ex {
import "cmd/compile/internal/ir"
*ir.OldNode -> ir.Node
}
'
: # Back in ir, finish conversion clumsily with sed,
: # because type checking and circular aliases do not mix.
cd ../ir
sed -i '' '
/type Node = \*OldNode/d
s/\*OldNode/Node/g
s/^func (n Node)/func (n *OldNode)/
s/OldNode/node/g
s/type INode interface/type Node interface/
s/var _ INode = (Node)(nil)/var _ Node = (*node)(nil)/
' *.go
gofmt -w *.go
sed -i '' '
s/{Func{}, 136, 248}/{Func{}, 152, 280}/
s/{Name{}, 32, 56}/{Name{}, 44, 80}/
s/{Param{}, 24, 48}/{Param{}, 44, 88}/
s/{node{}, 76, 128}/{node{}, 88, 152}/
' sizeof_test.go
cd ../ssa
sed -i '' '
s/{LocalSlot{}, 28, 40}/{LocalSlot{}, 32, 48}/
' sizeof_test.go
cd ../gc
sed -i '' 's/\*ir.Node/ir.Node/' mkbuiltin.go
cd ../../../..
go install std cmd
cd cmd/compile
go test -u || go test -u
Change-Id: I196bbe3b648e4701662e4a2bada40bf155e2a553
Reviewed-on: https://go-review.googlesource.com/c/go/+/272935
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/compile/internal/gc/closure.go')
| -rw-r--r-- | src/cmd/compile/internal/gc/closure.go | 40 |
1 files changed, 20 insertions, 20 deletions
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 2dce7b7f03..2901ae41d6 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -13,7 +13,7 @@ import ( "fmt" ) -func (p *noder) funcLit(expr *syntax.FuncLit) *ir.Node { +func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node { xtype := p.typeExpr(expr.Type) ntype := p.typeExpr(expr.Type) @@ -78,7 +78,7 @@ func (p *noder) funcLit(expr *syntax.FuncLit) *ir.Node { // function associated with the closure. // TODO: This creation of the named function should probably really be done in a // separate pass from type-checking. -func typecheckclosure(clo *ir.Node, top int) { +func typecheckclosure(clo ir.Node, top int) { fn := clo.Func() dcl := fn.Decl // Set current associated iota value, so iota can be used inside @@ -140,7 +140,7 @@ var globClosgen int // closurename generates a new unique name for a closure within // outerfunc. -func closurename(outerfunc *ir.Node) *types.Sym { +func closurename(outerfunc ir.Node) *types.Sym { outer := "glob." prefix := "func" gen := &globClosgen @@ -172,7 +172,7 @@ var capturevarscomplete bool // by value or by reference. // We use value capturing for values <= 128 bytes that are never reassigned // after capturing (effectively constant). -func capturevars(dcl *ir.Node) { +func capturevars(dcl ir.Node) { lno := base.Pos base.Pos = dcl.Pos() fn := dcl.Func() @@ -227,7 +227,7 @@ func capturevars(dcl *ir.Node) { // transformclosure is called in a separate phase after escape analysis. // It transform closure bodies to properly reference captured variables. -func transformclosure(dcl *ir.Node) { +func transformclosure(dcl ir.Node) { lno := base.Pos base.Pos = dcl.Pos() fn := dcl.Func() @@ -253,7 +253,7 @@ func transformclosure(dcl *ir.Node) { // We are going to insert captured variables before input args. var params []*types.Field - var decls []*ir.Node + var decls []ir.Node for _, v := range fn.ClosureVars.Slice() { if !v.Name().Byval() { // If v of type T is captured by reference, @@ -284,7 +284,7 @@ func transformclosure(dcl *ir.Node) { dcl.SetType(f.Type()) // update type of ODCLFUNC } else { // The closure is not called, so it is going to stay as closure. - var body []*ir.Node + var body []ir.Node offset := int64(Widthptr) for _, v := range fn.ClosureVars.Slice() { // cv refers to the field inside of closure OSTRUCTLIT. @@ -332,13 +332,13 @@ func transformclosure(dcl *ir.Node) { // hasemptycvars reports whether closure clo has an // empty list of captured vars. -func hasemptycvars(clo *ir.Node) bool { +func hasemptycvars(clo ir.Node) bool { return clo.Func().ClosureVars.Len() == 0 } // closuredebugruntimecheck applies boilerplate checks for debug flags // and compiling runtime -func closuredebugruntimecheck(clo *ir.Node) { +func closuredebugruntimecheck(clo ir.Node) { if base.Debug.Closure > 0 { if clo.Esc() == EscHeap { base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func().ClosureVars) @@ -354,7 +354,7 @@ func closuredebugruntimecheck(clo *ir.Node) { // closureType returns the struct type used to hold all the information // needed in the closure for clo (clo must be a OCLOSURE node). // The address of a variable of the returned type can be cast to a func. -func closureType(clo *ir.Node) *types.Type { +func closureType(clo ir.Node) *types.Type { // Create closure in the form of a composite literal. // supposing the closure captures an int i and a string s // and has one float64 argument and no results, @@ -368,7 +368,7 @@ func closureType(clo *ir.Node) *types.Type { // The information appears in the binary in the form of type descriptors; // the struct is unnamed so that closures in multiple packages with the // same struct type can share the descriptor. - fields := []*ir.Node{ + fields := []ir.Node{ namedfield(".F", types.Types[types.TUINTPTR]), } for _, v := range clo.Func().ClosureVars.Slice() { @@ -383,7 +383,7 @@ func closureType(clo *ir.Node) *types.Type { return typ } -func walkclosure(clo *ir.Node, init *ir.Nodes) *ir.Node { +func walkclosure(clo ir.Node, init *ir.Nodes) ir.Node { fn := clo.Func() // If no closure vars, don't bother wrapping. @@ -399,7 +399,7 @@ func walkclosure(clo *ir.Node, init *ir.Nodes) *ir.Node { clos := ir.Nod(ir.OCOMPLIT, nil, typenod(typ)) clos.SetEsc(clo.Esc()) - clos.PtrList().Set(append([]*ir.Node{ir.Nod(ir.OCFUNC, fn.Nname, nil)}, fn.ClosureEnter.Slice()...)) + clos.PtrList().Set(append([]ir.Node{ir.Nod(ir.OCFUNC, fn.Nname, nil)}, fn.ClosureEnter.Slice()...)) clos = ir.Nod(ir.OADDR, clos, nil) clos.SetEsc(clo.Esc()) @@ -419,7 +419,7 @@ func walkclosure(clo *ir.Node, init *ir.Nodes) *ir.Node { return walkexpr(clos, init) } -func typecheckpartialcall(dot *ir.Node, sym *types.Sym) { +func typecheckpartialcall(dot ir.Node, sym *types.Sym) { switch dot.Op() { case ir.ODOTINTER, ir.ODOTMETH: break @@ -440,7 +440,7 @@ func typecheckpartialcall(dot *ir.Node, sym *types.Sym) { // makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed // for partial calls. -func makepartialcall(dot *ir.Node, t0 *types.Type, meth *types.Sym) *ir.Node { +func makepartialcall(dot ir.Node, t0 *types.Type, meth *types.Sym) ir.Node { rcvrtype := dot.Left().Type() sym := methodSymSuffix(rcvrtype, meth, "-fm") @@ -484,7 +484,7 @@ func makepartialcall(dot *ir.Node, t0 *types.Type, meth *types.Sym) *ir.Node { ptr := NewName(lookup(".this")) declare(ptr, ir.PAUTO) ptr.Name().SetUsed(true) - var body []*ir.Node + var body []ir.Node if rcvrtype.IsPtr() || rcvrtype.IsInterface() { ptr.SetType(rcvrtype) body = append(body, ir.Nod(ir.OAS, ptr, cv)) @@ -522,8 +522,8 @@ func makepartialcall(dot *ir.Node, t0 *types.Type, meth *types.Sym) *ir.Node { // partialCallType returns the struct type used to hold all the information // needed in the closure for n (n must be a OCALLPART node). // The address of a variable of the returned type can be cast to a func. -func partialCallType(n *ir.Node) *types.Type { - t := tostruct([]*ir.Node{ +func partialCallType(n ir.Node) *types.Type { + t := tostruct([]ir.Node{ namedfield("F", types.Types[types.TUINTPTR]), namedfield("R", n.Left().Type()), }) @@ -531,7 +531,7 @@ func partialCallType(n *ir.Node) *types.Type { return t } -func walkpartialcall(n *ir.Node, init *ir.Nodes) *ir.Node { +func walkpartialcall(n ir.Node, init *ir.Nodes) ir.Node { // Create closure in the form of a composite literal. // For x.M with receiver (x) type T, the generated code looks like: // @@ -579,7 +579,7 @@ func walkpartialcall(n *ir.Node, init *ir.Nodes) *ir.Node { // callpartMethod returns the *types.Field representing the method // referenced by method value n. -func callpartMethod(n *ir.Node) *types.Field { +func callpartMethod(n ir.Node) *types.Field { if n.Op() != ir.OCALLPART { base.Fatalf("expected OCALLPART, got %v", n) } |
