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

View as plain text