-
Notifications
You must be signed in to change notification settings - Fork 0
/
mgc_gccgo.go
171 lines (149 loc) · 4.93 KB
/
mgc_gccgo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// gccgo-specific support for GC.
package runtime
import (
"runtime/internal/sys"
"unsafe"
)
// For gccgo, use go:linkname to export compiler-called functions.
//
//go:linkname gcWriteBarrier
// gcRoot is a single GC root: a variable plus a ptrmask.
//go:notinheap
type gcRoot struct {
decl unsafe.Pointer // Pointer to variable.
size uintptr // Size of variable.
ptrdata uintptr // Length of gcdata.
gcdata *uint8 // Pointer mask.
}
// gcRootList is the set of GC roots for a package.
// The next field is used to put this all into a linked list.
// count gives the real length of the array.
type gcRootList struct {
next *gcRootList
count int
roots [1 << 26]gcRoot
}
// roots is the list of GC roots for the program.
// The compiler keeps this variable itself off the list.
var gcRoots *gcRootList
// Slice containing pointers to all reachable gcRoot's sorted by
// starting address (generated at init time from 'gcRoots').
// The compiler also keeps this variable itself off the list.
// The storage backing this slice is allocated via persistentalloc(), the
// idea being that we don't want to treat the slice itself as a global
// variable, since it points to things that don't need to be scanned
// themselves.
var gcRootsIndex []*gcRoot
// rootradixsort performs an in-place radix sort of the 'arr' rootptr slice.
// Note: not a stable sort, however we expect it to be called only on slices
// with no duplicate entries, so this should not matter.
func rootradixsort(arr []*gcRoot, lo, hi int, bit uint) {
// Partition the array into two bins based on the values at the
// specified bit position: 0's bin (grown from the left) and and
// 1's bin (grown from the right). We keep two boundary markers,
// the 0's boundary "zbb" (which grows to the right) and the 1's
// boundary "obb" (which grows to the left). At each step we
// examine the bit for the right-of-ZBB element: if it is zero, we
// leave it in place and move the ZBB to the right. If the bit is
// not zero, then we swap the ZBB and OBB elements and move the
// OBB to the left. When this is done, the two partitions are then
// sorted using the next lower bit.
// 0's bin boundary, initially set to before the first element
zbb := lo - 1
// 1's bin boundary, set to just beyond the last element
obb := hi + 1
// mask to pick up bit of interest
bmask := uintptr(1) << bit
for obb-zbb > 1 {
zbbval := uintptr(arr[zbb+1].decl) & bmask
if zbbval == 0 {
// Move zbb one to the right
zbb++
} else {
// Move obb one to the left and swap
arr[obb-1], arr[zbb+1] = arr[zbb+1], arr[obb-1]
obb--
}
}
if bit != 0 {
// NB: in most cases there is just a single partition to visit
// so if we wanted to reduce stack space we could check for this
// and insert a goto back up to the top.
if zbb-lo > 0 {
rootradixsort(arr, lo, zbb, bit-1)
}
if hi-obb > 0 {
rootradixsort(arr, obb, hi, bit-1)
}
}
}
//go:nowritebarrier
func createGcRootsIndex() {
// Count roots
nroots := 0
gcr := gcRoots
for gcr != nil {
nroots += gcr.count
gcr = gcr.next
}
// Construct the gcRootsIndex slice. Use non-heap storage for the array
// backing the slice.
sp := (*notInHeapSlice)(unsafe.Pointer(&gcRootsIndex))
sp.array = (*notInHeap)(persistentalloc1(sys.PtrSize*uintptr(nroots), sys.PtrSize, &memstats.other_sys))
if sp.array == nil {
throw("runtime: cannot allocate memory")
}
sp.len = nroots
sp.cap = nroots
// Populate the roots index slice
gcr = gcRoots
k := 0
for gcr != nil {
for i := 0; i < gcr.count; i++ {
gcRootsIndex[k] = &gcr.roots[i]
k++
}
gcr = gcr.next
}
// Sort it by starting address.
rootradixsort(gcRootsIndex, 0, nroots-1, sys.PtrSize*8-1)
}
// registerGCRoots is called by compiler-generated code.
//go:linkname registerGCRoots
// registerGCRoots is called by init functions to register the GC
// roots for a package. The init functions are run sequentially at
// the start of the program, so no locking is needed.
func registerGCRoots(r *gcRootList) {
r.next = gcRoots
gcRoots = r
}
// checkPreempt is called when the preempt field in the running G is true.
// It preempts the goroutine if it is safe to do so.
// If preemptscan is true, this scans the stack for the garbage collector
// and carries on.
func checkPreempt() {
gp := getg()
if !gp.preempt || gp != gp.m.curg || !canPreemptM(gp.m) {
return
}
if gp.preemptStop {
mcall(preemptPark)
}
// Act like goroutine called runtime.Gosched.
mcall(gopreempt_m)
}
// gcWriteBarrier implements a write barrier. This is implemented in
// assembly in the gc library, but there is no special advantage to
// doing so with gccgo.
//go:nosplit
//go:nowritebarrier
func gcWriteBarrier(dst *uintptr, src uintptr) {
buf := &getg().m.p.ptr().wbBuf
if !buf.putFast(src, *dst) {
wbBufFlush(dst, src)
}
*dst = src
}