-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/link: linker should be able to remove init functions with no side-effects #19533
Comments
I'm not opposed to this idea but I think it's going to be difficult to implement effectively as a linker function. Compiler generated init functions are going to make function calls. For the linker to eliminate a compiler generated init function, it will have to traverse the entire transitive set of function calls made by the init function to make sure that they do not change any referenced global variables. In this case we are going to see function calls to runtime.makemap I think a simpler approach might be for the compiler to generate separate init functions for each global variable that requires one, and to annotate the init function in some way to say that this init function does not do anything important other than initialize a global variable. Then it would be easy for the linker to not count references from that function when deciding whether a global variable is used, and to discard the init function along with the global variable if it is not used. That approach would of course not be as general, but I think it would be simpler to implement, more likely to be correct, and I suspect it would catch many of the cases where variables can actually be eliminated. |
I tried to keep the example minimal for the sake of focusing the problem, but I have a few more complex cases in mind, for the sake of analyzing what the correct solution would be. For instance, what about this: package main
import (
"os"
"math"
"fmt"
"strconv"
)
var Data1 float64
var Data2 float64
func init() {
value := os.Getenv("TEST")
val, _ := strconv.ParseFloat(value, 64)
Data1 = math.Sqrt(val)
}
func init() {
Data2 = Data1 + 1.0
}
func init() {
Data1 = Data2 + 1.0
} In this case, if For instance, I assume that the Go language specification tells us that, instead, those init functions couldn't be dropped if the printed something or possibly panicked (with no way for the compiler to prove that the panic is never triggered). |
In that example, your |
Another instance of this issue is flag definitions for programs that don't use flags. See for instance https://go-review.googlesource.com/c/go/+/49251 where code was changed into non-idiomatic way to avoid unused dependencies. |
Is this a duplicate of #14840? |
In this example:
testlinker/a/a.go
testlinker/main.go
the linker is unable to remove references to
DieDieType
because it is referenced bya.init
. But this init function, generated by the compiler to initializeDieDieValue
, is actually useless, because it has no side-effects and only initializes a global variable that is then unreferenced. So the init function could be dropped, and this in turn would remove references to the types.This is distilled from the analysis in #19523, where just referencing a constant in a package causes a very large growth of binary size because of a deep chain of dependencies which are not used by the program. I haven't fully tracked everything so I can't swear that all init functions are side-effects-free in that case, but I think there's a reasonable chance that it would fix that instance.
The text was updated successfully, but these errors were encountered: