aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssa/regalloc_test.go
diff options
context:
space:
mode:
authorDavid Chase <drchase@google.com>2018-05-25 16:08:13 -0400
committerDavid Chase <drchase@google.com>2018-05-30 16:39:21 +0000
commit31e1c30f55165785dd12e7c67babedeb950a721d (patch)
tree55ab705ca77332a378a820df25e08824a625dd1b /src/cmd/compile/internal/ssa/regalloc_test.go
parentd5bc3b96c6fb758561e6274c8f69232623157ca4 (diff)
downloadgo-31e1c30f55165785dd12e7c67babedeb950a721d.tar.gz
go-31e1c30f55165785dd12e7c67babedeb950a721d.zip
cmd/compile: do not allow regalloc to LoadReg G register
On architectures where G is stored in a register, it is possible for a variable to allocated to it, and subsequently that variable may be spilled and reloaded, for example because of an intervening call. If such an allocation reaches a join point and it is the primary predecessor, it becomes the target of a reload, which is only usually right. Fix: guard all the LoadReg ops, and spill value in the G register (if any) before merges (in the same way that 387 FP registers are freed between blocks). Includes test. Fixes #25504. Change-Id: I0482a53e20970c7315bf09c0e407ae5bba2fe05d Reviewed-on: https://go-review.googlesource.com/114695 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
Diffstat (limited to 'src/cmd/compile/internal/ssa/regalloc_test.go')
-rw-r--r--src/cmd/compile/internal/ssa/regalloc_test.go49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssa/regalloc_test.go b/src/cmd/compile/internal/ssa/regalloc_test.go
index 02751a9349..bb8be5e7ac 100644
--- a/src/cmd/compile/internal/ssa/regalloc_test.go
+++ b/src/cmd/compile/internal/ssa/regalloc_test.go
@@ -36,6 +36,55 @@ func TestLiveControlOps(t *testing.T) {
checkFunc(f.f)
}
+// Test to make sure G register is never reloaded from spill (spill of G is okay)
+// See #25504
+func TestNoGetgLoadReg(t *testing.T) {
+ /*
+ Original:
+ func fff3(i int) *g {
+ gee := getg()
+ if i == 0 {
+ fff()
+ }
+ return gee // here
+ }
+ */
+ c := testConfigARM64(t)
+ f := c.Fun("b1",
+ Bloc("b1",
+ Valu("v1", OpInitMem, types.TypeMem, 0, nil),
+ Valu("v6", OpArg, c.config.Types.Int64, 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64)),
+ Valu("v8", OpGetG, c.config.Types.Int64.PtrTo(), 0, nil, "v1"),
+ Valu("v11", OpARM64CMPconst, types.TypeFlags, 0, nil, "v6"),
+ Eq("v11", "b2", "b4"),
+ ),
+ Bloc("b4",
+ Goto("b3"),
+ ),
+ Bloc("b3",
+ Valu("v14", OpPhi, types.TypeMem, 0, nil, "v1", "v12"),
+ Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
+ Valu("v16", OpARM64MOVDstore, types.TypeMem, 0, nil, "v8", "sb", "v14"),
+ Exit("v16"),
+ ),
+ Bloc("b2",
+ Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, nil, "v1"),
+ Goto("b3"),
+ ),
+ )
+ regalloc(f.f)
+ checkFunc(f.f)
+ // Double-check that we never restore to the G register. Regalloc should catch it, but check again anyway.
+ r := f.f.RegAlloc
+ for _, b := range f.blocks {
+ for _, v := range b.Values {
+ if v.Op == OpLoadReg && r[v.ID].String() == "g" {
+ t.Errorf("Saw OpLoadReg targeting g register: %s", v.LongString())
+ }
+ }
+ }
+}
+
// Test to make sure we don't push spills into loops.
// See issue #19595.
func TestSpillWithLoop(t *testing.T) {