From 3f400c42f939cd6c0c7749ca4f81d272f5afd726 Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Thu, 18 Apr 2024 09:23:30 +0800 Subject: [PATCH 1/8] Revert "cmd/cgo: disable #cgo noescape/nocallback until Go 1.23" This reverts commit 607e020150f1bba84beec11aceadbc42abf33986. Reason for revert: Go1.22 is released. It's aggressive to introdcue #cgo noescape/nocallback in Go1.22, as in #63739 And it won't be a problem again while upgrading from Go1.22 to Go1.23. fix #56378 Signed-off-by: doujiang24 --- src/cmd/cgo/doc.go | 24 +++++++++++++++++++ src/cmd/cgo/gcc.go | 2 -- src/cmd/cgo/internal/test/test.go | 3 +-- .../testdata/notmatchedcfunction.go | 3 +-- src/runtime/crash_cgo_test.go | 2 -- .../testdata/testprogcgo/cgonocallback.go | 3 ++- .../testdata/testprogcgo/cgonoescape.go | 2 +- 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 3d4789fafb77ec..35601be47c3c89 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -421,6 +421,30 @@ passing uninitialized C memory to Go code if the Go code is going to store pointer values in it. Zero out the memory in C before passing it to Go. +# Optimizing calls of C code + +When passing a Go pointer to a C function the compiler normally ensures +that the Go object lives on the heap. If the C function does not keep +a copy of the Go pointer, and never passes the Go pointer back to Go code, +then this is unnecessary. The #cgo noescape directive may be used to tell +the compiler that no Go pointers escape via the named C function. +If the noescape directive is used and the C function does not handle the +pointer safely, the program may crash or see memory corruption. + +For example: + + // #cgo noescape cFunctionName + +When a Go function calls a C function, it prepares for the C function to +call back to a Go function. the #cgo nocallback directive may be used to +tell the compiler that these preparations are not necessary. +If the nocallback directive is used and the C function does call back into +Go code, the program will panic. + +For example: + + // #cgo nocallback cFunctionName + # Special cases A few special C types which would normally be represented by a pointer diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 6c23e59adf19eb..504a5650631cd3 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -94,10 +94,8 @@ func (f *File) ProcessCgoDirectives() { directive := fields[1] funcName := fields[2] if directive == "nocallback" { - fatalf("#cgo nocallback disabled until Go 1.23") f.NoCallbacks[funcName] = true } else if directive == "noescape" { - fatalf("#cgo noescape disabled until Go 1.23") f.NoEscapes[funcName] = true } } diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go index 374689631d77ab..a9ae071c58eefe 100644 --- a/src/cmd/cgo/internal/test/test.go +++ b/src/cmd/cgo/internal/test/test.go @@ -117,8 +117,7 @@ int add(int x, int y) { // escape vs noescape -// TODO(#56378): enable in Go 1.23: -// #cgo noescape handleGoStringPointerNoescape +#cgo noescape handleGoStringPointerNoescape void handleGoStringPointerNoescape(void *s) {} void handleGoStringPointerEscape(void *s) {} diff --git a/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go b/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go index 5ec9ec5d4a9790..46afeefcc0f65c 100644 --- a/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go +++ b/src/cmd/cgo/internal/testerrors/testdata/notmatchedcfunction.go @@ -5,8 +5,7 @@ package main /* -// TODO(#56378): change back to "#cgo noescape noMatchedCFunction: no matched C function" in Go 1.23 -// ERROR MESSAGE: #cgo noescape disabled until Go 1.23 +// ERROR MESSAGE: #cgo noescape noMatchedCFunction: no matched C function #cgo noescape noMatchedCFunction */ import "C" diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index f9fbdd8f1cd14f..c77f603bed7354 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -754,7 +754,6 @@ func TestNeedmDeadlock(t *testing.T) { } func TestCgoNoCallback(t *testing.T) { - t.Skip("TODO(#56378): enable in Go 1.23") got := runTestProg(t, "testprogcgo", "CgoNoCallback") want := "function marked with #cgo nocallback called back into Go" if !strings.Contains(got, want) { @@ -763,7 +762,6 @@ func TestCgoNoCallback(t *testing.T) { } func TestCgoNoEscape(t *testing.T) { - t.Skip("TODO(#56378): enable in Go 1.23") got := runTestProg(t, "testprogcgo", "CgoNoEscape") want := "OK\n" if got != want { diff --git a/src/runtime/testdata/testprogcgo/cgonocallback.go b/src/runtime/testdata/testprogcgo/cgonocallback.go index c13bf271a4aa33..8cbbfd1957d668 100644 --- a/src/runtime/testdata/testprogcgo/cgonocallback.go +++ b/src/runtime/testdata/testprogcgo/cgonocallback.go @@ -8,7 +8,8 @@ package main // But it do callback to go in this test, Go should crash here. /* -// TODO(#56378): #cgo nocallback runCShouldNotCallback +#cgo nocallback runCShouldNotCallback + extern void runCShouldNotCallback(); */ import "C" diff --git a/src/runtime/testdata/testprogcgo/cgonoescape.go b/src/runtime/testdata/testprogcgo/cgonoescape.go index f5eebac677ad1c..056be44889b264 100644 --- a/src/runtime/testdata/testprogcgo/cgonoescape.go +++ b/src/runtime/testdata/testprogcgo/cgonoescape.go @@ -13,7 +13,7 @@ package main // 2. less than 100 new allocated heap objects after invoking withoutNoEscape 100 times. /* -// TODO(#56378): #cgo noescape runCWithNoEscape +#cgo noescape runCWithNoEscape void runCWithNoEscape(void *p) { } From 672e5be0d3c92a1ef591de8b20eaf8c3f4bf593e Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Tue, 2 Jul 2024 10:44:27 +0800 Subject: [PATCH 2/8] fix escape Signed-off-by: doujiang24 --- src/cmd/cgo/out.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 5329cb3cd2d224..d428b30569ee44 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -104,6 +104,9 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "var _Cgo_always_false bool\n") fmt.Fprintf(fgo2, "//go:linkname _Cgo_use runtime.cgoUse\n") fmt.Fprintf(fgo2, "func _Cgo_use(interface{})\n") + fmt.Fprintf(fgo2, "//go:linkname _Cgo_keepalive runtime.KeepAlive\n") + fmt.Fprintf(fgo2, "//go:noescape\n") + fmt.Fprintf(fgo2, "func _Cgo_keepalive(interface{})\n") } fmt.Fprintf(fgo2, "//go:linkname _Cgo_no_callback runtime.cgoNoCallback\n") fmt.Fprintf(fgo2, "func _Cgo_no_callback(bool)\n") @@ -641,17 +644,20 @@ func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name, callsMalloc *bool) { fmt.Fprintf(fgo2, "\t_Cgo_no_callback(false)\n") } - // skip _Cgo_use when noescape exist, + // use _Cgo_keepalive instead of _Cgo_use when noescape exist, // so that the compiler won't force to escape them to heap. - if !p.noEscapes[n.C] { - fmt.Fprintf(fgo2, "\tif _Cgo_always_false {\n") - if d.Type.Params != nil { - for i := range d.Type.Params.List { - fmt.Fprintf(fgo2, "\t\t_Cgo_use(p%d)\n", i) - } + // instead, make the compiler keep them alive by using _Cgo_keepalive. + touchFunc := "_Cgo_use" + if p.noEscapes[n.C] { + touchFunc = "_Cgo_keepalive" + } + fmt.Fprintf(fgo2, "\tif _Cgo_always_false {\n") + if d.Type.Params != nil { + for _, name := range paramnames { + fmt.Fprintf(fgo2, "\t\t%s(%s)\n", touchFunc, name) } - fmt.Fprintf(fgo2, "\t}\n") } + fmt.Fprintf(fgo2, "\t}\n") fmt.Fprintf(fgo2, "\treturn\n") fmt.Fprintf(fgo2, "}\n") } From 2f4b5d8c824969aa70418d8edd3eefe09deaf14e Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Fri, 5 Jul 2024 20:05:10 +0800 Subject: [PATCH 3/8] add test Signed-off-by: doujiang24 --- src/runtime/crash_cgo_test.go | 9 +++ .../testdata/testprogcgo/issue63739.go | 61 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/runtime/testdata/testprogcgo/issue63739.go diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index c77f603bed7354..43e4b7370beb6f 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -769,6 +769,15 @@ func TestCgoNoEscape(t *testing.T) { } } +// Issue #63739. +func TestCgoEscapeWithMultiplePointers(t *testing.T) { + got := runTestProg(t, "testprogcgo", "CgoEscapeWithMultiplePointers") + want := "OK\n" + if got != want { + t.Fatalf("want %s, got %s\n", want, got) + } +} + func TestCgoTracebackGoroutineProfile(t *testing.T) { output := runTestProg(t, "testprogcgo", "GoroutineProfile") want := "OK\n" diff --git a/src/runtime/testdata/testprogcgo/issue63739.go b/src/runtime/testdata/testprogcgo/issue63739.go new file mode 100644 index 00000000000000..f0be5219328c9b --- /dev/null +++ b/src/runtime/testdata/testprogcgo/issue63739.go @@ -0,0 +1,61 @@ +// Copyright 2020 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. + +//go:build !plan9 && !windows +// +build !plan9,!windows + +package main + +// This is for issue #56378. +// After we retiring _Cgo_use for parameters, the compiler will treat the +// parameters, start from the second, as non-alive. Then, they will be marked +// as scalar in stackmap, which means the pointer won't be copied correctly +// in copyStack. + +/* +int add_from_multiple_pointers(int *a, int *b, int *c) { + return *a + *b + *c; +} +#cgo noescape add_from_multiple_pointers +#cgo nocallback add_from_multiple_pointers +*/ +import "C" + +import ( + "fmt" + "os" + "time" +) + +const ( + maxStack = 1024 +) + +func init() { + register("CgoEscapeWithMultiplePointers", CgoEscapeWithMultiplePointers) +} + +func CgoEscapeWithMultiplePointers() { + stackGrow(maxStack) + fmt.Println("OK") +} + +//go:noinline +func testCWithMultiplePointers() { + var a C.int = 1 + var b C.int = 2 + var c C.int = 3 + v := C.add_from_multiple_pointers(&a, &b, &c) + if v != 6 { + fmt.Printf("%d + %d + %d != %d", a, b, c, v) + } +} + +func stackGrow(n int) { + if n == 0 { + return + } + testCWithMultiplePointers() + stackGrow(n - 1) +} From 34903bff570841321fd2f4d91ec15dd16fc457e7 Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Fri, 5 Jul 2024 21:01:53 +0800 Subject: [PATCH 4/8] use cgoKeepAlive Signed-off-by: doujiang24 --- src/cmd/cgo/out.go | 2 +- src/runtime/cgo.go | 12 ++++++++++-- src/runtime/linkname.go | 1 + src/runtime/testdata/testprogcgo/issue63739.go | 11 ++++++----- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index d428b30569ee44..3717a6554151d9 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -104,7 +104,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "var _Cgo_always_false bool\n") fmt.Fprintf(fgo2, "//go:linkname _Cgo_use runtime.cgoUse\n") fmt.Fprintf(fgo2, "func _Cgo_use(interface{})\n") - fmt.Fprintf(fgo2, "//go:linkname _Cgo_keepalive runtime.KeepAlive\n") + fmt.Fprintf(fgo2, "//go:linkname _Cgo_keepalive runtime.cgoKeepAlive\n") fmt.Fprintf(fgo2, "//go:noescape\n") fmt.Fprintf(fgo2, "func _Cgo_keepalive(interface{})\n") } diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go index 8285d87fcf673b..b6352f2b7acbeb 100644 --- a/src/runtime/cgo.go +++ b/src/runtime/cgo.go @@ -72,11 +72,19 @@ var cgoHasExtraM bool // cgoUse should not actually be called (see cgoAlwaysFalse). func cgoUse(any) { throw("cgoUse should not be called") } +// cgoKeepAlive is called by cgo-generated code (using go:linkname to get at +// an unexported name). Unlike cgoUse The calls serve one purposes: +// 1) they keep the argument alive until the call site; the call is emitted after +// the end of the (presumed) use of the argument by C. +// cgoKeepAlive should not actually be called (see cgoAlwaysFalse). +func cgoKeepAlive(any) { throw("cgoKeepAlive should not be called") } + // cgoAlwaysFalse is a boolean value that is always false. -// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }. +// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }, +// or if cgoAlwaysFalse { cgoUse(p) }. // The compiler cannot see that cgoAlwaysFalse is always false, // so it emits the test and keeps the call, giving the desired -// escape analysis result. The test is cheaper than the call. +// escape/alive analysis result. The test is cheaper than the call. var cgoAlwaysFalse bool var cgo_yield = &_cgo_yield diff --git a/src/runtime/linkname.go b/src/runtime/linkname.go index dd7f67425103e3..eec190c39cfd1e 100644 --- a/src/runtime/linkname.go +++ b/src/runtime/linkname.go @@ -13,6 +13,7 @@ import _ "unsafe" //go:linkname _cgo_panic_internal //go:linkname cgoAlwaysFalse //go:linkname cgoUse +//go:linkname cgoKeepAlive //go:linkname cgoCheckPointer //go:linkname cgoCheckResult //go:linkname cgoNoCallback diff --git a/src/runtime/testdata/testprogcgo/issue63739.go b/src/runtime/testdata/testprogcgo/issue63739.go index f0be5219328c9b..2c925adab2b5f3 100644 --- a/src/runtime/testdata/testprogcgo/issue63739.go +++ b/src/runtime/testdata/testprogcgo/issue63739.go @@ -11,10 +11,13 @@ package main // After we retiring _Cgo_use for parameters, the compiler will treat the // parameters, start from the second, as non-alive. Then, they will be marked // as scalar in stackmap, which means the pointer won't be copied correctly -// in copyStack. +// in copystack. /* int add_from_multiple_pointers(int *a, int *b, int *c) { + *a = *a + 1; + *b = *b + 1; + *c = *c + 1; return *a + *b + *c; } #cgo noescape add_from_multiple_pointers @@ -24,8 +27,6 @@ import "C" import ( "fmt" - "os" - "time" ) const ( @@ -47,8 +48,8 @@ func testCWithMultiplePointers() { var b C.int = 2 var c C.int = 3 v := C.add_from_multiple_pointers(&a, &b, &c) - if v != 6 { - fmt.Printf("%d + %d + %d != %d", a, b, c, v) + if v != 9 || a != 2 || b != 3 || c != 4 { + fmt.Printf("%d + %d + %d != %d\n", a, b, c, v) } } From 409823499288a0b12ae70e71b6e6639f66ba07f9 Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Sun, 7 Jul 2024 18:19:43 +0800 Subject: [PATCH 5/8] fix windows test Signed-off-by: doujiang24 --- src/runtime/testdata/testprogcgo/issue63739.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/runtime/testdata/testprogcgo/issue63739.go b/src/runtime/testdata/testprogcgo/issue63739.go index 2c925adab2b5f3..74dbb044f09dfe 100644 --- a/src/runtime/testdata/testprogcgo/issue63739.go +++ b/src/runtime/testdata/testprogcgo/issue63739.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !plan9 && !windows -// +build !plan9,!windows - package main // This is for issue #56378. From 8dc86e03b01a236b6d066f593f747e293e78d1d2 Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Wed, 17 Jul 2024 14:11:33 +0800 Subject: [PATCH 6/8] still escaple pointer when only noescape exists. Signed-off-by: doujiang24 --- src/cmd/cgo/internal/test/test.go | 1 + src/cmd/cgo/out.go | 4 ++-- src/runtime/testdata/testprogcgo/cgonoescape.go | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go index a9ae071c58eefe..34a812ed22c699 100644 --- a/src/cmd/cgo/internal/test/test.go +++ b/src/cmd/cgo/internal/test/test.go @@ -118,6 +118,7 @@ int add(int x, int y) { // escape vs noescape #cgo noescape handleGoStringPointerNoescape +#cgo nocallback handleGoStringPointerNoescape void handleGoStringPointerNoescape(void *s) {} void handleGoStringPointerEscape(void *s) {} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 3717a6554151d9..75b8d9245e4226 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -644,11 +644,11 @@ func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name, callsMalloc *bool) { fmt.Fprintf(fgo2, "\t_Cgo_no_callback(false)\n") } - // use _Cgo_keepalive instead of _Cgo_use when noescape exist, + // use _Cgo_keepalive instead of _Cgo_use when noescape & nocallback exist, // so that the compiler won't force to escape them to heap. // instead, make the compiler keep them alive by using _Cgo_keepalive. touchFunc := "_Cgo_use" - if p.noEscapes[n.C] { + if p.noEscapes[n.C] && p.noCallbacks[n.C] { touchFunc = "_Cgo_keepalive" } fmt.Fprintf(fgo2, "\tif _Cgo_always_false {\n") diff --git a/src/runtime/testdata/testprogcgo/cgonoescape.go b/src/runtime/testdata/testprogcgo/cgonoescape.go index 056be44889b264..2b7c7a9c551f46 100644 --- a/src/runtime/testdata/testprogcgo/cgonoescape.go +++ b/src/runtime/testdata/testprogcgo/cgonoescape.go @@ -14,6 +14,7 @@ package main /* #cgo noescape runCWithNoEscape +#cgo nocallback runCWithNoEscape void runCWithNoEscape(void *p) { } From 766ea1b71a9158384095f076b8b311b0a4a658eb Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Thu, 15 Aug 2024 22:45:34 +0800 Subject: [PATCH 7/8] address comments. Signed-off-by: doujiang24 --- src/runtime/cgo.go | 2 +- src/runtime/crash_cgo_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go index b6352f2b7acbeb..1cbece8bc14455 100644 --- a/src/runtime/cgo.go +++ b/src/runtime/cgo.go @@ -81,7 +81,7 @@ func cgoKeepAlive(any) { throw("cgoKeepAlive should not be called") } // cgoAlwaysFalse is a boolean value that is always false. // The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }, -// or if cgoAlwaysFalse { cgoUse(p) }. +// or if cgoAlwaysFalse { cgoKeepAlive(p) }. // The compiler cannot see that cgoAlwaysFalse is always false, // so it emits the test and keeps the call, giving the desired // escape/alive analysis result. The test is cheaper than the call. diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 43e4b7370beb6f..46693cbaff6e5b 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -774,7 +774,7 @@ func TestCgoEscapeWithMultiplePointers(t *testing.T) { got := runTestProg(t, "testprogcgo", "CgoEscapeWithMultiplePointers") want := "OK\n" if got != want { - t.Fatalf("want %s, got %s\n", want, got) + t.Fatalf("output is %s; want %s", got, want) } } From 42fcc506d6a7681ef24ac36a5904b57bda4b15cd Mon Sep 17 00:00:00 2001 From: doujiang24 Date: Fri, 16 Aug 2024 15:26:24 +0800 Subject: [PATCH 8/8] address comments. Signed-off-by: doujiang24 --- src/cmd/cgo/doc.go | 2 +- src/cmd/cgo/out.go | 4 ++-- src/runtime/cgo.go | 9 +++++---- src/runtime/testdata/testprogcgo/issue63739.go | 10 +++++----- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 35601be47c3c89..fc6e6d98b75006 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -436,7 +436,7 @@ For example: // #cgo noescape cFunctionName When a Go function calls a C function, it prepares for the C function to -call back to a Go function. the #cgo nocallback directive may be used to +call back to a Go function. The #cgo nocallback directive may be used to tell the compiler that these preparations are not necessary. If the nocallback directive is used and the C function does call back into Go code, the program will panic. diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 75b8d9245e4226..05cd19251a0dd7 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -644,9 +644,9 @@ func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name, callsMalloc *bool) { fmt.Fprintf(fgo2, "\t_Cgo_no_callback(false)\n") } - // use _Cgo_keepalive instead of _Cgo_use when noescape & nocallback exist, + // Use _Cgo_keepalive instead of _Cgo_use when noescape & nocallback exist, // so that the compiler won't force to escape them to heap. - // instead, make the compiler keep them alive by using _Cgo_keepalive. + // Instead, make the compiler keep them alive by using _Cgo_keepalive. touchFunc := "_Cgo_use" if p.noEscapes[n.C] && p.noCallbacks[n.C] { touchFunc = "_Cgo_keepalive" diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go index 1cbece8bc14455..eca905bad95158 100644 --- a/src/runtime/cgo.go +++ b/src/runtime/cgo.go @@ -73,10 +73,11 @@ var cgoHasExtraM bool func cgoUse(any) { throw("cgoUse should not be called") } // cgoKeepAlive is called by cgo-generated code (using go:linkname to get at -// an unexported name). Unlike cgoUse The calls serve one purposes: -// 1) they keep the argument alive until the call site; the call is emitted after -// the end of the (presumed) use of the argument by C. -// cgoKeepAlive should not actually be called (see cgoAlwaysFalse). +// an unexported name). This call keeps its argument alive until the call site; +// cgo emits the call after the last possible use of the argument by C code. +// cgoKeepAlive is marked in the cgo-generated code as //go:noescape, so +// unlike cgoUse it does not force the argument to escape to the heap. +// This is used to implement the #cgo noescape directive. func cgoKeepAlive(any) { throw("cgoKeepAlive should not be called") } // cgoAlwaysFalse is a boolean value that is always false. diff --git a/src/runtime/testdata/testprogcgo/issue63739.go b/src/runtime/testdata/testprogcgo/issue63739.go index 74dbb044f09dfe..dbe37b6d0e732c 100644 --- a/src/runtime/testdata/testprogcgo/issue63739.go +++ b/src/runtime/testdata/testprogcgo/issue63739.go @@ -4,11 +4,11 @@ package main -// This is for issue #56378. -// After we retiring _Cgo_use for parameters, the compiler will treat the -// parameters, start from the second, as non-alive. Then, they will be marked -// as scalar in stackmap, which means the pointer won't be copied correctly -// in copystack. +// This is for issue #63739. +// Ensure that parameters are kept alive until the end of the C call. If not, +// then a stack copy at just the right time while calling into C might think +// that any stack pointers are not alive and fail to update them, causing the C +// function to see the old, no longer correct, pointer values. /* int add_from_multiple_pointers(int *a, int *b, int *c) {