]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: refactor preserveStmt
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Wed, 10 Dec 2025 16:16:24 +0000 (23:16 +0700)
committerGopher Robot <gobot@golang.org>
Thu, 22 Jan 2026 15:30:08 +0000 (07:30 -0800)
By breaking it into smaller functions with duplicated logic removed.

Change-Id: I710414e8d13d91c19fb4bf4da1a31b00ee88ccee
Reviewed-on: https://go-review.googlesource.com/c/go/+/728961
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>

src/cmd/compile/internal/bloop/bloop.go

index 7a376d292bb763e6069b0e63c17851ce0eac310d..f2111cf95272a8a1d41aa1048359cb1c3d3cc12a 100644 (file)
@@ -81,25 +81,36 @@ func getAddressableNameFromNode(n ir.Node) *ir.Name {
        return nil
 }
 
+// getKeepAliveNodes analyzes an IR node and returns a list of nodes that must be kept alive.
+func getKeepAliveNodes(pos src.XPos, n ir.Node) ir.Nodes {
+       name := getAddressableNameFromNode(n)
+       if name != nil {
+               debugName(name, pos)
+               return ir.Nodes{name}
+       } else if deref := n.(*ir.StarExpr); deref != nil {
+               if base.Flag.LowerM > 1 {
+                       base.WarnfAt(pos, "dereference will be kept alive")
+               }
+               return ir.Nodes{deref}
+       } else if base.Flag.LowerM > 1 {
+               base.WarnfAt(pos, "expr is unknown to bloop pass")
+       }
+       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
 // after curNode's execution.
-func keepAliveAt(ns []ir.Node, curNode ir.Node) ir.Node {
+func keepAliveAt(ns ir.Nodes, curNode ir.Node) ir.Node {
        if len(ns) == 0 {
                return curNode
        }
 
        pos := curNode.Pos()
-       calls := []ir.Node{curNode}
+       calls := ir.Nodes{curNode}
        for _, n := range ns {
-               if n == nil {
-                       continue
-               }
-               if n.Sym() == nil {
-                       continue
-               }
-               if n.Sym().IsBlank() {
+               if n == nil || n.Sym() == nil || n.Sym().IsBlank() {
                        continue
                }
                if !ir.IsAddressable(n) {
@@ -111,9 +122,7 @@ func keepAliveAt(ns []ir.Node, curNode ir.Node) ir.Node {
                        arg.TypeWord = srcRType0
                        arg.SrcRType = srcRType0
                }
-               callExpr := typecheck.Call(pos,
-                       typecheck.LookupRuntime("KeepAlive"),
-                       []ir.Node{arg}, false).(*ir.CallExpr)
+               callExpr := typecheck.Call(pos, typecheck.LookupRuntime("KeepAlive"), ir.Nodes{arg}, false).(*ir.CallExpr)
                callExpr.IsCompilerVarLive = true
                callExpr.NoInline = true
                calls = append(calls, callExpr)
@@ -132,135 +141,97 @@ func debugName(name *ir.Name, pos src.XPos) {
        }
 }
 
+// preserveCallResults assigns the results of a call statement to temporary variables to ensure they remain alive.
+func preserveCallResults(curFn *ir.Func, call *ir.CallExpr) ir.Node {
+       var ns ir.Nodes
+       lhs := make(ir.Nodes, call.Fun.Type().NumResults())
+       for i, res := range call.Fun.Type().Results() {
+               tmp := typecheck.TempAt(call.Pos(), curFn, res.Type)
+               lhs[i] = tmp
+               ns = append(ns, tmp)
+       }
+
+       if base.Flag.LowerM > 1 {
+               plural := ""
+               if call.Fun.Type().NumResults() > 1 {
+                       plural = "s"
+               }
+               base.WarnfAt(call.Pos(), "function result%s will be kept alive", plural)
+       }
+
+       assign := typecheck.AssignExpr(ir.NewAssignListStmt(call.Pos(), ir.OAS2, lhs, ir.Nodes{call})).(*ir.AssignListStmt)
+       assign.Def = true
+       return keepAliveAt(ns, assign)
+}
+
+// preserveCallArgs ensures the arguments of a call statement are kept alive by transforming them into temporaries if necessary.
+func preserveCallArgs(curFn *ir.Func, call *ir.CallExpr) ir.Node {
+       var argTmps ir.Nodes
+       var names ir.Nodes
+       preserveTmp := func(pos src.XPos, n ir.Node) ir.Node {
+               tmp := typecheck.TempAt(pos, curFn, n.Type())
+               argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(pos, tmp, n)))
+               names = append(names, tmp)
+               if base.Flag.LowerM > 1 {
+                       base.WarnfAt(call.Pos(), "function arg will be kept alive")
+               }
+               return tmp
+       }
+       for i, a := range call.Args {
+               if name := getAddressableNameFromNode(a); name != nil {
+                       // If they are name, keep them alive directly.
+                       debugName(name, call.Pos())
+                       names = append(names, name)
+               } else if a.Op() == ir.OSLICELIT {
+                       // variadic args are encoded as slice literal.
+                       s := a.(*ir.CompLitExpr)
+                       var ns ir.Nodes
+                       for i, elem := range s.List {
+                               if name := getAddressableNameFromNode(elem); name != nil {
+                                       debugName(name, call.Pos())
+                                       ns = append(ns, name)
+                               } else {
+                                       // We need a temporary to save this arg.
+                                       s.List[i] = preserveTmp(elem.Pos(), elem)
+                               }
+                       }
+                       names = append(names, ns...)
+               } else {
+                       // expressions, we need to assign them to temps and change the original arg to reference them.
+                       call.Args[i] = preserveTmp(call.Pos(), a)
+               }
+       }
+       if len(argTmps) > 0 {
+               argTmps = append(argTmps, call)
+               return keepAliveAt(names, ir.NewBlockStmt(call.Pos(), argTmps))
+       }
+       return keepAliveAt(names, call)
+}
+
 // preserveStmt transforms stmt so that any names defined/assigned within it
 // are used after stmt's execution, preventing their dead code elimination
 // and dead store elimination. The return value is the transformed statement.
-func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
-       ret = stmt
+func preserveStmt(curFn *ir.Func, stmt ir.Node) ir.Node {
        switch n := stmt.(type) {
        case *ir.AssignStmt:
-               // Peel down struct and slice indexing to get the names
-               name := getAddressableNameFromNode(n.X)
-               if name != nil {
-                       debugName(name, n.Pos())
-                       ret = keepAliveAt([]ir.Node{name}, n)
-               } else if deref := n.X.(*ir.StarExpr); deref != nil {
-                       ret = keepAliveAt([]ir.Node{deref}, n)
-                       if base.Flag.LowerM > 1 {
-                               base.WarnfAt(n.Pos(), "dereference will be kept alive")
-                       }
-               } else if base.Flag.LowerM > 1 {
-                       base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
-               }
+               return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
        case *ir.AssignListStmt:
-               ns := []ir.Node{}
+               var ns ir.Nodes
                for _, lhs := range n.Lhs {
-                       name := getAddressableNameFromNode(lhs)
-                       if name != nil {
-                               debugName(name, n.Pos())
-                               ns = append(ns, name)
-                       } else if deref := lhs.(*ir.StarExpr); deref != nil {
-                               ns = append(ns, deref)
-                               if base.Flag.LowerM > 1 {
-                                       base.WarnfAt(n.Pos(), "dereference will be kept alive")
-                               }
-                       } else if base.Flag.LowerM > 1 {
-                               base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
-                       }
+                       ns = append(ns, getKeepAliveNodes(n.Pos(), lhs)...)
                }
-               ret = keepAliveAt(ns, n)
+               return keepAliveAt(ns, n)
        case *ir.AssignOpStmt:
-               name := getAddressableNameFromNode(n.X)
-               if name != nil {
-                       debugName(name, n.Pos())
-                       ret = keepAliveAt([]ir.Node{name}, n)
-               } else if deref := n.X.(*ir.StarExpr); deref != nil {
-                       ret = keepAliveAt([]ir.Node{deref}, n)
-                       if base.Flag.LowerM > 1 {
-                               base.WarnfAt(n.Pos(), "dereference will be kept alive")
-                       }
-               } else if base.Flag.LowerM > 1 {
-                       base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
-               }
+               return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
        case *ir.CallExpr:
-               curNode := stmt
+               // The function's results are not assigned, preserve them.
                if n.Fun != nil && n.Fun.Type() != nil && n.Fun.Type().NumResults() != 0 {
-                       ns := []ir.Node{}
-                       // This function's results are not assigned, assign them to
-                       // auto tmps and then keepAliveAt these autos.
-                       // Note: markStmt assumes the context that it's called - this CallExpr is
-                       // not within another OAS2, which is guaranteed by the case above.
-                       results := n.Fun.Type().Results()
-                       lhs := make([]ir.Node, len(results))
-                       for i, res := range results {
-                               tmp := typecheck.TempAt(n.Pos(), curFn, res.Type)
-                               lhs[i] = tmp
-                               ns = append(ns, tmp)
-                       }
-
-                       // Create an assignment statement.
-                       assign := typecheck.AssignExpr(
-                               ir.NewAssignListStmt(n.Pos(), ir.OAS2, lhs,
-                                       []ir.Node{n})).(*ir.AssignListStmt)
-                       assign.Def = true
-                       curNode = assign
-                       plural := ""
-                       if len(results) > 1 {
-                               plural = "s"
-                       }
-                       if base.Flag.LowerM > 1 {
-                               base.WarnfAt(n.Pos(), "function result%s will be kept alive", plural)
-                       }
-                       ret = keepAliveAt(ns, curNode)
-               } else {
-                       // This function probably doesn't return anything, keep its args alive.
-                       argTmps := []ir.Node{}
-                       names := []ir.Node{}
-                       for i, a := range n.Args {
-                               if name := getAddressableNameFromNode(a); name != nil {
-                                       // If they are name, keep them alive directly.
-                                       debugName(name, n.Pos())
-                                       names = append(names, name)
-                               } else if a.Op() == ir.OSLICELIT {
-                                       // variadic args are encoded as slice literal.
-                                       s := a.(*ir.CompLitExpr)
-                                       ns := []ir.Node{}
-                                       for i, elem := range s.List {
-                                               if name := getAddressableNameFromNode(elem); name != nil {
-                                                       debugName(name, n.Pos())
-                                                       ns = append(ns, name)
-                                               } else {
-                                                       // We need a temporary to save this arg.
-                                                       tmp := typecheck.TempAt(elem.Pos(), curFn, elem.Type())
-                                                       argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(elem.Pos(), tmp, elem)))
-                                                       names = append(names, tmp)
-                                                       s.List[i] = tmp
-                                                       if base.Flag.LowerM > 1 {
-                                                               base.WarnfAt(n.Pos(), "function arg will be kept alive")
-                                                       }
-                                               }
-                                       }
-                                       names = append(names, ns...)
-                               } else {
-                                       // expressions, we need to assign them to temps and change the original arg to reference
-                                       // them.
-                                       tmp := typecheck.TempAt(n.Pos(), curFn, a.Type())
-                                       argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(n.Pos(), tmp, a)))
-                                       names = append(names, tmp)
-                                       n.Args[i] = tmp
-                                       if base.Flag.LowerM > 1 {
-                                               base.WarnfAt(n.Pos(), "function arg will be kept alive")
-                                       }
-                               }
-                       }
-                       if len(argTmps) > 0 {
-                               argTmps = append(argTmps, n)
-                               curNode = ir.NewBlockStmt(n.Pos(), argTmps)
-                       }
-                       ret = keepAliveAt(names, curNode)
+                       return preserveCallResults(curFn, n)
                }
+               // This function doesn't return anything, keep its args alive.
+               return preserveCallArgs(curFn, n)
        }
-       return
+       return stmt
 }
 
 func preserveStmts(curFn *ir.Func, list ir.Nodes) {