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