c70ff9f9be
The gc compiler has started permitting go:linkname comments with a single argument to mean that a function should be externally visible outside the package. Implement this in the Go frontend. Change the libgo runtime package to use it, rather than repeating the name just to export a function. Remove a couple of unnecessary go:linkname comments on declarations. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/192197 From-SVN: r275239
197 lines
5.7 KiB
Go
197 lines
5.7 KiB
Go
// 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 || gp.m.locks != 0 || gp.m.mallocing != 0 || gp.m.preemptoff != "" || gp.m.incgo {
|
|
return
|
|
}
|
|
|
|
// Synchronize with scang.
|
|
gp.scanningself = true
|
|
casgstatus(gp, _Grunning, _Gwaiting)
|
|
if gp.preemptscan {
|
|
for !castogscanstatus(gp, _Gwaiting, _Gscanwaiting) {
|
|
// Likely to be racing with the GC as
|
|
// it sees a _Gwaiting and does the
|
|
// stack scan. If so, gcworkdone will
|
|
// be set and gcphasework will simply
|
|
// return.
|
|
}
|
|
if !gp.gcscandone {
|
|
mp := acquirem()
|
|
gcw := &gp.m.p.ptr().gcw
|
|
scanstack(gp, gcw)
|
|
releasem(mp)
|
|
gp.gcscandone = true
|
|
}
|
|
gp.preemptscan = false
|
|
gp.preempt = false
|
|
casfrom_Gscanstatus(gp, _Gscanwaiting, _Gwaiting)
|
|
// This clears gcscanvalid.
|
|
casgstatus(gp, _Gwaiting, _Grunning)
|
|
gp.scanningself = false
|
|
return
|
|
}
|
|
|
|
// Act like goroutine called runtime.Gosched.
|
|
casgstatus(gp, _Gwaiting, _Grunning)
|
|
gp.scanningself = false
|
|
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
|
|
}
|