Commit 35ea6246 authored by Richard Musiol's avatar Richard Musiol Committed by Brad Fitzpatrick

runtime: add js/wasm architecture

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: 's avatarAustin Clements <austin@google.com>
parent cc0aaff4
......@@ -146,13 +146,13 @@ async function compile(source) {
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);
......
// 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
// 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
// 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
// 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
......@@ -8,6 +8,7 @@
// +build !mips64le
// +build !mips
// +build !mipsle
// +build !wasm
package runtime
......
......@@ -15,6 +15,10 @@ import (
// 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)
......
......@@ -2,7 +2,7 @@
// 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
......
......@@ -147,7 +147,7 @@ func infoBigStruct() []byte {
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
......
......@@ -6,7 +6,7 @@
// 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
......
// 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
}
......@@ -2,6 +2,8 @@
// 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"
......
......@@ -15,4 +15,5 @@ const (
MIPS64
PPC64
S390X
Wasm
)
// 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
......@@ -2,7 +2,7 @@
// 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
......
// 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
}
}
}
......@@ -204,7 +204,9 @@ const (
// 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
......@@ -387,7 +389,7 @@ func mallocinit() {
_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:
//
......
// 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)
}
// 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
// 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
......@@ -8,6 +8,7 @@
// +build !nacl
// +build !linux !amd64
// +build !linux !arm64
// +build !js
package runtime
......
......@@ -125,9 +125,11 @@ func main() {
// 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
......@@ -1891,6 +1893,9 @@ func newm1(mp *m) {
//
// 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
}
......@@ -2699,6 +2704,11 @@ func goexit0(gp *g) {
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")
......@@ -3497,6 +3507,9 @@ func Breakpoint() {
// 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)
......@@ -3545,6 +3558,9 @@ func lockOSThread() {
// 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
......
// 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
......@@ -993,7 +993,7 @@ func newstack() {
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
}
......
......@@ -6,6 +6,7 @@
// +build !solaris
// +build !windows
// +build !nacl
// +build !js
package runtime
......
// 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
}
// 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
......@@ -285,7 +285,7 @@ func (t *_type) textOff(off textOff) unsafe.Pointer {
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")
}
......
......@@ -2,7 +2,7 @@
// 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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment