runtime: only initialize os.runtime_args when needed

This generally means that code size is reduced, especially when the os
package is not imported.

Specifically:

  - On Linux (which currently statically links musl), it avoids calling
    malloc, which avoids including the musl C heap for small programs
    saving around 1.6kB.
  - On WASI, it avoids initializing the args slice when the os package
    is not used. This reduces binary size by around 1kB.
This commit is contained in:
Ayke van Laethem
2021-11-04 20:57:43 +01:00
committed by Ron Evans
parent 670fcf59d8
commit fb33f3813d
4 changed files with 67 additions and 52 deletions
+31 -22
View File
@@ -74,28 +74,9 @@ func postinit() {}
func main(argc int32, argv *unsafe.Pointer) int {
preinit()
// Make args global big enough so that it can store all command line
// arguments. Unfortunately this has to be done with some magic as the heap
// is not yet initialized.
argsSlice := (*struct {
ptr unsafe.Pointer
len uintptr
cap uintptr
})(unsafe.Pointer(&args))
argsSlice.ptr = malloc(uintptr(argc) * (unsafe.Sizeof(uintptr(0))) * 3)
argsSlice.len = uintptr(argc)
argsSlice.cap = uintptr(argc)
// Initialize command line parameters.
for i := 0; i < int(argc); i++ {
// Convert the C string to a Go string.
length := strlen(*argv)
arg := (*_string)(unsafe.Pointer(&args[i]))
arg.length = length
arg.ptr = (*byte)(*argv)
// This is the Go equivalent of "argc++" in C.
argv = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(argv)))
}
// Store argc and argv for later use.
main_argc = argc
main_argv = argv
// Obtain the initial stack pointer right before calling the run() function.
// The run function has been moved to a separate (non-inlined) function so
@@ -107,6 +88,34 @@ func main(argc int32, argv *unsafe.Pointer) int {
return 0
}
var (
main_argc int32
main_argv *unsafe.Pointer
args []string
)
//go:linkname os_runtime_args os.runtime_args
func os_runtime_args() []string {
if args == nil {
// Make args slice big enough so that it can store all command line
// arguments.
args = make([]string, main_argc)
// Initialize command line parameters.
argv := main_argv
for i := 0; i < int(main_argc); i++ {
// Convert the C string to a Go string.
length := strlen(*argv)
arg := (*_string)(unsafe.Pointer(&args[i]))
arg.length = length
arg.ptr = (*byte)(*argv)
// This is the Go equivalent of "argv++" in C.
argv = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(argv)))
}
}
return args
}
// Must be a separate function to get the correct stack pointer.
//go:noinline
func runMain() {