1// Copyright 2014 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime_test
6
7import (
8	"bytes"
9	"runtime"
10	"testing"
11)
12
13const (
14	typeScalar  = 0
15	typePointer = 1
16)
17
18// TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info.
19func TestGCInfo(t *testing.T) {
20	verifyGCInfo(t, "bss Ptr", &bssPtr, infoPtr)
21	verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
22	verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar)
23	verifyGCInfo(t, "bss BigStruct", &bssBigStruct, infoBigStruct())
24	verifyGCInfo(t, "bss string", &bssString, infoString)
25	verifyGCInfo(t, "bss slice", &bssSlice, infoSlice)
26	verifyGCInfo(t, "bss eface", &bssEface, infoEface)
27	verifyGCInfo(t, "bss iface", &bssIface, infoIface)
28
29	verifyGCInfo(t, "data Ptr", &dataPtr, infoPtr)
30	verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr)
31	verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar)
32	verifyGCInfo(t, "data BigStruct", &dataBigStruct, infoBigStruct())
33	verifyGCInfo(t, "data string", &dataString, infoString)
34	verifyGCInfo(t, "data slice", &dataSlice, infoSlice)
35	verifyGCInfo(t, "data eface", &dataEface, infoEface)
36	verifyGCInfo(t, "data iface", &dataIface, infoIface)
37
38	{
39		var x Ptr
40		verifyGCInfo(t, "stack Ptr", &x, infoPtr)
41		runtime.KeepAlive(x)
42	}
43	{
44		var x ScalarPtr
45		verifyGCInfo(t, "stack ScalarPtr", &x, infoScalarPtr)
46		runtime.KeepAlive(x)
47	}
48	{
49		var x PtrScalar
50		verifyGCInfo(t, "stack PtrScalar", &x, infoPtrScalar)
51		runtime.KeepAlive(x)
52	}
53	{
54		var x BigStruct
55		verifyGCInfo(t, "stack BigStruct", &x, infoBigStruct())
56		runtime.KeepAlive(x)
57	}
58	{
59		var x string
60		verifyGCInfo(t, "stack string", &x, infoString)
61		runtime.KeepAlive(x)
62	}
63	{
64		var x []string
65		verifyGCInfo(t, "stack slice", &x, infoSlice)
66		runtime.KeepAlive(x)
67	}
68	{
69		var x any
70		verifyGCInfo(t, "stack eface", &x, infoEface)
71		runtime.KeepAlive(x)
72	}
73	{
74		var x Iface
75		verifyGCInfo(t, "stack iface", &x, infoIface)
76		runtime.KeepAlive(x)
77	}
78
79	for i := 0; i < 10; i++ {
80		verifyGCInfo(t, "heap Ptr", runtime.Escape(new(Ptr)), trimDead(infoPtr))
81		verifyGCInfo(t, "heap PtrSlice", runtime.Escape(&make([]*byte, 10)[0]), trimDead(infoPtr10))
82		verifyGCInfo(t, "heap ScalarPtr", runtime.Escape(new(ScalarPtr)), trimDead(infoScalarPtr))
83		verifyGCInfo(t, "heap ScalarPtrSlice", runtime.Escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4))
84		verifyGCInfo(t, "heap PtrScalar", runtime.Escape(new(PtrScalar)), trimDead(infoPtrScalar))
85		verifyGCInfo(t, "heap BigStruct", runtime.Escape(new(BigStruct)), trimDead(infoBigStruct()))
86		verifyGCInfo(t, "heap string", runtime.Escape(new(string)), trimDead(infoString))
87		verifyGCInfo(t, "heap eface", runtime.Escape(new(any)), trimDead(infoEface))
88		verifyGCInfo(t, "heap iface", runtime.Escape(new(Iface)), trimDead(infoIface))
89	}
90}
91
92func verifyGCInfo(t *testing.T, name string, p any, mask0 []byte) {
93	mask := runtime.GCMask(p)
94	if bytes.HasPrefix(mask, mask0) {
95		// Just the prefix matching is OK.
96		//
97		// The Go runtime's pointer/scalar iterator generates pointers beyond
98		// the size of the type, up to the size of the size class. This space
99		// is safe for the GC to scan since it's zero, and GCBits checks to
100		// make sure that's true. But we need to handle the fact that the bitmap
101		// may be larger than we expect.
102		return
103	}
104	t.Errorf("bad GC program for %v:\nwant %+v\ngot  %+v", name, mask0, mask)
105}
106
107func trimDead(mask []byte) []byte {
108	for len(mask) > 0 && mask[len(mask)-1] == typeScalar {
109		mask = mask[:len(mask)-1]
110	}
111	return mask
112}
113
114var infoPtr = []byte{typePointer}
115
116type Ptr struct {
117	*byte
118}
119
120var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer}
121
122type ScalarPtr struct {
123	q int
124	w *int
125	e int
126	r *int
127	t int
128	y *int
129}
130
131var infoScalarPtr = []byte{typeScalar, typePointer, typeScalar, typePointer, typeScalar, typePointer}
132
133var infoScalarPtr4 = append(append(append(append([]byte(nil), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...)
134
135type PtrScalar struct {
136	q *int
137	w int
138	e *int
139	r int
140	t *int
141	y int
142}
143
144var infoPtrScalar = []byte{typePointer, typeScalar, typePointer, typeScalar, typePointer, typeScalar}
145
146type BigStruct struct {
147	q *int
148	w byte
149	e [17]byte
150	r []byte
151	t int
152	y uint16
153	u uint64
154	i string
155}
156
157func infoBigStruct() []byte {
158	switch runtime.GOARCH {
159	case "386", "arm", "mips", "mipsle":
160		return []byte{
161			typePointer,                                                // q *int
162			typeScalar, typeScalar, typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
163			typePointer, typeScalar, typeScalar, // r []byte
164			typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64
165			typePointer, typeScalar, // i string
166		}
167	case "arm64", "amd64", "loong64", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "wasm":
168		return []byte{
169			typePointer,                        // q *int
170			typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
171			typePointer, typeScalar, typeScalar, // r []byte
172			typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64
173			typePointer, typeScalar, // i string
174		}
175	default:
176		panic("unknown arch")
177	}
178}
179
180type Iface interface {
181	f()
182}
183
184type IfaceImpl int
185
186func (IfaceImpl) f() {
187}
188
189var (
190	// BSS
191	bssPtr       Ptr
192	bssScalarPtr ScalarPtr
193	bssPtrScalar PtrScalar
194	bssBigStruct BigStruct
195	bssString    string
196	bssSlice     []string
197	bssEface     any
198	bssIface     Iface
199
200	// DATA
201	dataPtr             = Ptr{new(byte)}
202	dataScalarPtr       = ScalarPtr{q: 1}
203	dataPtrScalar       = PtrScalar{w: 1}
204	dataBigStruct       = BigStruct{w: 1}
205	dataString          = "foo"
206	dataSlice           = []string{"foo"}
207	dataEface     any   = 42
208	dataIface     Iface = IfaceImpl(42)
209
210	infoString = []byte{typePointer, typeScalar}
211	infoSlice  = []byte{typePointer, typeScalar, typeScalar}
212	infoEface  = []byte{typeScalar, typePointer}
213	infoIface  = []byte{typeScalar, typePointer}
214)
215