-
Notifications
You must be signed in to change notification settings - Fork 18.5k
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.