Text file src/runtime/asm_wasm.s

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  #include "go_asm.h"
     6  #include "go_tls.h"
     7  #include "funcdata.h"
     8  #include "textflag.h"
     9  
    10  TEXT runtime·rt0_go(SB), NOSPLIT|NOFRAME|TOPFRAME, $0
    11  	// save m->g0 = g0
    12  	MOVD $runtime·g0(SB), runtime·m0+m_g0(SB)
    13  	// save m0 to g0->m
    14  	MOVD $runtime·m0(SB), runtime·g0+g_m(SB)
    15  	// set g to g0
    16  	MOVD $runtime·g0(SB), g
    17  	CALLNORESUME runtime·check(SB)
    18  #ifdef GOOS_js
    19  	CALLNORESUME runtime·args(SB)
    20  #endif
    21  	CALLNORESUME runtime·osinit(SB)
    22  	CALLNORESUME runtime·schedinit(SB)
    23  	MOVD $runtime·mainPC(SB), 0(SP)
    24  	CALLNORESUME runtime·newproc(SB)
    25  	CALL runtime·mstart(SB) // WebAssembly stack will unwind when switching to another goroutine
    26  	UNDEF
    27  
    28  TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0
    29  	CALL	runtime·mstart0(SB)
    30  	RET // not reached
    31  
    32  DATA  runtime·mainPC+0(SB)/8,$runtime·main(SB)
    33  GLOBL runtime·mainPC(SB),RODATA,$8
    34  
    35  TEXT runtime·gogo(SB), NOSPLIT, $0-8
    36  	MOVD buf+0(FP), R0
    37  	MOVD gobuf_g(R0), R1
    38  	MOVD 0(R1), R2	// make sure g != nil
    39  	MOVD R1, g
    40  	MOVD gobuf_sp(R0), SP
    41  
    42  	// Put target PC at -8(SP), wasm_pc_f_loop will pick it up
    43  	Get SP
    44  	I32Const $8
    45  	I32Sub
    46  	I64Load gobuf_pc(R0)
    47  	I64Store $0
    48  
    49  	MOVD gobuf_ctxt(R0), CTXT
    50  	// clear to help garbage collector
    51  	MOVD $0, gobuf_sp(R0)
    52  	MOVD $0, gobuf_ctxt(R0)
    53  
    54  	I32Const $1
    55  	Return
    56  
    57  // func mcall(fn func(*g))
    58  // Switch to m->g0's stack, call fn(g).
    59  // Fn must never return. It should gogo(&g->sched)
    60  // to keep running g.
    61  TEXT runtime·mcall(SB), NOSPLIT, $0-8
    62  	// CTXT = fn
    63  	MOVD fn+0(FP), CTXT
    64  	// R1 = g.m
    65  	MOVD g_m(g), R1
    66  	// R2 = g0
    67  	MOVD m_g0(R1), R2
    68  
    69  	// save state in g->sched
    70  	MOVD 0(SP), g_sched+gobuf_pc(g)     // caller's PC
    71  	MOVD $fn+0(FP), g_sched+gobuf_sp(g) // caller's SP
    72  
    73  	// if g == g0 call badmcall
    74  	Get g
    75  	Get R2
    76  	I64Eq
    77  	If
    78  		JMP runtime·badmcall(SB)
    79  	End
    80  
    81  	// switch to g0's stack
    82  	I64Load (g_sched+gobuf_sp)(R2)
    83  	I64Const $8
    84  	I64Sub
    85  	I32WrapI64
    86  	Set SP
    87  
    88  	// set arg to current g
    89  	MOVD g, 0(SP)
    90  
    91  	// switch to g0
    92  	MOVD R2, g
    93  
    94  	// call fn
    95  	Get CTXT
    96  	I32WrapI64
    97  	I64Load $0
    98  	CALL
    99  
   100  	Get SP
   101  	I32Const $8
   102  	I32Add
   103  	Set SP
   104  
   105  	JMP runtime·badmcall2(SB)
   106  
   107  // func systemstack(fn func())
   108  TEXT runtime·systemstack(SB), NOSPLIT, $0-8
   109  	// R0 = fn
   110  	MOVD fn+0(FP), R0
   111  	// R1 = g.m
   112  	MOVD g_m(g), R1
   113  	// R2 = g0
   114  	MOVD m_g0(R1), R2
   115  
   116  	// if g == g0
   117  	Get g
   118  	Get R2
   119  	I64Eq
   120  	If
   121  		// no switch:
   122  		MOVD R0, CTXT
   123  
   124  		Get CTXT
   125  		I32WrapI64
   126  		I64Load $0
   127  		JMP
   128  	End
   129  
   130  	// if g != m.curg
   131  	Get g
   132  	I64Load m_curg(R1)
   133  	I64Ne
   134  	If
   135  		CALLNORESUME runtime·badsystemstack(SB)
   136  		CALLNORESUME runtime·abort(SB)
   137  	End
   138  
   139  	// switch:
   140  
   141  	// save state in g->sched. Pretend to
   142  	// be systemstack_switch if the G stack is scanned.
   143  	MOVD $runtime·systemstack_switch(SB), g_sched+gobuf_pc(g)
   144  
   145  	MOVD SP, g_sched+gobuf_sp(g)
   146  
   147  	// switch to g0
   148  	MOVD R2, g
   149  
   150  	// make it look like mstart called systemstack on g0, to stop traceback
   151  	I64Load (g_sched+gobuf_sp)(R2)
   152  	I64Const $8
   153  	I64Sub
   154  	Set R3
   155  
   156  	MOVD $runtime·mstart(SB), 0(R3)
   157  	MOVD R3, SP
   158  
   159  	// call fn
   160  	MOVD R0, CTXT
   161  
   162  	Get CTXT
   163  	I32WrapI64
   164  	I64Load $0
   165  	CALL
   166  
   167  	// switch back to g
   168  	MOVD g_m(g), R1
   169  	MOVD m_curg(R1), R2
   170  	MOVD R2, g
   171  	MOVD g_sched+gobuf_sp(R2), SP
   172  	MOVD $0, g_sched+gobuf_sp(R2)
   173  	RET
   174  
   175  TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
   176  	RET
   177  
   178  TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0
   179  	UNDEF
   180  
   181  TEXT runtime·asminit(SB), NOSPLIT, $0-0
   182  	// No per-thread init.
   183  	RET
   184  
   185  TEXT ·publicationBarrier(SB), NOSPLIT, $0-0
   186  	RET
   187  
   188  TEXT runtime·procyieldAsm(SB), NOSPLIT, $0-0 // FIXME
   189  	RET
   190  
   191  TEXT runtime·breakpoint(SB), NOSPLIT, $0-0
   192  	UNDEF
   193  
   194  // func switchToCrashStack0(fn func())
   195  TEXT runtime·switchToCrashStack0(SB), NOSPLIT, $0-8
   196  	MOVD fn+0(FP), CTXT	// context register
   197  	MOVD	g_m(g), R2	// curm
   198  
   199  	// set g to gcrash
   200  	MOVD	$runtime·gcrash(SB), g	// g = &gcrash
   201  	MOVD	R2, g_m(g)	// g.m = curm
   202  	MOVD	g, m_g0(R2)	// curm.g0 = g
   203  
   204  	// switch to crashstack
   205  	I64Load (g_stack+stack_hi)(g)
   206  	I64Const $(-4*8)
   207  	I64Add
   208  	I32WrapI64
   209  	Set SP
   210  
   211  	// call target function
   212  	Get CTXT
   213  	I32WrapI64
   214  	I64Load $0
   215  	CALL
   216  
   217  	// should never return
   218  	CALL	runtime·abort(SB)
   219  	UNDEF
   220  
   221  // Called during function prolog when more stack is needed.
   222  //
   223  // The traceback routines see morestack on a g0 as being
   224  // the top of a stack (for example, morestack calling newstack
   225  // calling the scheduler calling newm calling gc), so we must
   226  // record an argument size. For that purpose, it has no arguments.
   227  TEXT runtime·morestack(SB), NOSPLIT, $0-0
   228  	// R1 = g.m
   229  	MOVD g_m(g), R1
   230  
   231  	// R2 = g0
   232  	MOVD m_g0(R1), R2
   233  
   234  	// Set g->sched to context in f.
   235  	NOP	SP	// tell vet SP changed - stop checking offsets
   236  	MOVD 0(SP), g_sched+gobuf_pc(g)
   237  	MOVD $8(SP), g_sched+gobuf_sp(g) // f's SP
   238  	MOVD CTXT, g_sched+gobuf_ctxt(g)
   239  
   240  	// Cannot grow scheduler stack (m->g0).
   241  	Get g
   242  	Get R2
   243  	I64Eq
   244  	If
   245  		CALLNORESUME runtime·badmorestackg0(SB)
   246  		CALLNORESUME runtime·abort(SB)
   247  	End
   248  
   249  	// Cannot grow signal stack (m->gsignal).
   250  	Get g
   251  	I64Load m_gsignal(R1)
   252  	I64Eq
   253  	If
   254  		CALLNORESUME runtime·badmorestackgsignal(SB)
   255  		CALLNORESUME runtime·abort(SB)
   256  	End
   257  
   258  	// Called from f.
   259  	// Set m->morebuf to f's caller.
   260  	MOVD 8(SP), m_morebuf+gobuf_pc(R1)
   261  	MOVD $16(SP), m_morebuf+gobuf_sp(R1) // f's caller's SP
   262  	MOVD g, m_morebuf+gobuf_g(R1)
   263  
   264  	// Call newstack on m->g0's stack.
   265  	MOVD R2, g
   266  	MOVD g_sched+gobuf_sp(R2), SP
   267  	CALL runtime·newstack(SB)
   268  	UNDEF // crash if newstack returns
   269  
   270  // morestack but not preserving ctxt.
   271  TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
   272  	MOVD $0, CTXT
   273  	JMP runtime·morestack(SB)
   274  
   275  TEXT ·asmcgocall(SB), NOSPLIT, $0-0
   276  	UNDEF
   277  
   278  #define DISPATCH(NAME, MAXSIZE) \
   279  	Get R0; \
   280  	I64Const $MAXSIZE; \
   281  	I64LeU; \
   282  	If; \
   283  		JMP NAME(SB); \
   284  	End
   285  
   286  TEXT ·reflectcall(SB), NOSPLIT, $0-48
   287  	I64Load fn+8(FP)
   288  	I64Eqz
   289  	If
   290  		CALLNORESUME runtime·sigpanic<ABIInternal>(SB)
   291  	End
   292  
   293  	MOVW frameSize+32(FP), R0
   294  
   295  	DISPATCH(runtime·call16, 16)
   296  	DISPATCH(runtime·call32, 32)
   297  	DISPATCH(runtime·call64, 64)
   298  	DISPATCH(runtime·call128, 128)
   299  	DISPATCH(runtime·call256, 256)
   300  	DISPATCH(runtime·call512, 512)
   301  	DISPATCH(runtime·call1024, 1024)
   302  	DISPATCH(runtime·call2048, 2048)
   303  	DISPATCH(runtime·call4096, 4096)
   304  	DISPATCH(runtime·call8192, 8192)
   305  	DISPATCH(runtime·call16384, 16384)
   306  	DISPATCH(runtime·call32768, 32768)
   307  	DISPATCH(runtime·call65536, 65536)
   308  	DISPATCH(runtime·call131072, 131072)
   309  	DISPATCH(runtime·call262144, 262144)
   310  	DISPATCH(runtime·call524288, 524288)
   311  	DISPATCH(runtime·call1048576, 1048576)
   312  	DISPATCH(runtime·call2097152, 2097152)
   313  	DISPATCH(runtime·call4194304, 4194304)
   314  	DISPATCH(runtime·call8388608, 8388608)
   315  	DISPATCH(runtime·call16777216, 16777216)
   316  	DISPATCH(runtime·call33554432, 33554432)
   317  	DISPATCH(runtime·call67108864, 67108864)
   318  	DISPATCH(runtime·call134217728, 134217728)
   319  	DISPATCH(runtime·call268435456, 268435456)
   320  	DISPATCH(runtime·call536870912, 536870912)
   321  	DISPATCH(runtime·call1073741824, 1073741824)
   322  	JMP runtime·badreflectcall(SB)
   323  
   324  #define CALLFN(NAME, MAXSIZE) \
   325  TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
   326  	NO_LOCAL_POINTERS; \
   327  	MOVW stackArgsSize+24(FP), R0; \
   328  	\
   329  	Get R0; \
   330  	I64Eqz; \
   331  	Not; \
   332  	If; \
   333  		Get SP; \
   334  		I64Load stackArgs+16(FP); \
   335  		I32WrapI64; \
   336  		I64Load stackArgsSize+24(FP); \
   337  		I32WrapI64; \
   338  		MemoryCopy; \
   339  	End; \
   340  	\
   341  	MOVD f+8(FP), CTXT; \
   342  	Get CTXT; \
   343  	I32WrapI64; \
   344  	I64Load $0; \
   345  	CALL; \
   346  	\
   347  	I64Load32U stackRetOffset+28(FP); \
   348  	Set R0; \
   349  	\
   350  	MOVD stackArgsType+0(FP), RET0; \
   351  	\
   352  	I64Load stackArgs+16(FP); \
   353  	Get R0; \
   354  	I64Add; \
   355  	Set RET1; \
   356  	\
   357  	Get SP; \
   358  	I64ExtendI32U; \
   359  	Get R0; \
   360  	I64Add; \
   361  	Set RET2; \
   362  	\
   363  	I64Load32U stackArgsSize+24(FP); \
   364  	Get R0; \
   365  	I64Sub; \
   366  	Set RET3; \
   367  	\
   368  	CALL callRet<>(SB); \
   369  	RET
   370  
   371  // callRet copies return values back at the end of call*. This is a
   372  // separate function so it can allocate stack space for the arguments
   373  // to reflectcallmove. It does not follow the Go ABI; it expects its
   374  // arguments in registers.
   375  TEXT callRet<>(SB), NOSPLIT, $40-0
   376  	NO_LOCAL_POINTERS
   377  	MOVD RET0, 0(SP)
   378  	MOVD RET1, 8(SP)
   379  	MOVD RET2, 16(SP)
   380  	MOVD RET3, 24(SP)
   381  	MOVD $0,   32(SP)
   382  	CALL runtime·reflectcallmove(SB)
   383  	RET
   384  
   385  CALLFN(·call16, 16)
   386  CALLFN(·call32, 32)
   387  CALLFN(·call64, 64)
   388  CALLFN(·call128, 128)
   389  CALLFN(·call256, 256)
   390  CALLFN(·call512, 512)
   391  CALLFN(·call1024, 1024)
   392  CALLFN(·call2048, 2048)
   393  CALLFN(·call4096, 4096)
   394  CALLFN(·call8192, 8192)
   395  CALLFN(·call16384, 16384)
   396  CALLFN(·call32768, 32768)
   397  CALLFN(·call65536, 65536)
   398  CALLFN(·call131072, 131072)
   399  CALLFN(·call262144, 262144)
   400  CALLFN(·call524288, 524288)
   401  CALLFN(·call1048576, 1048576)
   402  CALLFN(·call2097152, 2097152)
   403  CALLFN(·call4194304, 4194304)
   404  CALLFN(·call8388608, 8388608)
   405  CALLFN(·call16777216, 16777216)
   406  CALLFN(·call33554432, 33554432)
   407  CALLFN(·call67108864, 67108864)
   408  CALLFN(·call134217728, 134217728)
   409  CALLFN(·call268435456, 268435456)
   410  CALLFN(·call536870912, 536870912)
   411  CALLFN(·call1073741824, 1073741824)
   412  
   413  TEXT runtime·goexit(SB), NOSPLIT|TOPFRAME, $0-0
   414  	NOP // first PC of goexit is skipped
   415  	CALL runtime·goexit1(SB) // does not return
   416  	UNDEF
   417  
   418  TEXT runtime·cgocallback(SB), NOSPLIT, $0-24
   419  	UNDEF
   420  
   421  // gcWriteBarrier informs the GC about heap pointer writes.
   422  //
   423  // gcWriteBarrier does NOT follow the Go ABI. It accepts the
   424  // number of bytes of buffer needed as a wasm argument
   425  // (put on the TOS by the caller, lives in local R0 in this body)
   426  // and returns a pointer to the buffer space as a wasm result
   427  // (left on the TOS in this body, appears on the wasm stack
   428  // in the caller).
   429  TEXT gcWriteBarrier<>(SB), NOSPLIT, $0
   430  	Loop
   431  		// R3 = g.m
   432  		MOVD g_m(g), R3
   433  		// R4 = p
   434  		MOVD m_p(R3), R4
   435  		// R5 = wbBuf.next
   436  		MOVD p_wbBuf+wbBuf_next(R4), R5
   437  
   438  		// Increment wbBuf.next
   439  		Get R5
   440  		Get R0
   441  		I64Add
   442  		Set R5
   443  
   444  		// Is the buffer full?
   445  		Get R5
   446  		I64Load (p_wbBuf+wbBuf_end)(R4)
   447  		I64LeU
   448  		If
   449  			// Commit to the larger buffer.
   450  			MOVD R5, p_wbBuf+wbBuf_next(R4)
   451  
   452  			// Make return value (the original next position)
   453  			Get R5
   454  			Get R0
   455  			I64Sub
   456  
   457  			Return
   458  		End
   459  
   460  		// Flush
   461  		CALLNORESUME runtime·wbBufFlush(SB)
   462  
   463  		// Retry
   464  		Br $0
   465  	End
   466  
   467  TEXT runtime·gcWriteBarrier1<ABIInternal>(SB),NOSPLIT,$0
   468  	I64Const $8
   469  	Call	gcWriteBarrier<>(SB)
   470  	Return
   471  TEXT runtime·gcWriteBarrier2<ABIInternal>(SB),NOSPLIT,$0
   472  	I64Const $16
   473  	Call	gcWriteBarrier<>(SB)
   474  	Return
   475  TEXT runtime·gcWriteBarrier3<ABIInternal>(SB),NOSPLIT,$0
   476  	I64Const $24
   477  	Call	gcWriteBarrier<>(SB)
   478  	Return
   479  TEXT runtime·gcWriteBarrier4<ABIInternal>(SB),NOSPLIT,$0
   480  	I64Const $32
   481  	Call	gcWriteBarrier<>(SB)
   482  	Return
   483  TEXT runtime·gcWriteBarrier5<ABIInternal>(SB),NOSPLIT,$0
   484  	I64Const $40
   485  	Call	gcWriteBarrier<>(SB)
   486  	Return
   487  TEXT runtime·gcWriteBarrier6<ABIInternal>(SB),NOSPLIT,$0
   488  	I64Const $48
   489  	Call	gcWriteBarrier<>(SB)
   490  	Return
   491  TEXT runtime·gcWriteBarrier7<ABIInternal>(SB),NOSPLIT,$0
   492  	I64Const $56
   493  	Call	gcWriteBarrier<>(SB)
   494  	Return
   495  TEXT runtime·gcWriteBarrier8<ABIInternal>(SB),NOSPLIT,$0
   496  	I64Const $64
   497  	Call	gcWriteBarrier<>(SB)
   498  	Return
   499  
   500  TEXT wasm_pc_f_loop(SB),NOSPLIT,$0
   501  // Call the function for the current PC_F. Repeat until PAUSE != 0 indicates pause or exit.
   502  // The WebAssembly stack may unwind, e.g. when switching goroutines.
   503  // The Go stack on the linear memory is then used to jump to the correct functions
   504  // with this loop, without having to restore the full WebAssembly stack.
   505  // It is expected to have a pending call before entering the loop, so check PAUSE first.
   506  	Get PAUSE
   507  	I32Eqz
   508  	If
   509  	loop:
   510  		Loop
   511  			// Get PC_B & PC_F from -8(SP)
   512  			Get SP
   513  			I32Const $8
   514  			I32Sub
   515  			I32Load16U $0 // PC_B
   516  
   517  			Get SP
   518  			I32Const $8
   519  			I32Sub
   520  			I32Load $2 // PC_F
   521  
   522  			CallIndirect $0
   523  			Drop
   524  
   525  			Get PAUSE
   526  			I32Eqz
   527  			BrIf loop
   528  		End
   529  	End
   530  
   531  	I32Const $0
   532  	Set PAUSE
   533  
   534  	Return
   535  
   536  // wasm_pc_f_loop_export is like wasm_pc_f_loop, except that this takes an
   537  // argument (on Wasm stack) that is a PC_F, and the loop stops when we get
   538  // to that PC in a normal return (not unwinding).
   539  // This is for handling an wasmexport function when it needs to switch the
   540  // stack.
   541  TEXT wasm_pc_f_loop_export(SB),NOSPLIT,$0
   542  	Get PAUSE
   543  	I32Eqz
   544  outer:
   545  	If
   546  		// R1 is whether a function return normally (0) or unwinding (1).
   547  		// Start with unwinding.
   548  		I32Const $1
   549  		Set R1
   550  	loop:
   551  		Loop
   552  			// Get PC_F & PC_B from -8(SP)
   553  			Get SP
   554  			I32Const $8
   555  			I32Sub
   556  			I32Load $2 // PC_F
   557  			Tee R2
   558  
   559  			Get R0
   560  			I32Eq
   561  			If // PC_F == R0, we're at the stop PC
   562  				Get R1
   563  				I32Eqz
   564  				// Break if it is a normal return
   565  				BrIf outer // actually jump to after the corresponding End
   566  			End
   567  
   568  			Get SP
   569  			I32Const $8
   570  			I32Sub
   571  			I32Load16U $0 // PC_B
   572  
   573  			Get R2 // PC_F
   574  			CallIndirect $0
   575  			Set R1 // save return/unwinding state for next iteration
   576  
   577  			Get PAUSE
   578  			I32Eqz
   579  			BrIf loop
   580  		End
   581  	End
   582  
   583  	I32Const $0
   584  	Set PAUSE
   585  
   586  	Return
   587  
   588  TEXT wasm_export_lib(SB),NOSPLIT,$0
   589  	UNDEF
   590  
   591  TEXT runtime·pause(SB), NOSPLIT, $0-8
   592  	MOVD newsp+0(FP), SP
   593  	I32Const $1
   594  	Set PAUSE
   595  	RETUNWIND
   596  
   597  // Called if a wasmexport function is called before runtime initialization
   598  TEXT runtime·notInitialized(SB), NOSPLIT, $0
   599  	MOVD $runtime·wasmStack+(m0Stack__size-16-8)(SB), SP
   600  	I32Const $0 // entry PC_B
   601  	Call runtime·notInitialized1(SB)
   602  	Drop
   603  	I32Const $0 // entry PC_B
   604  	Call runtime·abort(SB)
   605  	UNDEF
   606  

View as plain text