Description
What version of Go are you using (go version
)?
1.13.5
Does this issue reproduce with the latest release?
yes
The map of builtin functions that can be invoked from templates is defined as a global variable here.
It references the call
function, which uses reflect.Call
to invoke a user function. This is one of the conditions that disables dead code elimination in the linker.
At the moment Go is unable to elide unused global maps - see #31704 and long discussion on #2559.
As a result, if anything imports text/template, even transitively, dead code elimination is disabled for entire program.
Consider this simple program: https://github.com/rojer/go-dce-bug
pkg1.MyStruct.Junk method is unused and should be eliminated. However, as described here, DCE is disabled and it is not eliminated:
$ go build && go tool nm go-dce-bug | grep -E '(Junk|unused)'
4c5710 T github.com/rojer/go-dce-bug/pkg1.(*MyStruct).Junk
524d20 R github.com/rojer/go-dce-bug/pkg1.(*MyStruct).Junk.stkobj
The reason is, pkg2 imports text/template
. the function that references text/template is unused, and even if it were, it does nit use templates as such, just a simple exported function. But since compiler cannot properly elide global map initializers, reflect.Call
ends up being marked reachable via text/template.builtins
and text/template.call
.
Bottom line is, the text/template.builtins
map has very high cost to the program and until Go compiler gets better at eliminating unused global variables, its initalization should be removed from package scope and the map should be initialized on first use.