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) {
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)
}
}
+// 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) {