]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fix //go:uintptrescapes for basic method calls
authorMatthew Dempsky <mdempsky@google.com>
Tue, 1 Oct 2019 18:22:07 +0000 (11:22 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 5 Nov 2019 00:26:30 +0000 (00:26 +0000)
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 <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/gc/order.go
test/uintptrescapes2.go
test/uintptrescapes3.go [new file with mode: 0644]

index 90d7baa602e0b8f3759e35286995319d11246ca2..461f51b7c925347b9cf81b7c936d172454b63121 100644 (file)
@@ -411,7 +411,7 @@ func (o *Order) call(n *Node) {
        n.Right = o.expr(n.Right, nil) // ODDDARG temp
        o.exprList(n.List)
 
-       if n.Op != OCALLFUNC {
+       if n.Op != OCALLFUNC && n.Op != OCALLMETH {
                return
        }
        keepAlive := func(i int) {
index 866efd94d84e1f5d000c65dd0ba4e23a37cf1c39..3ff1d940425863caae41c03e59a1a57d379e97e0 100644 (file)
@@ -1,4 +1,4 @@
-// errorcheck -0 -m -live
+// errorcheck -0 -l -m -live
 
 // Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -13,31 +13,53 @@ import (
 )
 
 //go:uintptrescapes
-//go:noinline
 func F1(a uintptr) {} // ERROR "escaping uintptr"
 
 //go:uintptrescapes
-//go:noinline
 func F2(a ...uintptr) {} // ERROR "escaping ...uintptr"
 
 //go:uintptrescapes
-//go:noinline
 func F3(uintptr) {} // ERROR "escaping uintptr"
 
 //go:uintptrescapes
-//go:noinline
 func F4(...uintptr) {} // ERROR "escaping ...uintptr"
 
-func G() {
+type T struct{}
+
+//go:uintptrescapes
+func (T) M1(a uintptr) {} // ERROR "escaping uintptr"
+
+//go:uintptrescapes
+func (T) M2(a ...uintptr) {} // ERROR "escaping ...uintptr" "leaking param: a"
+
+func TestF1() {
        var t int                        // ERROR "moved to heap"
        F1(uintptr(unsafe.Pointer(&t)))  // ERROR "live at call to F1: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
+}
+
+func TestF3() {
        var t2 int                       // ERROR "moved to heap"
-       F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp"
+       F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }
 
-func H() {
+func TestM1() {
+       var t T
+       var v int                         // ERROR "moved to heap"
+       t.M1(uintptr(unsafe.Pointer(&v))) // ERROR "live at call to T.M1: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
+}
+
+func TestF2() {
        var v int                                 // ERROR "moved to heap"
        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$"
+}
+
+func TestF4() {
        var v2 int                                // ERROR "moved to heap"
-       F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap"
+       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$"
+}
+
+func TestM2() {
+       var t T
+       var v int                                  // ERROR "moved to heap"
+       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$"
 }
diff --git a/test/uintptrescapes3.go b/test/uintptrescapes3.go
new file mode 100644 (file)
index 0000000..92be5d1
--- /dev/null
@@ -0,0 +1,63 @@
+// run
+
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that //go:uintptrescapes works for methods.
+
+package main
+
+import (
+       "fmt"
+       "runtime"
+       "unsafe"
+)
+
+var callback func()
+
+//go:noinline
+//go:uintptrescapes
+func F(ptr uintptr) { callback() }
+
+//go:noinline
+//go:uintptrescapes
+func Fv(ptrs ...uintptr) { callback() }
+
+type T struct{}
+
+//go:noinline
+//go:uintptrescapes
+func (T) M(ptr uintptr) { callback() }
+
+//go:noinline
+//go:uintptrescapes
+func (T) Mv(ptrs ...uintptr) { callback() }
+
+// Each test should pass uintptr(ptr) as an argument to a function call,
+// which in turn should call callback. The callback checks that ptr is kept alive.
+var tests = []func(ptr unsafe.Pointer){
+       func(ptr unsafe.Pointer) { F(uintptr(ptr)) },
+       func(ptr unsafe.Pointer) { Fv(uintptr(ptr)) },
+       func(ptr unsafe.Pointer) { T{}.M(uintptr(ptr)) },
+       func(ptr unsafe.Pointer) { T{}.Mv(uintptr(ptr)) },
+}
+
+func main() {
+       for i, test := range tests {
+               finalized := false
+
+               ptr := new([64]byte)
+               runtime.SetFinalizer(ptr, func(*[64]byte) {
+                       finalized = true
+               })
+
+               callback = func() {
+                       runtime.GC()
+                       if finalized {
+                               fmt.Printf("test #%d failed\n", i)
+                       }
+               }
+               test(unsafe.Pointer(ptr))
+       }
+}