Source file
src/runtime/unsafepoint_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "internal/abi"
9 "internal/testenv"
10 "os"
11 "os/exec"
12 "reflect"
13 "regexp"
14 "runtime"
15 "strconv"
16 "strings"
17 "testing"
18 "unsafe"
19 )
20
21
22
23 func setGlobalPointer() {
24 globalPointer = nil
25 }
26
27 var globalPointer *int
28
29 func TestUnsafePoint(t *testing.T) {
30 testenv.MustHaveExec(t)
31 switch runtime.GOARCH {
32 case "amd64", "arm64", "loong64":
33 default:
34 t.Skipf("test not enabled for %s", runtime.GOARCH)
35 }
36
37
38
39 f := runtime.FuncForPC(reflect.ValueOf(setGlobalPointer).Pointer())
40
41
42
43
44
45
46 cmd := exec.Command(testenv.GoToolPath(t), "tool", "objdump", "-s", "setGlobalPointer", os.Args[0])
47 out, err := cmd.CombinedOutput()
48 if err != nil {
49 t.Fatalf("can't objdump %v:\n%s", err, out)
50 }
51 lines := strings.Split(string(out), "\n")[1:]
52
53
54 var entry uint64
55 var startedWB bool
56 var doneWB bool
57 instructionCount := 0
58 unsafeCount := 0
59 for _, line := range lines {
60 line = strings.TrimSpace(line)
61 t.Logf("%s", line)
62 parts := strings.Fields(line)
63 if len(parts) < 4 {
64 continue
65 }
66 if !strings.HasPrefix(parts[0], "unsafepoint_test.go:") {
67 continue
68 }
69 pc, err := strconv.ParseUint(parts[1][2:], 16, 64)
70 if err != nil {
71 t.Fatalf("can't parse pc %s: %v", parts[1], err)
72 }
73 if entry == 0 {
74 entry = pc
75 }
76
77
78
79 unsafe := runtime.UnsafePoint(f.Entry() + uintptr(pc-entry))
80 t.Logf("unsafe: %v\n", unsafe)
81 instructionCount++
82 if unsafe {
83 unsafeCount++
84 }
85
86
87 if startedWB && !doneWB && !unsafe {
88 t.Errorf("instruction %s must be marked unsafe, but isn't", parts[1])
89 }
90
91
92 switch runtime.GOARCH {
93 case "arm64":
94 if parts[3] == "MOVWU" {
95
96
97 startedWB = true
98 }
99 if parts[3] == "MOVD" && parts[4] == "ZR," {
100
101
102 doneWB = true
103 }
104 case "amd64":
105 if parts[3] == "CMPL" {
106 startedWB = true
107 }
108 if parts[3] == "MOVQ" && (parts[4] == "$0x0," || parts[4] == "X15,") {
109 doneWB = true
110 }
111 case "loong64":
112 if parts[3] == "MOVWU" {
113
114
115 startedWB = true
116 }
117 if parts[3] == "MOVV" && parts[4] == "R0," {
118
119
120 doneWB = true
121 }
122 }
123 }
124
125 if instructionCount == 0 {
126 t.Errorf("no instructions")
127 }
128 if unsafeCount == instructionCount {
129 t.Errorf("no interruptible instructions")
130 }
131
132
133
134
135
136 }
137
138
139
140
141
142
143
144
145
146
147
148 type tailCallInner interface{ M() int }
149
150 type tailCallImpl struct{}
151
152 func (tailCallImpl) M() int { return 42 }
153
154 type tailCallOuter struct{ tailCallInner }
155
156 var tailCallValue tailCallInner = tailCallOuter{tailCallImpl{}}
157
158 func TestUnsafePointTailCall(t *testing.T) {
159 switch runtime.GOARCH {
160 case "ppc64", "ppc64le":
161 default:
162 t.Skipf("test not enabled for %s", runtime.GOARCH)
163 }
164 testenv.MustHaveExec(t)
165
166 if got := tailCallValue.M(); got != 42 {
167 t.Fatalf("tailCallValue.M() = %d, want 42", got)
168 }
169
170
171
172 iface := (*struct {
173 tab *abi.ITab
174 data unsafe.Pointer
175 })(unsafe.Pointer(&tailCallValue))
176 f := runtime.FuncForPC(iface.tab.Fun[0])
177 if f == nil {
178 t.Fatal("no func for the tailCallOuter.M wrapper")
179 }
180
181
182 cmd := exec.Command(testenv.GoToolPath(t), "tool", "objdump", "-s", "^"+regexp.QuoteMeta(f.Name())+"$", os.Args[0])
183 out, err := cmd.CombinedOutput()
184 if err != nil {
185 t.Fatalf("can't objdump %v:\n%s", err, out)
186 }
187
188
189
190 var entry uint64
191 branches := 0
192 for _, line := range strings.Split(string(out), "\n")[1:] {
193 parts := strings.Fields(strings.TrimSpace(line))
194 if len(parts) < 4 || !strings.HasPrefix(parts[0], "<autogenerated>:") {
195 continue
196 }
197 pc, err := strconv.ParseUint(parts[1][2:], 16, 64)
198 if err != nil {
199 t.Fatalf("can't parse pc %s: %v", parts[1], err)
200 }
201 if entry == 0 {
202 entry = pc
203 }
204 t.Logf("%s", strings.TrimSpace(line))
205 if parts[3] != "BR" || parts[4] != "(CTR)" {
206 continue
207 }
208 branches++
209 if !runtime.UnsafePoint(f.Entry() + uintptr(pc-entry)) {
210 t.Errorf("%s\n\tbranch through CTR must be marked unsafe, but isn't", strings.TrimSpace(line))
211 }
212 }
213 if branches != 1 {
214 t.Errorf("found %d branches through CTR in %s, want 1; output:\n%s", branches, f.Name(), out)
215 }
216 }
217
View as plain text