cmd/compile: ensure bloop only kept alive addressable nodes

Fixes #76636

Change-Id: I881f88dbf62a901452c1d77e6ffca651451c7790
Reviewed-on: https://go-review.googlesource.com/c/go/+/725420
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
Cuong Manh Le 2025-12-01 16:54:54 +07:00 committed by Keith Randall
parent 7cab1b1b26
commit 509ddf3868
2 changed files with 24 additions and 5 deletions

View file

@ -73,6 +73,14 @@ func getNameFromNode(n ir.Node) *ir.Name {
return nil
}
// getAddressableNameFromNode is like getNameFromNode but returns nil if the node is not addressable.
func getAddressableNameFromNode(n ir.Node) *ir.Name {
if name := getNameFromNode(n); name != nil && ir.IsAddressable(name) {
return name
}
return nil
}
// keepAliveAt returns a statement that is either curNode, or a
// block containing curNode followed by a call to runtime.KeepAlive for each
// node in ns. These calls ensure that nodes in ns will be live until
@ -94,6 +102,9 @@ func keepAliveAt(ns []ir.Node, curNode ir.Node) ir.Node {
if n.Sym().IsBlank() {
continue
}
if !ir.IsAddressable(n) {
base.FatalfAt(n.Pos(), "keepAliveAt: node %v is not addressable", n)
}
arg := ir.NewConvExpr(pos, ir.OCONV, types.Types[types.TUNSAFEPTR], typecheck.NodAddr(n))
if !n.Type().IsInterface() {
srcRType0 := reflectdata.TypePtrAt(pos, n.Type())
@ -129,7 +140,7 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
switch n := stmt.(type) {
case *ir.AssignStmt:
// Peel down struct and slice indexing to get the names
name := getNameFromNode(n.X)
name := getAddressableNameFromNode(n.X)
if name != nil {
debugName(name, n.Pos())
ret = keepAliveAt([]ir.Node{name}, n)
@ -144,7 +155,7 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
case *ir.AssignListStmt:
ns := []ir.Node{}
for _, lhs := range n.Lhs {
name := getNameFromNode(lhs)
name := getAddressableNameFromNode(lhs)
if name != nil {
debugName(name, n.Pos())
ns = append(ns, name)
@ -159,7 +170,7 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
}
ret = keepAliveAt(ns, n)
case *ir.AssignOpStmt:
name := getNameFromNode(n.X)
name := getAddressableNameFromNode(n.X)
if name != nil {
debugName(name, n.Pos())
ret = keepAliveAt([]ir.Node{name}, n)
@ -206,7 +217,7 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
argTmps := []ir.Node{}
names := []ir.Node{}
for i, a := range n.Args {
if name := getNameFromNode(a); name != nil {
if name := getAddressableNameFromNode(a); name != nil {
// If they are name, keep them alive directly.
debugName(name, n.Pos())
names = append(names, name)
@ -215,7 +226,7 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
s := a.(*ir.CompLitExpr)
ns := []ir.Node{}
for i, elem := range s.List {
if name := getNameFromNode(elem); name != nil {
if name := getAddressableNameFromNode(elem); name != nil {
debugName(name, n.Pos())
ns = append(ns, name)
} else {

View file

@ -25,6 +25,12 @@ func caninlineVariadic(x ...int) { // ERROR "can inline caninlineVariadic" "x do
something = x[0]
}
func receiver(f func()) { // ERROR "can inline receiver" "f does not escape"
f()
}
func argument() {} // ERROR "can inline argument"
func test(b *testing.B, localsink, cond int) { // ERROR ".*"
for i := 0; i < b.N; i++ {
caninline(1) // ERROR "inlining call to caninline"
@ -49,5 +55,7 @@ func test(b *testing.B, localsink, cond int) { // ERROR ".*"
{
caninline(1) // ERROR "inlining call to caninline" "function result will be kept alive"
}
receiver(argument) // ERROR inlining call to receiver" "function arg will be kept alive"
}
}