runtime: clean up escaping in tests

There are several tests in the runtime that need to force various
things to escape to the heap. This CL centralizes this functionality
into runtime.Escape, defined in export_test.

Change-Id: I2de2519661603ad46c372877a9c93efef8e7a857
Reviewed-on: https://go-review.googlesource.com/c/go/+/402178
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Austin Clements <austin@google.com>
Auto-Submit: Austin Clements <austin@google.com>
This commit is contained in:
Austin Clements 2022-04-25 17:21:58 -04:00 committed by Gopher Robot
parent c15d0a93c7
commit 123e27170a
5 changed files with 31 additions and 57 deletions

View File

@ -85,7 +85,7 @@ func GCMask(x any) (ret []byte) {
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")
@ -109,7 +109,7 @@ func RunSchedLocalQueueStealTest() {
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
@ -157,7 +157,7 @@ func RunSchedLocalQueueEmptyTest(iters int) {
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
@ -1260,7 +1260,7 @@ func NewGCController(gcPercent int) *GCController {
// 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
@ -1334,13 +1334,13 @@ func (c *GCController) SetMaxIdleMarkWorkers(max int32) {
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
}

View File

@ -284,7 +284,7 @@ func TestGCTestIsReachable(t *testing.T) {
runtime.KeepAlive(half)
}
var pointerClassSink *int
var pointerClassBSS *int
var pointerClassData = 42
func TestGCTestPointerClass(t *testing.T) {
@ -300,10 +300,9 @@ func TestGCTestPointerClass(t *testing.T) {
}
var onStack int
var notOnStack int
pointerClassSink = &notOnStack
check(unsafe.Pointer(&onStack), "stack")
check(unsafe.Pointer(&notOnStack), "heap")
check(unsafe.Pointer(&pointerClassSink), "bss")
check(unsafe.Pointer(runtime.Escape(&notOnStack)), "heap")
check(unsafe.Pointer(&pointerClassBSS), "bss")
check(unsafe.Pointer(&pointerClassData), "data")
check(nil, "other")
}
@ -614,14 +613,13 @@ func BenchmarkReadMemStats(b *testing.B) {
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() {

View File

@ -77,15 +77,15 @@ func TestGCInfo(t *testing.T) {
}
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))
}
}
@ -104,13 +104,6 @@ func trimDead(mask []byte) []byte {
return mask
}
var gcinfoSink any
func escape(p any) any {
gcinfoSink = p
return p
}
var infoPtr = []byte{typePointer}
type Ptr struct {

View File

@ -173,12 +173,6 @@ func TestTinyAlloc(t *testing.T) {
}
}
var (
tinyByteSink *byte
tinyUint32Sink *uint32
tinyObj12Sink *obj12
)
type obj12 struct {
a uint64
b uint32
@ -205,8 +199,8 @@ func TestTinyAllocIssue37262(t *testing.T) {
// 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
}
@ -218,22 +212,17 @@ func TestTinyAllocIssue37262(t *testing.T) {
// 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()
}

View File

@ -673,8 +673,6 @@ func TestIgnoreBogusMapHint(t *testing.T) {
}
}
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
@ -710,7 +708,7 @@ func TestMapBuckets(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 := 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)
}
@ -720,7 +718,6 @@ func TestMapBuckets(t *testing.T) {
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) {
@ -735,7 +732,7 @@ func TestMapBuckets(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)
}
@ -745,7 +742,6 @@ func TestMapBuckets(t *testing.T) {
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) {
@ -760,7 +756,7 @@ func TestMapBuckets(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)
}
@ -770,7 +766,6 @@ func TestMapBuckets(t *testing.T) {
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) {
@ -785,7 +780,7 @@ func TestMapBuckets(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)
}
@ -795,7 +790,6 @@ func TestMapBuckets(t *testing.T) {
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
}
})