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