github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/src/cmd/compile/internal/ssa/check.go (about) 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 5 package ssa 6 7 import ( 8 "math" 9 ) 10 11 // checkFunc checks invariants of f. 12 func checkFunc(f *Func) { 13 blockMark := make([]bool, f.NumBlocks()) 14 valueMark := make([]bool, f.NumValues()) 15 16 for _, b := range f.Blocks { 17 if blockMark[b.ID] { 18 f.Fatalf("block %s appears twice in %s!", b, f.Name) 19 } 20 blockMark[b.ID] = true 21 if b.Func != f { 22 f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name) 23 } 24 25 for i, e := range b.Preds { 26 if se := e.b.Succs[e.i]; se.b != b || se.i != i { 27 f.Fatalf("block pred/succ not crosslinked correctly %d:%s %d:%s", i, b, se.i, se.b) 28 } 29 } 30 for i, e := range b.Succs { 31 if pe := e.b.Preds[e.i]; pe.b != b || pe.i != i { 32 f.Fatalf("block succ/pred not crosslinked correctly %d:%s %d:%s", i, b, pe.i, pe.b) 33 } 34 } 35 36 switch b.Kind { 37 case BlockExit: 38 if len(b.Succs) != 0 { 39 f.Fatalf("exit block %s has successors", b) 40 } 41 if b.Control == nil { 42 f.Fatalf("exit block %s has no control value", b) 43 } 44 if !b.Control.Type.IsMemory() { 45 f.Fatalf("exit block %s has non-memory control value %s", b, b.Control.LongString()) 46 } 47 case BlockRet: 48 if len(b.Succs) != 0 { 49 f.Fatalf("ret block %s has successors", b) 50 } 51 if b.Control == nil { 52 f.Fatalf("ret block %s has nil control", b) 53 } 54 if !b.Control.Type.IsMemory() { 55 f.Fatalf("ret block %s has non-memory control value %s", b, b.Control.LongString()) 56 } 57 case BlockRetJmp: 58 if len(b.Succs) != 0 { 59 f.Fatalf("retjmp block %s len(Succs)==%d, want 0", b, len(b.Succs)) 60 } 61 if b.Control == nil { 62 f.Fatalf("retjmp block %s has nil control", b) 63 } 64 if !b.Control.Type.IsMemory() { 65 f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Control.LongString()) 66 } 67 if b.Aux == nil { 68 f.Fatalf("retjmp block %s has nil Aux field", b) 69 } 70 case BlockPlain: 71 if len(b.Succs) != 1 { 72 f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) 73 } 74 if b.Control != nil { 75 f.Fatalf("plain block %s has non-nil control %s", b, b.Control.LongString()) 76 } 77 case BlockIf: 78 if len(b.Succs) != 2 { 79 f.Fatalf("if block %s len(Succs)==%d, want 2", b, len(b.Succs)) 80 } 81 if b.Control == nil { 82 f.Fatalf("if block %s has no control value", b) 83 } 84 if !b.Control.Type.IsBoolean() { 85 f.Fatalf("if block %s has non-bool control value %s", b, b.Control.LongString()) 86 } 87 case BlockDefer: 88 if len(b.Succs) != 2 { 89 f.Fatalf("defer block %s len(Succs)==%d, want 2", b, len(b.Succs)) 90 } 91 if b.Control == nil { 92 f.Fatalf("defer block %s has no control value", b) 93 } 94 if !b.Control.Type.IsMemory() { 95 f.Fatalf("defer block %s has non-memory control value %s", b, b.Control.LongString()) 96 } 97 case BlockFirst: 98 if len(b.Succs) != 2 { 99 f.Fatalf("plain/dead block %s len(Succs)==%d, want 2", b, len(b.Succs)) 100 } 101 if b.Control != nil { 102 f.Fatalf("plain/dead block %s has a control value", b) 103 } 104 } 105 if len(b.Succs) != 2 && b.Likely != BranchUnknown { 106 f.Fatalf("likeliness prediction %d for block %s with %d successors", b.Likely, b, len(b.Succs)) 107 } 108 109 for _, v := range b.Values { 110 // Check to make sure argument count makes sense (argLen of -1 indicates 111 // variable length args) 112 nArgs := opcodeTable[v.Op].argLen 113 if nArgs != -1 && int32(len(v.Args)) != nArgs { 114 f.Fatalf("value %s has %d args, expected %d", v.LongString(), 115 len(v.Args), nArgs) 116 } 117 118 // Check to make sure aux values make sense. 119 canHaveAux := false 120 canHaveAuxInt := false 121 switch opcodeTable[v.Op].auxType { 122 case auxNone: 123 case auxBool: 124 if v.AuxInt < 0 || v.AuxInt > 1 { 125 f.Fatalf("bad bool AuxInt value for %v", v) 126 } 127 canHaveAuxInt = true 128 case auxInt8: 129 if v.AuxInt != int64(int8(v.AuxInt)) { 130 f.Fatalf("bad int8 AuxInt value for %v", v) 131 } 132 canHaveAuxInt = true 133 case auxInt16: 134 if v.AuxInt != int64(int16(v.AuxInt)) { 135 f.Fatalf("bad int16 AuxInt value for %v", v) 136 } 137 canHaveAuxInt = true 138 case auxInt32: 139 if v.AuxInt != int64(int32(v.AuxInt)) { 140 f.Fatalf("bad int32 AuxInt value for %v", v) 141 } 142 canHaveAuxInt = true 143 case auxInt64, auxFloat64: 144 canHaveAuxInt = true 145 case auxInt128: 146 // AuxInt must be zero, so leave canHaveAuxInt set to false. 147 case auxFloat32: 148 canHaveAuxInt = true 149 if !isExactFloat32(v) { 150 f.Fatalf("value %v has an AuxInt value that is not an exact float32", v) 151 } 152 case auxString, auxSym, auxTyp: 153 canHaveAux = true 154 case auxSymOff, auxSymValAndOff, auxTypSize: 155 canHaveAuxInt = true 156 canHaveAux = true 157 case auxSymInt32: 158 if v.AuxInt != int64(int32(v.AuxInt)) { 159 f.Fatalf("bad int32 AuxInt value for %v", v) 160 } 161 canHaveAuxInt = true 162 canHaveAux = true 163 default: 164 f.Fatalf("unknown aux type for %s", v.Op) 165 } 166 if !canHaveAux && v.Aux != nil { 167 f.Fatalf("value %s has an Aux value %v but shouldn't", v.LongString(), v.Aux) 168 } 169 if !canHaveAuxInt && v.AuxInt != 0 { 170 f.Fatalf("value %s has an AuxInt value %d but shouldn't", v.LongString(), v.AuxInt) 171 } 172 173 for i, arg := range v.Args { 174 if arg == nil { 175 f.Fatalf("value %s has nil arg", v.LongString()) 176 } 177 if v.Op != OpPhi { 178 // For non-Phi ops, memory args must be last, if present 179 if arg.Type.IsMemory() && i != len(v.Args)-1 { 180 f.Fatalf("value %s has non-final memory arg (%d < %d)", v.LongString(), i, len(v.Args)-1) 181 } 182 } 183 } 184 185 if valueMark[v.ID] { 186 f.Fatalf("value %s appears twice!", v.LongString()) 187 } 188 valueMark[v.ID] = true 189 190 if v.Block != b { 191 f.Fatalf("%s.block != %s", v, b) 192 } 193 if v.Op == OpPhi && len(v.Args) != len(b.Preds) { 194 f.Fatalf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) 195 } 196 197 if v.Op == OpAddr { 198 if len(v.Args) == 0 { 199 f.Fatalf("no args for OpAddr %s", v.LongString()) 200 } 201 if v.Args[0].Op != OpSP && v.Args[0].Op != OpSB { 202 f.Fatalf("bad arg to OpAddr %v", v) 203 } 204 } 205 206 if f.RegAlloc != nil && f.Config.SoftFloat && v.Type.IsFloat() { 207 f.Fatalf("unexpected floating-point type %v", v.LongString()) 208 } 209 210 // TODO: check for cycles in values 211 // TODO: check type 212 } 213 } 214 215 // Check to make sure all Blocks referenced are in the function. 216 if !blockMark[f.Entry.ID] { 217 f.Fatalf("entry block %v is missing", f.Entry) 218 } 219 for _, b := range f.Blocks { 220 for _, c := range b.Preds { 221 if !blockMark[c.b.ID] { 222 f.Fatalf("predecessor block %v for %v is missing", c, b) 223 } 224 } 225 for _, c := range b.Succs { 226 if !blockMark[c.b.ID] { 227 f.Fatalf("successor block %v for %v is missing", c, b) 228 } 229 } 230 } 231 232 if len(f.Entry.Preds) > 0 { 233 f.Fatalf("entry block %s of %s has predecessor(s) %v", f.Entry, f.Name, f.Entry.Preds) 234 } 235 236 // Check to make sure all Values referenced are in the function. 237 for _, b := range f.Blocks { 238 for _, v := range b.Values { 239 for i, a := range v.Args { 240 if !valueMark[a.ID] { 241 f.Fatalf("%v, arg %d of %s, is missing", a, i, v.LongString()) 242 } 243 } 244 } 245 if b.Control != nil && !valueMark[b.Control.ID] { 246 f.Fatalf("control value for %s is missing: %v", b, b.Control) 247 } 248 } 249 for b := f.freeBlocks; b != nil; b = b.succstorage[0].b { 250 if blockMark[b.ID] { 251 f.Fatalf("used block b%d in free list", b.ID) 252 } 253 } 254 for v := f.freeValues; v != nil; v = v.argstorage[0] { 255 if valueMark[v.ID] { 256 f.Fatalf("used value v%d in free list", v.ID) 257 } 258 } 259 260 // Check to make sure all args dominate uses. 261 if f.RegAlloc == nil { 262 // Note: regalloc introduces non-dominating args. 263 // See TODO in regalloc.go. 264 sdom := f.sdom() 265 for _, b := range f.Blocks { 266 for _, v := range b.Values { 267 for i, arg := range v.Args { 268 x := arg.Block 269 y := b 270 if v.Op == OpPhi { 271 y = b.Preds[i].b 272 } 273 if !domCheck(f, sdom, x, y) { 274 f.Fatalf("arg %d of value %s does not dominate, arg=%s", i, v.LongString(), arg.LongString()) 275 } 276 } 277 } 278 if b.Control != nil && !domCheck(f, sdom, b.Control.Block, b) { 279 f.Fatalf("control value %s for %s doesn't dominate", b.Control, b) 280 } 281 } 282 } 283 284 // Check loop construction 285 if f.RegAlloc == nil && f.pass != nil { // non-nil pass allows better-targeted debug printing 286 ln := f.loopnest() 287 if !ln.hasIrreducible { 288 po := f.postorder() // use po to avoid unreachable blocks. 289 for _, b := range po { 290 for _, s := range b.Succs { 291 bb := s.Block() 292 if ln.b2l[b.ID] == nil && ln.b2l[bb.ID] != nil && bb != ln.b2l[bb.ID].header { 293 f.Fatalf("block %s not in loop branches to non-header block %s in loop", b.String(), bb.String()) 294 } 295 if ln.b2l[b.ID] != nil && ln.b2l[bb.ID] != nil && bb != ln.b2l[bb.ID].header && !ln.b2l[b.ID].isWithinOrEq(ln.b2l[bb.ID]) { 296 f.Fatalf("block %s in loop branches to non-header block %s in non-containing loop", b.String(), bb.String()) 297 } 298 } 299 } 300 } 301 } 302 303 // Check use counts 304 uses := make([]int32, f.NumValues()) 305 for _, b := range f.Blocks { 306 for _, v := range b.Values { 307 for _, a := range v.Args { 308 uses[a.ID]++ 309 } 310 } 311 if b.Control != nil { 312 uses[b.Control.ID]++ 313 } 314 } 315 for _, b := range f.Blocks { 316 for _, v := range b.Values { 317 if v.Uses != uses[v.ID] { 318 f.Fatalf("%s has %d uses, but has Uses=%d", v, uses[v.ID], v.Uses) 319 } 320 } 321 } 322 323 memCheck(f) 324 } 325 326 func memCheck(f *Func) { 327 // Check that if a tuple has a memory type, it is second. 328 for _, b := range f.Blocks { 329 for _, v := range b.Values { 330 if v.Type.IsTuple() && v.Type.FieldType(0).IsMemory() { 331 f.Fatalf("memory is first in a tuple: %s\n", v.LongString()) 332 } 333 } 334 } 335 336 // Single live memory checks. 337 // These checks only work if there are no memory copies. 338 // (Memory copies introduce ambiguity about which mem value is really live. 339 // probably fixable, but it's easier to avoid the problem.) 340 // For the same reason, disable this check if some memory ops are unused. 341 for _, b := range f.Blocks { 342 for _, v := range b.Values { 343 if (v.Op == OpCopy || v.Uses == 0) && v.Type.IsMemory() { 344 return 345 } 346 } 347 if b != f.Entry && len(b.Preds) == 0 { 348 return 349 } 350 } 351 352 // Compute live memory at the end of each block. 353 lastmem := make([]*Value, f.NumBlocks()) 354 ss := newSparseSet(f.NumValues()) 355 for _, b := range f.Blocks { 356 // Mark overwritten memory values. Those are args of other 357 // ops that generate memory values. 358 ss.clear() 359 for _, v := range b.Values { 360 if v.Op == OpPhi || !v.Type.IsMemory() { 361 continue 362 } 363 if m := v.MemoryArg(); m != nil { 364 ss.add(m.ID) 365 } 366 } 367 // There should be at most one remaining unoverwritten memory value. 368 for _, v := range b.Values { 369 if !v.Type.IsMemory() { 370 continue 371 } 372 if ss.contains(v.ID) { 373 continue 374 } 375 if lastmem[b.ID] != nil { 376 f.Fatalf("two live memory values in %s: %s and %s", b, lastmem[b.ID], v) 377 } 378 lastmem[b.ID] = v 379 } 380 // If there is no remaining memory value, that means there was no memory update. 381 // Take any memory arg. 382 if lastmem[b.ID] == nil { 383 for _, v := range b.Values { 384 if v.Op == OpPhi { 385 continue 386 } 387 m := v.MemoryArg() 388 if m == nil { 389 continue 390 } 391 if lastmem[b.ID] != nil && lastmem[b.ID] != m { 392 f.Fatalf("two live memory values in %s: %s and %s", b, lastmem[b.ID], m) 393 } 394 lastmem[b.ID] = m 395 } 396 } 397 } 398 // Propagate last live memory through storeless blocks. 399 for { 400 changed := false 401 for _, b := range f.Blocks { 402 if lastmem[b.ID] != nil { 403 continue 404 } 405 for _, e := range b.Preds { 406 p := e.b 407 if lastmem[p.ID] != nil { 408 lastmem[b.ID] = lastmem[p.ID] 409 changed = true 410 break 411 } 412 } 413 } 414 if !changed { 415 break 416 } 417 } 418 // Check merge points. 419 for _, b := range f.Blocks { 420 for _, v := range b.Values { 421 if v.Op == OpPhi && v.Type.IsMemory() { 422 for i, a := range v.Args { 423 if a != lastmem[b.Preds[i].b.ID] { 424 f.Fatalf("inconsistent memory phi %s %d %s %s", v.LongString(), i, a, lastmem[b.Preds[i].b.ID]) 425 } 426 } 427 } 428 } 429 } 430 431 // Check that only one memory is live at any point. 432 if f.scheduled { 433 for _, b := range f.Blocks { 434 var mem *Value // the current live memory in the block 435 for _, v := range b.Values { 436 if v.Op == OpPhi { 437 if v.Type.IsMemory() { 438 mem = v 439 } 440 continue 441 } 442 if mem == nil && len(b.Preds) > 0 { 443 // If no mem phi, take mem of any predecessor. 444 mem = lastmem[b.Preds[0].b.ID] 445 } 446 for _, a := range v.Args { 447 if a.Type.IsMemory() && a != mem { 448 f.Fatalf("two live mems @ %s: %s and %s", v, mem, a) 449 } 450 } 451 if v.Type.IsMemory() { 452 mem = v 453 } 454 } 455 } 456 } 457 458 // Check that after scheduling, phis are always first in the block. 459 if f.scheduled { 460 for _, b := range f.Blocks { 461 seenNonPhi := false 462 for _, v := range b.Values { 463 switch v.Op { 464 case OpPhi: 465 if seenNonPhi { 466 f.Fatalf("phi after non-phi @ %s: %s", b, v) 467 } 468 case OpRegKill: 469 if f.RegAlloc == nil { 470 f.Fatalf("RegKill seen before register allocation @ %s: %s", b, v) 471 } 472 default: 473 seenNonPhi = true 474 } 475 } 476 } 477 } 478 } 479 480 // domCheck reports whether x dominates y (including x==y). 481 func domCheck(f *Func, sdom SparseTree, x, y *Block) bool { 482 if !sdom.isAncestorEq(f.Entry, y) { 483 // unreachable - ignore 484 return true 485 } 486 return sdom.isAncestorEq(x, y) 487 } 488 489 // isExactFloat32 reports whether v has an AuxInt that can be exactly represented as a float32. 490 func isExactFloat32(v *Value) bool { 491 x := v.AuxFloat() 492 return math.Float64bits(x) == math.Float64bits(float64(float32(x))) 493 }