Source file src/cmd/compile/internal/ssacompile/check.go

     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 ssacompile
     6  
     7  import (
     8  	"math"
     9  	"math/bits"
    10  
    11  	"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  )
    17  
    18  // checkFunc checks invariants of f.
    19  func checkFunc(f *ssa.Func) {
    20  	blockMark := make([]bool, f.NumBlocks())
    21  	valueMark := make([]bool, f.NumValues())
    22  
    23  	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] = true
    28  		if b.Func != f {
    29  			f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name)
    30  		}
    31  
    32  		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  		}
    42  
    43  		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  		}
   116  
   117  		for _, v := range b.Values {
   118  			// Check to make sure argument count makes sense (argLen of -1 indicates
   119  			// variable length args)
   120  			nArgs := ssaop.OpcodeTable[v.Op].ArgLen
   121  			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  			}
   125  
   126  			// Check to make sure aux values make sense.
   127  			canHaveAux := false
   128  			canHaveAuxInt := false
   129  			// 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 = true
   137  			case ssaop.AuxTypeInt8:
   138  				if v.AuxInt != int64(int8(v.AuxInt)) {
   139  					f.Fatalf("bad int8 AuxInt value for %v", v)
   140  				}
   141  				canHaveAuxInt = true
   142  			case ssaop.AuxTypeInt16:
   143  				if v.AuxInt != int64(int16(v.AuxInt)) {
   144  					f.Fatalf("bad int16 AuxInt value for %v", v)
   145  				}
   146  				canHaveAuxInt = true
   147  			case ssaop.AuxTypeInt32:
   148  				if v.AuxInt != int64(int32(v.AuxInt)) {
   149  					f.Fatalf("bad int32 AuxInt value for %v", v)
   150  				}
   151  				canHaveAuxInt = true
   152  			case ssaop.AuxTypeInt64, ssaop.AuxTypeARM64BitField, ssaop.AuxTypeARM64ConditionalParams:
   153  				canHaveAuxInt = true
   154  			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 = true
   162  			case ssaop.AuxTypeFloat32:
   163  				canHaveAuxInt = true
   164  				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 = true
   172  				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 = true
   180  			case ssaop.AuxTypeCallOff:
   181  				canHaveAuxInt = true
   182  				fallthrough
   183  			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 = true
   192  			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 = true
   197  				canHaveAuxInt = true
   198  			case ssaop.AuxTypeSym, ssaop.AuxTypeTyp:
   199  				canHaveAux = true
   200  			case ssaop.AuxTypeSymOff, ssaop.AuxTypeSymValAndOff, ssaop.AuxTypeTypSize:
   201  				canHaveAuxInt = true
   202  				canHaveAux = true
   203  			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 = true
   208  			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 = true
   213  			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 = true
   218  			case ssaop.AuxTypeFlagConstant:
   219  				if v.AuxInt < 0 || v.AuxInt > 15 {
   220  					f.Fatalf("bad FlagConstant AuxInt value for %v", v)
   221  				}
   222  				canHaveAuxInt = true
   223  			case ssaop.AuxTypePanicBoundsC, ssaop.AuxTypePanicBoundsCC:
   224  				canHaveAux = true
   225  				canHaveAuxInt = true
   226  			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  			}
   235  
   236  			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 present
   242  					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  			}
   247  
   248  			if valueMark[v.ID] {
   249  				f.Fatalf("value %s appears twice!", v.LongString())
   250  			}
   251  			valueMark[v.ID] = true
   252  
   253  			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  			}
   259  
   260  			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  			}
   268  
   269  			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  			}
   280  
   281  			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  			}
   284  
   285  			if f.RegAlloc != nil && f.Config.SoftFloat && v.Type.IsFloat() {
   286  				f.Fatalf("unexpected floating-point type %v", v.LongString())
   287  			}
   288  
   289  			// 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, and
   335  				// 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 int64
   360  			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 = 1
   368  			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 = 2
   376  			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 = 4
   383  			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 = 8
   389  			}
   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  			}
   397  
   398  			// TODO: check for cycles in values
   399  		}
   400  	}
   401  
   402  	// 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  	}
   418  
   419  	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  	}
   422  
   423  	// 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  	}
   448  
   449  	// 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.Block
   458  					y := b
   459  					if v.Op == ssaop.OpPhi {
   460  						y = b.Preds[i].B
   461  					}
   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  	}
   474  
   475  	// Check loop construction
   476  	if f.RegAlloc == nil && f.Pass != nil { // non-nil pass allows better-targeted debug printing
   477  		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  	}
   493  
   494  	// Check use counts
   495  	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  	}
   513  
   514  	memCheck(f)
   515  }
   516  
   517  func 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  	}
   526  
   527  	// 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  				return
   536  			}
   537  		}
   538  		if b != f.Entry && len(b.Preds) == 0 {
   539  			return
   540  		}
   541  	}
   542  
   543  	// 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 other
   548  		// ops that generate memory values.
   549  		ss.Clear()
   550  		for _, v := range b.Values {
   551  			if v.Op == ssaop.OpPhi || !v.Type.IsMemory() {
   552  				continue
   553  			}
   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  				continue
   562  			}
   563  			if ss.Contains(v.ID) {
   564  				continue
   565  			}
   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] = v
   570  		}
   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  					continue
   577  				}
   578  				m := v.MemoryArg()
   579  				if m == nil {
   580  					continue
   581  				}
   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] = m
   586  			}
   587  		}
   588  	}
   589  	// Propagate last live memory through storeless blocks.
   590  	for {
   591  		changed := false
   592  		for _, b := range f.Blocks {
   593  			if lastmem[b.ID] != nil {
   594  				continue
   595  			}
   596  			for _, e := range b.Preds {
   597  				p := e.B
   598  				if lastmem[p.ID] != nil {
   599  					lastmem[b.ID] = lastmem[p.ID]
   600  					changed = true
   601  					break
   602  				}
   603  			}
   604  		}
   605  		if !changed {
   606  			break
   607  		}
   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  	}
   621  
   622  	// 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 block
   626  			for _, v := range b.Values {
   627  				if v.Op == ssaop.OpPhi {
   628  					if v.Type.IsMemory() {
   629  						mem = v
   630  					}
   631  					continue
   632  				}
   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 = v
   644  				}
   645  			}
   646  		}
   647  	}
   648  
   649  	// Check that after scheduling, phis are always first in the block.
   650  	if f.Scheduled {
   651  		for _, b := range f.Blocks {
   652  			seenNonPhi := false
   653  			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 = true
   661  				}
   662  			}
   663  		}
   664  	}
   665  }
   666  
   667  // domCheck reports whether x dominates y (including x==y).
   668  func domCheck(f *ssa.Func, sdom ssa.SparseTree, x, y *ssa.Block) bool {
   669  	if !sdom.IsAncestorEq(f.Entry, y) {
   670  		// unreachable - ignore
   671  		return true
   672  	}
   673  	return sdom.IsAncestorEq(x, y)
   674  }
   675  
   676  // isExactFloat32 reports whether x can be exactly represented as a float32.
   677  func isExactFloat32(x float64) bool {
   678  	// Check the mantissa is in range.
   679  	if bits.TrailingZeros64(math.Float64bits(x)) < 52-23 {
   680  		return false
   681  	}
   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  }
   685  

View as plain text