Skip to content

Commit b3bd7ab

Browse files
committed
cmd/compile: fix //go:uintptrescapes for basic method calls
The logic for keeping arguments alive for calls to //go:uintptrescapes functions was only applying to direct function calls. This CL changes it to also apply to direct method calls, which should address most uses of Proc.Call and LazyProc.Call. It's still an open question (#34684) whether other call forms (e.g., method expressions, or indirect calls via function values, method values, or interfaces). Fixes #34474. Change-Id: I874f97145972b0e237a4c9e8926156298f4d6ce0 Reviewed-on: https://go-review.googlesource.com/c/go/+/198043 Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 6cbd737 commit b3bd7ab

File tree

3 files changed

+95
-10
lines changed

3 files changed

+95
-10
lines changed

src/cmd/compile/internal/gc/order.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ func (o *Order) call(n *Node) {
411411
n.Right = o.expr(n.Right, nil) // ODDDARG temp
412412
o.exprList(n.List)
413413

414-
if n.Op != OCALLFUNC {
414+
if n.Op != OCALLFUNC && n.Op != OCALLMETH {
415415
return
416416
}
417417
keepAlive := func(i int) {

test/uintptrescapes2.go

+31-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// errorcheck -0 -m -live
1+
// errorcheck -0 -l -m -live
22

33
// Copyright 2016 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
@@ -13,31 +13,53 @@ import (
1313
)
1414

1515
//go:uintptrescapes
16-
//go:noinline
1716
func F1(a uintptr) {} // ERROR "escaping uintptr"
1817

1918
//go:uintptrescapes
20-
//go:noinline
2119
func F2(a ...uintptr) {} // ERROR "escaping ...uintptr"
2220

2321
//go:uintptrescapes
24-
//go:noinline
2522
func F3(uintptr) {} // ERROR "escaping uintptr"
2623

2724
//go:uintptrescapes
28-
//go:noinline
2925
func F4(...uintptr) {} // ERROR "escaping ...uintptr"
3026

31-
func G() {
27+
type T struct{}
28+
29+
//go:uintptrescapes
30+
func (T) M1(a uintptr) {} // ERROR "escaping uintptr"
31+
32+
//go:uintptrescapes
33+
func (T) M2(a ...uintptr) {} // ERROR "escaping ...uintptr" "leaking param: a"
34+
35+
func TestF1() {
3236
var t int // ERROR "moved to heap"
3337
F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
38+
}
39+
40+
func TestF3() {
3441
var t2 int // ERROR "moved to heap"
35-
F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp"
42+
F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
3643
}
3744

38-
func H() {
45+
func TestM1() {
46+
var t T
47+
var v int // ERROR "moved to heap"
48+
t.M1(uintptr(unsafe.Pointer(&v))) // ERROR "live at call to T.M1: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
49+
}
50+
51+
func TestF2() {
3952
var v int // ERROR "moved to heap"
4053
F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
54+
}
55+
56+
func TestF4() {
4157
var v2 int // ERROR "moved to heap"
42-
F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap"
58+
F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
59+
}
60+
61+
func TestM2() {
62+
var t T
63+
var v int // ERROR "moved to heap"
64+
t.M2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to T.M2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
4365
}

test/uintptrescapes3.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// run
2+
3+
// Copyright 2019 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Test that //go:uintptrescapes works for methods.
8+
9+
package main
10+
11+
import (
12+
"fmt"
13+
"runtime"
14+
"unsafe"
15+
)
16+
17+
var callback func()
18+
19+
//go:noinline
20+
//go:uintptrescapes
21+
func F(ptr uintptr) { callback() }
22+
23+
//go:noinline
24+
//go:uintptrescapes
25+
func Fv(ptrs ...uintptr) { callback() }
26+
27+
type T struct{}
28+
29+
//go:noinline
30+
//go:uintptrescapes
31+
func (T) M(ptr uintptr) { callback() }
32+
33+
//go:noinline
34+
//go:uintptrescapes
35+
func (T) Mv(ptrs ...uintptr) { callback() }
36+
37+
// Each test should pass uintptr(ptr) as an argument to a function call,
38+
// which in turn should call callback. The callback checks that ptr is kept alive.
39+
var tests = []func(ptr unsafe.Pointer){
40+
func(ptr unsafe.Pointer) { F(uintptr(ptr)) },
41+
func(ptr unsafe.Pointer) { Fv(uintptr(ptr)) },
42+
func(ptr unsafe.Pointer) { T{}.M(uintptr(ptr)) },
43+
func(ptr unsafe.Pointer) { T{}.Mv(uintptr(ptr)) },
44+
}
45+
46+
func main() {
47+
for i, test := range tests {
48+
finalized := false
49+
50+
ptr := new([64]byte)
51+
runtime.SetFinalizer(ptr, func(*[64]byte) {
52+
finalized = true
53+
})
54+
55+
callback = func() {
56+
runtime.GC()
57+
if finalized {
58+
fmt.Printf("test #%d failed\n", i)
59+
}
60+
}
61+
test(unsafe.Pointer(ptr))
62+
}
63+
}

0 commit comments

Comments
 (0)