mirror of
https://github.com/golang/go.git
synced 2026-02-07 02:09:55 +00:00
cmd/compile: refactor preserveStmt
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>
This commit is contained in:
parent
540ccca0a7
commit
adb64adfd7
1 changed files with 99 additions and 128 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue