runtime: wrap procyield assembly and check for 0

procyield will currently loop infinitely if passed 0 on several
platforms. This change sidesteps this bug by renaming procyield to
procyieldAsm, and adding a wrapper named procyield that checks for
cycles == 0. The benefit of this structure is that procyield called
with a constant cycle count of 0 will be inlined and constant folded
away, the expected behavior of a procyield of 0 cycles.

A follow-up change will fix the assembly to not have this footgun
anymore.

Change-Id: I7068abfeb961bc0fa475e216836f7c0e46b38373
Reviewed-on: https://go-review.googlesource.com/c/go/+/712663
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Michael Anthony Knyszek 2025-10-17 19:53:36 +00:00 committed by Gopher Robot
parent ee4d2c312d
commit f838faffe2
12 changed files with 29 additions and 13 deletions

View file

@ -597,7 +597,7 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824)
TEXT runtime·procyield(SB),NOSPLIT,$0-0
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
MOVL cycles+0(FP), AX
again:
PAUSE

View file

@ -815,7 +815,7 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824)
TEXT runtime·procyield(SB),NOSPLIT,$0-0
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
MOVL cycles+0(FP), AX
again:
PAUSE

View file

@ -839,7 +839,7 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-12
TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-12
JMP runtime·memhash64Fallback(SB)
TEXT runtime·procyield(SB),NOSPLIT|NOFRAME,$0
TEXT runtime·procyieldAsm(SB),NOSPLIT|NOFRAME,$0
MOVW cycles+0(FP), R1
MOVW $0, R0
yieldloop:

View file

@ -1036,7 +1036,7 @@ aesloop:
VMOV V0.D[0], R0
RET
TEXT runtime·procyield(SB),NOSPLIT,$0-0
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
MOVWU cycles+0(FP), R0
again:
YIELD

View file

@ -505,7 +505,7 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824)
TEXT runtime·procyield(SB),NOSPLIT,$0-0
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
RET
// Save state of caller into g->sched.

View file

@ -408,7 +408,7 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824)
TEXT runtime·procyield(SB),NOSPLIT,$0-0
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
RET
// Save state of caller into g->sched,

View file

@ -406,7 +406,7 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824)
TEXT runtime·procyield(SB),NOSPLIT,$0-4
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-4
RET
// Save state of caller into g->sched,

View file

@ -612,7 +612,7 @@ CALLFN(·call268435456, 268435456)
CALLFN(·call536870912, 536870912)
CALLFN(·call1073741824, 1073741824)
TEXT runtime·procyield(SB),NOSPLIT|NOFRAME,$0-4
TEXT runtime·procyieldAsm(SB),NOSPLIT|NOFRAME,$0-4
MOVW cycles+0(FP), R7
// POWER does not have a pause/yield instruction equivalent.
// Instead, we can lower the program priority by setting the

View file

@ -367,8 +367,8 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0
MOV gobuf_pc(T0), T0
JALR ZERO, T0
// func procyield(cycles uint32)
TEXT runtime·procyield(SB),NOSPLIT,$0-0
// func procyieldAsm(cycles uint32)
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
RET
// Switch to m->g0's stack, call fn(g).

View file

@ -506,7 +506,7 @@ CALLFN(·call1073741824, 1073741824)
TEXT callfnMVC<>(SB),NOSPLIT|NOFRAME,$0-0
MVC $1, 0(R4), 0(R6)
TEXT runtime·procyield(SB),NOSPLIT,$0-0
TEXT runtime·procyieldAsm(SB),NOSPLIT,$0-0
RET
// Save state of caller into g->sched,

View file

@ -200,7 +200,7 @@ TEXT runtime·asminit(SB), NOSPLIT, $0-0
TEXT ·publicationBarrier(SB), NOSPLIT, $0-0
RET
TEXT runtime·procyield(SB), NOSPLIT, $0-0 // FIXME
TEXT runtime·procyieldAsm(SB), NOSPLIT, $0-0 // FIXME
RET
TEXT runtime·breakpoint(SB), NOSPLIT, $0-0

View file

@ -274,7 +274,23 @@ func reflectcall(stackArgsType *_type, fn, stackArgs unsafe.Pointer, stackArgsSi
// See go.dev/issue/67401.
//
//go:linkname procyield
func procyield(cycles uint32)
//go:nosplit
func procyield(cycles uint32) {
if cycles == 0 {
return
}
procyieldAsm(cycles)
}
// procyieldAsm is the assembly implementation of procyield.
//
// It may loop infinitely if called with cycles == 0. Prefer
// procyield, which will compile down to nothing in such cases,
// instead.
//
// FIXME: The implementation really should not loop infinitely if
// the number of cycles is 0.
func procyieldAsm(cycles uint32)
type neverCallThisFunction struct{}