mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 18:47:47 +00:00
fb33f3813d
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.
223 lines
5.6 KiB
Go
223 lines
5.6 KiB
Go
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal
|
|
// +build !nintendoswitch
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
//export write
|
|
func libc_write(fd int32, buf unsafe.Pointer, count uint) int
|
|
|
|
//export usleep
|
|
func usleep(usec uint) int
|
|
|
|
//export malloc
|
|
func malloc(size uintptr) unsafe.Pointer
|
|
|
|
// void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
|
// Note: off_t is defined as int64 because:
|
|
// - musl (used on Linux) always defines it as int64
|
|
// - darwin is practically always 64-bit anyway
|
|
//export mmap
|
|
func mmap(addr unsafe.Pointer, length uintptr, prot, flags, fd int, offset int64) unsafe.Pointer
|
|
|
|
//export abort
|
|
func abort()
|
|
|
|
//export exit
|
|
func exit(code int)
|
|
|
|
//export clock_gettime
|
|
func libc_clock_gettime(clk_id int32, ts *timespec)
|
|
|
|
//export __clock_gettime64
|
|
func libc_clock_gettime64(clk_id int32, ts *timespec)
|
|
|
|
// Portable (64-bit) variant of clock_gettime.
|
|
func clock_gettime(clk_id int32, ts *timespec) {
|
|
if TargetBits == 32 {
|
|
// This is a 32-bit architecture (386, arm, etc).
|
|
// We would like to use the 64-bit version of this function so that
|
|
// binaries will continue to run after Y2038.
|
|
// For more information:
|
|
// - https://musl.libc.org/time64.html
|
|
// - https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
|
|
libc_clock_gettime64(clk_id, ts)
|
|
} else {
|
|
// This is a 64-bit architecture (amd64, arm64, etc).
|
|
// Use the regular variant, because it already fixes the Y2038 problem
|
|
// by using 64-bit integer types.
|
|
libc_clock_gettime(clk_id, ts)
|
|
}
|
|
}
|
|
|
|
type timeUnit int64
|
|
|
|
// Note: tv_sec and tv_nsec normally vary in size by platform. However, we're
|
|
// using the time64 variant (see clock_gettime above), so the formats are the
|
|
// same between 32-bit and 64-bit architectures.
|
|
// There is one issue though: on big-endian systems, tv_nsec would be incorrect.
|
|
// But we don't support big-endian systems yet (as of 2021) so this is fine.
|
|
type timespec struct {
|
|
tv_sec int64 // time_t with time64 support (always 64-bit)
|
|
tv_nsec int64 // unsigned 64-bit integer on all time64 platforms
|
|
}
|
|
|
|
var stackTop uintptr
|
|
|
|
func postinit() {}
|
|
|
|
// Entry point for Go. Initialize all packages and call main.main().
|
|
//export main
|
|
func main(argc int32, argv *unsafe.Pointer) int {
|
|
preinit()
|
|
|
|
// 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
|
|
// that the correct stack pointer is read.
|
|
stackTop = getCurrentStackPointer()
|
|
runMain()
|
|
|
|
// For libc compatibility.
|
|
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() {
|
|
run()
|
|
}
|
|
|
|
//go:extern environ
|
|
var environ *unsafe.Pointer
|
|
|
|
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
|
func syscall_runtime_envs() []string {
|
|
// Count how many environment variables there are.
|
|
env := environ
|
|
numEnvs := 0
|
|
for *env != nil {
|
|
numEnvs++
|
|
env = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(env)) + unsafe.Sizeof(environ)))
|
|
}
|
|
|
|
// Create a string slice of all environment variables.
|
|
// This requires just a single heap allocation.
|
|
env = environ
|
|
envs := make([]string, 0, numEnvs)
|
|
for *env != nil {
|
|
ptr := *env
|
|
length := strlen(ptr)
|
|
s := _string{
|
|
ptr: (*byte)(ptr),
|
|
length: length,
|
|
}
|
|
envs = append(envs, *(*string)(unsafe.Pointer(&s)))
|
|
env = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(env)) + unsafe.Sizeof(environ)))
|
|
}
|
|
|
|
return envs
|
|
}
|
|
|
|
func putchar(c byte) {
|
|
buf := [1]byte{c}
|
|
libc_write(1, unsafe.Pointer(&buf[0]), 1)
|
|
}
|
|
|
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
|
// The OS API works in nanoseconds so no conversion necessary.
|
|
return int64(ticks)
|
|
}
|
|
|
|
func nanosecondsToTicks(ns int64) timeUnit {
|
|
// The OS API works in nanoseconds so no conversion necessary.
|
|
return timeUnit(ns)
|
|
}
|
|
|
|
func sleepTicks(d timeUnit) {
|
|
// timeUnit is in nanoseconds, so need to convert to microseconds here.
|
|
usleep(uint(d) / 1000)
|
|
}
|
|
|
|
func getTime(clock int32) uint64 {
|
|
ts := timespec{}
|
|
clock_gettime(clock, &ts)
|
|
return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec)
|
|
}
|
|
|
|
// Return monotonic time in nanoseconds.
|
|
func monotime() uint64 {
|
|
return getTime(clock_MONOTONIC_RAW)
|
|
}
|
|
|
|
func ticks() timeUnit {
|
|
return timeUnit(monotime())
|
|
}
|
|
|
|
//go:linkname now time.now
|
|
func now() (sec int64, nsec int32, mono int64) {
|
|
ts := timespec{}
|
|
clock_gettime(clock_REALTIME, &ts)
|
|
sec = int64(ts.tv_sec)
|
|
nsec = int32(ts.tv_nsec)
|
|
mono = nanotime()
|
|
return
|
|
}
|
|
|
|
//go:linkname syscall_Exit syscall.Exit
|
|
func syscall_Exit(code int) {
|
|
exit(code)
|
|
}
|
|
|
|
func extalloc(size uintptr) unsafe.Pointer {
|
|
return malloc(size)
|
|
}
|
|
|
|
//export free
|
|
func extfree(ptr unsafe.Pointer)
|
|
|
|
// TinyGo does not yet support any form of parallelism on an OS, so these can be
|
|
// left empty.
|
|
|
|
//go:linkname procPin sync/atomic.runtime_procPin
|
|
func procPin() {
|
|
}
|
|
|
|
//go:linkname procUnpin sync/atomic.runtime_procUnpin
|
|
func procUnpin() {
|
|
}
|