// 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 fv+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 ·reflectcall(SB), NOSPLIT, $0-32
|
I64Load fn+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
|