cmd/compile: keep pointer input arguments live throughout function
Introduce a KeepAlive op which makes sure that its argument is kept
live until the KeepAlive. Use KeepAlive to mark pointer input
arguments as live after each function call and at each return.
We do this change only for pointer arguments. Those are the
critical ones to handle because they might have finalizers.
Doing compound arguments (slices, structs, ...) is more complicated
because we would need to track field liveness individually (we do
that for auto variables now, but inputs requires extra trickery).
Turn off the automatic marking of args as live. That way, when args
are explicitly nulled, plive will know that the original argument is
dead.
The KeepAlive op will be the eventual implementation of
runtime.KeepAlive.
Fixes #15277
Change-Id: I5f223e65d99c9f8342c03fbb1512c4d363e903e5
Reviewed-on: https://go-review.googlesource.com/22365
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index fdf040d..7bae8b4 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -161,6 +161,10 @@
// the function.
s.returns = append(s.returns, n)
}
+ if n.Class == PPARAM && s.canSSA(n) && n.Type.IsPtrShaped() {
+ s.ptrargs = append(s.ptrargs, n)
+ n.SetNotLiveAtEnd(true) // SSA takes care of this explicitly
+ }
case PAUTO | PHEAP:
// TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition
aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n})
@@ -293,6 +297,10 @@
// list of PPARAMOUT (return) variables. Does not include PPARAM|PHEAP vars.
returns []*Node
+ // list of PPARAM SSA-able pointer-shaped args. We ensure these are live
+ // throughout the function to help users avoid premature finalizers.
+ ptrargs []*Node
+
cgoUnsafeArgs bool
noWB bool
WBLineno int32 // line number of first write barrier. 0=no write barriers
@@ -988,8 +996,7 @@
// Store SSAable PPARAMOUT variables back to stack locations.
for _, n := range s.returns {
- aux := &ssa.ArgSymbol{Typ: n.Type, Node: n}
- addr := s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
+ addr := s.decladdrs[n]
val := s.variable(n, n.Type)
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, n, s.mem())
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, n.Type.Size(), addr, val, s.mem())
@@ -998,6 +1005,16 @@
// currently.
}
+ // Keep input pointer args live until the return. This is a bandaid
+ // fix for 1.7 for what will become in 1.8 explicit runtime.KeepAlive calls.
+ // For <= 1.7 we guarantee that pointer input arguments live to the end of
+ // the function to prevent premature (from the user's point of view)
+ // execution of finalizers. See issue 15277.
+ // TODO: remove for 1.8?
+ for _, n := range s.ptrargs {
+ s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
+ }
+
// Do actual return.
m := s.mem()
b := s.endBlock()
@@ -2648,6 +2665,10 @@
// Start exit block, find address of result.
s.startBlock(bNext)
+ // Keep input pointer args live across calls. This is a bandaid until 1.8.
+ for _, n := range s.ptrargs {
+ s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
+ }
res := n.Left.Type.Results()
if res.NumFields() == 0 || k != callNormal {
// call has no return value. Continue with the next statement.
@@ -2997,6 +3018,11 @@
b.AddEdgeTo(bNext)
s.startBlock(bNext)
+ // Keep input pointer args live across calls. This is a bandaid until 1.8.
+ for _, n := range s.ptrargs {
+ s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
+ }
+
// Load results
res := make([]*ssa.Value, len(results))
for i, t := range results {