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, $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	CALLNORESUME runtime·args(SB)
19	CALLNORESUME runtime·osinit(SB)
20	CALLNORESUME runtime·schedinit(SB)
21	MOVD $0, 0(SP)
22	MOVD $runtime·mainPC(SB), 8(SP)
23	CALLNORESUME runtime·newproc(SB)
24	CALL runtime·mstart(SB) // WebAssembly stack will unwind when switching to another goroutine
25	UNDEF
26
27DATA  runtime·mainPC+0(SB)/8,$runtime·main(SB)
28GLOBL runtime·mainPC(SB),RODATA,$8
29
30// func checkASM() bool
31TEXT ·checkASM(SB), NOSPLIT, $0-1
32	MOVB $1, ret+0(FP)
33	RET
34
35TEXT runtime·gogo(SB), NOSPLIT, $0-8
36	MOVD buf+0(FP), R0
37	MOVD gobuf_g(R0), g
38	MOVD gobuf_sp(R0), SP
39
40	// Put target PC at -8(SP), wasm_pc_f_loop will pick it up
41	Get SP
42	I32Const $8
43	I32Sub
44	I64Load gobuf_pc(R0)
45	I64Store $0
46
47	MOVD gobuf_ret(R0), RET0
48	MOVD gobuf_ctxt(R0), CTXT
49	// clear to help garbage collector
50	MOVD $0, gobuf_sp(R0)
51	MOVD $0, gobuf_ret(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.
61TEXT 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	MOVD g, g_sched+gobuf_g(g)
73
74	// if g == g0 call badmcall
75	Get g
76	Get R2
77	I64Eq
78	If
79		JMP runtime·badmcall(SB)
80	End
81
82	// switch to g0's stack
83	I64Load (g_sched+gobuf_sp)(R2)
84	I64Const $8
85	I64Sub
86	I32WrapI64
87	Set SP
88
89	// set arg to current g
90	MOVD g, 0(SP)
91
92	// switch to g0
93	MOVD R2, g
94
95	// call fn
96	Get CTXT
97	I32WrapI64
98	I64Load $0
99	CALL
100
101	Get SP
102	I32Const $8
103	I32Add
104	Set SP
105
106	JMP runtime·badmcall2(SB)
107
108// func systemstack(fn func())
109TEXT runtime·systemstack(SB), NOSPLIT, $0-8
110	// R0 = fn
111	MOVD fn+0(FP), R0
112	// R1 = g.m
113	MOVD g_m(g), R1
114	// R2 = g0
115	MOVD m_g0(R1), R2
116
117	// if g == g0
118	Get g
119	Get R2
120	I64Eq
121	If
122		// no switch:
123		MOVD R0, CTXT
124
125		Get CTXT
126		I32WrapI64
127		I64Load $0
128		JMP
129	End
130
131	// if g != m.curg
132	Get g
133	I64Load m_curg(R1)
134	I64Ne
135	If
136		CALLNORESUME runtime·badsystemstack(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	MOVD g, g_sched+gobuf_g(g)
147
148	// switch to g0
149	MOVD R2, g
150
151	// make it look like mstart called systemstack on g0, to stop traceback
152	I64Load (g_sched+gobuf_sp)(R2)
153	I64Const $8
154	I64Sub
155	Set R3
156
157	MOVD $runtime·mstart(SB), 0(R3)
158	MOVD R3, SP
159
160	// call fn
161	MOVD R0, CTXT
162
163	Get CTXT
164	I32WrapI64
165	I64Load $0
166	CALL
167
168	// switch back to g
169	MOVD g_m(g), R1
170	MOVD m_curg(R1), R2
171	MOVD R2, g
172	MOVD g_sched+gobuf_sp(R2), SP
173	MOVD $0, g_sched+gobuf_sp(R2)
174	RET
175
176TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
177	RET
178
179// AES hashing not implemented for wasm
180TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32
181	JMP	runtime·memhashFallback(SB)
182TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24
183	JMP	runtime·strhashFallback(SB)
184TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24
185	JMP	runtime·memhash32Fallback(SB)
186TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24
187	JMP	runtime·memhash64Fallback(SB)
188
189TEXT runtime·return0(SB), NOSPLIT, $0-0
190	MOVD $0, RET0
191	RET
192
193TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
194	MOVD fv+0(FP), CTXT
195
196	Get CTXT
197	I64Eqz
198	If
199		CALLNORESUME runtime·sigpanic(SB)
200	End
201
202	// caller sp after CALL
203	I64Load argp+8(FP)
204	I64Const $8
205	I64Sub
206	I32WrapI64
207	Set SP
208
209	// decrease PC_B by 1 to CALL again
210	Get SP
211	I32Load16U (SP)
212	I32Const $1
213	I32Sub
214	I32Store16 $0
215
216	// but first run the deferred function
217	Get CTXT
218	I32WrapI64
219	I64Load $0
220	JMP
221
222TEXT runtime·asminit(SB), NOSPLIT, $0-0
223	// No per-thread init.
224	RET
225
226TEXT ·publicationBarrier(SB), NOSPLIT, $0-0
227	RET
228
229TEXT runtime·procyield(SB), NOSPLIT, $0-0 // FIXME
230	RET
231
232TEXT runtime·breakpoint(SB), NOSPLIT, $0-0
233	UNDEF
234
235// Called during function prolog when more stack is needed.
236//
237// The traceback routines see morestack on a g0 as being
238// the top of a stack (for example, morestack calling newstack
239// calling the scheduler calling newm calling gc), so we must
240// record an argument size. For that purpose, it has no arguments.
241TEXT runtime·morestack(SB), NOSPLIT, $0-0
242	// R1 = g.m
243	MOVD g_m(g), R1
244
245	// R2 = g0
246	MOVD m_g0(R1), R2
247
248	// Cannot grow scheduler stack (m->g0).
249	Get g
250	Get R1
251	I64Eq
252	If
253		CALLNORESUME runtime·badmorestackg0(SB)
254	End
255
256	// Cannot grow signal stack (m->gsignal).
257	Get g
258	I64Load m_gsignal(R1)
259	I64Eq
260	If
261		CALLNORESUME runtime·badmorestackgsignal(SB)
262	End
263
264	// Called from f.
265	// Set m->morebuf to f's caller.
266	NOP	SP	// tell vet SP changed - stop checking offsets
267	MOVD 8(SP), m_morebuf+gobuf_pc(R1)
268	MOVD $16(SP), m_morebuf+gobuf_sp(R1) // f's caller's SP
269	MOVD g, m_morebuf+gobuf_g(R1)
270
271	// Set g->sched to context in f.
272	MOVD 0(SP), g_sched+gobuf_pc(g)
273	MOVD g, g_sched+gobuf_g(g)
274	MOVD $8(SP), g_sched+gobuf_sp(g) // f's SP
275	MOVD CTXT, g_sched+gobuf_ctxt(g)
276
277	// Call newstack on m->g0's stack.
278	MOVD R2, g
279	MOVD g_sched+gobuf_sp(R2), SP
280	CALL runtime·newstack(SB)
281	UNDEF // crash if newstack returns
282
283// morestack but not preserving ctxt.
284TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
285	MOVD $0, CTXT
286	JMP runtime·morestack(SB)
287
288TEXT ·asmcgocall(SB), NOSPLIT, $0-0
289	UNDEF
290
291TEXT ·cgocallback_gofunc(SB), NOSPLIT, $16-32
292	UNDEF
293
294#define DISPATCH(NAME, MAXSIZE) \
295	Get R0; \
296	I64Const $MAXSIZE; \
297	I64LeU; \
298	If; \
299		JMP NAME(SB); \
300	End
301
302TEXT ·reflectcall(SB), NOSPLIT, $0-32
303	I64Load fn+8(FP)
304	I64Eqz
305	If
306		CALLNORESUME runtime·sigpanic(SB)
307	End
308
309	MOVW argsize+24(FP), R0
310
311	DISPATCH(runtime·call32, 32)
312	DISPATCH(runtime·call64, 64)
313	DISPATCH(runtime·call128, 128)
314	DISPATCH(runtime·call256, 256)
315	DISPATCH(runtime·call512, 512)
316	DISPATCH(runtime·call1024, 1024)
317	DISPATCH(runtime·call2048, 2048)
318	DISPATCH(runtime·call4096, 4096)
319	DISPATCH(runtime·call8192, 8192)
320	DISPATCH(runtime·call16384, 16384)
321	DISPATCH(runtime·call32768, 32768)
322	DISPATCH(runtime·call65536, 65536)
323	DISPATCH(runtime·call131072, 131072)
324	DISPATCH(runtime·call262144, 262144)
325	DISPATCH(runtime·call524288, 524288)
326	DISPATCH(runtime·call1048576, 1048576)
327	DISPATCH(runtime·call2097152, 2097152)
328	DISPATCH(runtime·call4194304, 4194304)
329	DISPATCH(runtime·call8388608, 8388608)
330	DISPATCH(runtime·call16777216, 16777216)
331	DISPATCH(runtime·call33554432, 33554432)
332	DISPATCH(runtime·call67108864, 67108864)
333	DISPATCH(runtime·call134217728, 134217728)
334	DISPATCH(runtime·call268435456, 268435456)
335	DISPATCH(runtime·call536870912, 536870912)
336	DISPATCH(runtime·call1073741824, 1073741824)
337	JMP runtime·badreflectcall(SB)
338
339#define CALLFN(NAME, MAXSIZE) \
340TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
341	NO_LOCAL_POINTERS; \
342	MOVW argsize+24(FP), R0; \
343	\
344	Get R0; \
345	I64Eqz; \
346	Not; \
347	If; \
348		Get SP; \
349		I64Load argptr+16(FP); \
350		I32WrapI64; \
351		I64Load argsize+24(FP); \
352		I64Const $3; \
353		I64ShrU; \
354		I32WrapI64; \
355		Call runtime·wasmMove(SB); \
356	End; \
357	\
358	MOVD f+8(FP), CTXT; \
359	Get CTXT; \
360	I32WrapI64; \
361	I64Load $0; \
362	CALL; \
363	\
364	I64Load32U retoffset+28(FP); \
365	Set R0; \
366	\
367	MOVD argtype+0(FP), RET0; \
368	\
369	I64Load argptr+16(FP); \
370	Get R0; \
371	I64Add; \
372	Set RET1; \
373	\
374	Get SP; \
375	I64ExtendI32U; \
376	Get R0; \
377	I64Add; \
378	Set RET2; \
379	\
380	I64Load32U argsize+24(FP); \
381	Get R0; \
382	I64Sub; \
383	Set RET3; \
384	\
385	CALL callRet<>(SB); \
386	RET
387
388// callRet copies return values back at the end of call*. This is a
389// separate function so it can allocate stack space for the arguments
390// to reflectcallmove. It does not follow the Go ABI; it expects its
391// arguments in registers.
392TEXT callRet<>(SB), NOSPLIT, $32-0
393	NO_LOCAL_POINTERS
394	MOVD RET0, 0(SP)
395	MOVD RET1, 8(SP)
396	MOVD RET2, 16(SP)
397	MOVD RET3, 24(SP)
398	CALL runtime·reflectcallmove(SB)
399	RET
400
401CALLFNcall32, 32)
402CALLFNcall64, 64)
403CALLFNcall128, 128)
404CALLFNcall256, 256)
405CALLFNcall512, 512)
406CALLFNcall1024, 1024)
407CALLFNcall2048, 2048)
408CALLFNcall4096, 4096)
409CALLFNcall8192, 8192)
410CALLFNcall16384, 16384)
411CALLFNcall32768, 32768)
412CALLFNcall65536, 65536)
413CALLFNcall131072, 131072)
414CALLFNcall262144, 262144)
415CALLFNcall524288, 524288)
416CALLFNcall1048576, 1048576)
417CALLFNcall2097152, 2097152)
418CALLFNcall4194304, 4194304)
419CALLFNcall8388608, 8388608)
420CALLFNcall16777216, 16777216)
421CALLFNcall33554432, 33554432)
422CALLFNcall67108864, 67108864)
423CALLFNcall134217728, 134217728)
424CALLFNcall268435456, 268435456)
425CALLFNcall536870912, 536870912)
426CALLFNcall1073741824, 1073741824)
427
428TEXT runtime·goexit(SB), NOSPLIT, $0-0
429	NOP // first PC of goexit is skipped
430	CALL runtime·goexit1(SB) // does not return
431	UNDEF
432
433TEXT runtime·cgocallback(SB), NOSPLIT, $32-32
434	UNDEF
435
436// gcWriteBarrier performs a heap pointer write and informs the GC.
437//
438// gcWriteBarrier does NOT follow the Go ABI. It has two WebAssembly parameters:
439// R0: the destination of the write (i64)
440// R1: the value being written (i64)
441TEXT runtime·gcWriteBarrier(SB), NOSPLIT, $16
442	// R3 = g.m
443	MOVD g_m(g), R3
444	// R4 = p
445	MOVD m_p(R3), R4
446	// R5 = wbBuf.next
447	MOVD p_wbBuf+wbBuf_next(R4), R5
448
449	// Record value
450	MOVD R1, 0(R5)
451	// Record *slot
452	MOVD (R0), 8(R5)
453
454	// Increment wbBuf.next
455	Get R5
456	I64Const $16
457	I64Add
458	Set R5
459	MOVD R5, p_wbBuf+wbBuf_next(R4)
460
461	Get R5
462	I64Load (p_wbBuf+wbBuf_end)(R4)
463	I64Eq
464	If
465		// Flush
466		MOVD R0, 0(SP)
467		MOVD R1, 8(SP)
468		CALLNORESUME runtime·wbBufFlush(SB)
469	End
470
471	// Do the write
472	MOVD R1, (R0)
473
474	RET
475