Home | History | Annotate | Download | only in runtime
      1 // Copyright 2016 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 // +build mips mipsle
      6 
      7 #include "go_asm.h"
      8 #include "go_tls.h"
      9 #include "funcdata.h"
     10 #include "textflag.h"
     11 
     12 #define	REGCTXT	R22
     13 
     14 TEXT runtimert0_go(SB),NOSPLIT,$0
     15 	// R29 = stack; R4 = argc; R5 = argv
     16 
     17 	ADDU	$-12, R29
     18 	MOVW	R4, 4(R29)	// argc
     19 	MOVW	R5, 8(R29)	// argv
     20 
     21 	// create istack out of the given (operating system) stack.
     22 	// _cgo_init may update stackguard.
     23 	MOVW	$runtimeg0(SB), g
     24 	MOVW	$(-64*1024), R23
     25 	ADD	R23, R29, R1
     26 	MOVW	R1, g_stackguard0(g)
     27 	MOVW	R1, g_stackguard1(g)
     28 	MOVW	R1, (g_stack+stack_lo)(g)
     29 	MOVW	R29, (g_stack+stack_hi)(g)
     30 
     31 	// if there is a _cgo_init, call it using the gcc ABI.
     32 	MOVW	_cgo_init(SB), R25
     33 	BEQ	R25, nocgo
     34 	ADDU	$-16, R29
     35 	MOVW	R0, R7	// arg 3: not used
     36 	MOVW	R0, R6	// arg 2: not used
     37 	MOVW	$setg_gcc<>(SB), R5	// arg 1: setg
     38 	MOVW	g, R4	// arg 0: G
     39 	JAL	(R25)
     40 	ADDU	$16, R29
     41 
     42 nocgo:
     43 	// update stackguard after _cgo_init
     44 	MOVW	(g_stack+stack_lo)(g), R1
     45 	ADD	$const__StackGuard, R1
     46 	MOVW	R1, g_stackguard0(g)
     47 	MOVW	R1, g_stackguard1(g)
     48 
     49 	// set the per-goroutine and per-mach "registers"
     50 	MOVW	$runtimem0(SB), R1
     51 
     52 	// save m->g0 = g0
     53 	MOVW	g, m_g0(R1)
     54 	// save m0 to g0->m
     55 	MOVW	R1, g_m(g)
     56 
     57 	JAL	runtimecheck(SB)
     58 
     59 	// args are already prepared
     60 	JAL	runtimeargs(SB)
     61 	JAL	runtimeosinit(SB)
     62 	JAL	runtimeschedinit(SB)
     63 
     64 	// create a new goroutine to start program
     65 	MOVW	$runtimemainPC(SB), R1	// entry
     66 	ADDU	$-12, R29
     67 	MOVW	R1, 8(R29)
     68 	MOVW	R0, 4(R29)
     69 	MOVW	R0, 0(R29)
     70 	JAL	runtimenewproc(SB)
     71 	ADDU	$12, R29
     72 
     73 	// start this M
     74 	JAL	runtimemstart(SB)
     75 
     76 	UNDEF
     77 	RET
     78 
     79 DATA	runtimemainPC+0(SB)/4,$runtimemain(SB)
     80 GLOBL	runtimemainPC(SB),RODATA,$4
     81 
     82 TEXT runtimebreakpoint(SB),NOSPLIT,$0-0
     83 	BREAK
     84 	RET
     85 
     86 TEXT runtimeasminit(SB),NOSPLIT,$0-0
     87 	RET
     88 
     89 /*
     90  *  go-routine
     91  */
     92 
     93 // void gosave(Gobuf*)
     94 // save state in Gobuf; setjmp
     95 TEXT runtimegosave(SB),NOSPLIT,$-4-4
     96 	MOVW	buf+0(FP), R1
     97 	MOVW	R29, gobuf_sp(R1)
     98 	MOVW	R31, gobuf_pc(R1)
     99 	MOVW	g, gobuf_g(R1)
    100 	MOVW	R0, gobuf_lr(R1)
    101 	MOVW	R0, gobuf_ret(R1)
    102 	// Assert ctxt is zero. See func save.
    103 	MOVW	gobuf_ctxt(R1), R1
    104 	BEQ	R1, 2(PC)
    105 	JAL	runtimebadctxt(SB)
    106 	RET
    107 
    108 // void gogo(Gobuf*)
    109 // restore state from Gobuf; longjmp
    110 TEXT runtimegogo(SB),NOSPLIT,$8-4
    111 	MOVW	buf+0(FP), R3
    112 	MOVW	gobuf_g(R3), g	// make sure g is not nil
    113 	JAL	runtimesave_g(SB)
    114 
    115 	MOVW	0(g), R2
    116 	MOVW	gobuf_sp(R3), R29
    117 	MOVW	gobuf_lr(R3), R31
    118 	MOVW	gobuf_ret(R3), R1
    119 	MOVW	gobuf_ctxt(R3), REGCTXT
    120 	MOVW	R0, gobuf_sp(R3)
    121 	MOVW	R0, gobuf_ret(R3)
    122 	MOVW	R0, gobuf_lr(R3)
    123 	MOVW	R0, gobuf_ctxt(R3)
    124 	MOVW	gobuf_pc(R3), R4
    125 	JMP	(R4)
    126 
    127 // void mcall(fn func(*g))
    128 // Switch to m->g0's stack, call fn(g).
    129 // Fn must never return. It should gogo(&g->sched)
    130 // to keep running g.
    131 TEXT runtimemcall(SB),NOSPLIT,$-4-4
    132 	// Save caller state in g->sched
    133 	MOVW	R29, (g_sched+gobuf_sp)(g)
    134 	MOVW	R31, (g_sched+gobuf_pc)(g)
    135 	MOVW	R0, (g_sched+gobuf_lr)(g)
    136 	MOVW	g, (g_sched+gobuf_g)(g)
    137 
    138 	// Switch to m->g0 & its stack, call fn.
    139 	MOVW	g, R1
    140 	MOVW	g_m(g), R3
    141 	MOVW	m_g0(R3), g
    142 	JAL	runtimesave_g(SB)
    143 	BNE	g, R1, 2(PC)
    144 	JMP	runtimebadmcall(SB)
    145 	MOVW	fn+0(FP), REGCTXT	// context
    146 	MOVW	0(REGCTXT), R4	// code pointer
    147 	MOVW	(g_sched+gobuf_sp)(g), R29	// sp = m->g0->sched.sp
    148 	ADDU	$-8, R29	// make room for 1 arg and fake LR
    149 	MOVW	R1, 4(R29)
    150 	MOVW	R0, 0(R29)
    151 	JAL	(R4)
    152 	JMP	runtimebadmcall2(SB)
    153 
    154 // systemstack_switch is a dummy routine that systemstack leaves at the bottom
    155 // of the G stack.  We need to distinguish the routine that
    156 // lives at the bottom of the G stack from the one that lives
    157 // at the top of the system stack because the one at the top of
    158 // the system stack terminates the stack walk (see topofstack()).
    159 TEXT runtimesystemstack_switch(SB),NOSPLIT,$0-0
    160 	UNDEF
    161 	JAL	(R31)	// make sure this function is not leaf
    162 	RET
    163 
    164 // func systemstack(fn func())
    165 TEXT runtimesystemstack(SB),NOSPLIT,$0-4
    166 	MOVW	fn+0(FP), R1	// R1 = fn
    167 	MOVW	R1, REGCTXT	// context
    168 	MOVW	g_m(g), R2	// R2 = m
    169 
    170 	MOVW	m_gsignal(R2), R3	// R3 = gsignal
    171 	BEQ	g, R3, noswitch
    172 
    173 	MOVW	m_g0(R2), R3	// R3 = g0
    174 	BEQ	g, R3, noswitch
    175 
    176 	MOVW	m_curg(R2), R4
    177 	BEQ	g, R4, switch
    178 
    179 	// Bad: g is not gsignal, not g0, not curg. What is it?
    180 	// Hide call from linker nosplit analysis.
    181 	MOVW	$runtimebadsystemstack(SB), R4
    182 	JAL	(R4)
    183 
    184 switch:
    185 	// save our state in g->sched.  Pretend to
    186 	// be systemstack_switch if the G stack is scanned.
    187 	MOVW	$runtimesystemstack_switch(SB), R4
    188 	ADDU	$8, R4	// get past prologue
    189 	MOVW	R4, (g_sched+gobuf_pc)(g)
    190 	MOVW	R29, (g_sched+gobuf_sp)(g)
    191 	MOVW	R0, (g_sched+gobuf_lr)(g)
    192 	MOVW	g, (g_sched+gobuf_g)(g)
    193 
    194 	// switch to g0
    195 	MOVW	R3, g
    196 	JAL	runtimesave_g(SB)
    197 	MOVW	(g_sched+gobuf_sp)(g), R1
    198 	// make it look like mstart called systemstack on g0, to stop traceback
    199 	ADDU	$-4, R1
    200 	MOVW	$runtimemstart(SB), R2
    201 	MOVW	R2, 0(R1)
    202 	MOVW	R1, R29
    203 
    204 	// call target function
    205 	MOVW	0(REGCTXT), R4	// code pointer
    206 	JAL	(R4)
    207 
    208 	// switch back to g
    209 	MOVW	g_m(g), R1
    210 	MOVW	m_curg(R1), g
    211 	JAL	runtimesave_g(SB)
    212 	MOVW	(g_sched+gobuf_sp)(g), R29
    213 	MOVW	R0, (g_sched+gobuf_sp)(g)
    214 	RET
    215 
    216 noswitch:
    217 	// already on m stack, just call directly
    218 	// Using a tail call here cleans up tracebacks since we won't stop
    219 	// at an intermediate systemstack.
    220 	MOVW	0(REGCTXT), R4	// code pointer
    221 	MOVW	0(R29), R31	// restore LR
    222 	ADD	$4, R29
    223 	JMP	(R4)
    224 
    225 /*
    226  * support for morestack
    227  */
    228 
    229 // Called during function prolog when more stack is needed.
    230 // Caller has already loaded:
    231 // R1: framesize, R2: argsize, R3: LR
    232 //
    233 // The traceback routines see morestack on a g0 as being
    234 // the top of a stack (for example, morestack calling newstack
    235 // calling the scheduler calling newm calling gc), so we must
    236 // record an argument size. For that purpose, it has no arguments.
    237 TEXT runtimemorestack(SB),NOSPLIT,$-4-0
    238 	// Cannot grow scheduler stack (m->g0).
    239 	MOVW	g_m(g), R7
    240 	MOVW	m_g0(R7), R8
    241 	BNE	g, R8, 3(PC)
    242 	JAL	runtimebadmorestackg0(SB)
    243 	JAL	runtimeabort(SB)
    244 
    245 	// Cannot grow signal stack (m->gsignal).
    246 	MOVW	m_gsignal(R7), R8
    247 	BNE	g, R8, 3(PC)
    248 	JAL	runtimebadmorestackgsignal(SB)
    249 	JAL	runtimeabort(SB)
    250 
    251 	// Called from f.
    252 	// Set g->sched to context in f.
    253 	MOVW	R29, (g_sched+gobuf_sp)(g)
    254 	MOVW	R31, (g_sched+gobuf_pc)(g)
    255 	MOVW	R3, (g_sched+gobuf_lr)(g)
    256 	MOVW	REGCTXT, (g_sched+gobuf_ctxt)(g)
    257 
    258 	// Called from f.
    259 	// Set m->morebuf to f's caller.
    260 	MOVW	R3, (m_morebuf+gobuf_pc)(R7)	// f's caller's PC
    261 	MOVW	R29, (m_morebuf+gobuf_sp)(R7)	// f's caller's SP
    262 	MOVW	g, (m_morebuf+gobuf_g)(R7)
    263 
    264 	// Call newstack on m->g0's stack.
    265 	MOVW	m_g0(R7), g
    266 	JAL	runtimesave_g(SB)
    267 	MOVW	(g_sched+gobuf_sp)(g), R29
    268 	// Create a stack frame on g0 to call newstack.
    269 	MOVW	R0, -4(R29)	// Zero saved LR in frame
    270 	ADDU	$-4, R29
    271 	JAL	runtimenewstack(SB)
    272 
    273 	// Not reached, but make sure the return PC from the call to newstack
    274 	// is still in this function, and not the beginning of the next.
    275 	UNDEF
    276 
    277 TEXT runtimemorestack_noctxt(SB),NOSPLIT,$0-0
    278 	MOVW	R0, REGCTXT
    279 	JMP	runtimemorestack(SB)
    280 
    281 // reflectcall: call a function with the given argument list
    282 // func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
    283 // we don't have variable-sized frames, so we use a small number
    284 // of constant-sized-frame functions to encode a few bits of size in the pc.
    285 
    286 #define DISPATCH(NAME,MAXSIZE)	\
    287 	MOVW	$MAXSIZE, R23;	\
    288 	SGTU	R1, R23, R23;	\
    289 	BNE	R23, 3(PC);	\
    290 	MOVW	$NAME(SB), R4;	\
    291 	JMP	(R4)
    292 
    293 TEXT reflectcall(SB),NOSPLIT,$0-20
    294 	JMP	reflectcall(SB)
    295 
    296 TEXT reflectcall(SB),NOSPLIT,$-4-20
    297 	MOVW	argsize+12(FP), R1
    298 
    299 	DISPATCH(runtimecall16, 16)
    300 	DISPATCH(runtimecall32, 32)
    301 	DISPATCH(runtimecall64, 64)
    302 	DISPATCH(runtimecall128, 128)
    303 	DISPATCH(runtimecall256, 256)
    304 	DISPATCH(runtimecall512, 512)
    305 	DISPATCH(runtimecall1024, 1024)
    306 	DISPATCH(runtimecall2048, 2048)
    307 	DISPATCH(runtimecall4096, 4096)
    308 	DISPATCH(runtimecall8192, 8192)
    309 	DISPATCH(runtimecall16384, 16384)
    310 	DISPATCH(runtimecall32768, 32768)
    311 	DISPATCH(runtimecall65536, 65536)
    312 	DISPATCH(runtimecall131072, 131072)
    313 	DISPATCH(runtimecall262144, 262144)
    314 	DISPATCH(runtimecall524288, 524288)
    315 	DISPATCH(runtimecall1048576, 1048576)
    316 	DISPATCH(runtimecall2097152, 2097152)
    317 	DISPATCH(runtimecall4194304, 4194304)
    318 	DISPATCH(runtimecall8388608, 8388608)
    319 	DISPATCH(runtimecall16777216, 16777216)
    320 	DISPATCH(runtimecall33554432, 33554432)
    321 	DISPATCH(runtimecall67108864, 67108864)
    322 	DISPATCH(runtimecall134217728, 134217728)
    323 	DISPATCH(runtimecall268435456, 268435456)
    324 	DISPATCH(runtimecall536870912, 536870912)
    325 	DISPATCH(runtimecall1073741824, 1073741824)
    326 	MOVW	$runtimebadreflectcall(SB), R4
    327 	JMP	(R4)
    328 
    329 #define CALLFN(NAME,MAXSIZE)	\
    330 TEXT NAME(SB),WRAPPER,$MAXSIZE-20;	\
    331 	NO_LOCAL_POINTERS;	\
    332 	/* copy arguments to stack */		\
    333 	MOVW	arg+8(FP), R1;	\
    334 	MOVW	argsize+12(FP), R2;	\
    335 	MOVW	R29, R3;	\
    336 	ADDU	$4, R3;	\
    337 	ADDU	R3, R2;	\
    338 	BEQ	R3, R2, 6(PC);	\
    339 	MOVBU	(R1), R4;	\
    340 	ADDU	$1, R1;	\
    341 	MOVBU	R4, (R3);	\
    342 	ADDU	$1, R3;	\
    343 	JMP	-5(PC);	\
    344 	/* call function */			\
    345 	MOVW	f+4(FP), REGCTXT;	\
    346 	MOVW	(REGCTXT), R4;	\
    347 	PCDATA	$PCDATA_StackMapIndex, $0;	\
    348 	JAL	(R4);	\
    349 	/* copy return values back */		\
    350 	MOVW	argtype+0(FP), R5;	\
    351 	MOVW	arg+8(FP), R1;	\
    352 	MOVW	n+12(FP), R2;	\
    353 	MOVW	retoffset+16(FP), R4;	\
    354 	ADDU	$4, R29, R3;	\
    355 	ADDU	R4, R3;	\
    356 	ADDU	R4, R1;	\
    357 	SUBU	R4, R2;	\
    358 	JAL	callRet<>(SB);		\
    359 	RET
    360 
    361 // callRet copies return values back at the end of call*. This is a
    362 // separate function so it can allocate stack space for the arguments
    363 // to reflectcallmove. It does not follow the Go ABI; it expects its
    364 // arguments in registers.
    365 TEXT callRet<>(SB), NOSPLIT, $16-0
    366 	MOVW	R5, 4(R29)
    367 	MOVW	R1, 8(R29)
    368 	MOVW	R3, 12(R29)
    369 	MOVW	R2, 16(R29)
    370 	JAL	runtimereflectcallmove(SB)
    371 	RET
    372 
    373 CALLFN(call16, 16)
    374 CALLFN(call32, 32)
    375 CALLFN(call64, 64)
    376 CALLFN(call128, 128)
    377 CALLFN(call256, 256)
    378 CALLFN(call512, 512)
    379 CALLFN(call1024, 1024)
    380 CALLFN(call2048, 2048)
    381 CALLFN(call4096, 4096)
    382 CALLFN(call8192, 8192)
    383 CALLFN(call16384, 16384)
    384 CALLFN(call32768, 32768)
    385 CALLFN(call65536, 65536)
    386 CALLFN(call131072, 131072)
    387 CALLFN(call262144, 262144)
    388 CALLFN(call524288, 524288)
    389 CALLFN(call1048576, 1048576)
    390 CALLFN(call2097152, 2097152)
    391 CALLFN(call4194304, 4194304)
    392 CALLFN(call8388608, 8388608)
    393 CALLFN(call16777216, 16777216)
    394 CALLFN(call33554432, 33554432)
    395 CALLFN(call67108864, 67108864)
    396 CALLFN(call134217728, 134217728)
    397 CALLFN(call268435456, 268435456)
    398 CALLFN(call536870912, 536870912)
    399 CALLFN(call1073741824, 1073741824)
    400 
    401 TEXT runtimeprocyield(SB),NOSPLIT,$0-4
    402 	RET
    403 
    404 // void jmpdefer(fv, sp);
    405 // called from deferreturn.
    406 // 1. grab stored LR for caller
    407 // 2. sub 8 bytes to get back to JAL deferreturn
    408 // 3. JMP to fn
    409 TEXT runtimejmpdefer(SB),NOSPLIT,$0-8
    410 	MOVW	0(R29), R31
    411 	ADDU	$-8, R31
    412 
    413 	MOVW	fv+0(FP), REGCTXT
    414 	MOVW	argp+4(FP), R29
    415 	ADDU	$-4, R29
    416 	NOR	R0, R0	// prevent scheduling
    417 	MOVW	0(REGCTXT), R4
    418 	JMP	(R4)
    419 
    420 // Save state of caller into g->sched. Smashes R1.
    421 TEXT gosave<>(SB),NOSPLIT,$-4
    422 	MOVW	R31, (g_sched+gobuf_pc)(g)
    423 	MOVW	R29, (g_sched+gobuf_sp)(g)
    424 	MOVW	R0, (g_sched+gobuf_lr)(g)
    425 	MOVW	R0, (g_sched+gobuf_ret)(g)
    426 	// Assert ctxt is zero. See func save.
    427 	MOVW	(g_sched+gobuf_ctxt)(g), R1
    428 	BEQ	R1, 2(PC)
    429 	JAL	runtimebadctxt(SB)
    430 	RET
    431 
    432 // func asmcgocall(fn, arg unsafe.Pointer) int32
    433 // Call fn(arg) on the scheduler stack,
    434 // aligned appropriately for the gcc ABI.
    435 // See cgocall.go for more details.
    436 TEXT asmcgocall(SB),NOSPLIT,$0-12
    437 	MOVW	fn+0(FP), R25
    438 	MOVW	arg+4(FP), R4
    439 
    440 	MOVW	R29, R3	// save original stack pointer
    441 	MOVW	g, R2
    442 
    443 	// Figure out if we need to switch to m->g0 stack.
    444 	// We get called to create new OS threads too, and those
    445 	// come in on the m->g0 stack already.
    446 	MOVW	g_m(g), R5
    447 	MOVW	m_g0(R5), R6
    448 	BEQ	R6, g, g0
    449 
    450 	JAL	gosave<>(SB)
    451 	MOVW	R6, g
    452 	JAL	runtimesave_g(SB)
    453 	MOVW	(g_sched+gobuf_sp)(g), R29
    454 
    455 	// Now on a scheduling stack (a pthread-created stack).
    456 g0:
    457 	// Save room for two of our pointers and O32 frame.
    458 	ADDU	$-24, R29
    459 	AND	$~7, R29	// O32 ABI expects 8-byte aligned stack on function entry
    460 	MOVW	R2, 16(R29)	// save old g on stack
    461 	MOVW	(g_stack+stack_hi)(R2), R2
    462 	SUBU	R3, R2
    463 	MOVW	R2, 20(R29)	// save depth in old g stack (can't just save SP, as stack might be copied during a callback)
    464 	JAL	(R25)
    465 
    466 	// Restore g, stack pointer. R2 is return value.
    467 	MOVW	16(R29), g
    468 	JAL	runtimesave_g(SB)
    469 	MOVW	(g_stack+stack_hi)(g), R5
    470 	MOVW	20(R29), R6
    471 	SUBU	R6, R5
    472 	MOVW	R5, R29
    473 
    474 	MOVW	R2, ret+8(FP)
    475 	RET
    476 
    477 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
    478 // Turn the fn into a Go func (by taking its address) and call
    479 // cgocallback_gofunc.
    480 TEXT runtimecgocallback(SB),NOSPLIT,$16-16
    481 	MOVW	$fn+0(FP), R1
    482 	MOVW	R1, 4(R29)
    483 	MOVW	frame+4(FP), R1
    484 	MOVW	R1, 8(R29)
    485 	MOVW	framesize+8(FP), R1
    486 	MOVW	R1, 12(R29)
    487 	MOVW	ctxt+12(FP), R1
    488 	MOVW	R1, 16(R29)
    489 	MOVW	$runtimecgocallback_gofunc(SB), R1
    490 	JAL	(R1)
    491 	RET
    492 
    493 // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt)
    494 // See cgocall.go for more details.
    495 TEXT cgocallback_gofunc(SB),NOSPLIT,$8-16
    496 	NO_LOCAL_POINTERS
    497 
    498 	// Load m and g from thread-local storage.
    499 	MOVB	runtimeiscgo(SB), R1
    500 	BEQ	R1, nocgo
    501 	JAL	runtimeload_g(SB)
    502 nocgo:
    503 
    504 	// If g is nil, Go did not create the current thread.
    505 	// Call needm to obtain one for temporary use.
    506 	// In this case, we're running on the thread stack, so there's
    507 	// lots of space, but the linker doesn't know. Hide the call from
    508 	// the linker analysis by using an indirect call.
    509 	BEQ	g, needm
    510 
    511 	MOVW	g_m(g), R3
    512 	MOVW	R3, savedm-4(SP)
    513 	JMP	havem
    514 
    515 needm:
    516 	MOVW	g, savedm-4(SP) // g is zero, so is m.
    517 	MOVW	$runtimeneedm(SB), R4
    518 	JAL	(R4)
    519 
    520 	// Set m->sched.sp = SP, so that if a panic happens
    521 	// during the function we are about to execute, it will
    522 	// have a valid SP to run on the g0 stack.
    523 	// The next few lines (after the havem label)
    524 	// will save this SP onto the stack and then write
    525 	// the same SP back to m->sched.sp. That seems redundant,
    526 	// but if an unrecovered panic happens, unwindm will
    527 	// restore the g->sched.sp from the stack location
    528 	// and then systemstack will try to use it. If we don't set it here,
    529 	// that restored SP will be uninitialized (typically 0) and
    530 	// will not be usable.
    531 	MOVW	g_m(g), R3
    532 	MOVW	m_g0(R3), R1
    533 	MOVW	R29, (g_sched+gobuf_sp)(R1)
    534 
    535 havem:
    536 	// Now there's a valid m, and we're running on its m->g0.
    537 	// Save current m->g0->sched.sp on stack and then set it to SP.
    538 	// Save current sp in m->g0->sched.sp in preparation for
    539 	// switch back to m->curg stack.
    540 	// NOTE: unwindm knows that the saved g->sched.sp is at 4(R29) aka savedsp-8(SP).
    541 	MOVW	m_g0(R3), R1
    542 	MOVW	(g_sched+gobuf_sp)(R1), R2
    543 	MOVW	R2, savedsp-8(SP)
    544 	MOVW	R29, (g_sched+gobuf_sp)(R1)
    545 
    546 	// Switch to m->curg stack and call runtime.cgocallbackg.
    547 	// Because we are taking over the execution of m->curg
    548 	// but *not* resuming what had been running, we need to
    549 	// save that information (m->curg->sched) so we can restore it.
    550 	// We can restore m->curg->sched.sp easily, because calling
    551 	// runtime.cgocallbackg leaves SP unchanged upon return.
    552 	// To save m->curg->sched.pc, we push it onto the stack.
    553 	// This has the added benefit that it looks to the traceback
    554 	// routine like cgocallbackg is going to return to that
    555 	// PC (because the frame we allocate below has the same
    556 	// size as cgocallback_gofunc's frame declared above)
    557 	// so that the traceback will seamlessly trace back into
    558 	// the earlier calls.
    559 	//
    560 	// In the new goroutine, -4(SP) is unused (where SP refers to
    561 	// m->curg's SP while we're setting it up, before we've adjusted it).
    562 	MOVW	m_curg(R3), g
    563 	JAL	runtimesave_g(SB)
    564 	MOVW	(g_sched+gobuf_sp)(g), R2 // prepare stack as R2
    565 	MOVW	(g_sched+gobuf_pc)(g), R4
    566 	MOVW	R4, -12(R2)
    567 	MOVW    ctxt+12(FP), R1
    568 	MOVW    R1, -8(R2)
    569 	MOVW	$-12(R2), R29
    570 	JAL	runtimecgocallbackg(SB)
    571 
    572 	// Restore g->sched (== m->curg->sched) from saved values.
    573 	MOVW	0(R29), R4
    574 	MOVW	R4, (g_sched+gobuf_pc)(g)
    575 	MOVW	$12(R29), R2
    576 	MOVW	R2, (g_sched+gobuf_sp)(g)
    577 
    578 	// Switch back to m->g0's stack and restore m->g0->sched.sp.
    579 	// (Unlike m->curg, the g0 goroutine never uses sched.pc,
    580 	// so we do not have to restore it.)
    581 	MOVW	g_m(g), R3
    582 	MOVW	m_g0(R3), g
    583 	JAL	runtimesave_g(SB)
    584 	MOVW	(g_sched+gobuf_sp)(g), R29
    585 	MOVW	savedsp-8(SP), R2
    586 	MOVW	R2, (g_sched+gobuf_sp)(g)
    587 
    588 	// If the m on entry was nil, we called needm above to borrow an m
    589 	// for the duration of the call. Since the call is over, return it with dropm.
    590 	MOVW	savedm-4(SP), R3
    591 	BNE	R3, droppedm
    592 	MOVW	$runtimedropm(SB), R4
    593 	JAL	(R4)
    594 droppedm:
    595 
    596 	// Done!
    597 	RET
    598 
    599 // void setg(G*); set g. for use by needm.
    600 // This only happens if iscgo, so jump straight to save_g
    601 TEXT runtimesetg(SB),NOSPLIT,$0-4
    602 	MOVW	gg+0(FP), g
    603 	JAL	runtimesave_g(SB)
    604 	RET
    605 
    606 // void setg_gcc(G*); set g in C TLS.
    607 // Must obey the gcc calling convention.
    608 TEXT setg_gcc<>(SB),NOSPLIT,$0
    609 	MOVW	R4, g
    610 	JAL	runtimesave_g(SB)
    611 	RET
    612 
    613 TEXT runtimegetcallerpc(SB),NOSPLIT,$-4-4
    614 	MOVW	0(R29), R1	// LR saved by caller
    615 	MOVW	R1, ret+0(FP)
    616 	RET
    617 
    618 TEXT runtimeabort(SB),NOSPLIT,$0-0
    619 	UNDEF
    620 
    621 // Not implemented.
    622 TEXT runtimeaeshash(SB),NOSPLIT,$0
    623 	UNDEF
    624 
    625 // Not implemented.
    626 TEXT runtimeaeshash32(SB),NOSPLIT,$0
    627 	UNDEF
    628 
    629 // Not implemented.
    630 TEXT runtimeaeshash64(SB),NOSPLIT,$0
    631 	UNDEF
    632 
    633 // Not implemented.
    634 TEXT runtimeaeshashstr(SB),NOSPLIT,$0
    635 	UNDEF
    636 
    637 // memequal(a, b unsafe.Pointer, size uintptr) bool
    638 TEXT runtimememequal(SB),NOSPLIT,$0-13
    639 	MOVW	a+0(FP), R1
    640 	MOVW	b+4(FP), R2
    641 	BEQ	R1, R2, eq
    642 	MOVW	size+8(FP), R3
    643 	ADDU	R1, R3, R4
    644 loop:
    645 	BNE	R1, R4, test
    646 	MOVW	$1, R1
    647 	MOVB	R1, ret+12(FP)
    648 	RET
    649 test:
    650 	MOVBU	(R1), R6
    651 	ADDU	$1, R1
    652 	MOVBU	(R2), R7
    653 	ADDU	$1, R2
    654 	BEQ	R6, R7, loop
    655 
    656 	MOVB	R0, ret+12(FP)
    657 	RET
    658 eq:
    659 	MOVW	$1, R1
    660 	MOVB	R1, ret+12(FP)
    661 	RET
    662 
    663 // memequal_varlen(a, b unsafe.Pointer) bool
    664 TEXT runtimememequal_varlen(SB),NOSPLIT,$0-9
    665 	MOVW	a+0(FP), R1
    666 	MOVW	b+4(FP), R2
    667 	BEQ	R1, R2, eq
    668 	MOVW	4(REGCTXT), R3	// compiler stores size at offset 4 in the closure
    669 	ADDU	R1, R3, R4
    670 loop:
    671 	BNE	R1, R4, test
    672 	MOVW	$1, R1
    673 	MOVB	R1, ret+8(FP)
    674 	RET
    675 test:
    676 	MOVBU	(R1), R6
    677 	ADDU	$1, R1
    678 	MOVBU	(R2), R7
    679 	ADDU	$1, R2
    680 	BEQ	R6, R7, loop
    681 
    682 	MOVB	R0, ret+8(FP)
    683 	RET
    684 eq:
    685 	MOVW	$1, R1
    686 	MOVB	R1, ret+8(FP)
    687 	RET
    688 
    689 TEXT bytesEqual(SB),NOSPLIT,$0-25
    690 	MOVW	a_len+4(FP), R3
    691 	MOVW	b_len+16(FP), R4
    692 	BNE	R3, R4, noteq	// unequal lengths are not equal
    693 
    694 	MOVW	a+0(FP), R1
    695 	MOVW	b+12(FP), R2
    696 	ADDU	R1, R3	// end
    697 
    698 loop:
    699 	BEQ	R1, R3, equal	// reached the end
    700 	MOVBU	(R1), R6
    701 	ADDU	$1, R1
    702 	MOVBU	(R2), R7
    703 	ADDU	$1, R2
    704 	BEQ	R6, R7, loop
    705 
    706 noteq:
    707 	MOVB	R0, ret+24(FP)
    708 	RET
    709 
    710 equal:
    711 	MOVW	$1, R1
    712 	MOVB	R1, ret+24(FP)
    713 	RET
    714 
    715 TEXT bytesIndexByte(SB),NOSPLIT,$0-20
    716 	MOVW	s+0(FP), R1
    717 	MOVW	s_len+4(FP), R2
    718 	MOVBU	c+12(FP), R3	// byte to find
    719 	ADDU	$1, R1, R4	// store base+1 for later
    720 	ADDU	R1, R2	// end
    721 
    722 loop:
    723 	BEQ	R1, R2, notfound
    724 	MOVBU	(R1), R5
    725 	ADDU	$1, R1
    726 	BNE	R3, R5, loop
    727 
    728 	SUBU	R4, R1	// R1 will be one beyond the position we want so remove (base+1)
    729 	MOVW	R1, ret+16(FP)
    730 	RET
    731 
    732 notfound:
    733 	MOVW	$-1, R1
    734 	MOVW	R1, ret+16(FP)
    735 	RET
    736 
    737 TEXT stringsIndexByte(SB),NOSPLIT,$0-16
    738 	MOVW	s_base+0(FP), R1
    739 	MOVW	s_len+4(FP), R2
    740 	MOVBU	c+8(FP), R3	// byte to find
    741 	ADDU	$1, R1, R4	// store base+1 for later
    742 	ADDU	R1, R2	// end
    743 
    744 loop:
    745 	BEQ	R1, R2, notfound
    746 	MOVBU	(R1), R5
    747 	ADDU	$1, R1
    748 	BNE	R3, R5, loop
    749 
    750 	SUBU	R4, R1	// remove (base+1)
    751 	MOVW	R1, ret+12(FP)
    752 	RET
    753 
    754 notfound:
    755 	MOVW	$-1, R1
    756 	MOVW	R1, ret+12(FP)
    757 	RET
    758 
    759 TEXT runtimecmpstring(SB),NOSPLIT,$0-20
    760 	MOVW	s1_base+0(FP), R3
    761 	MOVW	s1_len+4(FP), R1
    762 	MOVW	s2_base+8(FP), R4
    763 	MOVW	s2_len+12(FP), R2
    764 	BEQ	R3, R4, samebytes
    765 	SGTU	R1, R2, R7
    766 	MOVW	R1, R8
    767 	CMOVN	R7, R2, R8	// R8 is min(R1, R2)
    768 
    769 	ADDU	R3, R8	// R3 is current byte in s1, R8 is last byte in s1 to compare
    770 loop:
    771 	BEQ	R3, R8, samebytes	// all compared bytes were the same; compare lengths
    772 
    773 	MOVBU	(R3), R6
    774 	ADDU	$1, R3
    775 	MOVBU	(R4), R7
    776 	ADDU	$1, R4
    777 	BEQ	R6, R7 , loop
    778 	// bytes differed
    779 	SGTU	R6, R7, R8
    780 	MOVW	$-1, R6
    781 	CMOVZ	R8, R6, R8
    782 	JMP	cmp_ret
    783 samebytes:
    784 	SGTU	R1, R2, R6
    785 	SGTU	R2, R1, R7
    786 	SUBU	R7, R6, R8
    787 cmp_ret:
    788 	MOVW	R8, ret+16(FP)
    789 	RET
    790 
    791 TEXT bytesCompare(SB),NOSPLIT,$0-28
    792 	MOVW	s1_base+0(FP), R3
    793 	MOVW	s2_base+12(FP), R4
    794 	MOVW	s1_len+4(FP), R1
    795 	MOVW	s2_len+16(FP), R2
    796 	BEQ	R3, R4, samebytes
    797 	SGTU	R1, R2, R7
    798 	MOVW	R1, R8
    799 	CMOVN	R7, R2, R8	// R8 is min(R1, R2)
    800 
    801 	ADDU	R3, R8	// R3 is current byte in s1, R8 is last byte in s1 to compare
    802 loop:
    803 	BEQ	R3, R8, samebytes
    804 
    805 	MOVBU	(R3), R6
    806 	ADDU	$1, R3
    807 	MOVBU	(R4), R7
    808 	ADDU	$1, R4
    809 	BEQ	R6, R7 , loop
    810 
    811 	SGTU	R6, R7, R8
    812 	MOVW	$-1, R6
    813 	CMOVZ	R8, R6, R8
    814 	JMP	cmp_ret
    815 samebytes:
    816 	SGTU	R1, R2, R6
    817 	SGTU	R2, R1, R7
    818 	SUBU	R7, R6, R8
    819 cmp_ret:
    820 	MOVW	R8, ret+24(FP)
    821 	RET
    822 
    823 TEXT runtimereturn0(SB),NOSPLIT,$0
    824 	MOVW	$0, R1
    825 	RET
    826 
    827 // Called from cgo wrappers, this function returns g->m->curg.stack.hi.
    828 // Must obey the gcc calling convention.
    829 TEXT _cgo_topofstack(SB),NOSPLIT,$-4
    830 	// g (R30), R3 and REGTMP (R23) might be clobbered by load_g. R30 and R23
    831 	// are callee-save in the gcc calling convention, so save them.
    832 	MOVW	R23, R8
    833 	MOVW	g, R9
    834 	MOVW	R31, R10 // this call frame does not save LR
    835 
    836 	JAL	runtimeload_g(SB)
    837 	MOVW	g_m(g), R1
    838 	MOVW	m_curg(R1), R1
    839 	MOVW	(g_stack+stack_hi)(R1), R2 // return value in R2
    840 
    841 	MOVW	R8, R23
    842 	MOVW	R9, g
    843 	MOVW	R10, R31
    844 
    845 	RET
    846 
    847 // The top-most function running on a goroutine
    848 // returns to goexit+PCQuantum.
    849 TEXT runtimegoexit(SB),NOSPLIT,$-4-0
    850 	NOR	R0, R0	// NOP
    851 	JAL	runtimegoexit1(SB)	// does not return
    852 	// traceback from goexit1 must hit code range of goexit
    853 	NOR	R0, R0	// NOP
    854 
    855 TEXT checkASM(SB),NOSPLIT,$0-1
    856 	MOVW	$1, R1
    857 	MOVB	R1, ret+0(FP)
    858 	RET
    859