mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Test that we really do move spills down to the dominator of all the uses. Also add a test where go1.8 would have moved the spill out of the loop into two exit points, but go1.9 doesn't move the spill. This is a case where the 1.9 spill moving code does not subsume the 1.8 spill moving code. Maybe we resurrect moving-spills-out-of-loops CL to fix this one. (I suspect it wouldn't be worth the effort, but would be happy to hear evidence otherwise.) Update #20472 Change-Id: I7dbf8d65e7f4d675d14e5ecf502887cebda35d2a Reviewed-on: https://go-review.googlesource.com/44038 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
181 lines
5.1 KiB
Go
181 lines
5.1 KiB
Go
// Copyright 2015 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 ssa
|
|
|
|
import (
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/src"
|
|
"testing"
|
|
)
|
|
|
|
func TestLiveControlOps(t *testing.T) {
|
|
c := testConfig(t)
|
|
f := c.Fun("entry",
|
|
Bloc("entry",
|
|
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
|
|
Valu("x", OpAMD64MOVLconst, c.config.Types.Int8, 1, nil),
|
|
Valu("y", OpAMD64MOVLconst, c.config.Types.Int8, 2, nil),
|
|
Valu("a", OpAMD64TESTB, types.TypeFlags, 0, nil, "x", "y"),
|
|
Valu("b", OpAMD64TESTB, types.TypeFlags, 0, nil, "y", "x"),
|
|
Eq("a", "if", "exit"),
|
|
),
|
|
Bloc("if",
|
|
Eq("b", "plain", "exit"),
|
|
),
|
|
Bloc("plain",
|
|
Goto("exit"),
|
|
),
|
|
Bloc("exit",
|
|
Exit("mem"),
|
|
),
|
|
)
|
|
flagalloc(f.f)
|
|
regalloc(f.f)
|
|
checkFunc(f.f)
|
|
}
|
|
|
|
// Test to make sure we don't push spills into loops.
|
|
// See issue #19595.
|
|
func TestSpillWithLoop(t *testing.T) {
|
|
c := testConfig(t)
|
|
f := c.Fun("entry",
|
|
Bloc("entry",
|
|
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
|
|
Valu("ptr", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64)),
|
|
Valu("cond", OpArg, c.config.Types.Bool, 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Bool)),
|
|
Valu("ld", OpAMD64MOVQload, c.config.Types.Int64, 0, nil, "ptr", "mem"), // this value needs a spill
|
|
Goto("loop"),
|
|
),
|
|
Bloc("loop",
|
|
Valu("memphi", OpPhi, types.TypeMem, 0, nil, "mem", "call"),
|
|
Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, nil, "memphi"),
|
|
Valu("test", OpAMD64CMPBconst, types.TypeFlags, 0, nil, "cond"),
|
|
Eq("test", "next", "exit"),
|
|
),
|
|
Bloc("next",
|
|
Goto("loop"),
|
|
),
|
|
Bloc("exit",
|
|
Valu("store", OpAMD64MOVQstore, types.TypeMem, 0, nil, "ptr", "ld", "call"),
|
|
Exit("store"),
|
|
),
|
|
)
|
|
regalloc(f.f)
|
|
checkFunc(f.f)
|
|
for _, v := range f.blocks["loop"].Values {
|
|
if v.Op == OpStoreReg {
|
|
t.Errorf("spill inside loop %s", v.LongString())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSpillMove1(t *testing.T) {
|
|
c := testConfig(t)
|
|
f := c.Fun("entry",
|
|
Bloc("entry",
|
|
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
|
|
Valu("x", OpArg, c.config.Types.Int64, 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64)),
|
|
Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64.PtrTo())),
|
|
Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"),
|
|
Goto("loop1"),
|
|
),
|
|
Bloc("loop1",
|
|
Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"),
|
|
Eq("a", "loop2", "exit1"),
|
|
),
|
|
Bloc("loop2",
|
|
Eq("a", "loop1", "exit2"),
|
|
),
|
|
Bloc("exit1",
|
|
// store before call, y is available in a register
|
|
Valu("mem2", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem"),
|
|
Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem2"),
|
|
Exit("mem3"),
|
|
),
|
|
Bloc("exit2",
|
|
// store after call, y must be loaded from a spill location
|
|
Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"),
|
|
Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"),
|
|
Exit("mem5"),
|
|
),
|
|
)
|
|
flagalloc(f.f)
|
|
regalloc(f.f)
|
|
checkFunc(f.f)
|
|
// Spill should be moved to exit2.
|
|
if numSpills(f.blocks["loop1"]) != 0 {
|
|
t.Errorf("spill present from loop1")
|
|
}
|
|
if numSpills(f.blocks["loop2"]) != 0 {
|
|
t.Errorf("spill present in loop2")
|
|
}
|
|
if numSpills(f.blocks["exit1"]) != 0 {
|
|
t.Errorf("spill present in exit1")
|
|
}
|
|
if numSpills(f.blocks["exit2"]) != 1 {
|
|
t.Errorf("spill missing in exit2")
|
|
}
|
|
|
|
}
|
|
|
|
func TestSpillMove2(t *testing.T) {
|
|
c := testConfig(t)
|
|
f := c.Fun("entry",
|
|
Bloc("entry",
|
|
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
|
|
Valu("x", OpArg, c.config.Types.Int64, 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64)),
|
|
Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64.PtrTo())),
|
|
Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"),
|
|
Goto("loop1"),
|
|
),
|
|
Bloc("loop1",
|
|
Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"),
|
|
Eq("a", "loop2", "exit1"),
|
|
),
|
|
Bloc("loop2",
|
|
Eq("a", "loop1", "exit2"),
|
|
),
|
|
Bloc("exit1",
|
|
// store after call, y must be loaded from a spill location
|
|
Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"),
|
|
Valu("mem3", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem2"),
|
|
Exit("mem3"),
|
|
),
|
|
Bloc("exit2",
|
|
// store after call, y must be loaded from a spill location
|
|
Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"),
|
|
Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"),
|
|
Exit("mem5"),
|
|
),
|
|
)
|
|
flagalloc(f.f)
|
|
regalloc(f.f)
|
|
checkFunc(f.f)
|
|
// There should be a spill in loop1, and nowhere else.
|
|
// TODO: resurrect moving spills out of loops? We could put spills at the start of both exit1 and exit2.
|
|
if numSpills(f.blocks["loop1"]) != 1 {
|
|
t.Errorf("spill missing from loop1")
|
|
}
|
|
if numSpills(f.blocks["loop2"]) != 0 {
|
|
t.Errorf("spill present in loop2")
|
|
}
|
|
if numSpills(f.blocks["exit1"]) != 0 {
|
|
t.Errorf("spill present in exit1")
|
|
}
|
|
if numSpills(f.blocks["exit2"]) != 0 {
|
|
t.Errorf("spill present in exit2")
|
|
}
|
|
|
|
}
|
|
|
|
func numSpills(b *Block) int {
|
|
n := 0
|
|
for _, v := range b.Values {
|
|
if v.Op == OpStoreReg {
|
|
n++
|
|
}
|
|
}
|
|
return n
|
|
}
|