runtime: make asmcgocall more robust to missing G

Being able to call asmcgocall without a G is useful for code shared
between different stages of the runtime initialization and thread
creation.

Cq-Include-Trybots: luci.golang.try:gotip-darwin-arm64_15,gotip-linux-mips64le,gotip-linux-ppc64le_power10,gotip-linux-riscv64,gotip-openbsd-ppc64,gotip-openbsd-amd64
Change-Id: Ic427764de197e648e8b9987c98c3b7521512cc5c
Reviewed-on: https://go-review.googlesource.com/c/go/+/750541
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
qmuntal
2026-03-02 08:46:46 +01:00
committed by Quim Muntal
parent a481ef071e
commit 0a56bf8858
10 changed files with 74 additions and 18 deletions

View File

@@ -665,6 +665,13 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-12
// We get called to create new OS threads too, and those
// come in on the m->g0 stack already. Or we might already
// be on the m->gsignal stack.
#ifdef GOOS_windows
// On Windows, get_tls might return garbage if the thread
// has never called into Go, so check tls_g directly.
MOVL runtime·tls_g(SB), CX
CMPL CX, $0
JEQ nosave
#endif
get_tls(CX)
MOVL g(CX), DI
CMPL DI, $0
@@ -741,7 +748,7 @@ loadg:
#ifdef GOOS_windows
MOVL $0, BP
CMPL CX, $0
JEQ 2(PC) // TODO
JEQ needm
#endif
MOVL g(CX), BP
CMPL BP, $0

View File

@@ -1015,10 +1015,6 @@ nosave:
// This code is like the above sequence but without saving/restoring g
// and without worrying about the stack moving out from under us
// (because we're on a system stack, not a goroutine stack).
// The above code could be used directly if already on a system stack,
// but then the only path through this code would be a rare case on Solaris.
// Using this code for all "already on system stack" calls exercises it more,
// which should help keep it correct.
MOVQ fn+0(FP), AX
MOVQ arg+8(FP), BX
MOVQ SP, DX

View File

@@ -630,10 +630,6 @@ nosave:
// This code is like the above sequence but without saving/restoring g
// and without worrying about the stack moving out from under us
// (because we're on a system stack, not a goroutine stack).
// The above code could be used directly if already on a system stack,
// but then the only path through this code would be a rare case on Solaris.
// Using this code for all "already on system stack" calls exercises it more,
// which should help keep it correct.
SUB $24, R13
BIC $0x7, R13 // alignment for gcc ABI
// save null g in case someone looks during debugging.

View File

@@ -1244,10 +1244,6 @@ nosave:
// This code is like the above sequence but without saving/restoring g
// and without worrying about the stack moving out from under us
// (because we're on a system stack, not a goroutine stack).
// The above code could be used directly if already on a system stack,
// but then the only path through this code would be a rare case on Solaris.
// Using this code for all "already on system stack" calls exercises it more,
// which should help keep it correct.
MOVD fn+0(FP), R1
MOVD arg+8(FP), R0
MOVD RSP, R2

View File

@@ -539,6 +539,7 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20
// Figure out if we need to switch to m->g0 stack.
// We get called to create new OS threads too, and those
// come in on the m->g0 stack already.
BEQ g, R0, nosave
MOVV g_m(g), R5
MOVV m_gsignal(R5), R6
BEQ R6, g, g0
@@ -571,6 +572,20 @@ g0:
MOVW R4, ret+16(FP)
RET
nosave:
// Running on a system stack, perhaps even without a g.
// Having no g can happen during thread creation or thread teardown.
MOVV fn+0(FP), R25
MOVV arg+8(FP), R4
MOVV R3, R12
ADDV $-16, R3
MOVV R0, 0(R3) // Where above code stores g, in case someone looks during debugging.
MOVV R12, 8(R3) // Save original stack pointer.
JAL (R25)
MOVV 8(R3), R3 // Restore stack pointer.
MOVW R4, ret+16(FP)
RET
// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$24-24

View File

