func RunSchedLocalQueueTest() {
_p_ := new(p)
gs := make([]g, len(_p_.runq))
- escape(gs) // Ensure gs doesn't move, since we use guintptrs
+ Escape(gs) // Ensure gs doesn't move, since we use guintptrs
for i := 0; i < len(_p_.runq); i++ {
if g, _ := runqget(_p_); g != nil {
throw("runq is not empty initially")
p1 := new(p)
p2 := new(p)
gs := make([]g, len(p1.runq))
- escape(gs) // Ensure gs doesn't move, since we use guintptrs
+ Escape(gs) // Ensure gs doesn't move, since we use guintptrs
for i := 0; i < len(p1.runq); i++ {
for j := 0; j < i; j++ {
gs[j].sig = 0
done := make(chan bool, 1)
p := new(p)
gs := make([]g, 2)
- escape(gs) // Ensure gs doesn't move, since we use guintptrs
+ Escape(gs) // Ensure gs doesn't move, since we use guintptrs
ready := new(uint32)
for i := 0; i < iters; i++ {
*ready = 0
// do 64-bit atomics on it, and if it gets stack-allocated
// on a 32-bit architecture, it may get allocated unaligned
// space.
- g := escape(new(GCController))
+ g := Escape(new(GCController))
g.gcControllerState.test = true // Mark it as a test copy.
g.init(int32(gcPercent))
return g
c.setMaxIdleMarkWorkers(max)
}
+var alwaysFalse bool
var escapeSink any
-//go:noinline
-//go:norace
-func escape[T any](x T) T {
- escapeSink = x
- escapeSink = nil
+func Escape[T any](x T) T {
+ if alwaysFalse {
+ escapeSink = x
+ }
return x
}
runtime.KeepAlive(half)
}
-var pointerClassSink *int
+var pointerClassBSS *int
var pointerClassData = 42
func TestGCTestPointerClass(t *testing.T) {
}
var onStack int
var notOnStack int
- pointerClassSink = ¬OnStack
check(unsafe.Pointer(&onStack), "stack")
- check(unsafe.Pointer(¬OnStack), "heap")
- check(unsafe.Pointer(&pointerClassSink), "bss")
+ check(unsafe.Pointer(runtime.Escape(¬OnStack)), "heap")
+ check(unsafe.Pointer(&pointerClassBSS), "bss")
check(unsafe.Pointer(&pointerClassData), "data")
check(nil, "other")
}
for i := range x {
x[i] = new([1024]byte)
}
- hugeSink = x
b.ResetTimer()
for i := 0; i < b.N; i++ {
runtime.ReadMemStats(&ms)
}
- hugeSink = nil
+ runtime.KeepAlive(x)
}
func applyGCLoad(b *testing.B) func() {
}
for i := 0; i < 10; i++ {
- verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(infoPtr))
- verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10))
- verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr))
- verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4))
- verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), trimDead(infoPtrScalar))
- verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), trimDead(infoBigStruct()))
- verifyGCInfo(t, "heap string", escape(new(string)), trimDead(infoString))
- verifyGCInfo(t, "heap eface", escape(new(any)), trimDead(infoEface))
- verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface))
+ verifyGCInfo(t, "heap Ptr", runtime.Escape(new(Ptr)), trimDead(infoPtr))
+ verifyGCInfo(t, "heap PtrSlice", runtime.Escape(&make([]*byte, 10)[0]), trimDead(infoPtr10))
+ verifyGCInfo(t, "heap ScalarPtr", runtime.Escape(new(ScalarPtr)), trimDead(infoScalarPtr))
+ verifyGCInfo(t, "heap ScalarPtrSlice", runtime.Escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4))
+ verifyGCInfo(t, "heap PtrScalar", runtime.Escape(new(PtrScalar)), trimDead(infoPtrScalar))
+ verifyGCInfo(t, "heap BigStruct", runtime.Escape(new(BigStruct)), trimDead(infoBigStruct()))
+ verifyGCInfo(t, "heap string", runtime.Escape(new(string)), trimDead(infoString))
+ verifyGCInfo(t, "heap eface", runtime.Escape(new(any)), trimDead(infoEface))
+ verifyGCInfo(t, "heap iface", runtime.Escape(new(Iface)), trimDead(infoIface))
}
}
return mask
}
-var gcinfoSink any
-
-func escape(p any) any {
- gcinfoSink = p
- return p
-}
-
var infoPtr = []byte{typePointer}
type Ptr struct {
}
}
-var (
- tinyByteSink *byte
- tinyUint32Sink *uint32
- tinyObj12Sink *obj12
-)
-
type obj12 struct {
a uint64
b uint32
// Make 1-byte allocations until we get a fresh tiny slot.
aligned := false
for i := 0; i < 16; i++ {
- tinyByteSink = new(byte)
- if uintptr(unsafe.Pointer(tinyByteSink))&0xf == 0xf {
+ x := runtime.Escape(new(byte))
+ if uintptr(unsafe.Pointer(x))&0xf == 0xf {
aligned = true
break
}
// Create a 4-byte object so that the current
// tiny slot is partially filled.
- tinyUint32Sink = new(uint32)
+ runtime.Escape(new(uint32))
// Create a 12-byte object, which fits into the
// tiny slot. If it actually gets place there,
// then the field "a" will be improperly aligned
// for atomic access on 32-bit architectures.
// This won't be true if issue 36606 gets resolved.
- tinyObj12Sink = new(obj12)
+ tinyObj12 := runtime.Escape(new(obj12))
// Try to atomically access "x.a".
- atomic.StoreUint64(&tinyObj12Sink.a, 10)
-
- // Clear the sinks.
- tinyByteSink = nil
- tinyUint32Sink = nil
- tinyObj12Sink = nil
+ atomic.StoreUint64(&tinyObj12.a, 10)
runtime.Releasem()
}
}
}
-var mapSink map[int]int
-
var mapBucketTests = [...]struct {
n int // n is the number of map elements
noescape int // number of expected buckets for non-escaping map
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
- escapingMap := map[int]int{}
+ escapingMap := runtime.Escape(map[int]int{})
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
- mapSink = escapingMap
}
})
t.Run("nohint", func(t *testing.T) {
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
- escapingMap := make(map[int]int)
+ escapingMap := runtime.Escape(make(map[int]int))
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
- mapSink = escapingMap
}
})
t.Run("makemap", func(t *testing.T) {
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
- escapingMap := make(map[int]int, tt.n)
+ escapingMap := runtime.Escape(make(map[int]int, tt.n))
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
- mapSink = escapingMap
}
})
t.Run("makemap64", func(t *testing.T) {
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
- escapingMap := make(map[int]int, tt.n)
+ escapingMap := runtime.Escape(make(map[int]int, tt.n))
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
- mapSink = escapingMap
}
})