diff --git a/src/internal/cpu/cpu_x86.mx b/src/internal/cpu/cpu_x86.mx index b418c188..403e963e 100644 --- a/src/internal/cpu/cpu_x86.mx +++ b/src/internal/cpu/cpu_x86.mx @@ -8,14 +8,10 @@ package cpu const CacheLinePadSize = 64 -// cpuid is implemented in cpu_x86.s. -func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) - -// xgetbv with ecx = 0 is implemented in cpu_x86.s. -func xgetbv() (eax, edx uint32) - -// getGOAMD64level is implemented in cpu_x86.s. Returns number in [1,4]. -func getGOAMD64level() int32 +// Moxie stubs: assembly intrinsics not available, report no features. +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) { return 0, 0, 0, 0 } +func xgetbv() (eax, edx uint32) { return 0, 0 } +func getGOAMD64level() int32 { return 1 } const ( // ecx bits diff --git a/src/internal/runtime/syscall/syscall_linux.mx b/src/internal/runtime/syscall/syscall_linux.mx index 49e5f8de..ca76333c 100644 --- a/src/internal/runtime/syscall/syscall_linux.mx +++ b/src/internal/runtime/syscall/syscall_linux.mx @@ -14,7 +14,17 @@ import ( // only contains very minimal support for Linux. // Syscall6 calls system call number 'num' with arguments a1-6. -func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +// Moxie: implemented via libc syscall(2) instead of raw assembly. +func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) { + r := libc_syscall(int32(num), a1, a2, a3, a4, a5, a6) + if r < 0 { + return 0, 0, uintptr(-r) + } + return uintptr(r), 0, 0 +} + +//export syscall +func libc_syscall(num int32, a1, a2, a3, a4, a5, a6 uintptr) int64 func EpollCreate1(flags int32) (fd int32, errno uintptr) { r1, _, e := Syscall6(SYS_EPOLL_CREATE1, uintptr(flags), 0, 0, 0, 0, 0) diff --git a/src/runtime/gc_stack_portable.mx b/src/runtime/gc_stack_portable.mx index ccd2a5e7..66c14607 100644 --- a/src/runtime/gc_stack_portable.mx +++ b/src/runtime/gc_stack_portable.mx @@ -3,18 +3,17 @@ package runtime import ( + "internal/task" "runtime/volatile" "unsafe" ) -// stackChainStart anchors the compiler-generated stack chain that tracks -// heap pointers in WASM function locals. MakeGCStackSlots inserts stores -// to this global at every function entry/exit. The volatile load in -// markStack prevents LLVM from DCE'ing those stores. -// -// Declared as a regular var (not //go:extern) so the linker places it at -// a non-zero BSS address. With //go:extern, wasm-ld resolves it to -// address 0, which triggers a nil panic on the volatile load. +func gcMarkReachable() { + markStack() + findGlobals(markRoots) +} + +//go:extern runtime.stackChainStart var stackChainStart *stackChainObject type stackChainObject struct { @@ -22,16 +21,37 @@ type stackChainObject struct { numSlots uintptr } -func gcMarkReachable() { - markStack() - findGlobals(markRoots) -} - +// markStack marks all root pointers found on the stack. +// +// - Goroutine stacks are heap allocated and always reachable in some way +// (for example through internal/task.currentTask) so they will always be +// scanned. +// - The system stack (aka startup stack) is not heap allocated, so even +// though it may be referenced it will not be scanned by default. +// +// The compiler also inserts code to store all globals in a chain via +// stackChainStart. Luckily we don't need to scan these, as these globals are +// stored on the goroutine stack and are therefore already getting scanned. func markStack() { - // Force LLVM to keep stackChainStart (and the MakeGCStackSlots - // bookkeeping that writes to it) alive. + // Hack to force LLVM to consider stackChainStart to be live. + // Without this hack, loads and stores may be considered dead and objects on + // the stack might not be correctly tracked. With this volatile load, LLVM + // is forced to consider stackChainStart (and everything it points to) as + // live. volatile.LoadUint32((*uint32)(unsafe.Pointer(&stackChainStart))) - markRoots(getCurrentStackPointer(), stackTop) + + // Scan the system stack. + var sysSP uintptr + if task.OnSystemStack() { + // We are on the system stack. + // Use the current stack pointer. + sysSP = getCurrentStackPointer() + } else { + // We are in a goroutine. + // Use the saved stack pointer. + sysSP = savedStackPointer + } + markRoots(sysSP, stackTop) } // trackPointer is a stub function call inserted by the compiler during IR @@ -39,13 +59,12 @@ func markStack() { // code. func trackPointer(ptr, alloca unsafe.Pointer) -// swapStackChain is referenced by internal/task for goroutine switching. -// No-op in moxie (no goroutines). -func swapStackChain(dst **stackChainObject) {} - -// scanstack is called from internal/task via go:linkname. -func scanstack(sp uintptr) { - markRoots(sp, stackTop) +// swapStackChain swaps the stack chain. +// This is called from internal/task when switching goroutines. +func swapStackChain(dst **stackChainObject) { + *dst, stackChainStart = stackChainStart, *dst } -func gcResumeWorld() {} +func gcResumeWorld() { + // Nothing to do here (single threaded). +} diff --git a/src/runtime/panic.mx b/src/runtime/panic.mx index 0a616088..e4ec5ada 100644 --- a/src/runtime/panic.mx +++ b/src/runtime/panic.mx @@ -10,16 +10,14 @@ import ( // trap is a compiler hint that this function cannot be executed. It is // translated into either a trap instruction or a call to abort(). // -//go:nobounds -func trap() { - trap_arch() -} +//export llvm.trap +func trap() // Inline assembly stub. It is essentially C longjmp but modified a bit for the // purposes of Moxie. It restores the stack pointer and jumps to the given pc. -func moxie_longjmp(frame *deferFrame) { - moxie_longjmp_arch(frame) -} +// +//export moxie_longjmp +func moxie_longjmp(frame *deferFrame) // Compiler intrinsic. // Returns whether recover is supported on the current architecture. diff --git a/src/runtime/pipe_channel.mx b/src/runtime/pipe_channel.mx index bd026e62..be8115e5 100644 --- a/src/runtime/pipe_channel.mx +++ b/src/runtime/pipe_channel.mx @@ -319,8 +319,8 @@ func PipeChanSend(ch *channel, encoded []byte) bool { return false } ticks++ - if ticks == 60000 { - println("pipe: send stalled 60s fd=", ch.pipeBoundFd, "chan=", ch.pipeChanID) + if ticks == 3000 { + println("pipe: send stalled 3s fd=", ch.pipeBoundFd, "chan=", ch.pipeChanID) } sleepTicks(nanosecondsToTicks(1_000_000)) } @@ -355,8 +355,8 @@ func PipeChanRecv(ch *channel) ([]byte, bool) { } if n < 0 { ticks++ - if ticks == 60000 { - println("pipe: recv stalled 60s fd=", ch.pipeBoundFd, "chan=", ch.pipeChanID) + if ticks == 3000 { + println("pipe: recv stalled 3s fd=", ch.pipeBoundFd, "chan=", ch.pipeChanID) } sleepTicks(nanosecondsToTicks(1_000_000)) continue diff --git a/src/runtime/poll.mx b/src/runtime/poll.mx index fad0a083..f117727c 100644 --- a/src/runtime/poll.mx +++ b/src/runtime/poll.mx @@ -1,5 +1,3 @@ -//go:build !moxie.wasm - package runtime // Netpoller for moxie on Linux using epoll. diff --git a/src/runtime/runtime.mx b/src/runtime/runtime.mx index d66ab00c..d9800831 100644 --- a/src/runtime/runtime.mx +++ b/src/runtime/runtime.mx @@ -57,6 +57,12 @@ func memzero(ptr unsafe.Pointer, size uintptr) // the current stack pointer in a platform-independent way. func stacksave() unsafe.Pointer +// Special LLVM intrinsic that returns the SP register on entry to the calling +// function. +// +//export llvm.sponentry.p0 +func llvm_sponentry() unsafe.Pointer + //export strlen func strlen(ptr unsafe.Pointer) uintptr diff --git a/src/runtime/runtime_wasm.mx b/src/runtime/runtime_wasm.mx index 8e024c86..0e894082 100644 --- a/src/runtime/runtime_wasm.mx +++ b/src/runtime/runtime_wasm.mx @@ -67,7 +67,7 @@ func growHeap() bool { if oldPages == -1 { return false } - setHeapEnd(uintptr(oldPages+8) * wasmPageSize) + heapEnd = uintptr(oldPages+8) * wasmPageSize return true } @@ -76,7 +76,7 @@ func growHeap() bool { //go:extern __data_start var globalsStart [0]byte -//go:extern __heap_base +//go:extern __data_end var globalsEnd [0]byte // Timing functions for WASM. @@ -153,11 +153,9 @@ func wasmAlloc(size int32) int32 { // //export _start func _start() { - stackTop = getCurrentStackPointer() allocateHeap() initRand() initHeap() - wasmTickOffset = monotime() initAll() wasmExportsReady = true callMain() diff --git a/src/syscall/syscall_linux.mx b/src/syscall/syscall_linux.mx index d733ca9b..c15dfafd 100644 --- a/src/syscall/syscall_linux.mx +++ b/src/syscall/syscall_linux.mx @@ -27,10 +27,10 @@ import ( // inbetween. //go:linkname runtime_entersyscall runtime.entersyscall -func runtime_entersyscall() +func runtime_entersyscall() {} //go:linkname runtime_exitsyscall runtime.exitsyscall -func runtime_exitsyscall() +func runtime_exitsyscall() {} // N.B. For the Syscall functions below: // @@ -99,8 +99,14 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) return } -func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) -func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1 uintptr, err Errno) +func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) { + r1, r2, _ = RawSyscall6(trap, a1, a2, a3, 0, 0, 0) + return +} +func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1 uintptr, err Errno) { + r1, _, err = RawSyscall6(trap, a1, a2, a3, 0, 0, 0) + return +} /* * Wrapped @@ -1101,7 +1107,11 @@ func Getpgrp() (pid int) { // all threads are in sync. // //go:uintptrescapes -func runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +func runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { + // Moxie: single-threaded, just do the syscall directly. + r1, r2, err = RawSyscall6(trap, a1, a2, a3, a4, a5, a6) + return +} // AllThreadsSyscall performs a syscall on each OS thread of the Go // runtime. It first invokes the syscall on one thread. Should that