@@ -452,6 +452,7 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20
// We get called to create new OS threads too, and those
// come in on the m->g0 stack already. Or we might already
// be on the m->gsignal stack.
BEQ g, R0, nosave
MOVV g_m(g), R5
MOVV m_gsignal(R5), R6
BEQ R6, g, g0
@@ -484,6 +485,20 @@ g0:
MOVW R2, ret+16(FP)
RET
nosave:
// Running on a system stack, perhaps even without a g.
// Having no g can happen during thread creation or thread teardown.
MOVV fn+0(FP), R25
MOVV arg+8(FP), R4
MOVV R29, R3
ADDV $-16, R29
MOVV R0, 0(R29) // Where above code stores g, in case someone looks during debugging.
MOVV R3, 8(R29) // Save original stack pointer.
JAL (R25)
MOVV 8(R29), R29 // Restore stack pointer.
MOVW R2, ret+16(FP)
RET
// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$24-24

View File

@@ -782,10 +782,6 @@ nosave:
// This code is like the above sequence but without saving/restoring g
// and without worrying about the stack moving out from under us
// (because we're on a system stack, not a goroutine stack).
// The above code could be used directly if already on a system stack,
// but then the only path through this code would be a rare case.
// Using this code for all "already on system stack" calls exercises it more,
// which should help keep it correct.
SUB $(asmcgocallSaveOffset+8), R1, R10
RLDCR $0, R10, $~15, R1 // 16-byte alignment for gcc ABI

View File

@@ -398,6 +398,7 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20
// We get called to create new OS threads too, and those
// come in on the m->g0 stack already. Or we might already
// be on the m->gsignal stack.
BEQZ g, nosave
MOV g_m(g), X6
MOV m_gsignal(X6), X7
BEQ X7, g, g0
@@ -431,6 +432,20 @@ g0:
MOVW X10, ret+16(FP)
RET
nosave:
// Running on a system stack, perhaps even without a g.
// Having no g can happen during thread creation or thread teardown.
MOV fn+0(FP), X11
MOV arg+8(FP), X10
MOV X2, X8
SUB $16, X2
MOV ZERO, 0(X2) // Where above code stores g, in case someone looks during debugging.
MOV X8, 8(X2) // Save original stack pointer.
JALR RA, (X11)
MOV 8(X2), X2 // Restore stack pointer.
MOVW X10, ret+16(FP)
RET
// func asminit()
TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
RET

View File

@@ -552,6 +552,7 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20
// We get called to create new OS threads too, and those
// come in on the m->g0 stack already. Or we might already
// be on the m->gsignal stack.
CMPBEQ g, $0, nosave
MOVD g_m(g), R6
MOVD m_gsignal(R6), R7
CMPBEQ R7, g, g0
@@ -589,6 +590,25 @@ g0:
MOVW R2, ret+16(FP)
RET
nosave:
// Running on a system stack, perhaps even without a g.
// Having no g can happen during thread creation or thread teardown.
MOVD fn+0(FP), R3
MOVD arg+8(FP), R4
MOVD R15, R2
SUB $176, R15
MOVD $~7, R6
AND R6, R15
MOVD $0, 168(R15) // Where above code stores g, in case someone looks during debugging.
MOVD R2, 160(R15) // Save original stack pointer.
MOVD $0, 0(R15) // clear back chain pointer
MOVD R4, R2 // arg in R2
BL R3
XOR R0, R0
MOVD 160(R15), R15 // Restore stack pointer.
MOVW R2, ret+16(FP)
RET
// cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
TEXT ·cgocallback(SB),NOSPLIT,$24-24

View File

@@ -85,7 +85,7 @@ func sched_yield_trampoline()
//go:nosplit
func osyield_no_g() {
asmcgocall_no_g(unsafe.Pointer(abi.FuncPCABI0(sched_yield_trampoline)), unsafe.Pointer(nil))
asmcgocall(unsafe.Pointer(abi.FuncPCABI0(sched_yield_trampoline)), unsafe.Pointer(nil))
}
// This is exported via linkname to assembly in runtime/cgo.