2424//
2525// - [UnwindStop]: The traceback ends at the user frame boundary.
2626// - [UnwindSkip]: The user frame is skipped and the traceback continues
27- // past it using the provided Next callback .
27+ // past it using the registered safepoint's unwind recipe .
2828// - [UnwindDeclare]: Like UnwindSkip, but the user frame is also
2929// described in tracebacks using the provided Describe callback.
3030//
3131// # GC integration
3232//
33- // If user code holds Go pointers (e.g., on a shadow stack), the
34- // ScanStack callback must be provided so the GC can find and mark those
35- // pointers. Failure to do so may cause the GC to collect live objects.
33+ // If user code holds Go pointers in an active frame, StackMaps must describe
34+ // those slots precisely. The runtime uses the same map both
35+ // while marking the goroutine stack and while relocating a growing stack.
36+ // Failure to describe a live pointer may cause the GC to collect its target
37+ // or leave a stale pointer after stack growth.
3638package jit
3739
3840import (
@@ -48,15 +50,46 @@ const (
4850 // UnwindStop ends the traceback at the user frame boundary.
4951 UnwindStop UnwindMode = iota
5052
51- // UnwindSkip skips user frames and continues unwinding using
52- // the Next callback . The user frames do not appear in tracebacks.
53+ // UnwindSkip skips user frames and continues unwinding using the
54+ // safepoint metadata . The user frames do not appear in tracebacks.
5355 UnwindSkip
5456
5557 // UnwindDeclare is like UnwindSkip but also describes user frames
5658 // in tracebacks using the Describe callback.
5759 UnwindDeclare
5860)
5961
62+ // StackMap describes pointer-bearing words in a user frame at one safepoint.
63+ // PointerMask has one bit per frame word in least-significant-bit-first order;
64+ // a set bit denotes a Go pointer. The runtime copies maps passed to Register
65+ // or AddStackMaps, so callers may release or reuse the input afterward.
66+ type StackMap struct {
67+ // PCOffset is the return-PC offset from Region.Start. Entries in a Region
68+ // must be strictly ordered by PCOffset.
69+ PCOffset uintptr
70+
71+ // FrameOffset is added to the user frame SP to locate the first word
72+ // described by PointerMask.
73+ FrameOffset uintptr
74+
75+ // FrameWords is the number of pointer-sized words described by the map.
76+ FrameWords uintptr
77+
78+ PointerMask []byte
79+
80+ // HasUnwind enables the declarative unwind recipe below. base starts at
81+ // sp+UnwindBaseOffset. If UnwindBaseUsesDelta is set, the uintptr stored at
82+ // sp+UnwindBaseDeltaOffset is added to base. CallerPC and CallerBP are read
83+ // from base plus their offsets; CallerSP is base+CallerSPOffset.
84+ HasUnwind bool
85+ UnwindBaseOffset uintptr
86+ UnwindBaseDeltaOffset uintptr
87+ UnwindBaseUsesDelta bool
88+ CallerPCOffset uintptr
89+ CallerSPOffset uintptr
90+ CallerBPOffset uintptr
91+ }
92+
6093// Region describes a range of executable memory containing user code.
6194type Region struct {
6295 // Start is the first byte of the code region (inclusive).
@@ -74,33 +107,30 @@ type Region struct {
74107 // UnwindDeclare. Must not allocate Go memory. May be nil.
75108 Describe func (pc uintptr ) (name , file string , line int , ok bool )
76109
77- // Next returns the caller's PC, SP, and BP for a frame at the
78- // given PC and SP. This allows the runtime to unwind past user
79- // frames. Must not allocate Go memory.
80- // Required for UnwindSkip and UnwindDeclare modes.
81- // May be nil for UnwindStop (traceback simply ends).
82- //
83- // The callback runs on the system stack and must not grow the stack.
84- // It receives:
85- // pc: the return address pointing into user code
86- // sp: the frame pointer of the Go callee (= user frame's SP before call)
87- // It must return:
88- // callerPC: the return address of the Go caller (above the user frame)
89- // callerSP: the SP of the Go caller
90- // callerBP: unused, reserved for future use
91- Next func (pc , sp uintptr ) (callerPC , callerSP , callerBP uintptr , ok bool )
92-
93- // ScanStack is called during garbage collection to report Go
94- // pointers held in user stack frames or shadow stacks.
95- // Must not allocate Go memory. May be nil.
96- ScanStack func (report func (ptr uintptr ))
110+ // StackMaps contains immutable GC and optional unwind metadata for
111+ // safepoints in this region. The runtime performs lookup itself without
112+ // calling user code. Declarative unwind recipes avoid executing instrumented
113+ // Go callbacks from the runtime system stack.
114+ // It may initially be nil when maps are published with Handle.AddStackMaps
115+ // before the corresponding code becomes reachable.
116+ StackMaps []StackMap
97117}
98118
99119// Handle represents a registered user frame region.
100120type Handle struct {
101121 handle uintptr
102122}
103123
124+ // AddStackMaps appends safepoints to a registered region. Entries must be
125+ // strictly ordered after all previously published entries. The runtime copies
126+ // maps and masks before publishing them to lock-free stack walkers.
127+ //
128+ // JIT compilers must call AddStackMaps after the corresponding machine code is
129+ // complete and before making that code reachable by another goroutine.
130+ func (h Handle ) AddStackMaps (stackMaps ... StackMap ) {
131+ addUserFrameStackMaps (h .handle , stackMaps )
132+ }
133+
104134// liveRegions keeps function pointers (closures) alive so the GC does
105135// not collect them. The runtime stores these pointers in persistentalloc
106136// memory that is invisible to the GC, so we must retain a reference here.
@@ -115,8 +145,7 @@ func Register(r Region) Handle {
115145 r .Start , r .End ,
116146 uint8 (r .Unwind ),
117147 r .Describe ,
118- r .Next ,
119- r .ScanStack ,
148+ r .StackMaps ,
120149 )
121150
122151 // Keep closures alive for the GC.
@@ -130,7 +159,10 @@ func Register(r Region) Handle {
130159 return Handle {handle : h }
131160}
132161
133- // Unregister removes the user frame region from the runtime.
162+ // Unregister removes the user frame region from the runtime. The caller must
163+ // ensure that no goroutine is executing code in the region, that no user frame
164+ // from the region remains on a goroutine stack, and that the code cannot be
165+ // entered again.
134166func (h Handle ) Unregister () {
135167 unregisterUserFrameRegion (h .handle )
136168
@@ -165,9 +197,11 @@ func userFramePreempt() bool
165197//go:linkname registerUserFrameRegion runtime/jit.registerUserFrameRegion
166198func registerUserFrameRegion (start , end uintptr , unwindMode uint8 ,
167199 describe func (pc uintptr ) (string , string , int , bool ),
168- next func (pc , sp uintptr ) (uintptr , uintptr , uintptr , bool ),
169- scanStack func (report func (ptr uintptr )),
200+ stackMaps []StackMap ,
170201) uintptr
171202
172203//go:linkname unregisterUserFrameRegion runtime/jit.unregisterUserFrameRegion
173204func unregisterUserFrameRegion (handle uintptr )
205+
206+ //go:linkname addUserFrameStackMaps runtime/jit.addUserFrameStackMaps
207+ func addUserFrameStackMaps (handle uintptr , stackMaps []StackMap )
0 commit comments