Source file src/cmd/compile/internal/ssa/value.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 ssa
     6  
     7  import (
     8  	"cmd/compile/internal/ir"
     9  	"cmd/compile/internal/types"
    10  	"cmd/internal/src"
    11  	"fmt"
    12  	"math"
    13  	"sort"
    14  	"strings"
    15  )
    16  
    17  // A Value represents a value in the SSA representation of the program.
    18  // The ID and Type fields must not be modified. The remainder may be modified
    19  // if they preserve the value of the Value (e.g. changing a (mul 2 x) to an (add x x)).
    20  type Value struct {
    21  	// A unique identifier for the value. For performance we allocate these IDs
    22  	// densely starting at 1.  There is no guarantee that there won't be occasional holes, though.
    23  	ID ID
    24  
    25  	// The operation that computes this value. See op.go.
    26  	Op Op
    27  
    28  	// The type of this value. Normally this will be a Go type, but there
    29  	// are a few other pseudo-types, see ../types/type.go.
    30  	Type *types.Type
    31  
    32  	// Auxiliary info for this value. The type of this information depends on the opcode and type.
    33  	// AuxInt is used for integer values, Aux is used for other values.
    34  	// Floats are stored in AuxInt using math.Float64bits(f).
    35  	// Unused portions of AuxInt are filled by sign-extending the used portion,
    36  	// even if the represented value is unsigned.
    37  	// Users of AuxInt which interpret AuxInt as unsigned (e.g. shifts) must be careful.
    38  	// Use Value.AuxUnsigned to get the zero-extended value of AuxInt.
    39  	AuxInt int64
    40  	Aux    Aux
    41  
    42  	// Arguments of this value
    43  	Args []*Value
    44  
    45  	// Containing basic block
    46  	Block *Block
    47  
    48  	// Source position
    49  	Pos src.XPos
    50  
    51  	// Use count. Each appearance in Value.Args and Block.Controls counts once.
    52  	Uses int32
    53  
    54  	// wasm: Value stays on the WebAssembly stack. This value will not get a "register" (WebAssembly variable)
    55  	// nor a slot on Go stack, and the generation of this value is delayed to its use time.
    56  	OnWasmStack bool
    57  
    58  	// Is this value in the per-function constant cache? If so, remove from cache before changing it or recycling it.
    59  	InCache bool
    60  
    61  	// Storage for the first three args
    62  	argstorage [3]*Value
    63  }
    64  
    65  // Examples:
    66  // Opcode          aux   args
    67  //  OpAdd          nil      2
    68  //  OpConst     string      0    string constant
    69  //  OpConst      int64      0    int64 constant
    70  //  OpAddcq      int64      1    amd64 op: v = arg[0] + constant
    71  
    72  // short form print. Just v#.
    73  func (v *Value) String() string {
    74  	if v == nil {
    75  		return "nil" // should never happen, but not panicking helps with debugging
    76  	}
    77  	return fmt.Sprintf("v%d", v.ID)
    78  }
    79  
    80  func (v *Value) AuxInt8() int8 {
    81  	if opcodeTable[v.Op].auxType != auxInt8 && opcodeTable[v.Op].auxType != auxNameOffsetInt8 {
    82  		v.Fatalf("op %s doesn't have an int8 aux field", v.Op)
    83  	}
    84  	return int8(v.AuxInt)
    85  }
    86  
    87  func (v *Value) AuxUInt8() uint8 {
    88  	if opcodeTable[v.Op].auxType != auxUInt8 {
    89  		v.Fatalf("op %s doesn't have a uint8 aux field", v.Op)
    90  	}
    91  	return uint8(v.AuxInt)
    92  }
    93  
    94  func (v *Value) AuxInt16() int16 {
    95  	if opcodeTable[v.Op].auxType != auxInt16 {
    96  		v.Fatalf("op %s doesn't have an int16 aux field", v.Op)
    97  	}
    98  	return int16(v.AuxInt)
    99  }
   100  
   101  func (v *Value) AuxInt32() int32 {
   102  	if opcodeTable[v.Op].auxType != auxInt32 {
   103  		v.Fatalf("op %s doesn't have an int32 aux field", v.Op)
   104  	}
   105  	return int32(v.AuxInt)
   106  }
   107  
   108  // AuxUnsigned returns v.AuxInt as an unsigned value for OpConst*.
   109  // v.AuxInt is always sign-extended to 64 bits, even if the
   110  // represented value is unsigned. This undoes that sign extension.
   111  func (v *Value) AuxUnsigned() uint64 {
   112  	c := v.AuxInt
   113  	switch v.Op {
   114  	case OpConst64:
   115  		return uint64(c)
   116  	case OpConst32:
   117  		return uint64(uint32(c))
   118  	case OpConst16:
   119  		return uint64(uint16(c))
   120  	case OpConst8:
   121  		return uint64(uint8(c))
   122  	}
   123  	v.Fatalf("op %s isn't OpConst*", v.Op)
   124  	return 0
   125  }
   126  
   127  func (v *Value) AuxFloat() float64 {
   128  	if opcodeTable[v.Op].auxType != auxFloat32 && opcodeTable[v.Op].auxType != auxFloat64 {
   129  		v.Fatalf("op %s doesn't have a float aux field", v.Op)
   130  	}
   131  	return math.Float64frombits(uint64(v.AuxInt))
   132  }
   133  func (v *Value) AuxValAndOff() ValAndOff {
   134  	if opcodeTable[v.Op].auxType != auxSymValAndOff {
   135  		v.Fatalf("op %s doesn't have a ValAndOff aux field", v.Op)
   136  	}
   137  	return ValAndOff(v.AuxInt)
   138  }
   139  
   140  func (v *Value) AuxArm64BitField() arm64BitField {
   141  	if opcodeTable[v.Op].auxType != auxARM64BitField {
   142  		v.Fatalf("op %s doesn't have a ARM64BitField aux field", v.Op)
   143  	}
   144  	return arm64BitField(v.AuxInt)
   145  }
   146  
   147  // long form print.  v# = opcode <type> [aux] args [: reg] (names)
   148  func (v *Value) LongString() string {
   149  	if v == nil {
   150  		return "<NIL VALUE>"
   151  	}
   152  	s := fmt.Sprintf("v%d = %s", v.ID, v.Op)
   153  	s += " <" + v.Type.String() + ">"
   154  	s += v.auxString()
   155  	for _, a := range v.Args {
   156  		s += fmt.Sprintf(" %v", a)
   157  	}
   158  	if v.Block == nil {
   159  		return s
   160  	}
   161  	r := v.Block.Func.RegAlloc
   162  	if int(v.ID) < len(r) && r[v.ID] != nil {
   163  		s += " : " + r[v.ID].String()
   164  	}
   165  	if reg := v.Block.Func.tempRegs[v.ID]; reg != nil {
   166  		s += " tmp=" + reg.String()
   167  	}
   168  	var names []string
   169  	for name, values := range v.Block.Func.NamedValues {
   170  		for _, value := range values {
   171  			if value == v {
   172  				names = append(names, name.String())
   173  				break // drop duplicates.
   174  			}
   175  		}
   176  	}
   177  	if len(names) != 0 {
   178  		sort.Strings(names) // Otherwise a source of variation in debugging output.
   179  		s += " (" + strings.Join(names, ", ") + ")"
   180  	}
   181  	return s
   182  }
   183  
   184  func (v *Value) auxString() string {
   185  	switch opcodeTable[v.Op].auxType {
   186  	case auxBool:
   187  		if v.AuxInt == 0 {
   188  			return " [false]"
   189  		} else {
   190  			return " [true]"
   191  		}
   192  	case auxInt8:
   193  		return fmt.Sprintf(" [%d]", v.AuxInt8())
   194  	case auxInt16:
   195  		return fmt.Sprintf(" [%d]", v.AuxInt16())
   196  	case auxInt32:
   197  		return fmt.Sprintf(" [%d]", v.AuxInt32())
   198  	case auxInt64, auxInt128:
   199  		return fmt.Sprintf(" [%d]", v.AuxInt)
   200  	case auxUInt8:
   201  		return fmt.Sprintf(" [%d]", v.AuxUInt8())
   202  	case auxARM64BitField:
   203  		lsb := v.AuxArm64BitField().lsb()
   204  		width := v.AuxArm64BitField().width()
   205  		return fmt.Sprintf(" [lsb=%d,width=%d]", lsb, width)
   206  	case auxFloat32, auxFloat64:
   207  		return fmt.Sprintf(" [%g]", v.AuxFloat())
   208  	case auxString:
   209  		return fmt.Sprintf(" {%q}", v.Aux)
   210  	case auxSym, auxCall, auxTyp:
   211  		if v.Aux != nil {
   212  			return fmt.Sprintf(" {%v}", v.Aux)
   213  		}
   214  		return ""
   215  	case auxSymOff, auxCallOff, auxTypSize, auxNameOffsetInt8:
   216  		s := ""
   217  		if v.Aux != nil {
   218  			s = fmt.Sprintf(" {%v}", v.Aux)
   219  		}
   220  		if v.AuxInt != 0 || opcodeTable[v.Op].auxType == auxNameOffsetInt8 {
   221  			s += fmt.Sprintf(" [%v]", v.AuxInt)
   222  		}
   223  		return s
   224  	case auxSymValAndOff:
   225  		s := ""
   226  		if v.Aux != nil {
   227  			s = fmt.Sprintf(" {%v}", v.Aux)
   228  		}
   229  		return s + fmt.Sprintf(" [%s]", v.AuxValAndOff())
   230  	case auxCCop:
   231  		return fmt.Sprintf(" [%s]", Op(v.AuxInt))
   232  	case auxS390XCCMask, auxS390XRotateParams:
   233  		return fmt.Sprintf(" {%v}", v.Aux)
   234  	case auxFlagConstant:
   235  		return fmt.Sprintf("[%s]", flagConstant(v.AuxInt))
   236  	case auxNone:
   237  		return ""
   238  	default:
   239  		// If you see this, add a case above instead.
   240  		return fmt.Sprintf("[auxtype=%d AuxInt=%d Aux=%v]", opcodeTable[v.Op].auxType, v.AuxInt, v.Aux)
   241  	}
   242  }
   243  
   244  // If/when midstack inlining is enabled (-l=4), the compiler gets both larger and slower.
   245  // Not-inlining this method is a help (*Value.reset and *Block.NewValue0 are similar).
   246  //
   247  //go:noinline
   248  func (v *Value) AddArg(w *Value) {
   249  	if v.Args == nil {
   250  		v.resetArgs() // use argstorage
   251  	}
   252  	v.Args = append(v.Args, w)
   253  	w.Uses++
   254  }
   255  
   256  //go:noinline
   257  func (v *Value) AddArg2(w1, w2 *Value) {
   258  	if v.Args == nil {
   259  		v.resetArgs() // use argstorage
   260  	}
   261  	v.Args = append(v.Args, w1, w2)
   262  	w1.Uses++
   263  	w2.Uses++
   264  }
   265  
   266  //go:noinline
   267  func (v *Value) AddArg3(w1, w2, w3 *Value) {
   268  	if v.Args == nil {
   269  		v.resetArgs() // use argstorage
   270  	}
   271  	v.Args = append(v.Args, w1, w2, w3)
   272  	w1.Uses++
   273  	w2.Uses++
   274  	w3.Uses++
   275  }
   276  
   277  //go:noinline
   278  func (v *Value) AddArg4(w1, w2, w3, w4 *Value) {
   279  	v.Args = append(v.Args, w1, w2, w3, w4)
   280  	w1.Uses++
   281  	w2.Uses++
   282  	w3.Uses++
   283  	w4.Uses++
   284  }
   285  
   286  //go:noinline
   287  func (v *Value) AddArg5(w1, w2, w3, w4, w5 *Value) {
   288  	v.Args = append(v.Args, w1, w2, w3, w4, w5)
   289  	w1.Uses++
   290  	w2.Uses++
   291  	w3.Uses++
   292  	w4.Uses++
   293  	w5.Uses++
   294  }
   295  
   296  //go:noinline
   297  func (v *Value) AddArg6(w1, w2, w3, w4, w5, w6 *Value) {
   298  	v.Args = append(v.Args, w1, w2, w3, w4, w5, w6)
   299  	w1.Uses++
   300  	w2.Uses++
   301  	w3.Uses++
   302  	w4.Uses++
   303  	w5.Uses++
   304  	w6.Uses++
   305  }
   306  
   307  func (v *Value) AddArgs(a ...*Value) {
   308  	if v.Args == nil {
   309  		v.resetArgs() // use argstorage
   310  	}
   311  	v.Args = append(v.Args, a...)
   312  	for _, x := range a {
   313  		x.Uses++
   314  	}
   315  }
   316  func (v *Value) SetArg(i int, w *Value) {
   317  	v.Args[i].Uses--
   318  	v.Args[i] = w
   319  	w.Uses++
   320  }
   321  func (v *Value) SetArgs1(a *Value) {
   322  	v.resetArgs()
   323  	v.AddArg(a)
   324  }
   325  func (v *Value) SetArgs2(a, b *Value) {
   326  	v.resetArgs()
   327  	v.AddArg(a)
   328  	v.AddArg(b)
   329  }
   330  func (v *Value) SetArgs3(a, b, c *Value) {
   331  	v.resetArgs()
   332  	v.AddArg(a)
   333  	v.AddArg(b)
   334  	v.AddArg(c)
   335  }
   336  func (v *Value) SetArgs4(a, b, c, d *Value) {
   337  	v.resetArgs()
   338  	v.AddArg(a)
   339  	v.AddArg(b)
   340  	v.AddArg(c)
   341  	v.AddArg(d)
   342  }
   343  
   344  func (v *Value) resetArgs() {
   345  	for _, a := range v.Args {
   346  		a.Uses--
   347  	}
   348  	v.argstorage[0] = nil
   349  	v.argstorage[1] = nil
   350  	v.argstorage[2] = nil
   351  	v.Args = v.argstorage[:0]
   352  }
   353  
   354  // reset is called from most rewrite rules.
   355  // Allowing it to be inlined increases the size
   356  // of cmd/compile by almost 10%, and slows it down.
   357  //
   358  //go:noinline
   359  func (v *Value) reset(op Op) {
   360  	if v.InCache {
   361  		v.Block.Func.unCache(v)
   362  	}
   363  	v.Op = op
   364  	v.resetArgs()
   365  	v.AuxInt = 0
   366  	v.Aux = nil
   367  }
   368  
   369  // invalidateRecursively marks a value as invalid (unused)
   370  // and after decrementing reference counts on its Args,
   371  // also recursively invalidates any of those whose use
   372  // count goes to zero.  It returns whether any of the
   373  // invalidated values was marked with IsStmt.
   374  //
   375  // BEWARE of doing this *before* you've applied intended
   376  // updates to SSA.
   377  func (v *Value) invalidateRecursively() bool {
   378  	lostStmt := v.Pos.IsStmt() == src.PosIsStmt
   379  	if v.InCache {
   380  		v.Block.Func.unCache(v)
   381  	}
   382  	v.Op = OpInvalid
   383  
   384  	for _, a := range v.Args {
   385  		a.Uses--
   386  		if a.Uses == 0 {
   387  			lost := a.invalidateRecursively()
   388  			lostStmt = lost || lostStmt
   389  		}
   390  	}
   391  
   392  	v.argstorage[0] = nil
   393  	v.argstorage[1] = nil
   394  	v.argstorage[2] = nil
   395  	v.Args = v.argstorage[:0]
   396  
   397  	v.AuxInt = 0
   398  	v.Aux = nil
   399  	return lostStmt
   400  }
   401  
   402  // copyOf is called from rewrite rules.
   403  // It modifies v to be (Copy a).
   404  //
   405  //go:noinline
   406  func (v *Value) copyOf(a *Value) {
   407  	if v == a {
   408  		return
   409  	}
   410  	if v.InCache {
   411  		v.Block.Func.unCache(v)
   412  	}
   413  	v.Op = OpCopy
   414  	v.resetArgs()
   415  	v.AddArg(a)
   416  	v.AuxInt = 0
   417  	v.Aux = nil
   418  	v.Type = a.Type
   419  }
   420  
   421  // copyInto makes a new value identical to v and adds it to the end of b.
   422  // unlike copyIntoWithXPos this does not check for v.Pos being a statement.
   423  func (v *Value) copyInto(b *Block) *Value {
   424  	c := b.NewValue0(v.Pos.WithNotStmt(), v.Op, v.Type) // Lose the position, this causes line number churn otherwise.
   425  	c.Aux = v.Aux
   426  	c.AuxInt = v.AuxInt
   427  	c.AddArgs(v.Args...)
   428  	for _, a := range v.Args {
   429  		if a.Type.IsMemory() {
   430  			v.Fatalf("can't move a value with a memory arg %s", v.LongString())
   431  		}
   432  	}
   433  	return c
   434  }
   435  
   436  // copyIntoWithXPos makes a new value identical to v and adds it to the end of b.
   437  // The supplied position is used as the position of the new value.
   438  // Because this is used for rematerialization, check for case that (rematerialized)
   439  // input to value with position 'pos' carried a statement mark, and that the supplied
   440  // position (of the instruction using the rematerialized value) is not marked, and
   441  // preserve that mark if its line matches the supplied position.
   442  func (v *Value) copyIntoWithXPos(b *Block, pos src.XPos) *Value {
   443  	if v.Pos.IsStmt() == src.PosIsStmt && pos.IsStmt() != src.PosIsStmt && v.Pos.SameFileAndLine(pos) {
   444  		pos = pos.WithIsStmt()
   445  	}
   446  	c := b.NewValue0(pos, v.Op, v.Type)
   447  	c.Aux = v.Aux
   448  	c.AuxInt = v.AuxInt
   449  	c.AddArgs(v.Args...)
   450  	for _, a := range v.Args {
   451  		if a.Type.IsMemory() {
   452  			v.Fatalf("can't move a value with a memory arg %s", v.LongString())
   453  		}
   454  	}
   455  	return c
   456  }
   457  
   458  func (v *Value) Logf(msg string, args ...interface{}) { v.Block.Logf(msg, args...) }
   459  func (v *Value) Log() bool                            { return v.Block.Log() }
   460  func (v *Value) Fatalf(msg string, args ...interface{}) {
   461  	v.Block.Func.fe.Fatalf(v.Pos, msg, args...)
   462  }
   463  
   464  // isGenericIntConst reports whether v is a generic integer constant.
   465  func (v *Value) isGenericIntConst() bool {
   466  	return v != nil && (v.Op == OpConst64 || v.Op == OpConst32 || v.Op == OpConst16 || v.Op == OpConst8)
   467  }
   468  
   469  // ResultReg returns the result register assigned to v, in cmd/internal/obj/$ARCH numbering.
   470  // It is similar to Reg and Reg0, except that it is usable interchangeably for all Value Ops.
   471  // If you know v.Op, using Reg or Reg0 (as appropriate) will be more efficient.
   472  func (v *Value) ResultReg() int16 {
   473  	reg := v.Block.Func.RegAlloc[v.ID]
   474  	if reg == nil {
   475  		v.Fatalf("nil reg for value: %s\n%s\n", v.LongString(), v.Block.Func)
   476  	}
   477  	if pair, ok := reg.(LocPair); ok {
   478  		reg = pair[0]
   479  	}
   480  	if reg == nil {
   481  		v.Fatalf("nil reg0 for value: %s\n%s\n", v.LongString(), v.Block.Func)
   482  	}
   483  	return reg.(*Register).objNum
   484  }
   485  
   486  // Reg returns the register assigned to v, in cmd/internal/obj/$ARCH numbering.
   487  func (v *Value) Reg() int16 {
   488  	reg := v.Block.Func.RegAlloc[v.ID]
   489  	if reg == nil {
   490  		v.Fatalf("nil register for value: %s\n%s\n", v.LongString(), v.Block.Func)
   491  	}
   492  	return reg.(*Register).objNum
   493  }
   494  
   495  // Reg0 returns the register assigned to the first output of v, in cmd/internal/obj/$ARCH numbering.
   496  func (v *Value) Reg0() int16 {
   497  	reg := v.Block.Func.RegAlloc[v.ID].(LocPair)[0]
   498  	if reg == nil {
   499  		v.Fatalf("nil first register for value: %s\n%s\n", v.LongString(), v.Block.Func)
   500  	}
   501  	return reg.(*Register).objNum
   502  }
   503  
   504  // Reg1 returns the register assigned to the second output of v, in cmd/internal/obj/$ARCH numbering.
   505  func (v *Value) Reg1() int16 {
   506  	reg := v.Block.Func.RegAlloc[v.ID].(LocPair)[1]
   507  	if reg == nil {
   508  		v.Fatalf("nil second register for value: %s\n%s\n", v.LongString(), v.Block.Func)
   509  	}
   510  	return reg.(*Register).objNum
   511  }
   512  
   513  // RegTmp returns the temporary register assigned to v, in cmd/internal/obj/$ARCH numbering.
   514  func (v *Value) RegTmp() int16 {
   515  	reg := v.Block.Func.tempRegs[v.ID]
   516  	if reg == nil {
   517  		v.Fatalf("nil tmp register for value: %s\n%s\n", v.LongString(), v.Block.Func)
   518  	}
   519  	return reg.objNum
   520  }
   521  
   522  func (v *Value) RegName() string {
   523  	reg := v.Block.Func.RegAlloc[v.ID]
   524  	if reg == nil {
   525  		v.Fatalf("nil register for value: %s\n%s\n", v.LongString(), v.Block.Func)
   526  	}
   527  	return reg.(*Register).name
   528  }
   529  
   530  // MemoryArg returns the memory argument for the Value.
   531  // The returned value, if non-nil, will be memory-typed (or a tuple with a memory-typed second part).
   532  // Otherwise, nil is returned.
   533  func (v *Value) MemoryArg() *Value {
   534  	if v.Op == OpPhi {
   535  		v.Fatalf("MemoryArg on Phi")
   536  	}
   537  	na := len(v.Args)
   538  	if na == 0 {
   539  		return nil
   540  	}
   541  	if m := v.Args[na-1]; m.Type.IsMemory() {
   542  		return m
   543  	}
   544  	return nil
   545  }
   546  
   547  // LackingPos indicates whether v is a value that is unlikely to have a correct
   548  // position assigned to it.  Ignoring such values leads to more user-friendly positions
   549  // assigned to nearby values and the blocks containing them.
   550  func (v *Value) LackingPos() bool {
   551  	// The exact definition of LackingPos is somewhat heuristically defined and may change
   552  	// in the future, for example if some of these operations are generated more carefully
   553  	// with respect to their source position.
   554  	return v.Op == OpVarDef || v.Op == OpVarLive || v.Op == OpPhi ||
   555  		(v.Op == OpFwdRef || v.Op == OpCopy) && v.Type == types.TypeMem
   556  }
   557  
   558  // removeable reports whether the value v can be removed from the SSA graph entirely
   559  // if its use count drops to 0.
   560  func (v *Value) removeable() bool {
   561  	if v.Type.IsVoid() {
   562  		// Void ops (inline marks), must stay.
   563  		return false
   564  	}
   565  	if opcodeTable[v.Op].nilCheck {
   566  		// Nil pointer checks must stay.
   567  		return false
   568  	}
   569  	if v.Type.IsMemory() {
   570  		// We don't need to preserve all memory ops, but we do need
   571  		// to keep calls at least (because they might have
   572  		// synchronization operations we can't see).
   573  		return false
   574  	}
   575  	if v.Op.HasSideEffects() {
   576  		// These are mostly synchronization operations.
   577  		return false
   578  	}
   579  	return true
   580  }
   581  
   582  // AutoVar returns a *Name and int64 representing the auto variable and offset within it
   583  // where v should be spilled.
   584  func AutoVar(v *Value) (*ir.Name, int64) {
   585  	if loc, ok := v.Block.Func.RegAlloc[v.ID].(LocalSlot); ok {
   586  		if v.Type.Size() > loc.Type.Size() {
   587  			v.Fatalf("spill/restore type %s doesn't fit in slot type %s", v.Type, loc.Type)
   588  		}
   589  		return loc.N, loc.Off
   590  	}
   591  	// Assume it is a register, return its spill slot, which needs to be live
   592  	nameOff := v.Aux.(*AuxNameOffset)
   593  	return nameOff.Name, nameOff.Offset
   594  }
   595  
   596  // CanSSA reports whether values of type t can be represented as a Value.
   597  func CanSSA(t *types.Type) bool {
   598  	types.CalcSize(t)
   599  	if t.Size() > int64(4*types.PtrSize) {
   600  		// 4*Widthptr is an arbitrary constant. We want it
   601  		// to be at least 3*Widthptr so slices can be registerized.
   602  		// Too big and we'll introduce too much register pressure.
   603  		return false
   604  	}
   605  	switch t.Kind() {
   606  	case types.TARRAY:
   607  		// We can't do larger arrays because dynamic indexing is
   608  		// not supported on SSA variables.
   609  		// TODO: allow if all indexes are constant.
   610  		if t.NumElem() <= 1 {
   611  			return CanSSA(t.Elem())
   612  		}
   613  		return false
   614  	case types.TSTRUCT:
   615  		if t.NumFields() > MaxStruct {
   616  			return false
   617  		}
   618  		for _, t1 := range t.Fields() {
   619  			if !CanSSA(t1.Type) {
   620  				return false
   621  			}
   622  		}
   623  		return true
   624  	default:
   625  		return true
   626  	}
   627  }
   628  

View as plain text