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

View as plain text