cmd/compile: keep blank nodes alive in b.loop

The current bloop pass implementation skips blank nodes silently. This
CL makes it aware of that and keep them alive in temps.

Fixes #77654.

Change-Id: Iaffa5194ba1f0fe8d7c80a4c8e5c9a65a47bf534
Reviewed-on: https://go-review.googlesource.com/c/go/+/754920
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Junyang Shao
2026-03-12 20:56:24 +00:00
parent b3ee07636a
commit 9ce4f3876e
2 changed files with 51 additions and 2 deletions

View File

@@ -216,11 +216,49 @@ func preserveCallArgs(curFn *ir.Func, call *ir.CallExpr) ir.Node {
func preserveStmt(curFn *ir.Func, stmt ir.Node) ir.Node {
switch n := stmt.(type) {
case *ir.AssignStmt:
// If the left hand side is blank, we need to assign it to a temp
// so that it can be kept alive.
if ir.IsBlank(n.X) {
tmp := typecheck.TempAt(n.Pos(), curFn, n.Y.Type())
n.X = tmp
n.Def = true
n.PtrInit().Append(typecheck.Stmt(ir.NewDecl(n.Pos(), ir.ODCL, tmp)))
stmt = typecheck.AssignExpr(n)
n = stmt.(*ir.AssignStmt)
}
return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
case *ir.AssignListStmt:
var ns ir.Nodes
for _, lhs := range n.Lhs {
ns = append(ns, getKeepAliveNodes(n.Pos(), lhs)...)
hasBlank := false
for i, lhs := range n.Lhs {
if ir.IsBlank(lhs) {
// If the left hand side has blanks, we need to assign them to temps
// so that they can be kept alive.
var typ *types.Type
// AssignListStmt can have tuple or a list of expressions on the right hand side.
if len(n.Rhs) == 1 && n.Rhs[0].Type() != nil &&
n.Rhs[0].Type().IsTuple() &&
len(n.Lhs) == n.Rhs[0].Type().NumFields() {
typ = n.Rhs[0].Type().Field(i).Type
} else if len(n.Rhs) == len(n.Lhs) {
typ = n.Rhs[i].Type()
} else {
// Unrecognized shapes, skip?
base.WarnfAt(n.Pos(), "unrecognized shape for assign list stmt for blank assignment")
continue
}
tmp := typecheck.TempAt(n.Pos(), curFn, typ)
n.Lhs[i] = tmp
n.PtrInit().Append(typecheck.Stmt(ir.NewDecl(n.Pos(), ir.ODCL, tmp)))
hasBlank = true
}
ns = append(ns, getKeepAliveNodes(n.Pos(), n.Lhs[i])...)
}
if hasBlank {
// blank nodes are rewritten to temps, we need to typecheck the node again.
n.Def = true
stmt = typecheck.AssignExpr(n)
n = stmt.(*ir.AssignListStmt)
}
return keepAliveAt(ns, n)
case *ir.AssignOpStmt:

View File

@@ -15,6 +15,10 @@ func caninline(x int) int { // ERROR "can inline caninline"
return x
}
func caninlineMulti(x int) (int, int) { // ERROR "can inline caninlineMulti"
return x, x
}
var something int
func caninlineNoRet(x int) { // ERROR "can inline caninlineNoRet"
@@ -56,6 +60,13 @@ func test(b *testing.B, localsink, cond int) { // ERROR ".*"
caninline(1) // ERROR "inlining call to caninline" "function result will be kept alive"
}
_ = caninline(1) // ERROR "inlining call to caninline" ".*autotmp.* will be kept alive"
// An assign list stmt with a single rhs expression returning multiple values via a tuple.
_, _ = caninlineMulti(1) // ERROR "inlining call to caninlineMulti" ".*autotmp.* will be kept alive"
// An assign list stmt with multiple rhs expressions.
_, _ = caninline(1), caninline(2) // ERROR "inlining call to caninline" ".*autotmp.* will be kept alive"
receiver(argument) // ERROR inlining call to receiver" "function arg will be kept alive"
}
}