Skip to content

Commit eb0f24e

Browse files
committed
runtime/jit: grow user frame registry dynamically
1 parent 991f6ce commit eb0f24e

2 files changed

Lines changed: 63 additions & 25 deletions

File tree

src/runtime/jit/jit_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,29 @@ func TestMultipleRegisterUnregister(t *testing.T) {
7272
}
7373
}
7474

75+
// TestManyRegisterUnregister verifies that the registry grows with its users.
76+
// JITs commonly allocate one registered region per executable arena.
77+
func TestManyRegisterUnregister(t *testing.T) {
78+
code := make([]byte, 128)
79+
addr, size, err := allocExecutable(code)
80+
if err != nil {
81+
t.Fatalf("allocExecutable: %v", err)
82+
}
83+
defer freeExecutable(addr, size)
84+
85+
handles := make([]jit.Handle, len(code))
86+
for i := range handles {
87+
handles[i] = jit.Register(jit.Region{
88+
Start: addr + uintptr(i),
89+
End: addr + uintptr(i+1),
90+
Unwind: jit.UnwindStop,
91+
})
92+
}
93+
for i := len(handles) - 1; i >= 0; i-- {
94+
handles[i].Unregister()
95+
}
96+
}
97+
7598
// TestConcurrentRegisterUnregister tests that concurrent registration
7699
// and unregistration do not crash.
77100
func TestConcurrentRegisterUnregister(t *testing.T) {

src/runtime/userframe.go

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,13 @@ const (
8787
// on an immutable snapshot. This avoids data races without requiring
8888
// the reader to take a lock (which is important because the reader runs
8989
// on the system stack during stack unwinding and signal handling).
90-
const maxUserFrameRegions = 64
9190

9291
// userFrameSnapshot is the immutable snapshot published to readers.
92+
// regions is a variable-length trailing array allocated by
93+
// allocUserFrameSnapshot.
9394
type userFrameSnapshot struct {
94-
regions [maxUserFrameRegions]userFrameRegion
9595
count int
96+
regions [1]userFrameRegion
9697
}
9798

9899
var (
@@ -109,6 +110,29 @@ func loadUserFrameSnapshot() *userFrameSnapshot {
109110
return (*userFrameSnapshot)(p)
110111
}
111112

113+
func allocUserFrameSnapshot(count int) *userFrameSnapshot {
114+
if count < 0 {
115+
throw("registerUserFrameRegion: too many regions")
116+
}
117+
regionBytes := uintptr(count) * unsafe.Sizeof(userFrameRegion{})
118+
if count != 0 && regionBytes/unsafe.Sizeof(userFrameRegion{}) != uintptr(count) {
119+
throw("registerUserFrameRegion: too many regions")
120+
}
121+
bytes := unsafe.Offsetof(userFrameSnapshot{}.regions) + regionBytes
122+
if bytes < regionBytes {
123+
throw("registerUserFrameRegion: too many regions")
124+
}
125+
mem := persistentalloc(bytes, unsafe.Alignof(userFrameSnapshot{}), &memstats.other_sys)
126+
snap := (*userFrameSnapshot)(mem)
127+
snap.count = count
128+
return snap
129+
}
130+
131+
//go:nosplit
132+
func (snap *userFrameSnapshot) region(i int) *userFrameRegion {
133+
return (*userFrameRegion)(add(unsafe.Pointer(snap), unsafe.Offsetof(userFrameSnapshot{}.regions)+uintptr(i)*unsafe.Sizeof(userFrameRegion{})))
134+
}
135+
112136
func registerUserFrameRegion(r userFrameRegion) uintptr {
113137
if r.start >= r.end {
114138
throw("registerUserFrameRegion: invalid address range")
@@ -122,14 +146,9 @@ func registerUserFrameRegion(r userFrameRegion) uintptr {
122146
oldCount = old.count
123147
}
124148

125-
if oldCount >= maxUserFrameRegions {
126-
unlock(&userFrameLock)
127-
throw("registerUserFrameRegion: too many regions")
128-
}
129-
130149
// Check for overlaps.
131150
for i := 0; i < oldCount; i++ {
132-
fr := &old.regions[i]
151+
fr := old.region(i)
133152
if r.start < fr.end && r.end > fr.start {
134153
unlock(&userFrameLock)
135154
throw("registerUserFrameRegion: overlapping region")
@@ -141,27 +160,25 @@ func registerUserFrameRegion(r userFrameRegion) uintptr {
141160
r.handle = userFrameHandle
142161

143162
// Build new snapshot with the region inserted in sorted order.
144-
mem := persistentalloc(unsafe.Sizeof(userFrameSnapshot{}), unsafe.Sizeof(uintptr(0)), &memstats.other_sys)
145-
snap := (*userFrameSnapshot)(mem)
163+
snap := allocUserFrameSnapshot(oldCount + 1)
146164

147165
pos := oldCount
148166
for i := 0; i < oldCount; i++ {
149-
if r.start < old.regions[i].start {
167+
if r.start < old.region(i).start {
150168
pos = i
151169
break
152170
}
153171
}
154172
// Copy elements before pos.
155173
for i := 0; i < pos; i++ {
156-
snap.regions[i] = old.regions[i]
174+
*snap.region(i) = *old.region(i)
157175
}
158176
// Insert new region.
159-
snap.regions[pos] = r
177+
*snap.region(pos) = r
160178
// Copy elements after pos.
161179
for i := pos; i < oldCount; i++ {
162-
snap.regions[i+1] = old.regions[i]
180+
*snap.region(i + 1) = *old.region(i)
163181
}
164-
snap.count = oldCount + 1
165182

166183
// Publish atomically. Readers will see the complete new snapshot.
167184
atomic.StorepNoWB(unsafe.Pointer(&userFrameSnap), unsafe.Pointer(snap))
@@ -182,7 +199,7 @@ func unregisterUserFrameRegion(handle uintptr) {
182199
// Find the region.
183200
idx := -1
184201
for i := 0; i < old.count; i++ {
185-
if old.regions[i].handle == handle {
202+
if old.region(i).handle == handle {
186203
idx = i
187204
break
188205
}
@@ -193,17 +210,14 @@ func unregisterUserFrameRegion(handle uintptr) {
193210
}
194211

195212
// Build new snapshot without the region.
196-
mem := persistentalloc(unsafe.Sizeof(userFrameSnapshot{}), unsafe.Sizeof(uintptr(0)), &memstats.other_sys)
197-
snap := (*userFrameSnapshot)(mem)
213+
snap := allocUserFrameSnapshot(old.count - 1)
198214
j := 0
199215
for i := 0; i < old.count; i++ {
200216
if i != idx {
201-
snap.regions[j] = old.regions[i]
217+
*snap.region(j) = *old.region(i)
202218
j++
203219
}
204220
}
205-
snap.count = old.count - 1
206-
207221
atomic.StorepNoWB(unsafe.Pointer(&userFrameSnap), unsafe.Pointer(snap))
208222

209223
unlock(&userFrameLock)
@@ -222,12 +236,13 @@ func findUserFrameRegion(pc uintptr) *userFrameRegion {
222236
lo, hi := 0, snap.count
223237
for lo < hi {
224238
mid := int(uint(lo+hi) >> 1)
225-
if snap.regions[mid].end <= pc {
239+
region := snap.region(mid)
240+
if region.end <= pc {
226241
lo = mid + 1
227-
} else if snap.regions[mid].start > pc {
242+
} else if region.start > pc {
228243
hi = mid
229244
} else {
230-
return &snap.regions[mid]
245+
return region
231246
}
232247
}
233248
return nil
@@ -435,7 +450,7 @@ func jit_addUserFrameStackMaps(handle uintptr, stackMaps []userFrameStackMapInpu
435450
snap := loadUserFrameSnapshot()
436451
if snap != nil {
437452
for i := 0; i < snap.count; i++ {
438-
r := &snap.regions[i]
453+
r := snap.region(i)
439454
if r.handle == handle {
440455
appendUserFrameStackMaps(r.stackMaps, r.start, r.end, r.unwindMode, stackMaps)
441456
unlock(&userFrameLock)

0 commit comments

Comments
 (0)