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.
Этот коммит содержится в:
Ayke van Laethem 2021-11-04 20:57:43 +01:00 коммит произвёл Ron Evans
родитель 670fcf59d8
коммит fb33f3813d
4 изменённых файлов: 67 добавлений и 52 удалений

Просмотреть файл

@ -11,6 +11,14 @@ package runtime
// The primary use case is `tinygo test`, which takes some parameters (such as // The primary use case is `tinygo test`, which takes some parameters (such as
// -test.v). // -test.v).
// This is the default set of arguments, if nothing else has been set.
var args = []string{"/proc/self/exe"}
//go:linkname os_runtime_args os.runtime_args
func os_runtime_args() []string {
return args
}
var env []string var env []string
//go:linkname syscall_runtime_envs syscall.runtime_envs //go:linkname syscall_runtime_envs syscall.runtime_envs

Просмотреть файл

@ -23,16 +23,6 @@ func GOROOT() string {
return "/usr/local/go" return "/usr/local/go"
} }
// This is the default set of arguments, if nothing else has been set.
// This may be overriden by modifying this global at runtime init (for example,
// on Linux where there are real command line arguments).
var args = []string{"/proc/self/exe"}
//go:linkname os_runtime_args os.runtime_args
func os_runtime_args() []string {
return args
}
// Copy size bytes from src to dst. The memory areas must not overlap. // Copy size bytes from src to dst. The memory areas must not overlap.
// Calls to this function are converted to LLVM intrinsic calls such as // Calls to this function are converted to LLVM intrinsic calls such as
// llvm.memcpy.p0i8.p0i8.i32(dst, src, size, false). // llvm.memcpy.p0i8.p0i8.i32(dst, src, size, false).

Просмотреть файл

@ -74,28 +74,9 @@ func postinit() {}
func main(argc int32, argv *unsafe.Pointer) int { func main(argc int32, argv *unsafe.Pointer) int {
preinit() preinit()
// Make args global big enough so that it can store all command line // Store argc and argv for later use.
// arguments. Unfortunately this has to be done with some magic as the heap main_argc = argc
// is not yet initialized. main_argv = argv
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)))
}
// Obtain the initial stack pointer right before calling the run() function. // Obtain the initial stack pointer right before calling the run() function.
// The run function has been moved to a separate (non-inlined) function so // 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 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. // Must be a separate function to get the correct stack pointer.
//go:noinline //go:noinline
func runMain() { func runMain() {

Просмотреть файл

@ -26,13 +26,19 @@ func _start() {
// wasmtime ./program.wasm arg1 arg2 // wasmtime ./program.wasm arg1 arg2
func init() { func init() {
__wasm_call_ctors() __wasm_call_ctors()
}
// Read the number of args (argc) and the buffer size required to store all var args []string
// these args (argv).
//go:linkname os_runtime_args os.runtime_args
func os_runtime_args() []string {
if args == nil {
// Read the number of args (argc) and the buffer size required to store
// all these args (argv).
var argc, argv_buf_size uint32 var argc, argv_buf_size uint32
args_sizes_get(&argc, &argv_buf_size) args_sizes_get(&argc, &argv_buf_size)
if argc == 0 { if argc == 0 {
return return nil
} }
// Obtain the command line arguments // Obtain the command line arguments
@ -51,6 +57,8 @@ func init() {
args[i] = *(*string)(unsafe.Pointer(&argString)) args[i] = *(*string)(unsafe.Pointer(&argString))
} }
} }
return args
}
func ticksToNanoseconds(ticks timeUnit) int64 { func ticksToNanoseconds(ticks timeUnit) int64 {
return int64(ticks) return int64(ticks)