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
10TEXT 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
28TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0
29	CALL	runtime·mstart0(SB)
30	RET // not reached
31
32DATA  runtime·mainPC+0(SB)/8,$runtime·main(SB)
33GLOBL runtime·mainPC(SB),RODATA,$8
34
35// func checkASM() bool
36TEXT ·checkASM(SB), NOSPLIT, $0-1
37	MOVB $1, ret+0(FP)
38	RET
39
40TEXT 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.
68TEXT 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())
115TEXT 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
182TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
183	RET
184
185TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0
186	UNDEF
187
188// AES hashing not implemented for wasm
189TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32
190	JMP	runtime·memhashFallback(SB)
191TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24
192	JMP	runtime·strhashFallback(SB)
193TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24
194	JMP	runtime·memhash32Fallback(SB)
195TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24
196	JMP	runtime·memhash64Fallback(SB)
197
198TEXT runtime·return0(SB), NOSPLIT, $0-0
199	MOVD $0, RET0
200	RET
201
202TEXT runtime·asminit(SB), NOSPLIT, $0-0
203	// No per-thread init.
204	RET
205
206TEXT ·publicationBarrier(SB), NOSPLIT, $0-0
207	RET
208
209TEXT runtime·procyield(SB), NOSPLIT, $0-0 // FIXME
210	RET
211
212TEXT runtime·breakpoint(SB), NOSPLIT, $0-0
213	UNDEF
214
215// func switchToCrashStack0(fn func())
216TEXT 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.
248TEXT 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.
292TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
293	MOVD $0, CTXT
294	JMP runtime·morestack(SB)
295
296TEXT ·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
307TEXT ·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) \
346TEXT 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.
396TEXT 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
406CALLFNcall16, 16)
407CALLFNcall32, 32)
408CALLFNcall64, 64)
409CALLFNcall128, 128)
410CALLFNcall256, 256)
411CALLFNcall512, 512)
412CALLFNcall1024, 1024)
413CALLFNcall2048, 2048)
414CALLFNcall4096, 4096)
415CALLFNcall8192, 8192)
416CALLFNcall16384, 16384)
417CALLFNcall32768, 32768)
418CALLFNcall65536, 65536)
419CALLFNcall131072, 131072)
420CALLFNcall262144, 262144)
421CALLFNcall524288, 524288)
422CALLFNcall1048576, 1048576)
423CALLFNcall2097152, 2097152)
424CALLFNcall4194304, 4194304)
425CALLFNcall8388608, 8388608)
426CALLFNcall16777216, 16777216)
427CALLFNcall33554432, 33554432)
428CALLFNcall67108864, 67108864)
429CALLFNcall134217728, 134217728)
430CALLFNcall268435456, 268435456)
431CALLFNcall536870912, 536870912)
432CALLFNcall1073741824, 1073741824)
433
434TEXT 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
439TEXT 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).
450TEXT 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
488TEXT runtime·gcWriteBarrier1<ABIInternal>(SB),NOSPLIT,$0
489	I64Const $8
490	Call	gcWriteBarrier<>(SB)
491	Return
492TEXT runtime·gcWriteBarrier2<ABIInternal>(SB),NOSPLIT,$0
493	I64Const $16
494	Call	gcWriteBarrier<>(SB)
495	Return
496TEXT runtime·gcWriteBarrier3<ABIInternal>(SB),NOSPLIT,$0
497	I64Const $24
498	Call	gcWriteBarrier<>(SB)
499	Return
500TEXT runtime·gcWriteBarrier4<ABIInternal>(SB),NOSPLIT,$0
501	I64Const $32
502	Call	gcWriteBarrier<>(SB)
503	Return
504TEXT runtime·gcWriteBarrier5<ABIInternal>(SB),NOSPLIT,$0
505	I64Const $40
506	Call	gcWriteBarrier<>(SB)
507	Return
508TEXT runtime·gcWriteBarrier6<ABIInternal>(SB),NOSPLIT,$0
509	I64Const $48
510	Call	gcWriteBarrier<>(SB)
511	Return
512TEXT runtime·gcWriteBarrier7<ABIInternal>(SB),NOSPLIT,$0
513	I64Const $56
514	Call	gcWriteBarrier<>(SB)
515	Return
516TEXT runtime·gcWriteBarrier8<ABIInternal>(SB),NOSPLIT,$0
517	I64Const $64
518	Call	gcWriteBarrier<>(SB)
519	Return
520
521TEXT 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
557TEXT wasm_export_lib(SB),NOSPLIT,$0
558	UNDEF
559