cmd/compile: fix pointer maps for morestack
Verified with test and with single step watching changes to register
values across morestack calls, after reload.
Also added stack-growth test with pointer parameters of varying lifetime.
For #40724.
Change-Id: Idb5fe27786ac5c6665a734d41e68d3d39de2f4da
Reviewed-on: https://go-review.googlesource.com/c/go/+/294429
Trust: David Chase <drchase@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go
index 53ae797..48a26cf 100644
--- a/src/cmd/compile/internal/liveness/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -297,6 +297,22 @@
n, _ := ssa.AutoVar(v)
return n, ssa.SymWrite
+ case ssa.OpArgIntReg:
+ // This forces the spill slot for the register to be live at function entry.
+ // one of the following holds for a function F with pointer-valued register arg X:
+ // 0. No GC (so an uninitialized spill slot is okay)
+ // 1. GC at entry of F. GC is precise, but the spills around morestack initialize X's spill slot
+ // 2. Stack growth at entry of F. Same as GC.
+ // 3. GC occurs within F itself. This has to be from preemption, and thus GC is conservative.
+ // a. X is in a register -- then X is seen, and the spill slot is also scanned conservatively.
+ // b. X is spilled -- the spill slot is initialized, and scanned conservatively
+ // c. X is not live -- the spill slot is scanned conservatively, and it may contain X from an earlier spill.
+ // 4. GC within G, transitively called from F
+ // a. X is live at call site, therefore is spilled, to its spill slot (which is live because of subsequent LoadReg).
+ // b. X is not live at call site -- but neither is its spill slot.
+ n, _ := ssa.AutoVar(v)
+ return n, ssa.SymRead
+
case ssa.OpVarLive:
return v.Aux.(*ir.Name), ssa.SymRead
case ssa.OpVarDef, ssa.OpVarKill:
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 127e4ce..c20fc87 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -517,9 +517,13 @@
// AutoVar returns a *Name and int64 representing the auto variable and offset within it
// where v should be spilled.
func AutoVar(v *Value) (*ir.Name, int64) {
- loc := v.Block.Func.RegAlloc[v.ID].(LocalSlot)
- if v.Type.Size() > loc.Type.Size() {
- v.Fatalf("spill/restore type %s doesn't fit in slot type %s", v.Type, loc.Type)
+ if loc, ok := v.Block.Func.RegAlloc[v.ID].(LocalSlot); ok {
+ if v.Type.Size() > loc.Type.Size() {
+ v.Fatalf("spill/restore type %s doesn't fit in slot type %s", v.Type, loc.Type)
+ }
+ return loc.N, loc.Off
}
- return loc.N, loc.Off
+ // Assume it is a register, return its spill slot, which needs to be live
+ nameOff := v.Aux.(*AuxNameOffset)
+ return nameOff.Name, nameOff.Offset
}
diff --git a/test/abi/uglyfib.go b/test/abi/uglyfib.go
new file mode 100644
index 0000000..bde3548
--- /dev/null
+++ b/test/abi/uglyfib.go
@@ -0,0 +1,79 @@
+// run
+
+//go:build !wasm
+// +build !wasm
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+// This test is designed to provoke a stack growth
+// in a way that very likely leaves junk in the
+// parameter save area if they aren't saved or spilled
+// there, as appropriate.
+
+//go:registerparams
+//go:noinline
+func f(x int, xm1, xm2, p *int) {
+ var y = [2]int{x - 4, 0}
+ if x < 2 {
+ *p += x
+ return
+ }
+ x -= 3
+ g(*xm1, xm2, &x, p) // xm1 is no longer live.
+ h(*xm2, &x, &y[0], p) // xm2 is no longer live, but was spilled.
+}
+
+//go:registerparams
+//go:noinline
+func g(x int, xm1, xm2, p *int) {
+ var y = [3]int{x - 4, 0, 0}
+ if x < 2 {
+ *p += x
+ return
+ }
+ x -= 3
+ k(*xm2, &x, &y[0], p)
+ h(*xm1, xm2, &x, p)
+}
+
+//go:registerparams
+//go:noinline
+func h(x int, xm1, xm2, p *int) {
+ var y = [4]int{x - 4, 0, 0, 0}
+ if x < 2 {
+ *p += x
+ return
+ }
+ x -= 3
+ k(*xm1, xm2, &x, p)
+ f(*xm2, &x, &y[0], p)
+}
+
+//go:registerparams
+//go:noinline
+func k(x int, xm1, xm2, p *int) {
+ var y = [5]int{x - 4, 0, 0, 0, 0}
+ if x < 2 {
+ *p += x
+ return
+ }
+ x -= 3
+ f(*xm2, &x, &y[0], p)
+ g(*xm1, xm2, &x, p)
+}
+
+func main() {
+ x := 40
+ var y int
+ xm1 := x - 1
+ xm2 := x - 2
+ f(x, &xm1, &xm2, &y)
+
+ fmt.Printf("Fib(%d)=%d\n", x, y)
+}
diff --git a/test/abi/uglyfib.out b/test/abi/uglyfib.out
new file mode 100644
index 0000000..d892270
--- /dev/null
+++ b/test/abi/uglyfib.out
@@ -0,0 +1 @@
+Fib(40)=102334155