mirror of
https://github.com/golang/go.git
synced 2026-06-27 03:11:23 +00:00
cmd/compile: move FuncAssignments into ReassignOracle
The standalone ir.FuncAssignments walked the entire enclosing
function on every call. Move it into ReassignOracle, which collects
the same information in a single walk during Init alongside the
existing singleDef analysis.
compilebench results (this CL vs parent):
│ parent.txt │ commit.txt │
│ sec/op │ sec/op vs base │
Template 153.1m ± 16% 149.5m ± 7% ~ (p=0.699 n=6)
Unicode 115.0m ± 15% 116.1m ± 8% ~ (p=0.310 n=6)
GoTypes 876.2m ± 2% 872.1m ± 2% ~ (p=0.699 n=6)
Compiler 156.4m ± 9% 158.2m ± 8% ~ (p=0.818 n=6)
SSA 7.044 ± 1% 7.044 ± 1% ~ (p=0.937 n=6)
Flate 172.5m ± 4% 165.7m ± 11% ~ (p=0.132 n=6)
GoParser 168.0m ± 3% 169.5m ± 13% ~ (p=0.937 n=6)
Reflect 394.2m ± 3% 391.3m ± 4% ~ (p=0.937 n=6)
Tar 167.1m ± 13% 167.9m ± 15% ~ (p=0.589 n=6)
XML 200.0m ± 7% 197.5m ± 15% ~ (p=1.000 n=6)
LinkCompiler 638.3m ± 3% 631.2m ± 5% ~ (p=0.699 n=6)
ExternalLinkCompiler 2.197 ± 2% 2.192 ± 1% ~ (p=0.699 n=6)
LinkWithoutDebugCompiler 422.2m ± 4% 425.8m ± 3% ~ (p=0.093 n=6)
StdCmd 29.01 ± 2% 28.73 ± 1% ~ (p=0.394 n=6)
geomean 522.0m 519.4m -0.50%
│ parent.txt │ commit.txt │
│ user-sec/op │ user-sec/op vs base │
Template 727.0m ± 15% 692.6m ± 8% ~ (p=1.000 n=6)
Unicode 159.7m ± 6% 161.9m ± 6% ~ (p=0.589 n=6)
GoTypes 4.884 ± 1% 4.907 ± 1% ~ (p=0.937 n=6)
Compiler 470.5m ± 9% 476.9m ± 11% ~ (p=0.699 n=6)
SSA 37.17 ± 2% 37.40 ± 2% ~ (p=0.132 n=6)
Flate 776.9m ± 9% 749.1m ± 16% ~ (p=0.699 n=6)
GoParser 688.5m ± 5% 676.7m ± 6% ~ (p=0.937 n=6)
Reflect 1.861 ± 2% 1.910 ± 3% ~ (p=0.065 n=6)
Tar 750.6m ± 11% 762.4m ± 23% ~ (p=0.937 n=6)
XML 947.8m ± 13% 926.4m ± 12% ~ (p=0.485 n=6)
LinkCompiler 1.035 ± 6% 1.037 ± 5% ~ (p=0.818 n=6)
ExternalLinkCompiler 2.505 ± 2% 2.506 ± 2% ~ (p=0.699 n=6)
LinkWithoutDebugCompiler 479.0m ± 6% 490.4m ± 3% ~ (p=0.394 n=6)
geomean 1.181 1.180 -0.15%
│ parent.txt │ commit.txt │
│ text-bytes │ text-bytes vs base │
HelloSize 1.110Mi ± 0% 1.110Mi ± 0% ~ (p=1.000 n=6) ¹
CmdGoSize 14.14Mi ± 0% 14.14Mi ± 0% ~ (p=1.000 n=6) ¹
geomean 3.961Mi 3.961Mi +0.00%
¹ all samples are equal
│ parent.txt │ commit.txt │
│ data-bytes │ data-bytes vs base │
HelloSize 27.54Ki ± 0% 27.54Ki ± 0% ~ (p=1.000 n=6) ¹
CmdGoSize 431.4Ki ± 0% 431.4Ki ± 0% ~ (p=1.000 n=6) ¹
geomean 109.0Ki 109.0Ki +0.00%
¹ all samples are equal
│ parent.txt │ commit.txt │
│ bss-bytes │ bss-bytes vs base │
HelloSize 213.9Ki ± 0% 213.9Ki ± 0% ~ (p=1.000 n=6) ¹
CmdGoSize 32.27Mi ± 0% 32.27Mi ± 0% ~ (p=1.000 n=6) ¹
geomean 2.597Mi 2.597Mi +0.00%
¹ all samples are equal
│ parent.txt │ commit.txt │
│ exe-bytes │ exe-bytes vs base │
HelloSize 1.782Mi ± 0% 1.782Mi ± 0% ~ (p=1.000 n=6) ¹
CmdGoSize 21.29Mi ± 0% 21.29Mi ± 0% ~ (p=1.000 n=6) ¹
geomean 6.158Mi 6.158Mi +0.00%
¹ all samples are equal
Change-Id: Iba7c593a31946fb17450106d1ee0ba9e28eedba3
Reviewed-on: https://go-review.googlesource.com/c/go/+/771161
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
5af294bac7
commit
84e0c4965a
3 changed files with 74 additions and 106 deletions
|
|
@ -47,7 +47,8 @@ func (e *escape) call(ks []hole, call ir.Node) {
|
|||
if fn := ir.StaticCalleeName(v); fn != nil {
|
||||
fns = []*ir.Name{fn}
|
||||
} else if name, ok := v.(*ir.Name); ok {
|
||||
fns = resolveAssignedCallees(ir.FuncAssignments(name.Canonical()))
|
||||
ro := e.reassignOracle(e.curfn)
|
||||
fns = resolveAssignedCallees(ro.FuncAssignments(name.Canonical()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1020,95 +1020,6 @@ func Reassigned(name *Name) bool {
|
|||
return Any(name.Curfn, do)
|
||||
}
|
||||
|
||||
// FuncAssignments returns all simple (OAS) assignments of non-zero
|
||||
// values to name, if name is a func-typed local variable (PAUTO).
|
||||
// Zero-value assignments (nil, bare declarations) are ignored since
|
||||
// nil panics on call. Returns nil if the variable is not PAUTO, not
|
||||
// func-typed, address-taken, has any complex assignment (OAS2, ORANGE),
|
||||
// or has too many assignments. Assignments inside nested closures are
|
||||
// accepted because this is only used for escape analysis callee
|
||||
// resolution: the only alternative value is nil, which panics on call.
|
||||
//
|
||||
// TODO: fold this into [ReassignOracle] so it can share the single
|
||||
// walk with StaticValue and Reassigned.
|
||||
func FuncAssignments(name *Name) []*AssignStmt {
|
||||
if name.Class != PAUTO {
|
||||
return nil
|
||||
}
|
||||
base.AssertfAt(name.Curfn != nil, name.Pos(), "PAUTO %v has nil Curfn", name)
|
||||
if name.Addrtaken() {
|
||||
return nil
|
||||
}
|
||||
if name.Type().Kind() != types.TFUNC {
|
||||
return nil
|
||||
}
|
||||
var found []*AssignStmt
|
||||
if name.Defn != nil {
|
||||
if _, ok := name.Defn.(*AssignStmt); !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
isName := func(x Node) bool {
|
||||
if x == nil {
|
||||
return false
|
||||
}
|
||||
n, ok := OuterValue(x).(*Name)
|
||||
return ok && n.Canonical() == name
|
||||
}
|
||||
|
||||
var do func(n Node) bool
|
||||
do = func(n Node) bool {
|
||||
switch n.Op() {
|
||||
case OAS:
|
||||
as := n.(*AssignStmt)
|
||||
if isName(as.X) {
|
||||
if isNilAssign(as) {
|
||||
break
|
||||
}
|
||||
found = append(found, as)
|
||||
}
|
||||
case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2:
|
||||
as := n.(*AssignListStmt)
|
||||
for _, p := range as.Lhs {
|
||||
if isName(p) {
|
||||
found = nil
|
||||
return true
|
||||
}
|
||||
}
|
||||
case ORANGE:
|
||||
rs := n.(*RangeStmt)
|
||||
if isName(rs.Key) || isName(rs.Value) {
|
||||
found = nil
|
||||
return true
|
||||
}
|
||||
case OCLOSURE:
|
||||
n := n.(*ClosureExpr)
|
||||
if Any(n.Func, do) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if Any(name.Curfn, do) {
|
||||
return nil
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// isNilAssign reports whether as has a nil or absent RHS.
|
||||
func isNilAssign(as *AssignStmt) bool {
|
||||
if as.Y == nil {
|
||||
return true
|
||||
}
|
||||
y := as.Y
|
||||
for y.Op() == OCONVNOP {
|
||||
y = y.(*ConvExpr).X
|
||||
}
|
||||
return IsNil(y)
|
||||
}
|
||||
|
||||
// StaticCalleeName returns the ONAME/PFUNC for n, if known.
|
||||
func StaticCalleeName(n Node) *Name {
|
||||
switch n.Op() {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package ir
|
|||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/types"
|
||||
)
|
||||
|
||||
// A ReassignOracle efficiently answers queries about whether local
|
||||
|
|
@ -21,6 +22,15 @@ type ReassignOracle struct {
|
|||
// maps candidate name to its defining assignment (or
|
||||
// for params, defining func).
|
||||
singleDef map[*Name]Node
|
||||
|
||||
// funcAssigns tracks all known simple assignments (OAS) to
|
||||
// func-typed PAUTO variables. Only func-typed variables are
|
||||
// tracked because this data is used exclusively for callee
|
||||
// resolution in escape analysis. Deletion means the candidate was
|
||||
// invalidated (e.g., addr-taken, non-simple assignment form, or too
|
||||
// many assignments). Assignments inside nested closures are accepted
|
||||
// because the only alternative value is nil, which panics on call.
|
||||
funcAssigns map[*Name][]*AssignStmt
|
||||
}
|
||||
|
||||
// Init initializes the oracle based on the IR in function fn, laying
|
||||
|
|
@ -33,6 +43,7 @@ func (ro *ReassignOracle) Init(fn *Func) {
|
|||
// Collect candidate map. Start by adding function parameters
|
||||
// explicitly.
|
||||
ro.singleDef = make(map[*Name]Node)
|
||||
ro.funcAssigns = make(map[*Name][]*AssignStmt)
|
||||
sig := fn.Type()
|
||||
numParams := sig.NumRecvs() + sig.NumParams()
|
||||
for _, param := range fn.Dcl[:numParams] {
|
||||
|
|
@ -48,8 +59,21 @@ func (ro *ReassignOracle) Init(fn *Func) {
|
|||
var findLocals func(n Node) bool
|
||||
findLocals = func(n Node) bool {
|
||||
if nn, ok := n.(*Name); ok {
|
||||
if nn.Defn != nil && !nn.Addrtaken() && nn.Class == PAUTO {
|
||||
ro.singleDef[nn] = nn.Defn
|
||||
if nn.Class == PAUTO && !nn.Addrtaken() {
|
||||
isFunc := nn.Type().Kind() == types.TFUNC
|
||||
if nn.Defn == nil {
|
||||
// Bare declaration (e.g., "var f func()").
|
||||
if isFunc {
|
||||
ro.funcAssigns[nn] = nil
|
||||
}
|
||||
} else if _, ok := nn.Defn.(*AssignStmt); ok {
|
||||
ro.singleDef[nn] = nn.Defn
|
||||
if isFunc {
|
||||
ro.funcAssigns[nn] = nil
|
||||
}
|
||||
} else {
|
||||
ro.singleDef[nn] = nn.Defn
|
||||
}
|
||||
}
|
||||
} else if nn, ok := n.(*ClosureExpr); ok {
|
||||
Any(nn.Func, findLocals)
|
||||
|
|
@ -71,26 +95,35 @@ func (ro *ReassignOracle) Init(fn *Func) {
|
|||
|
||||
// pruneIfNeeded examines node nn appearing on the left hand side
|
||||
// of assignment statement asn to see if it contains a reassignment
|
||||
// to any nodes in our candidate map ro.singleDef; if a reassignment
|
||||
// is found, the corresponding name is deleted from singleDef.
|
||||
// to any nodes in our candidate maps; if a reassignment is found,
|
||||
// the corresponding name is deleted.
|
||||
pruneIfNeeded := func(nn Node, asn Node) {
|
||||
oname := outerName(nn)
|
||||
if oname == nil {
|
||||
return
|
||||
}
|
||||
defn, ok := ro.singleDef[oname]
|
||||
if !ok {
|
||||
return
|
||||
if defn, ok := ro.singleDef[oname]; ok {
|
||||
// any assignment to a param invalidates the entry.
|
||||
paramAssigned := oname.Class == PPARAM
|
||||
// assignment to local ok iff assignment is its orig def.
|
||||
localAssigned := (oname.Class == PAUTO && asn != defn)
|
||||
if paramAssigned || localAssigned {
|
||||
// We found an assignment to name N that doesn't
|
||||
// correspond to its original definition; remove
|
||||
// from candidates.
|
||||
delete(ro.singleDef, oname)
|
||||
}
|
||||
}
|
||||
// any assignment to a param invalidates the entry.
|
||||
paramAssigned := oname.Class == PPARAM
|
||||
// assignment to local ok iff assignment is its orig def.
|
||||
localAssigned := (oname.Class == PAUTO && asn != defn)
|
||||
if paramAssigned || localAssigned {
|
||||
// We found an assignment to name N that doesn't
|
||||
// correspond to its original definition; remove
|
||||
// from candidates.
|
||||
delete(ro.singleDef, oname)
|
||||
if _, ok := ro.funcAssigns[oname]; ok {
|
||||
as, isOAS := asn.(*AssignStmt)
|
||||
if isOAS && isNilAssign(as) {
|
||||
// Zero-value assignment (nil, bare decl), skip.
|
||||
} else if !isOAS {
|
||||
// Not a simple assignment: invalidate.
|
||||
delete(ro.funcAssigns, oname)
|
||||
} else {
|
||||
ro.funcAssigns[oname] = append(ro.funcAssigns[oname], as)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,3 +236,26 @@ func (ro *ReassignOracle) Reassigned(n *Name) bool {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FuncAssignments returns all known simple assignments to a func-typed
|
||||
// variable. For variables defined with := and a non-zero value, the
|
||||
// defining assignment is included. Returns nil if the variable is not
|
||||
// func-typed, was invalidated (addr-taken, non-simple assignment,
|
||||
// too many assignments), or has no tracked assignments. Assignments
|
||||
// inside nested closures are accepted because the only alternative
|
||||
// value is nil, which panics on call.
|
||||
func (ro *ReassignOracle) FuncAssignments(name *Name) []*AssignStmt {
|
||||
return ro.funcAssigns[name.Canonical()]
|
||||
}
|
||||
|
||||
// isNilAssign reports whether as has a nil or absent RHS.
|
||||
func isNilAssign(as *AssignStmt) bool {
|
||||
if as.Y == nil {
|
||||
return true
|
||||
}
|
||||
y := as.Y
|
||||
for y.Op() == OCONVNOP {
|
||||
y = y.(*ConvExpr).X
|
||||
}
|
||||
return IsNil(y)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue