// asyncPreempt is implemented in assembly.
func asyncPreempt()
+// asyncPreempt2 is the Go continuation of asyncPreempt.
+//
+// It must be deeply nosplit because there's untyped data on the stack from
+// asyncPreempt.
+//
+// It must not have any write barriers because we need to limit the amount of
+// stack it uses.
+//
//go:nosplit
+//go:nowritebarrierrec
func asyncPreempt2() {
// We can't grow the stack with untyped data from asyncPreempt, so switch to
// the system stack right away.
package runtime
-import "unsafe"
+import (
+ "internal/runtime/sys"
+ "unsafe"
+)
+
+// xRegState is long-lived extended register state. It is allocated off-heap and
+// manually managed.
+type xRegState struct {
+ _ sys.NotInHeap // Allocated from xRegAlloc
+ regs xRegs
+}
// xRegPerG stores extended register state while a goroutine is asynchronously
// preempted. This is nil otherwise, so we can reuse a (likely small) pool of
type xRegPerP struct {
// scratch temporary per-P space where [asyncPreempt] saves the register
// state before entering Go. It's quickly copied to per-G state.
- scratch xRegState
+ scratch xRegs
// cache is a 1-element allocation cache of extended register state used by
// asynchronous preemption. On entry to preemption, this is used as a simple
// If we ever need to save less state (e.g., avoid saving vector registers
// that aren't in use), we could have multiple allocation pools for
// different size states and copy only the registers we need.
- *dest = pp.xRegs.scratch
+ dest.regs = pp.xRegs.scratch
// Save on the G.
gp.xRegs.state = dest