mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 10:37:46 +00:00
8cd2a462b9
There is no reason to specialize this per chip as it is only ever used for JavaScript. Not only that, it is causing confusion and is yet another quirk to learn when porting the runtime to a new microcontroller.
176 lines
4.2 KiB
Go
176 lines
4.2 KiB
Go
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal
|
|
// +build !nintendoswitch
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
//export putchar
|
|
func _putchar(c int) int
|
|
|
|
//export usleep
|
|
func usleep(usec uint) int
|
|
|
|
//export malloc
|
|
func malloc(size uintptr) unsafe.Pointer
|
|
|
|
//export mmap
|
|
func mmap(addr unsafe.Pointer, length, prot, flags, fd int, offset int) unsafe.Pointer
|
|
|
|
//export abort
|
|
func abort()
|
|
|
|
//export exit
|
|
func exit(code int)
|
|
|
|
//export clock_gettime
|
|
func clock_gettime(clk_id int32, ts *timespec)
|
|
|
|
type timeUnit int64
|
|
|
|
// Note: tv_sec and tv_nsec vary in size by platform. They are 32-bit on 32-bit
|
|
// systems and 64-bit on 64-bit systems (at least on macOS/Linux), so we can
|
|
// simply use the 'int' type which does the same.
|
|
type timespec struct {
|
|
tv_sec int // time_t: follows the platform bitness
|
|
tv_nsec int // long: on Linux and macOS, follows the platform bitness
|
|
}
|
|
|
|
const CLOCK_MONOTONIC_RAW = 4
|
|
|
|
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()
|
|
|
|
// 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)))
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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) {
|
|
_putchar(int(c))
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
// Return monotonic time in nanoseconds.
|
|
//
|
|
// TODO: noescape
|
|
func monotime() uint64 {
|
|
ts := timespec{}
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts)
|
|
return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec)
|
|
}
|
|
|
|
func ticks() timeUnit {
|
|
return timeUnit(monotime())
|
|
}
|
|
|
|
//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() {
|
|
}
|