This commit adds the js/wasm architecture to the runtime package.
Currently WebAssembly has no support for threads yet, see
https://github.com/WebAssembly/design/issues/1073. Because of that,
there is no preemption of goroutines and no sysmon goroutine.
Design doc: https://docs.google.com/document/d/131vjr4DH6JFnb-blm_uRdaC0_Nv3OUwjEY5qVCxCup4
About WebAssembly assembly files: https://docs.google.com/document/d/1GRmy3rA4DiYtBlX-I1Jr_iHykbX8EixC3Mq0TCYqbKc
Updates #18892
Change-Id: I7f12d21b5180500d55ae9fd2f7e926a1731db391
Reviewed-on: https://go-review.googlesource.com/103877
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
async function run() {
let importObject = {
go: {
- // func wasmexit(code int32)
- "runtime.wasmexit": function (sp) {
+ // func wasmExit(code int32)
+ "runtime.wasmExit": function (sp) {
process.exit(mem().getInt32(sp + 8, true));
},
- // func wasmwrite(fd uintptr, p unsafe.Pointer, n int32)
- "runtime.wasmwrite": function (sp) {
+ // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
+ "runtime.wasmWrite": function (sp) {
const fd = getInt64(sp + 8);
const p = getInt64(sp + 16);
const n = mem().getInt32(sp + 24, true);
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cpu
+
+const CacheLineSize = 64
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+#include "funcdata.h"
+
+// makeFuncStub is the code half of the function returned by MakeFunc.
+// See the comment on the declaration of makeFuncStub in makefunc.go
+// for more details.
+// No arg size here; runtime pulls arg map out of the func value.
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$16
+ NO_LOCAL_POINTERS
+
+ MOVD CTXT, 0(SP)
+
+ Get SP
+ Get SP
+ I64ExtendUI32
+ I64Const $argframe+0(FP)
+ I64Add
+ I64Store $8
+
+ CALL ·callReflect(SB)
+ RET
+
+// methodValueCall is the code half of the function returned by makeMethodValue.
+// See the comment on the declaration of methodValueCall in makefunc.go
+// for more details.
+// No arg size here; runtime pulls arg map out of the func value.
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16
+ NO_LOCAL_POINTERS
+
+ MOVD CTXT, 0(SP)
+
+ Get SP
+ Get SP
+ I64ExtendUI32
+ I64Const $argframe+0(FP)
+ I64Add
+ I64Store $8
+
+ CALL ·callMethod(SB)
+ RET
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+TEXT runtime·rt0_go(SB), NOSPLIT, $0
+ // save m->g0 = g0
+ MOVD $runtime·g0(SB), runtime·m0+m_g0(SB)
+ // save m0 to g0->m
+ MOVD $runtime·m0(SB), runtime·g0+g_m(SB)
+ // set g to g0
+ MOVD $runtime·g0(SB), g
+ CALLNORESUME runtime·check(SB)
+ CALLNORESUME runtime·args(SB)
+ CALLNORESUME runtime·osinit(SB)
+ CALLNORESUME runtime·schedinit(SB)
+ MOVD $0, 0(SP)
+ MOVD $runtime·mainPC(SB), 8(SP)
+ CALLNORESUME runtime·newproc(SB)
+ CALL runtime·mstart(SB) // WebAssembly stack will unwind when switching to another goroutine
+ UNDEF
+
+DATA runtime·mainPC+0(SB)/8,$runtime·main(SB)
+GLOBL runtime·mainPC(SB),RODATA,$8
+
+// func checkASM() bool
+TEXT ·checkASM(SB), NOSPLIT, $0-1
+ MOVB $1, ret+0(FP)
+ RET
+
+TEXT runtime·gogo(SB), NOSPLIT, $0-8
+ MOVD buf+0(FP), R0
+ MOVD gobuf_g(R0), g
+ MOVD gobuf_sp(R0), SP
+
+ I64Load gobuf_pc(R0)
+ I32WrapI64
+ I32Const $16
+ I32ShrU
+ Set PC_F
+
+ I64Load gobuf_pc(R0)
+ I64Const $0xFFFF
+ I64And
+ I32WrapI64
+ Set PC_B
+
+ MOVD gobuf_ret(R0), RET0
+ MOVD gobuf_ctxt(R0), CTXT
+ // clear to help garbage collector
+ MOVD $0, gobuf_sp(R0)
+ MOVD $0, gobuf_ret(R0)
+ MOVD $0, gobuf_ctxt(R0)
+
+ I32Const $1
+ Return
+
+// func mcall(fn func(*g))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return. It should gogo(&g->sched)
+// to keep running g.
+TEXT runtime·mcall(SB), NOSPLIT, $0-8
+ // CTXT = fn
+ MOVD fn+0(FP), CTXT
+ // R1 = g.m
+ MOVD g_m(g), R1
+ // R2 = g0
+ MOVD m_g0(R1), R2
+
+ // save state in g->sched
+ MOVD 0(SP), g_sched+gobuf_pc(g) // caller's PC
+ MOVD $fn+0(FP), g_sched+gobuf_sp(g) // caller's SP
+ MOVD g, g_sched+gobuf_g(g)
+
+ // if g == g0 call badmcall
+ Get g
+ Get R2
+ I64Eq
+ If
+ JMP runtime·badmcall(SB)
+ End
+
+ // switch to g0's stack
+ I64Load (g_sched+gobuf_sp)(R2)
+ I64Const $8
+ I64Sub
+ I32WrapI64
+ Set SP
+
+ // set arg to current g
+ MOVD g, 0(SP)
+
+ // switch to g0
+ MOVD R2, g
+
+ // call fn
+ Get CTXT
+ I32WrapI64
+ I64Load $0
+ CALL
+
+ Get SP
+ I32Const $8
+ I32Add
+ Set SP
+
+ JMP runtime·badmcall2(SB)
+
+// func systemstack(fn func())
+TEXT runtime·systemstack(SB), NOSPLIT, $0-8
+ // R0 = fn
+ MOVD fn+0(FP), R0
+ // R1 = g.m
+ MOVD g_m(g), R1
+ // R2 = g0
+ MOVD m_g0(R1), R2
+
+ // if g == g0
+ Get g
+ Get R2
+ I64Eq
+ If
+ // no switch:
+ MOVD R0, CTXT
+
+ Get CTXT
+ I32WrapI64
+ I64Load $0
+ JMP
+ End
+
+ // if g != m.curg
+ Get g
+ I64Load m_curg(R1)
+ I64Ne
+ If
+ CALLNORESUME runtime·badsystemstack(SB)
+ End
+
+ // switch:
+
+ // save state in g->sched. Pretend to
+ // be systemstack_switch if the G stack is scanned.
+ MOVD $runtime·systemstack_switch(SB), g_sched+gobuf_pc(g)
+
+ MOVD SP, g_sched+gobuf_sp(g)
+ MOVD g, g_sched+gobuf_g(g)
+
+ // switch to g0
+ MOVD R2, g
+
+ // make it look like mstart called systemstack on g0, to stop traceback
+ I64Load (g_sched+gobuf_sp)(R2)
+ I64Const $8
+ I64Sub
+ Set R3
+
+ MOVD $runtime·mstart(SB), 0(R3)
+ MOVD R3, SP
+
+ // call fn
+ MOVD R0, CTXT
+
+ Get CTXT
+ I32WrapI64
+ I64Load $0
+ CALL
+
+ // switch back to g
+ MOVD g_m(g), R1
+ MOVD m_curg(R1), R2
+ MOVD R2, g
+ MOVD g_sched+gobuf_sp(R2), SP
+ MOVD $0, g_sched+gobuf_sp(R2)
+ RET
+
+TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
+ RET
+
+TEXT runtime·return0(SB), NOSPLIT, $0-0
+ MOVD $0, RET0
+ RET
+
+TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
+ MOVD fn+0(FP), CTXT
+
+ Get CTXT
+ I64Eqz
+ If
+ CALLNORESUME runtime·sigpanic(SB)
+ End
+
+ // caller sp after CALL
+ I64Load argp+8(FP)
+ I64Const $8
+ I64Sub
+ I32WrapI64
+ Set SP
+
+ // decrease PC_B by 1 to CALL again
+ Get SP
+ I32Load16U (SP)
+ I32Const $1
+ I32Sub
+ I32Store16 $0
+
+ // but first run the deferred function
+ Get CTXT
+ I32WrapI64
+ I64Load $0
+ JMP
+
+TEXT runtime·asminit(SB), NOSPLIT, $0-0
+ // No per-thread init.
+ RET
+
+TEXT ·publicationBarrier(SB), NOSPLIT, $0-0
+ RET
+
+TEXT runtime·procyield(SB), NOSPLIT, $0-0 // FIXME
+ RET
+
+TEXT runtime·breakpoint(SB), NOSPLIT, $0-0
+ UNDEF
+
+// Called during function prolog when more stack is needed.
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB), NOSPLIT, $0-0
+ // R1 = g.m
+ MOVD g_m(g), R1
+
+ // R2 = g0
+ MOVD m_g0(R1), R2
+
+ // Cannot grow scheduler stack (m->g0).
+ Get g
+ Get R1
+ I64Eq
+ If
+ CALLNORESUME runtime·badmorestackg0(SB)
+ End
+
+ // Cannot grow signal stack (m->gsignal).
+ Get g
+ I64Load m_gsignal(R1)
+ I64Eq
+ If
+ CALLNORESUME runtime·badmorestackgsignal(SB)
+ End
+
+ // Called from f.
+ // Set m->morebuf to f's caller.
+ MOVD 8(SP), m_morebuf+gobuf_pc(R1)
+ MOVD $16(SP), m_morebuf+gobuf_sp(R1) // f's caller's SP
+ MOVD g, m_morebuf+gobuf_g(R1)
+
+ // Set g->sched to context in f.
+ MOVD 0(SP), g_sched+gobuf_pc(g)
+ MOVD g, g_sched+gobuf_g(g)
+ MOVD $8(SP), g_sched+gobuf_sp(g) // f's SP
+ MOVD CTXT, g_sched+gobuf_ctxt(g)
+
+ // Call newstack on m->g0's stack.
+ MOVD R2, g
+ MOVD g_sched+gobuf_sp(R2), SP
+ CALL runtime·newstack(SB)
+ UNDEF // crash if newstack returns
+
+// morestack but not preserving ctxt.
+TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
+ MOVD $0, CTXT
+ JMP runtime·morestack(SB)
+
+TEXT ·asmcgocall(SB), NOSPLIT, $0-0
+ UNDEF
+
+TEXT ·cgocallback_gofunc(SB), NOSPLIT, $16-32
+ UNDEF
+
+#define DISPATCH(NAME, MAXSIZE) \
+ Get R0; \
+ I64Const $MAXSIZE; \
+ I64LeU; \
+ If; \
+ JMP NAME(SB); \
+ End
+
+TEXT reflect·call(SB), NOSPLIT, $0-0
+ JMP ·reflectcall(SB)
+
+TEXT ·reflectcall(SB), NOSPLIT, $0-32
+ I64Load f+8(FP)
+ I64Eqz
+ If
+ CALLNORESUME runtime·sigpanic(SB)
+ End
+
+ MOVW argsize+24(FP), R0
+
+ DISPATCH(runtime·call32, 32)
+ DISPATCH(runtime·call64, 64)
+ DISPATCH(runtime·call128, 128)
+ DISPATCH(runtime·call256, 256)
+ DISPATCH(runtime·call512, 512)
+ DISPATCH(runtime·call1024, 1024)
+ DISPATCH(runtime·call2048, 2048)
+ DISPATCH(runtime·call4096, 4096)
+ DISPATCH(runtime·call8192, 8192)
+ DISPATCH(runtime·call16384, 16384)
+ DISPATCH(runtime·call32768, 32768)
+ DISPATCH(runtime·call65536, 65536)
+ DISPATCH(runtime·call131072, 131072)
+ DISPATCH(runtime·call262144, 262144)
+ DISPATCH(runtime·call524288, 524288)
+ DISPATCH(runtime·call1048576, 1048576)
+ DISPATCH(runtime·call2097152, 2097152)
+ DISPATCH(runtime·call4194304, 4194304)
+ DISPATCH(runtime·call8388608, 8388608)
+ DISPATCH(runtime·call16777216, 16777216)
+ DISPATCH(runtime·call33554432, 33554432)
+ DISPATCH(runtime·call67108864, 67108864)
+ DISPATCH(runtime·call134217728, 134217728)
+ DISPATCH(runtime·call268435456, 268435456)
+ DISPATCH(runtime·call536870912, 536870912)
+ DISPATCH(runtime·call1073741824, 1073741824)
+ JMP runtime·badreflectcall(SB)
+
+#define CALLFN(NAME, MAXSIZE) \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
+ NO_LOCAL_POINTERS; \
+ MOVW argsize+24(FP), R0; \
+ \
+ Get R0; \
+ I64Eqz; \
+ Not; \
+ If; \
+ Get SP; \
+ I64Load argptr+16(FP); \
+ I32WrapI64; \
+ I64Load argsize+24(FP); \
+ I64Const $3; \
+ I64ShrU; \
+ I32WrapI64; \
+ Call runtime·wasmMove(SB); \
+ End; \
+ \
+ MOVD f+8(FP), CTXT; \
+ Get CTXT; \
+ I32WrapI64; \
+ I64Load $0; \
+ CALL; \
+ \
+ I64Load32U retoffset+28(FP); \
+ Set R0; \
+ \
+ MOVD argtype+0(FP), RET0; \
+ \
+ I64Load argptr+16(FP); \
+ Get R0; \
+ I64Add; \
+ Set RET1; \
+ \
+ Get SP; \
+ I64ExtendUI32; \
+ Get R0; \
+ I64Add; \
+ Set RET2; \
+ \
+ I64Load32U argsize+24(FP); \
+ Get R0; \
+ I64Sub; \
+ Set RET3; \
+ \
+ CALL callRet<>(SB); \
+ RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $32-0
+ NO_LOCAL_POINTERS
+ MOVD RET0, 0(SP)
+ MOVD RET1, 8(SP)
+ MOVD RET2, 16(SP)
+ MOVD RET3, 24(SP)
+ CALL runtime·reflectcallmove(SB)
+ RET
+
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
+
+TEXT runtime·goexit(SB), NOSPLIT, $0-0
+ NOP // first PC of goexit is skipped
+ CALL runtime·goexit1(SB) // does not return
+ UNDEF
+
+TEXT runtime·cgocallback(SB), NOSPLIT, $32-32
+ UNDEF
+
+// gcWriteBarrier performs a heap pointer write and informs the GC.
+//
+// gcWriteBarrier does NOT follow the Go ABI. It has two WebAssembly parameters:
+// R0: the destination of the write (i64)
+// R1: the value being written (i64)
+TEXT runtime·gcWriteBarrier(SB), NOSPLIT, $16
+ // R3 = g.m
+ MOVD g_m(g), R3
+ // R4 = p
+ MOVD m_p(R3), R4
+ // R5 = wbBuf.next
+ MOVD p_wbBuf+wbBuf_next(R4), R5
+
+ // Record value
+ MOVD R1, 0(R5)
+ // Record *slot
+ MOVD R0, 8(R5)
+
+ // Increment wbBuf.next
+ Get R5
+ I64Const $16
+ I64Add
+ Set R5
+ MOVD R5, p_wbBuf+wbBuf_next(R4)
+
+ Get R5
+ I64Load (p_wbBuf+wbBuf_end)(R4)
+ I64Eq
+ If
+ // Flush
+ MOVD R0, 0(SP)
+ MOVD R1, 8(SP)
+ CALLNORESUME runtime·wbBufFlush(SB)
+ End
+
+ // Do the write
+ MOVD R1, (R0)
+
+ RET
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT crosscall2(SB), NOSPLIT, $0
+ UNDEF
// +build !mips64le
// +build !mips
// +build !mipsle
+// +build !wasm
package runtime
// The number of logical CPUs on the local machine can be queried with NumCPU.
// This call will go away when the scheduler improves.
func GOMAXPROCS(n int) int {
+ if GOARCH == "wasm" && n > 1 {
+ n = 1 // WebAssembly has no threads yet, so only one CPU is possible.
+ }
+
lock(&sched.lock)
ret := int(gomaxprocs)
unlock(&sched.lock)
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
package runtime
typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64
typePointer, typeScalar, // i string
}
- case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x":
+ case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x", "wasm":
return []byte{
typePointer, // q *int
typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
// xxhash: https://code.google.com/p/xxhash/
// cityhash: https://code.google.com/p/cityhash/
-// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x
+// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x wasm
package runtime
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO(neelance): implement with actual atomic operations as soon as threads are available
+// See https://github.com/WebAssembly/design/issues/1073
+
+package atomic
+
+import "unsafe"
+
+//go:nosplit
+//go:noinline
+func Load(ptr *uint32) uint32 {
+ return *ptr
+}
+
+//go:nosplit
+//go:noinline
+func Loadp(ptr unsafe.Pointer) unsafe.Pointer {
+ return *(*unsafe.Pointer)(ptr)
+}
+
+//go:nosplit
+//go:noinline
+func Load64(ptr *uint64) uint64 {
+ return *ptr
+}
+
+//go:nosplit
+//go:noinline
+func Xadd(ptr *uint32, delta int32) uint32 {
+ new := *ptr + uint32(delta)
+ *ptr = new
+ return new
+}
+
+//go:nosplit
+//go:noinline
+func Xadd64(ptr *uint64, delta int64) uint64 {
+ new := *ptr + uint64(delta)
+ *ptr = new
+ return new
+}
+
+//go:nosplit
+//go:noinline
+func Xadduintptr(ptr *uintptr, delta uintptr) uintptr {
+ new := *ptr + delta
+ *ptr = new
+ return new
+}
+
+//go:nosplit
+//go:noinline
+func Xchg(ptr *uint32, new uint32) uint32 {
+ old := *ptr
+ *ptr = new
+ return old
+}
+
+//go:nosplit
+//go:noinline
+func Xchg64(ptr *uint64, new uint64) uint64 {
+ old := *ptr
+ *ptr = new
+ return old
+}
+
+//go:nosplit
+//go:noinline
+func Xchguintptr(ptr *uintptr, new uintptr) uintptr {
+ old := *ptr
+ *ptr = new
+ return old
+}
+
+//go:nosplit
+//go:noinline
+func And8(ptr *uint8, val uint8) {
+ *ptr = *ptr & val
+}
+
+//go:nosplit
+//go:noinline
+func Or8(ptr *uint8, val uint8) {
+ *ptr = *ptr | val
+}
+
+// NOTE: Do not add atomicxor8 (XOR is not idempotent).
+
+//go:nosplit
+//go:noinline
+func Cas64(ptr *uint64, old, new uint64) bool {
+ if *ptr == old {
+ *ptr = new
+ return true
+ }
+ return false
+}
+
+//go:nosplit
+//go:noinline
+func Store(ptr *uint32, val uint32) {
+ *ptr = val
+}
+
+//go:nosplit
+//go:noinline
+func Store64(ptr *uint64, val uint64) {
+ *ptr = val
+}
+
+//go:noinline
+//go:nosplit
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) {
+ *(*uintptr)(ptr) = uintptr(val)
+}
+
+//go:nosplit
+//go:noinline
+func Cas(ptr *uint32, old, new uint32) bool {
+ if *ptr == old {
+ *ptr = new
+ return true
+ }
+ return false
+}
+
+//go:nosplit
+//go:noinline
+func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
+ if *ptr == old {
+ *ptr = new
+ return true
+ }
+ return false
+}
+
+//go:nosplit
+//go:noinline
+func Casuintptr(ptr *uintptr, old, new uintptr) bool {
+ if *ptr == old {
+ *ptr = new
+ return true
+ }
+ return false
+}
+
+//go:nosplit
+//go:noinline
+func Storeuintptr(ptr *uintptr, new uintptr) {
+ *ptr = new
+}
+
+//go:nosplit
+//go:noinline
+func Loaduintptr(ptr *uintptr) uintptr {
+ return *ptr
+}
+
+//go:nosplit
+//go:noinline
+func Loaduint(ptr *uint) uint {
+ return *ptr
+}
+
+//go:nosplit
+//go:noinline
+func Loadint64(ptr *int64) int64 {
+ return *ptr
+}
+
+//go:nosplit
+//go:noinline
+func Xaddint64(ptr *int64, delta int64) int64 {
+ new := *ptr + delta
+ *ptr = new
+ return new
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !wasm
+
package atomic
import "unsafe"
MIPS64
PPC64
S390X
+ Wasm
)
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sys
+
+const (
+ ArchFamily = Wasm
+ BigEndian = false
+ CacheLineSize = 64
+ DefaultPhysPageSize = 65536
+ PCQuantum = 1
+ Int64Align = 8
+ HugePageSize = 0
+ MinFrameSize = 0
+)
+
+type Uintreg uint64
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x
+// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x wasm
package runtime
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package runtime
+
+// js/wasm has no support for threads yet. There is no preemption.
+// Waiting for a mutex or timeout is implemented as a busy loop
+// while allowing other goroutines to run.
+
+const (
+ mutex_unlocked = 0
+ mutex_locked = 1
+
+ active_spin = 4
+ active_spin_cnt = 30
+ passive_spin = 1
+)
+
+func lock(l *mutex) {
+ for l.key == mutex_locked {
+ Gosched()
+ }
+ l.key = mutex_locked
+}
+
+func unlock(l *mutex) {
+ if l.key == mutex_unlocked {
+ throw("unlock of unlocked lock")
+ }
+ l.key = mutex_unlocked
+}
+
+// One-time notifications.
+func noteclear(n *note) {
+ n.key = 0
+}
+
+func notewakeup(n *note) {
+ if n.key != 0 {
+ print("notewakeup - double wakeup (", n.key, ")\n")
+ throw("notewakeup - double wakeup")
+ }
+ n.key = 1
+}
+
+func notesleep(n *note) {
+ throw("notesleep not supported by js")
+}
+
+func notetsleep(n *note, ns int64) bool {
+ throw("notetsleep not supported by js")
+ return false
+}
+
+// same as runtime·notetsleep, but called on user g (not g0)
+func notetsleepg(n *note, ns int64) bool {
+ gp := getg()
+ if gp == gp.m.g0 {
+ throw("notetsleepg on g0")
+ }
+
+ deadline := nanotime() + ns
+ for {
+ if n.key != 0 {
+ return true
+ }
+ Gosched()
+ if ns >= 0 && nanotime() >= deadline {
+ return false
+ }
+ }
+}
// space because doing so is cheap.
// mips32 only has access to the low 2GB of virtual memory, so
// we further limit it to 31 bits.
- heapAddrBits = _64bit*48 + (1-_64bit)*(32-(sys.GoarchMips+sys.GoarchMipsle))
+ //
+ // WebAssembly currently has a limit of 4GB linear memory.
+ heapAddrBits = (_64bit*(1-sys.GoarchWasm))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle))
// maxAlloc is the maximum size of an allocation. On 64-bit,
// it's theoretically possible to allocate 1<<heapAddrBits bytes. On
_g_.m.mcache = allocmcache()
// Create initial arena growth hints.
- if sys.PtrSize == 8 {
+ if sys.PtrSize == 8 && GOARCH != "wasm" {
// On a 64-bit machine, we pick the following hints
// because:
//
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package runtime
+
+import (
+ "runtime/internal/sys"
+ "unsafe"
+)
+
+// Don't split the stack as this function may be invoked without a valid G,
+// which prevents us from allocating more stack.
+//go:nosplit
+func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer {
+ p := sysReserve(nil, n)
+ sysMap(p, n, sysStat)
+ return p
+}
+
+func sysUnused(v unsafe.Pointer, n uintptr) {
+}
+
+func sysUsed(v unsafe.Pointer, n uintptr) {
+}
+
+// Don't split the stack as this function may be invoked without a valid G,
+// which prevents us from allocating more stack.
+//go:nosplit
+func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) {
+ mSysStatDec(sysStat, n)
+}
+
+func sysFault(v unsafe.Pointer, n uintptr) {
+}
+
+var reserveEnd uintptr
+
+func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer {
+ // TODO(neelance): maybe unify with mem_plan9.go, depending on how https://github.com/WebAssembly/design/blob/master/FutureFeatures.md#finer-grained-control-over-memory turns out
+
+ if reserveEnd < lastmoduledatap.end {
+ reserveEnd = lastmoduledatap.end
+ }
+ if uintptr(v) < reserveEnd {
+ v = unsafe.Pointer(reserveEnd)
+ }
+ reserveEnd = uintptr(v) + n
+
+ current := currentMemory()
+ needed := int32(reserveEnd/sys.DefaultPhysPageSize + 1)
+ if current < needed {
+ if growMemory(needed-current) == -1 {
+ return nil
+ }
+ }
+
+ return v
+}
+
+func currentMemory() int32
+func growMemory(pages int32) int32
+
+func sysMap(v unsafe.Pointer, n uintptr, sysStat *uint64) {
+ mSysStatInc(sysStat, n)
+}
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-16
+ MOVD ptr+0(FP), R0
+ MOVD n+8(FP), R1
+
+loop:
+ Loop
+ Get R1
+ I64Eqz
+ If
+ RET
+ End
+
+ Get R0
+ I32WrapI64
+ I64Const $0
+ I64Store8 $0
+
+ Get R0
+ I64Const $1
+ I64Add
+ Set R0
+
+ Get R1
+ I64Const $1
+ I64Sub
+ Set R1
+
+ Br loop
+ End
+ UNDEF
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// void runtime·memmove(void*, void*, uintptr)
+TEXT runtime·memmove(SB), NOSPLIT, $0-24
+ MOVD to+0(FP), R0
+ MOVD from+8(FP), R1
+ MOVD n+16(FP), R2
+
+ Get R0
+ Get R1
+ I64LtU
+ If // forward
+exit_forward_64:
+ Block
+loop_forward_64:
+ Loop
+ Get R2
+ I64Const $8
+ I64LtU
+ BrIf exit_forward_64
+
+ MOVD 0(R1), 0(R0)
+
+ Get R0
+ I64Const $8
+ I64Add
+ Set R0
+
+ Get R1
+ I64Const $8
+ I64Add
+ Set R1
+
+ Get R2
+ I64Const $8
+ I64Sub
+ Set R2
+
+ Br loop_forward_64
+ End
+ End
+
+loop_forward_8:
+ Loop
+ Get R2
+ I64Eqz
+ If
+ RET
+ End
+
+ Get R0
+ I32WrapI64
+ I64Load8U (R1)
+ I64Store8 $0
+
+ Get R0
+ I64Const $1
+ I64Add
+ Set R0
+
+ Get R1
+ I64Const $1
+ I64Add
+ Set R1
+
+ Get R2
+ I64Const $1
+ I64Sub
+ Set R2
+
+ Br loop_forward_8
+ End
+
+ Else
+ // backward
+ Get R0
+ Get R2
+ I64Add
+ Set R0
+
+ Get R1
+ Get R2
+ I64Add
+ Set R1
+
+exit_backward_64:
+ Block
+loop_backward_64:
+ Loop
+ Get R2
+ I64Const $8
+ I64LtU
+ BrIf exit_backward_64
+
+ Get R0
+ I64Const $8
+ I64Sub
+ Set R0
+
+ Get R1
+ I64Const $8
+ I64Sub
+ Set R1
+
+ Get R2
+ I64Const $8
+ I64Sub
+ Set R2
+
+ MOVD 0(R1), 0(R0)
+
+ Br loop_backward_64
+ End
+ End
+
+loop_backward_8:
+ Loop
+ Get R2
+ I64Eqz
+ If
+ RET
+ End
+
+ Get R0
+ I64Const $1
+ I64Sub
+ Set R0
+
+ Get R1
+ I64Const $1
+ I64Sub
+ Set R1
+
+ Get R2
+ I64Const $1
+ I64Sub
+ Set R2
+
+ Get R0
+ I32WrapI64
+ I64Load8U (R1)
+ I64Store8 $0
+
+ Br loop_backward_8
+ End
+ End
+
+ UNDEF
// +build !nacl
// +build !linux !amd64
// +build !linux !arm64
+// +build !js
package runtime
// Allow newproc to start new Ms.
mainStarted = true
- systemstack(func() {
- newm(sysmon, nil)
- })
+ if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon
+ systemstack(func() {
+ newm(sysmon, nil)
+ })
+ }
// Lock the main goroutine onto this, the main OS thread,
// during initialization. Most programs won't care, but a few
//
// The calling thread must itself be in a known-good state.
func startTemplateThread() {
+ if GOARCH == "wasm" { // no threads on wasm yet
+ return
+ }
if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) {
return
}
gp.gcscanvalid = true
dropg()
+ if GOARCH == "wasm" { // no threads yet on wasm
+ gfput(_g_.m.p.ptr(), gp)
+ schedule() // never returns
+ }
+
if _g_.m.lockedInt != 0 {
print("invalid m->lockedInt = ", _g_.m.lockedInt, "\n")
throw("internal lockOSThread error")
// or else the m might be different in this function than in the caller.
//go:nosplit
func dolockOSThread() {
+ if GOARCH == "wasm" {
+ return // no threads on wasm yet
+ }
_g_ := getg()
_g_.m.lockedg.set(_g_)
_g_.lockedm.set(_g_.m)
// or else the m might be in different in this function than in the caller.
//go:nosplit
func dounlockOSThread() {
+ if GOARCH == "wasm" {
+ return // no threads on wasm yet
+ }
_g_ := getg()
if _g_.m.lockedInt != 0 || _g_.m.lockedExt != 0 {
return
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go_asm.h"
+#include "textflag.h"
+
+// _rt0_wasm_js does NOT follow the Go ABI. It has two WebAssembly parameters:
+// R0: argc (i32)
+// R1: argv (i32)
+TEXT _rt0_wasm_js(SB),NOSPLIT,$0
+ MOVD $runtime·wasmStack+m0Stack__size(SB), SP
+
+ Get SP
+ Get R0 // argc
+ I64ExtendUI32
+ I64Store $0
+
+ Get SP
+ Get R1 // argv
+ I64ExtendUI32
+ I64Store $8
+
+ I32Const $runtime·rt0_go(SB)
+ I32Const $16
+ I32ShrU
+ Set PC_F
+
+// Call the function for the current PC_F. Repeat until SP=0 indicates program end.
+// The WebAssembly stack may unwind, e.g. when switching goroutines.
+// The Go stack on the linear memory is then used to jump to the correct functions
+// with this loop, without having to restore the full WebAssembly stack.
+loop:
+ Loop
+ Get SP
+ I32Eqz
+ If
+ Return
+ End
+
+ Get PC_F
+ CallIndirect $0
+ Drop
+
+ Br loop
+ End
+
+TEXT _rt0_wasm_js_lib(SB),NOSPLIT,$0
+ UNDEF
throw("missing stack in newstack")
}
sp := gp.sched.sp
- if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 {
+ if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 || sys.ArchFamily == sys.Wasm {
// The call to morestack cost a word.
sp -= sys.PtrSize
}
// +build !solaris
// +build !windows
// +build !nacl
+// +build !js
package runtime
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+import (
+ "runtime/internal/sys"
+ "unsafe"
+)
+
+type m0Stack struct {
+ _ [8192 * sys.StackGuardMultiplier]byte
+}
+
+var wasmStack m0Stack
+
+func wasmMove()
+
+func wasmZero()
+
+func wasmDiv()
+
+func wasmTruncS()
+func wasmTruncU()
+
+func wasmExit(code int32)
+
+// adjust Gobuf as it if executed a call to fn with context ctxt
+// and then did an immediate gosave.
+func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
+ sp := buf.sp
+ if sys.RegSize > sys.PtrSize {
+ sp -= sys.PtrSize
+ *(*uintptr)(unsafe.Pointer(sp)) = 0
+ }
+ sp -= sys.PtrSize
+ *(*uintptr)(unsafe.Pointer(sp)) = buf.pc
+ buf.sp = sp
+ buf.pc = uintptr(fn)
+ buf.ctxt = ctxt
+}
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT runtime·wasmMove(SB), NOSPLIT, $0-0
+loop:
+ Loop
+ // *dst = *src
+ Get R0
+ Get R1
+ I64Load $0
+ I64Store $0
+
+ // n--
+ Get R2
+ I32Const $1
+ I32Sub
+ Set R2
+
+ // n == 0
+ Get R2
+ I32Eqz
+ If
+ Return
+ End
+
+ // dst += 8
+ Get R0
+ I32Const $8
+ I32Add
+ Set R0
+
+ // src += 8
+ Get R1
+ I32Const $8
+ I32Add
+ Set R1
+
+ Br loop
+ End
+ UNDEF
+
+TEXT runtime·wasmZero(SB), NOSPLIT, $0-0
+loop:
+ Loop
+ // *dst = 0
+ Get R0
+ I64Const $0
+ I64Store $0
+
+ // n--
+ Get R1
+ I32Const $1
+ I32Sub
+ Set R1
+
+ // n == 0
+ Get R1
+ I32Eqz
+ If
+ Return
+ End
+
+ // dst += 8
+ Get R0
+ I32Const $8
+ I32Add
+ Set R0
+
+ Br loop
+ End
+ UNDEF
+
+TEXT runtime·wasmDiv(SB), NOSPLIT, $0-0
+ Get R0
+ I64Const $-0x8000000000000000
+ I64Eq
+ If
+ Get R1
+ I64Const $-1
+ I64Eq
+ If
+ I64Const $-0x8000000000000000
+ Return
+ End
+ End
+ Get R0
+ Get R1
+ I64DivS
+ Return
+
+TEXT runtime·wasmTruncS(SB), NOSPLIT, $0-0
+ Get R0
+ Get R0
+ F64Ne // NaN
+ If
+ I64Const $0x8000000000000000
+ Return
+ End
+
+ Get R0
+ F64Const $9223372036854775807.
+ F64Gt
+ If
+ I64Const $0x8000000000000000
+ Return
+ End
+
+ Get R0
+ F64Const $-9223372036854775808.
+ F64Lt
+ If
+ I64Const $0x8000000000000000
+ Return
+ End
+
+ Get R0
+ I64TruncSF64
+ Return
+
+TEXT runtime·wasmTruncU(SB), NOSPLIT, $0-0
+ Get R0
+ Get R0
+ F64Ne // NaN
+ If
+ I64Const $0x8000000000000000
+ Return
+ End
+
+ Get R0
+ F64Const $18446744073709551615.
+ F64Gt
+ If
+ I64Const $0x8000000000000000
+ Return
+ End
+
+ Get R0
+ F64Const $0.
+ F64Lt
+ If
+ I64Const $0x8000000000000000
+ Return
+ End
+
+ Get R0
+ I64TruncUF64
+ Return
+
+TEXT runtime·exit(SB), NOSPLIT, $0-8
+ Call runtime·wasmExit(SB)
+ Drop
+ I32Const $0
+ Set SP
+ I32Const $1
+
+TEXT runtime·exitThread(SB), NOSPLIT, $0-0
+ UNDEF
+
+TEXT runtime·osyield(SB), NOSPLIT, $0-0
+ UNDEF
+
+TEXT runtime·usleep(SB), NOSPLIT, $0-0
+ RET // TODO(neelance): implement usleep
+
+TEXT runtime·currentMemory(SB), NOSPLIT, $0
+ Get SP
+ CurrentMemory
+ I32Store ret+0(FP)
+ RET
+
+TEXT runtime·growMemory(SB), NOSPLIT, $0
+ Get SP
+ I32Load pages+0(FP)
+ GrowMemory
+ I32Store ret+8(FP)
+ RET
+
+TEXT ·wasmExit(SB), NOSPLIT, $0
+ CallImport
+ RET
+
+TEXT ·wasmWrite(SB), NOSPLIT, $0
+ CallImport
+ RET
+
+TEXT ·nanotime(SB), NOSPLIT, $0
+ CallImport
+ RET
+
+TEXT ·walltime(SB), NOSPLIT, $0
+ CallImport
+ RET
res = md.text + uintptr(off)
}
- if res > md.etext {
+ if res > md.etext && GOARCH != "wasm" { // on wasm, functions do not live in the same address space as the linear memory
println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext))
throw("runtime: text offset out of range")
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le s390x
+// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le s390x wasm
package runtime