• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssa
6
7import (
8	"cmd/compile/internal/types"
9	"testing"
10)
11
12func TestLiveControlOps(t *testing.T) {
13	c := testConfig(t)
14	f := c.Fun("entry",
15		Bloc("entry",
16			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
17			Valu("x", OpAMD64MOVLconst, c.config.Types.Int8, 1, nil),
18			Valu("y", OpAMD64MOVLconst, c.config.Types.Int8, 2, nil),
19			Valu("a", OpAMD64TESTB, types.TypeFlags, 0, nil, "x", "y"),
20			Valu("b", OpAMD64TESTB, types.TypeFlags, 0, nil, "y", "x"),
21			Eq("a", "if", "exit"),
22		),
23		Bloc("if",
24			Eq("b", "plain", "exit"),
25		),
26		Bloc("plain",
27			Goto("exit"),
28		),
29		Bloc("exit",
30			Exit("mem"),
31		),
32	)
33	flagalloc(f.f)
34	regalloc(f.f)
35	checkFunc(f.f)
36}
37
38// Test to make sure G register is never reloaded from spill (spill of G is okay)
39// See #25504
40func TestNoGetgLoadReg(t *testing.T) {
41	/*
42		Original:
43		func fff3(i int) *g {
44			gee := getg()
45			if i == 0 {
46				fff()
47			}
48			return gee // here
49		}
50	*/
51	c := testConfigARM64(t)
52	f := c.Fun("b1",
53		Bloc("b1",
54			Valu("v1", OpInitMem, types.TypeMem, 0, nil),
55			Valu("v6", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)),
56			Valu("v8", OpGetG, c.config.Types.Int64.PtrTo(), 0, nil, "v1"),
57			Valu("v11", OpARM64CMPconst, types.TypeFlags, 0, nil, "v6"),
58			Eq("v11", "b2", "b4"),
59		),
60		Bloc("b4",
61			Goto("b3"),
62		),
63		Bloc("b3",
64			Valu("v14", OpPhi, types.TypeMem, 0, nil, "v1", "v12"),
65			Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
66			Valu("v16", OpARM64MOVDstore, types.TypeMem, 0, nil, "v8", "sb", "v14"),
67			Exit("v16"),
68		),
69		Bloc("b2",
70			Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "v1"),
71			Goto("b3"),
72		),
73	)
74	regalloc(f.f)
75	checkFunc(f.f)
76	// Double-check that we never restore to the G register. Regalloc should catch it, but check again anyway.
77	r := f.f.RegAlloc
78	for _, b := range f.blocks {
79		for _, v := range b.Values {
80			if v.Op == OpLoadReg && r[v.ID].String() == "g" {
81				t.Errorf("Saw OpLoadReg targeting g register: %s", v.LongString())
82			}
83		}
84	}
85}
86
87// Test to make sure we don't push spills into loops.
88// See issue #19595.
89func TestSpillWithLoop(t *testing.T) {
90	c := testConfig(t)
91	f := c.Fun("entry",
92		Bloc("entry",
93			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
94			Valu("ptr", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64)),
95			Valu("cond", OpArg, c.config.Types.Bool, 0, c.Temp(c.config.Types.Bool)),
96			Valu("ld", OpAMD64MOVQload, c.config.Types.Int64, 0, nil, "ptr", "mem"), // this value needs a spill
97			Goto("loop"),
98		),
99		Bloc("loop",
100			Valu("memphi", OpPhi, types.TypeMem, 0, nil, "mem", "call"),
101			Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "memphi"),
102			Valu("test", OpAMD64CMPBconst, types.TypeFlags, 0, nil, "cond"),
103			Eq("test", "next", "exit"),
104		),
105		Bloc("next",
106			Goto("loop"),
107		),
108		Bloc("exit",
109			Valu("store", OpAMD64MOVQstore, types.TypeMem, 0, nil, "ptr", "ld", "call"),
110			Exit("store"),
111		),
112	)
113	regalloc(f.f)
114	checkFunc(f.f)
115	for _, v := range f.blocks["loop"].Values {
116		if v.Op == OpStoreReg {
117			t.Errorf("spill inside loop %s", v.LongString())
118		}
119	}
120}
121
122func TestSpillMove1(t *testing.T) {
123	c := testConfig(t)
124	f := c.Fun("entry",
125		Bloc("entry",
126			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
127			Valu("x", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)),
128			Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64.PtrTo())),
129			Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"),
130			Goto("loop1"),
131		),
132		Bloc("loop1",
133			Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"),
134			Eq("a", "loop2", "exit1"),
135		),
136		Bloc("loop2",
137			Eq("a", "loop1", "exit2"),
138		),
139		Bloc("exit1",
140			// store before call, y is available in a register
141			Valu("mem2", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem"),
142			Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem2"),
143			Exit("mem3"),
144		),
145		Bloc("exit2",
146			// store after call, y must be loaded from a spill location
147			Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
148			Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"),
149			Exit("mem5"),
150		),
151	)
152	flagalloc(f.f)
153	regalloc(f.f)
154	checkFunc(f.f)
155	// Spill should be moved to exit2.
156	if numSpills(f.blocks["loop1"]) != 0 {
157		t.Errorf("spill present from loop1")
158	}
159	if numSpills(f.blocks["loop2"]) != 0 {
160		t.Errorf("spill present in loop2")
161	}
162	if numSpills(f.blocks["exit1"]) != 0 {
163		t.Errorf("spill present in exit1")
164	}
165	if numSpills(f.blocks["exit2"]) != 1 {
166		t.Errorf("spill missing in exit2")
167	}
168
169}
170
171func TestSpillMove2(t *testing.T) {
172	c := testConfig(t)
173	f := c.Fun("entry",
174		Bloc("entry",
175			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
176			Valu("x", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)),
177			Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64.PtrTo())),
178			Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"),
179			Goto("loop1"),
180		),
181		Bloc("loop1",
182			Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"),
183			Eq("a", "loop2", "exit1"),
184		),
185		Bloc("loop2",
186			Eq("a", "loop1", "exit2"),
187		),
188		Bloc("exit1",
189			// store after call, y must be loaded from a spill location
190			Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
191			Valu("mem3", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem2"),
192			Exit("mem3"),
193		),
194		Bloc("exit2",
195			// store after call, y must be loaded from a spill location
196			Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
197			Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"),
198			Exit("mem5"),
199		),
200	)
201	flagalloc(f.f)
202	regalloc(f.f)
203	checkFunc(f.f)
204	// There should be a spill in loop1, and nowhere else.
205	// TODO: resurrect moving spills out of loops? We could put spills at the start of both exit1 and exit2.
206	if numSpills(f.blocks["loop1"]) != 1 {
207		t.Errorf("spill missing from loop1")
208	}
209	if numSpills(f.blocks["loop2"]) != 0 {
210		t.Errorf("spill present in loop2")
211	}
212	if numSpills(f.blocks["exit1"]) != 0 {
213		t.Errorf("spill present in exit1")
214	}
215	if numSpills(f.blocks["exit2"]) != 0 {
216		t.Errorf("spill present in exit2")
217	}
218
219}
220
221func numSpills(b *Block) int {
222	n := 0
223	for _, v := range b.Values {
224		if v.Op == OpStoreReg {
225			n++
226		}
227	}
228	return n
229}
230