Commit message:
cmd/compile: treat singly-assigned func vars as static in escape analysis
When a function variable is declared without an initializer and then
assigned exactly once, escape analysis cannot identify the callee:
var f func(*int)
f = func(p *int) {} // callee not visible to StaticValue
f(new(int)) // new(int) conservatively escapes
This pattern is common for recursive closures:
var visit func(*Node)
visit = func(n *Node) {
for _, child := range n.Children {
visit(child)
}
}
visit(root)
There is a trick we can use here: a nil func cannot be called, because
it panics, and a panic cannot cause anything to escape. So for
variables declared without an initializer and assigned exactly once, we
can treat that single assignment as the static value for escape
analysis purposes, since the only other possible value (nil) is a dead
end.
Add ir.FuncSingleAssignment to find such singly-assigned func variables,
and use it as a fallback in escape analysis when StaticCalleeName fails.
This is restricted to func-typed variables only: tracking all local
variables is too expensive, and only func-typed variables benefit from
callee resolution. A future CL will instead do this in the "oracle",
but I am splitting that up as I think it is wasteful to do that and
then not use it to replace StaticValue, but apparently that has its
own issues.
This optimization resolves callees 7 times in std, 100 times in cmd,
51 times in github.com/microsoft/typescript-go, 276 times in x/tools,
and 547 times in x/tools/gopls. Most resolutions improve parameter
escape precision; of those, 5 heap allocations are eliminated in
std/cmd, 6 in x/tools, 5 in x/tools/gopls, and 16 in typescript-go.
compilebench results (this CL vs parent):
│ sec/op │ sec/op vs base │
Template 157.8m ± 6% 157.9m ± 10% ~ (p=0.699 n=6)
Unicode 121.7m ± 12% 122.1m ± 4% ~ (p=0.589 n=6)
GoTypes 897.4m ± 5% 902.5m ± 3% ~ (p=0.937 n=6)
Compiler 163.1m ± 5% 162.4m ± 2% ~ (p=0.818 n=6)
SSA 7.269 ± 2% 7.177 ± 1% ~ (p=0.699 n=6)
Flate 167.6m ± 5% 167.7m ± 14% ~ (p=1.000 n=6)
GoParser 180.5m ± 5% 179.1m ± 5% ~ (p=1.000 n=6)
Reflect 399.5m ± 2% 411.2m ± 7% ~ (p=0.310 n=6)
Tar 172.2m ± 7% 180.0m ± 9% ~ (p=0.699 n=6)
XML 201.1m ± 6% 197.4m ± 4% ~ (p=0.699 n=6)
LinkCompiler 675.7m ± 2% 672.7m ± 2% ~ (p=0.485 n=6)
ExternalLinkCompiler 2.313 ± 2% 2.330 ± 1% ~ (p=0.394 n=6)
LinkWithoutDebugCompiler 430.5m ± 3% 430.9m ± 6% ~ (p=0.818 n=6)
StdCmd 30.43 ± 5% 30.31 ± 2% ~ (p=0.699 n=6)
geomean 539.1m 540.6m +0.28%
Updates #59708
Fixes #70171