1// Copyright 2015 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package ssacompile67import (8 "math"9 "math/bits"1011 "cmd/compile/internal/ir"12 "cmd/compile/internal/ssa"13 "cmd/compile/internal/ssa/block"14 "cmd/compile/internal/ssa/ssaop"15 "cmd/internal/obj/s390x"16)1718// checkFunc checks invariants of f.19func checkFunc(f *ssa.Func) {20 blockMark := make([]bool, f.NumBlocks())21 valueMark := make([]bool, f.NumValues())2223 for _, b := range f.Blocks {24 if blockMark[b.ID] {25 f.Fatalf("block %s appears twice in %s!", b, f.Name)26 }27 blockMark[b.ID] = true28 if b.Func != f {29 f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name)30 }3132 for i, e := range b.Preds {33 if se := e.B.Succs[e.I]; se.B != b || se.I != i {34 f.Fatalf("block pred/succ not crosslinked correctly %d:%s %d:%s", i, b, se.I, se.B)35 }36 }37 for i, e := range b.Succs {38 if pe := e.B.Preds[e.I]; pe.B != b || pe.I != i {39 f.Fatalf("block succ/pred not crosslinked correctly %d:%s %d:%s", i, b, pe.I, pe.B)40 }41 }4243 switch b.Kind {44 case block.BlockExit:45 if len(b.Succs) != 0 {46 f.Fatalf("exit block %s has successors", b)47 }48 if b.NumControls() != 1 {49 f.Fatalf("exit block %s has no control value", b)50 }51 if !b.Controls[0].Type.IsMemory() {52 f.Fatalf("exit block %s has non-memory control value %s", b, b.Controls[0].LongString())53 }54 case block.BlockRet:55 if len(b.Succs) != 0 {56 f.Fatalf("ret block %s has successors", b)57 }58 if b.NumControls() != 1 {59 f.Fatalf("ret block %s has nil control", b)60 }61 if !b.Controls[0].Type.IsMemory() {62 f.Fatalf("ret block %s has non-memory control value %s", b, b.Controls[0].LongString())63 }64 case block.BlockRetJmp:65 if len(b.Succs) != 0 {66 f.Fatalf("retjmp block %s len(Succs)==%d, want 0", b, len(b.Succs))67 }68 if b.NumControls() != 1 {69 f.Fatalf("retjmp block %s has nil control", b)70 }71 if !b.Controls[0].Type.IsMemory() {72 f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Controls[0].LongString())73 }74 case block.BlockPlain:75 if len(b.Succs) != 1 {76 f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))77 }78 if b.NumControls() != 0 {79 f.Fatalf("plain block %s has non-nil control %s", b, b.Controls[0].LongString())80 }81 case block.BlockIf:82 if len(b.Succs) != 2 {83 f.Fatalf("if block %s len(Succs)==%d, want 2", b, len(b.Succs))84 }85 if b.NumControls() != 1 {86 f.Fatalf("if block %s has no control value", b)87 }88 if !b.Controls[0].Type.IsBoolean() {89 f.Fatalf("if block %s has non-bool control value %s", b, b.Controls[0].LongString())90 }91 case block.BlockDefer:92 if len(b.Succs) != 2 {93 f.Fatalf("defer block %s len(Succs)==%d, want 2", b, len(b.Succs))94 }95 if b.NumControls() != 1 {96 f.Fatalf("defer block %s has no control value", b)97 }98 if !b.Controls[0].Type.IsMemory() {99 f.Fatalf("defer block %s has non-memory control value %s", b, b.Controls[0].LongString())100 }101 case block.BlockFirst:102 if len(b.Succs) != 2 {103 f.Fatalf("plain/dead block %s len(Succs)==%d, want 2", b, len(b.Succs))104 }105 if b.NumControls() != 0 {106 f.Fatalf("plain/dead block %s has a control value", b)107 }108 case block.BlockJumpTable:109 if b.NumControls() != 1 {110 f.Fatalf("jumpTable block %s has no control value", b)111 }112 }113 if len(b.Succs) != 2 && b.Likely != ssa.BranchUnknown {114 f.Fatalf("likeliness prediction %d for block %s with %d successors", b.Likely, b, len(b.Succs))115 }116117 for _, v := range b.Values {118 // Check to make sure argument count makes sense (argLen of -1 indicates119 // variable length args)120 nArgs := ssaop.OpcodeTable[v.Op].ArgLen121 if nArgs != -1 && int32(len(v.Args)) != nArgs {122 f.Fatalf("value %s has %d args, expected %d", v.LongString(),123 len(v.Args), nArgs)124 }125126 // Check to make sure aux values make sense.127 canHaveAux := false128 canHaveAuxInt := false129 // TODO: enforce types of Aux in this switch (like auxString does below)130 switch ssaop.OpcodeTable[v.Op].AuxType {131 case ssaop.AuxTypeNone:132 case ssaop.AuxTypeBool:133 if v.AuxInt < 0 || v.AuxInt > 1 {134 f.Fatalf("bad bool AuxInt value for %v", v)135 }136 canHaveAuxInt = true137 case ssaop.AuxTypeInt8:138 if v.AuxInt != int64(int8(v.AuxInt)) {139 f.Fatalf("bad int8 AuxInt value for %v", v)140 }141 canHaveAuxInt = true142 case ssaop.AuxTypeInt16:143 if v.AuxInt != int64(int16(v.AuxInt)) {144 f.Fatalf("bad int16 AuxInt value for %v", v)145 }146 canHaveAuxInt = true147 case ssaop.AuxTypeInt32:148 if v.AuxInt != int64(int32(v.AuxInt)) {149 f.Fatalf("bad int32 AuxInt value for %v", v)150 }151 canHaveAuxInt = true152 case ssaop.AuxTypeInt64, ssaop.AuxTypeARM64BitField, ssaop.AuxTypeARM64ConditionalParams:153 canHaveAuxInt = true154 case ssaop.AuxTypeInt128:155 // AuxInt must be zero, so leave canHaveAuxInt set to false.156 case ssaop.AuxTypeUInt8:157 // Cast to int8 due to requirement of AuxInt, check its comment for details.158 if v.AuxInt != int64(int8(v.AuxInt)) {159 f.Fatalf("bad uint8 AuxInt value for %v, saw %d but need %d", v, v.AuxInt, int64(int8(v.AuxInt)))160 }161 canHaveAuxInt = true162 case ssaop.AuxTypeFloat32:163 canHaveAuxInt = true164 if math.IsNaN(v.AuxFloat()) {165 f.Fatalf("value %v has an AuxInt that encodes a NaN", v)166 }167 if !isExactFloat32(v.AuxFloat()) {168 f.Fatalf("value %v has an AuxInt value that is not an exact float32", v)169 }170 case ssaop.AuxTypeFloat64:171 canHaveAuxInt = true172 if math.IsNaN(v.AuxFloat()) {173 f.Fatalf("value %v has an AuxInt that encodes a NaN", v)174 }175 case ssaop.AuxTypeString:176 if _, ok := v.Aux.(ssa.StringAux); !ok {177 f.Fatalf("value %v has Aux type %T, want string", v, v.Aux)178 }179 canHaveAux = true180 case ssaop.AuxTypeCallOff:181 canHaveAuxInt = true182 fallthrough183 case ssaop.AuxTypeCall:184 if ac, ok := v.Aux.(*ssa.AuxCall); ok {185 if v.Op == ssaop.OpStaticCall && ac.Fn == nil {186 f.Fatalf("value %v has *AuxCall with nil Fn", v)187 }188 } else {189 f.Fatalf("value %v has Aux type %T, want *AuxCall", v, v.Aux)190 }191 canHaveAux = true192 case ssaop.AuxTypeNameOffsetInt8:193 if _, ok := v.Aux.(*ssa.AuxNameOffset); !ok {194 f.Fatalf("value %v has Aux type %T, want *AuxNameOffset", v, v.Aux)195 }196 canHaveAux = true197 canHaveAuxInt = true198 case ssaop.AuxTypeSym, ssaop.AuxTypeTyp:199 canHaveAux = true200 case ssaop.AuxTypeSymOff, ssaop.AuxTypeSymValAndOff, ssaop.AuxTypeTypSize:201 canHaveAuxInt = true202 canHaveAux = true203 case ssaop.AuxTypeCCop:204 if ssaop.OpcodeTable[ssaop.Op(v.AuxInt)].Name == "OpInvalid" {205 f.Fatalf("value %v has an AuxInt value that is not a valid opcode", v)206 }207 canHaveAuxInt = true208 case ssaop.AuxTypeS390XCCMask:209 if _, ok := v.Aux.(s390x.CCMask); !ok {210 f.Fatalf("bad type %T for S390XCCMask in %v", v.Aux, v)211 }212 canHaveAux = true213 case ssaop.AuxTypeS390XRotateParams:214 if _, ok := v.Aux.(s390x.RotateParams); !ok {215 f.Fatalf("bad type %T for S390XRotateParams in %v", v.Aux, v)216 }217 canHaveAux = true218 case ssaop.AuxTypeFlagConstant:219 if v.AuxInt < 0 || v.AuxInt > 15 {220 f.Fatalf("bad FlagConstant AuxInt value for %v", v)221 }222 canHaveAuxInt = true223 case ssaop.AuxTypePanicBoundsC, ssaop.AuxTypePanicBoundsCC:224 canHaveAux = true225 canHaveAuxInt = true226 default:227 f.Fatalf("unknown aux type for %s", v.Op)228 }229 if !canHaveAux && v.Aux != nil {230 f.Fatalf("value %s has an Aux value %v but shouldn't", v.LongString(), v.Aux)231 }232 if !canHaveAuxInt && v.AuxInt != 0 {233 f.Fatalf("value %s has an AuxInt value %d but shouldn't", v.LongString(), v.AuxInt)234 }235236 for i, arg := range v.Args {237 if arg == nil {238 f.Fatalf("value %s has nil arg", v.LongString())239 }240 if v.Op != ssaop.OpPhi {241 // For non-Phi ops, memory args must be last, if present242 if arg.Type.IsMemory() && i != len(v.Args)-1 {243 f.Fatalf("value %s has non-final memory arg (%d < %d)", v.LongString(), i, len(v.Args)-1)244 }245 }246 }247248 if valueMark[v.ID] {249 f.Fatalf("value %s appears twice!", v.LongString())250 }251 valueMark[v.ID] = true252253 if v.Block != b {254 f.Fatalf("%s.block != %s", v, b)255 }256 if v.Op == ssaop.OpPhi && len(v.Args) != len(b.Preds) {257 f.Fatalf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b)258 }259260 if v.Op == ssaop.OpAddr {261 if len(v.Args) == 0 {262 f.Fatalf("no args for OpAddr %s", v.LongString())263 }264 if v.Args[0].Op != ssaop.OpSB {265 f.Fatalf("bad arg to OpAddr %v", v)266 }267 }268269 if v.Op == ssaop.OpLocalAddr {270 if len(v.Args) != 2 {271 f.Fatalf("wrong # of args for OpLocalAddr %s", v.LongString())272 }273 if v.Args[0].Op != ssaop.OpSP {274 f.Fatalf("bad arg 0 to OpLocalAddr %v", v)275 }276 if !v.Args[1].Type.IsMemory() {277 f.Fatalf("bad arg 1 to OpLocalAddr %v", v)278 }279 }280281 if (v.Op == ssaop.OpStructMake || v.Op == ssaop.OpArrayMake1) && v.Type.Size() == 0 {282 f.Fatalf("zero-sized Make; use Empty instead %v", v)283 }284285 if f.RegAlloc != nil && f.Config.SoftFloat && v.Type.IsFloat() {286 f.Fatalf("unexpected floating-point type %v", v.LongString())287 }288289 // Check types.290 // TODO: more type checks?291 switch c := f.Config; v.Op {292 case ssaop.OpSP, ssaop.OpSB:293 if v.Type != c.Types.Uintptr {294 f.Fatalf("bad %s type: want uintptr, have %s",295 v.Op, v.Type.String())296 }297 case ssaop.OpStringLen:298 if v.Type != c.Types.Int {299 f.Fatalf("bad %s type: want int, have %s",300 v.Op, v.Type.String())301 }302 case ssaop.OpLoad:303 if !v.Args[1].Type.IsMemory() {304 f.Fatalf("bad arg 1 type to %s: want mem, have %s",305 v.Op, v.Args[1].Type.String())306 }307 case ssaop.OpStore:308 if !v.Type.IsMemory() {309 f.Fatalf("bad %s type: want mem, have %s",310 v.Op, v.Type.String())311 }312 if !v.Args[2].Type.IsMemory() {313 f.Fatalf("bad arg 2 type to %s: want mem, have %s",314 v.Op, v.Args[2].Type.String())315 }316 case ssaop.OpCondSelect:317 if !v.Args[2].Type.IsBoolean() {318 f.Fatalf("bad arg 2 type to %s: want boolean, have %s",319 v.Op, v.Args[2].Type.String())320 }321 case ssaop.OpAddPtr:322 if !v.Args[0].Type.IsPtrShaped() && v.Args[0].Type != c.Types.Uintptr {323 f.Fatalf("bad arg 0 type to %s: want ptr, have %s", v.Op, v.Args[0].LongString())324 }325 if !v.Args[1].Type.IsInteger() {326 f.Fatalf("bad arg 1 type to %s: want integer, have %s", v.Op, v.Args[1].LongString())327 }328 case ssaop.OpVarDef:329 n := v.Aux.(*ir.Name)330 if !n.Type().HasPointers() && !ssa.IsMergeCandidate(n) {331 f.Fatalf("vardef must be merge candidate or have pointer type %s", v.Aux.(*ir.Name).Type().String())332 }333 case ssaop.OpNilCheck:334 // nil checks have pointer type before scheduling, and335 // void type after scheduling.336 if f.Scheduled {337 if v.Uses != 0 {338 f.Fatalf("nilcheck must have 0 uses %s", v.Uses)339 }340 if !v.Type.IsVoid() {341 f.Fatalf("nilcheck must have void type %s", v.Type.String())342 }343 } else {344 if !v.Type.IsPtrShaped() && !v.Type.IsUintptr() {345 f.Fatalf("nilcheck must have pointer type %s", v.Type.String())346 }347 }348 if !v.Args[0].Type.IsPtrShaped() && !v.Args[0].Type.IsUintptr() {349 f.Fatalf("nilcheck must have argument of pointer type %s", v.Args[0].Type.String())350 }351 if !v.Args[1].Type.IsMemory() {352 f.Fatalf("bad arg 1 type to %s: want mem, have %s",353 v.Op, v.Args[1].Type.String())354 }355 }356 // Check size of args.357 // This list isn't exhaustive, just the common ops.358 // It also can't handle ops with args of different types, like shifts.359 var argSize int64360 switch v.Op {361 case ssaop.OpAdd8, ssaop.OpSub8, ssaop.OpMul8, ssaop.OpDiv8, ssaop.OpDiv8u, ssaop.OpMod8, ssaop.OpMod8u,362 ssaop.OpAnd8, ssaop.OpOr8, ssaop.OpXor8,363 ssaop.OpEq8, ssaop.OpNeq8, ssaop.OpLess8, ssaop.OpLeq8,364 ssaop.OpNeg8, ssaop.OpCom8,365 ssaop.OpSignExt8to16, ssaop.OpSignExt8to32, ssaop.OpSignExt8to64,366 ssaop.OpZeroExt8to16, ssaop.OpZeroExt8to32, ssaop.OpZeroExt8to64:367 argSize = 1368 case ssaop.OpAdd16, ssaop.OpSub16, ssaop.OpMul16, ssaop.OpDiv16, ssaop.OpDiv16u, ssaop.OpMod16, ssaop.OpMod16u,369 ssaop.OpAnd16, ssaop.OpOr16, ssaop.OpXor16,370 ssaop.OpEq16, ssaop.OpNeq16, ssaop.OpLess16, ssaop.OpLeq16,371 ssaop.OpNeg16, ssaop.OpCom16,372 ssaop.OpSignExt16to32, ssaop.OpSignExt16to64,373 ssaop.OpZeroExt16to32, ssaop.OpZeroExt16to64,374 ssaop.OpTrunc16to8:375 argSize = 2376 case ssaop.OpAdd32, ssaop.OpSub32, ssaop.OpMul32, ssaop.OpDiv32, ssaop.OpDiv32u, ssaop.OpMod32, ssaop.OpMod32u,377 ssaop.OpAnd32, ssaop.OpOr32, ssaop.OpXor32,378 ssaop.OpEq32, ssaop.OpNeq32, ssaop.OpLess32, ssaop.OpLeq32,379 ssaop.OpNeg32, ssaop.OpCom32,380 ssaop.OpSignExt32to64, ssaop.OpZeroExt32to64,381 ssaop.OpTrunc32to8, ssaop.OpTrunc32to16:382 argSize = 4383 case ssaop.OpAdd64, ssaop.OpSub64, ssaop.OpMul64, ssaop.OpDiv64, ssaop.OpDiv64u, ssaop.OpMod64, ssaop.OpMod64u,384 ssaop.OpAnd64, ssaop.OpOr64, ssaop.OpXor64,385 ssaop.OpEq64, ssaop.OpNeq64, ssaop.OpLess64, ssaop.OpLeq64,386 ssaop.OpNeg64, ssaop.OpCom64,387 ssaop.OpTrunc64to8, ssaop.OpTrunc64to16, ssaop.OpTrunc64to32:388 argSize = 8389 }390 if argSize != 0 {391 for i, arg := range v.Args {392 if arg.Type.Size() != argSize {393 f.Fatalf("arg %d to %s (%v) should be %d bytes in size, it is %s", i, v.Op, v, argSize, arg.Type.String())394 }395 }396 }397398 // TODO: check for cycles in values399 }400 }401402 // Check to make sure all Blocks referenced are in the function.403 if !blockMark[f.Entry.ID] {404 f.Fatalf("entry block %v is missing", f.Entry)405 }406 for _, b := range f.Blocks {407 for _, c := range b.Preds {408 if !blockMark[c.B.ID] {409 f.Fatalf("predecessor block %v for %v is missing", c, b)410 }411 }412 for _, c := range b.Succs {413 if !blockMark[c.B.ID] {414 f.Fatalf("successor block %v for %v is missing", c, b)415 }416 }417 }418419 if len(f.Entry.Preds) > 0 {420 f.Fatalf("entry block %s of %s has predecessor(s) %v", f.Entry, f.Name, f.Entry.Preds)421 }422423 // Check to make sure all Values referenced are in the function.424 for _, b := range f.Blocks {425 for _, v := range b.Values {426 for i, a := range v.Args {427 if !valueMark[a.ID] {428 f.Fatalf("%v, arg %d of %s, is missing", a, i, v.LongString())429 }430 }431 }432 for _, c := range b.ControlValues() {433 if !valueMark[c.ID] {434 f.Fatalf("control value for %s is missing: %v", b, c)435 }436 }437 }438 for b := f.FreeBlocks; b != nil; b = b.Succstorage[0].B {439 if blockMark[b.ID] {440 f.Fatalf("used block b%d in free list", b.ID)441 }442 }443 for v := f.FreeValues; v != nil; v = v.Argstorage[0] {444 if valueMark[v.ID] {445 f.Fatalf("used value v%d in free list", v.ID)446 }447 }448449 // Check to make sure all args dominate uses.450 if f.RegAlloc == nil {451 // Note: regalloc introduces non-dominating args.452 // See TODO in regalloc.go.453 sdom := f.Sdom()454 for _, b := range f.Blocks {455 for _, v := range b.Values {456 for i, arg := range v.Args {457 x := arg.Block458 y := b459 if v.Op == ssaop.OpPhi {460 y = b.Preds[i].B461 }462 if !domCheck(f, sdom, x, y) {463 f.Fatalf("arg %d of value %s does not dominate, arg=%s", i, v.LongString(), arg.LongString())464 }465 }466 }467 for _, c := range b.ControlValues() {468 if !domCheck(f, sdom, c.Block, b) {469 f.Fatalf("control value %s for %s doesn't dominate", c, b)470 }471 }472 }473 }474475 // Check loop construction476 if f.RegAlloc == nil && f.Pass != nil { // non-nil pass allows better-targeted debug printing477 ln := f.Loopnest()478 if !ln.HasIrreducible {479 po := f.Postorder() // use po to avoid unreachable blocks.480 for _, b := range po {481 for _, s := range b.Succs {482 bb := s.Block()483 if ln.B2L[b.ID] == nil && ln.B2L[bb.ID] != nil && bb != ln.B2L[bb.ID].Header {484 f.Fatalf("block %s not in loop branches to non-header block %s in loop", b.String(), bb.String())485 }486 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]) {487 f.Fatalf("block %s in loop branches to non-header block %s in non-containing loop", b.String(), bb.String())488 }489 }490 }491 }492 }493494 // Check use counts495 uses := make([]int32, f.NumValues())496 for _, b := range f.Blocks {497 for _, v := range b.Values {498 for _, a := range v.Args {499 uses[a.ID]++500 }501 }502 for _, c := range b.ControlValues() {503 uses[c.ID]++504 }505 }506 for _, b := range f.Blocks {507 for _, v := range b.Values {508 if v.Uses != uses[v.ID] {509 f.Fatalf("%s has %d uses, but has Uses=%d", v, uses[v.ID], v.Uses)510 }511 }512 }513514 memCheck(f)515}516517func memCheck(f *ssa.Func) {518 // Check that if a tuple has a memory type, it is second.519 for _, b := range f.Blocks {520 for _, v := range b.Values {521 if v.Type.IsTuple() && v.Type.FieldType(0).IsMemory() {522 f.Fatalf("memory is first in a tuple: %s\n", v.LongString())523 }524 }525 }526527 // Single live memory checks.528 // These checks only work if there are no memory copies.529 // (Memory copies introduce ambiguity about which mem value is really live.530 // probably fixable, but it's easier to avoid the problem.)531 // For the same reason, disable this check if some memory ops are unused.532 for _, b := range f.Blocks {533 for _, v := range b.Values {534 if (v.Op == ssaop.OpCopy || v.Uses == 0) && v.Type.IsMemory() {535 return536 }537 }538 if b != f.Entry && len(b.Preds) == 0 {539 return540 }541 }542543 // Compute live memory at the end of each block.544 lastmem := make([]*ssa.Value, f.NumBlocks())545 ss := ssa.NewSparseSet(f.NumValues())546 for _, b := range f.Blocks {547 // Mark overwritten memory values. Those are args of other548 // ops that generate memory values.549 ss.Clear()550 for _, v := range b.Values {551 if v.Op == ssaop.OpPhi || !v.Type.IsMemory() {552 continue553 }554 if m := v.MemoryArg(); m != nil {555 ss.Add(m.ID)556 }557 }558 // There should be at most one remaining unoverwritten memory value.559 for _, v := range b.Values {560 if !v.Type.IsMemory() {561 continue562 }563 if ss.Contains(v.ID) {564 continue565 }566 if lastmem[b.ID] != nil {567 f.Fatalf("two live memory values in %s: %s and %s", b, lastmem[b.ID], v)568 }569 lastmem[b.ID] = v570 }571 // If there is no remaining memory value, that means there was no memory update.572 // Take any memory arg.573 if lastmem[b.ID] == nil {574 for _, v := range b.Values {575 if v.Op == ssaop.OpPhi {576 continue577 }578 m := v.MemoryArg()579 if m == nil {580 continue581 }582 if lastmem[b.ID] != nil && lastmem[b.ID] != m {583 f.Fatalf("two live memory values in %s: %s and %s", b, lastmem[b.ID], m)584 }585 lastmem[b.ID] = m586 }587 }588 }589 // Propagate last live memory through storeless blocks.590 for {591 changed := false592 for _, b := range f.Blocks {593 if lastmem[b.ID] != nil {594 continue595 }596 for _, e := range b.Preds {597 p := e.B598 if lastmem[p.ID] != nil {599 lastmem[b.ID] = lastmem[p.ID]600 changed = true601 break602 }603 }604 }605 if !changed {606 break607 }608 }609 // Check merge points.610 for _, b := range f.Blocks {611 for _, v := range b.Values {612 if v.Op == ssaop.OpPhi && v.Type.IsMemory() {613 for i, a := range v.Args {614 if a != lastmem[b.Preds[i].B.ID] {615 f.Fatalf("inconsistent memory phi %s %d %s %s", v.LongString(), i, a, lastmem[b.Preds[i].B.ID])616 }617 }618 }619 }620 }621622 // Check that only one memory is live at any point.623 if f.Scheduled {624 for _, b := range f.Blocks {625 var mem *ssa.Value // the current live memory in the block626 for _, v := range b.Values {627 if v.Op == ssaop.OpPhi {628 if v.Type.IsMemory() {629 mem = v630 }631 continue632 }633 if mem == nil && len(b.Preds) > 0 {634 // If no mem phi, take mem of any predecessor.635 mem = lastmem[b.Preds[0].B.ID]636 }637 for _, a := range v.Args {638 if a.Type.IsMemory() && a != mem {639 f.Fatalf("two live mems @ %s: %s and %s", v, mem, a)640 }641 }642 if v.Type.IsMemory() {643 mem = v644 }645 }646 }647 }648649 // Check that after scheduling, phis are always first in the block.650 if f.Scheduled {651 for _, b := range f.Blocks {652 seenNonPhi := false653 for _, v := range b.Values {654 switch v.Op {655 case ssaop.OpPhi:656 if seenNonPhi {657 f.Fatalf("phi after non-phi @ %s: %s", b, v)658 }659 default:660 seenNonPhi = true661 }662 }663 }664 }665}666667// domCheck reports whether x dominates y (including x==y).668func domCheck(f *ssa.Func, sdom ssa.SparseTree, x, y *ssa.Block) bool {669 if !sdom.IsAncestorEq(f.Entry, y) {670 // unreachable - ignore671 return true672 }673 return sdom.IsAncestorEq(x, y)674}675676// isExactFloat32 reports whether x can be exactly represented as a float32.677func isExactFloat32(x float64) bool {678 // Check the mantissa is in range.679 if bits.TrailingZeros64(math.Float64bits(x)) < 52-23 {680 return false681 }682 // Check the exponent is in range. The mantissa check above is sufficient for NaN values.683 return math.IsNaN(x) || x == float64(float32(x))684}
Findings
✓ No findings reported for this file.