Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/compile/internal/ssa/op.go

Documentation: cmd/compile/internal/ssa

     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/types"
     9  	"cmd/internal/obj"
    10  	"fmt"
    11  )
    12  
    13  // An Op encodes the specific operation that a Value performs.
    14  // Opcodes' semantics can be modified by the type and aux fields of the Value.
    15  // For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type.
    16  // Semantics of each op are described in the opcode files in gen/*Ops.go.
    17  // There is one file for generic (architecture-independent) ops and one file
    18  // for each architecture.
    19  type Op int32
    20  
    21  type opInfo struct {
    22  	name              string
    23  	reg               regInfo
    24  	auxType           auxType
    25  	argLen            int32 // the number of arguments, -1 if variable length
    26  	asm               obj.As
    27  	generic           bool      // this is a generic (arch-independent) opcode
    28  	rematerializeable bool      // this op is rematerializeable
    29  	commutative       bool      // this operation is commutative (e.g. addition)
    30  	resultInArg0      bool      // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register
    31  	resultNotInArgs   bool      // outputs must not be allocated to the same registers as inputs
    32  	clobberFlags      bool      // this op clobbers flags register
    33  	call              bool      // is a function call
    34  	nilCheck          bool      // this op is a nil check on arg0
    35  	faultOnNilArg0    bool      // this op will fault if arg0 is nil (and aux encodes a small offset)
    36  	faultOnNilArg1    bool      // this op will fault if arg1 is nil (and aux encodes a small offset)
    37  	usesScratch       bool      // this op requires scratch memory space
    38  	hasSideEffects    bool      // for "reasons", not to be eliminated.  E.g., atomic store, #19182.
    39  	zeroWidth         bool      // op never translates into any machine code. example: copy, which may sometimes translate to machine code, is not zero-width.
    40  	unsafePoint       bool      // this op is an unsafe point, i.e. not safe for async preemption
    41  	symEffect         SymEffect // effect this op has on symbol in aux
    42  	scale             uint8     // amd64/386 indexed load scale
    43  }
    44  
    45  type inputInfo struct {
    46  	idx  int     // index in Args array
    47  	regs regMask // allowed input registers
    48  }
    49  
    50  type outputInfo struct {
    51  	idx  int     // index in output tuple
    52  	regs regMask // allowed output registers
    53  }
    54  
    55  type regInfo struct {
    56  	// inputs encodes the register restrictions for an instruction's inputs.
    57  	// Each entry specifies an allowed register set for a particular input.
    58  	// They are listed in the order in which regalloc should pick a register
    59  	// from the register set (most constrained first).
    60  	// Inputs which do not need registers are not listed.
    61  	inputs []inputInfo
    62  	// clobbers encodes the set of registers that are overwritten by
    63  	// the instruction (other than the output registers).
    64  	clobbers regMask
    65  	// outputs is the same as inputs, but for the outputs of the instruction.
    66  	outputs []outputInfo
    67  }
    68  
    69  type auxType int8
    70  
    71  type Param struct {
    72  	Type   *types.Type
    73  	Offset int32 // TODO someday this will be a register
    74  }
    75  
    76  type AuxCall struct {
    77  	Fn      *obj.LSym
    78  	args    []Param // Includes receiver for method calls.  Does NOT include hidden closure pointer.
    79  	results []Param
    80  }
    81  
    82  // ResultForOffset returns the index of the result at a particular offset among the results
    83  // This does not include the mem result for the call opcode.
    84  func (a *AuxCall) ResultForOffset(offset int64) int64 {
    85  	which := int64(-1)
    86  	for i := int64(0); i < a.NResults(); i++ { // note aux NResults does not include mem result.
    87  		if a.OffsetOfResult(i) == offset {
    88  			which = i
    89  			break
    90  		}
    91  	}
    92  	return which
    93  }
    94  
    95  // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc).
    96  func (a *AuxCall) OffsetOfResult(which int64) int64 {
    97  	return int64(a.results[which].Offset)
    98  }
    99  
   100  // OffsetOfArg returns the SP offset of argument which (indexed 0, 1, etc).
   101  func (a *AuxCall) OffsetOfArg(which int64) int64 {
   102  	return int64(a.args[which].Offset)
   103  }
   104  
   105  // TypeOfResult returns the type of result which (indexed 0, 1, etc).
   106  func (a *AuxCall) TypeOfResult(which int64) *types.Type {
   107  	return a.results[which].Type
   108  }
   109  
   110  // TypeOfArg returns the type of argument which (indexed 0, 1, etc).
   111  func (a *AuxCall) TypeOfArg(which int64) *types.Type {
   112  	return a.args[which].Type
   113  }
   114  
   115  // SizeOfResult returns the size of result which (indexed 0, 1, etc).
   116  func (a *AuxCall) SizeOfResult(which int64) int64 {
   117  	return a.TypeOfResult(which).Width
   118  }
   119  
   120  // SizeOfArg returns the size of argument which (indexed 0, 1, etc).
   121  func (a *AuxCall) SizeOfArg(which int64) int64 {
   122  	return a.TypeOfArg(which).Width
   123  }
   124  
   125  // NResults returns the number of results
   126  func (a *AuxCall) NResults() int64 {
   127  	return int64(len(a.results))
   128  }
   129  
   130  // LateExpansionResultType returns the result type (including trailing mem)
   131  // for a call that will be expanded later in the SSA phase.
   132  func (a *AuxCall) LateExpansionResultType() *types.Type {
   133  	var tys []*types.Type
   134  	for i := int64(0); i < a.NResults(); i++ {
   135  		tys = append(tys, a.TypeOfResult(i))
   136  	}
   137  	tys = append(tys, types.TypeMem)
   138  	return types.NewResults(tys)
   139  }
   140  
   141  // NArgs returns the number of arguments
   142  func (a *AuxCall) NArgs() int64 {
   143  	return int64(len(a.args))
   144  }
   145  
   146  // String returns
   147  // "AuxCall{<fn>(<args>)}"             if len(results) == 0;
   148  // "AuxCall{<fn>(<args>)<results[0]>}" if len(results) == 1;
   149  // "AuxCall{<fn>(<args>)(<results>)}"  otherwise.
   150  func (a *AuxCall) String() string {
   151  	var fn string
   152  	if a.Fn == nil {
   153  		fn = "AuxCall{nil" // could be interface/closure etc.
   154  	} else {
   155  		fn = fmt.Sprintf("AuxCall{%v", a.Fn)
   156  	}
   157  
   158  	if len(a.args) == 0 {
   159  		fn += "()"
   160  	} else {
   161  		s := "("
   162  		for _, arg := range a.args {
   163  			fn += fmt.Sprintf("%s[%v,%v]", s, arg.Type, arg.Offset)
   164  			s = ","
   165  		}
   166  		fn += ")"
   167  	}
   168  
   169  	if len(a.results) > 0 { // usual is zero or one; only some RT calls have more than one.
   170  		if len(a.results) == 1 {
   171  			fn += fmt.Sprintf("[%v,%v]", a.results[0].Type, a.results[0].Offset)
   172  		} else {
   173  			s := "("
   174  			for _, result := range a.results {
   175  				fn += fmt.Sprintf("%s[%v,%v]", s, result.Type, result.Offset)
   176  				s = ","
   177  			}
   178  			fn += ")"
   179  		}
   180  	}
   181  
   182  	return fn + "}"
   183  }
   184  
   185  // StaticAuxCall returns an AuxCall for a static call.
   186  func StaticAuxCall(sym *obj.LSym, args []Param, results []Param) *AuxCall {
   187  	return &AuxCall{Fn: sym, args: args, results: results}
   188  }
   189  
   190  // InterfaceAuxCall returns an AuxCall for an interface call.
   191  func InterfaceAuxCall(args []Param, results []Param) *AuxCall {
   192  	return &AuxCall{Fn: nil, args: args, results: results}
   193  }
   194  
   195  // ClosureAuxCall returns an AuxCall for a closure call.
   196  func ClosureAuxCall(args []Param, results []Param) *AuxCall {
   197  	return &AuxCall{Fn: nil, args: args, results: results}
   198  }
   199  
   200  const (
   201  	auxNone         auxType = iota
   202  	auxBool                 // auxInt is 0/1 for false/true
   203  	auxInt8                 // auxInt is an 8-bit integer
   204  	auxInt16                // auxInt is a 16-bit integer
   205  	auxInt32                // auxInt is a 32-bit integer
   206  	auxInt64                // auxInt is a 64-bit integer
   207  	auxInt128               // auxInt represents a 128-bit integer.  Always 0.
   208  	auxUInt8                // auxInt is an 8-bit unsigned integer
   209  	auxFloat32              // auxInt is a float32 (encoded with math.Float64bits)
   210  	auxFloat64              // auxInt is a float64 (encoded with math.Float64bits)
   211  	auxFlagConstant         // auxInt is a flagConstant
   212  	auxString               // aux is a string
   213  	auxSym                  // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none)
   214  	auxSymOff               // aux is a symbol, auxInt is an offset
   215  	auxSymValAndOff         // aux is a symbol, auxInt is a ValAndOff
   216  	auxTyp                  // aux is a type
   217  	auxTypSize              // aux is a type, auxInt is a size, must have Aux.(Type).Size() == AuxInt
   218  	auxCCop                 // aux is a ssa.Op that represents a flags-to-bool conversion (e.g. LessThan)
   219  	auxCall                 // aux is a *ssa.AuxCall
   220  	auxCallOff              // aux is a *ssa.AuxCall, AuxInt is int64 param (in+out) size
   221  
   222  	// architecture specific aux types
   223  	auxARM64BitField     // aux is an arm64 bitfield lsb and width packed into auxInt
   224  	auxS390XRotateParams // aux is a s390x rotate parameters object encoding start bit, end bit and rotate amount
   225  	auxS390XCCMask       // aux is a s390x 4-bit condition code mask
   226  	auxS390XCCMaskInt8   // aux is a s390x 4-bit condition code mask, auxInt is a int8 immediate
   227  	auxS390XCCMaskUint8  // aux is a s390x 4-bit condition code mask, auxInt is a uint8 immediate
   228  )
   229  
   230  // A SymEffect describes the effect that an SSA Value has on the variable
   231  // identified by the symbol in its Aux field.
   232  type SymEffect int8
   233  
   234  const (
   235  	SymRead SymEffect = 1 << iota
   236  	SymWrite
   237  	SymAddr
   238  
   239  	SymRdWr = SymRead | SymWrite
   240  
   241  	SymNone SymEffect = 0
   242  )
   243  
   244  // A Sym represents a symbolic offset from a base register.
   245  // Currently a Sym can be one of 3 things:
   246  //  - a *gc.Node, for an offset from SP (the stack pointer)
   247  //  - a *obj.LSym, for an offset from SB (the global pointer)
   248  //  - nil, for no offset
   249  type Sym interface {
   250  	String() string
   251  	CanBeAnSSASym()
   252  }
   253  
   254  // A ValAndOff is used by the several opcodes. It holds
   255  // both a value and a pointer offset.
   256  // A ValAndOff is intended to be encoded into an AuxInt field.
   257  // The zero ValAndOff encodes a value of 0 and an offset of 0.
   258  // The high 32 bits hold a value.
   259  // The low 32 bits hold a pointer offset.
   260  type ValAndOff int64
   261  
   262  func (x ValAndOff) Val() int64   { return int64(x) >> 32 }
   263  func (x ValAndOff) Val32() int32 { return int32(int64(x) >> 32) }
   264  func (x ValAndOff) Val16() int16 { return int16(int64(x) >> 32) }
   265  func (x ValAndOff) Val8() int8   { return int8(int64(x) >> 32) }
   266  
   267  func (x ValAndOff) Off() int64   { return int64(int32(x)) }
   268  func (x ValAndOff) Off32() int32 { return int32(x) }
   269  
   270  func (x ValAndOff) String() string {
   271  	return fmt.Sprintf("val=%d,off=%d", x.Val(), x.Off())
   272  }
   273  
   274  // validVal reports whether the value can be used
   275  // as an argument to makeValAndOff.
   276  func validVal(val int64) bool {
   277  	return val == int64(int32(val))
   278  }
   279  
   280  // validOff reports whether the offset can be used
   281  // as an argument to makeValAndOff.
   282  func validOff(off int64) bool {
   283  	return off == int64(int32(off))
   284  }
   285  
   286  // validValAndOff reports whether we can fit the value and offset into
   287  // a ValAndOff value.
   288  func validValAndOff(val, off int64) bool {
   289  	if !validVal(val) {
   290  		return false
   291  	}
   292  	if !validOff(off) {
   293  		return false
   294  	}
   295  	return true
   296  }
   297  
   298  func makeValAndOff32(val, off int32) ValAndOff {
   299  	return ValAndOff(int64(val)<<32 + int64(uint32(off)))
   300  }
   301  func makeValAndOff64(val, off int64) ValAndOff {
   302  	if !validValAndOff(val, off) {
   303  		panic("invalid makeValAndOff64")
   304  	}
   305  	return ValAndOff(val<<32 + int64(uint32(off)))
   306  }
   307  
   308  func (x ValAndOff) canAdd32(off int32) bool {
   309  	newoff := x.Off() + int64(off)
   310  	return newoff == int64(int32(newoff))
   311  }
   312  func (x ValAndOff) canAdd64(off int64) bool {
   313  	newoff := x.Off() + off
   314  	return newoff == int64(int32(newoff))
   315  }
   316  
   317  func (x ValAndOff) addOffset32(off int32) ValAndOff {
   318  	if !x.canAdd32(off) {
   319  		panic("invalid ValAndOff.addOffset32")
   320  	}
   321  	return makeValAndOff64(x.Val(), x.Off()+int64(off))
   322  }
   323  func (x ValAndOff) addOffset64(off int64) ValAndOff {
   324  	if !x.canAdd64(off) {
   325  		panic("invalid ValAndOff.addOffset64")
   326  	}
   327  	return makeValAndOff64(x.Val(), x.Off()+off)
   328  }
   329  
   330  // int128 is a type that stores a 128-bit constant.
   331  // The only allowed constant right now is 0, so we can cheat quite a bit.
   332  type int128 int64
   333  
   334  type BoundsKind uint8
   335  
   336  const (
   337  	BoundsIndex       BoundsKind = iota // indexing operation, 0 <= idx < len failed
   338  	BoundsIndexU                        // ... with unsigned idx
   339  	BoundsSliceAlen                     // 2-arg slicing operation, 0 <= high <= len failed
   340  	BoundsSliceAlenU                    // ... with unsigned high
   341  	BoundsSliceAcap                     // 2-arg slicing operation, 0 <= high <= cap failed
   342  	BoundsSliceAcapU                    // ... with unsigned high
   343  	BoundsSliceB                        // 2-arg slicing operation, 0 <= low <= high failed
   344  	BoundsSliceBU                       // ... with unsigned low
   345  	BoundsSlice3Alen                    // 3-arg slicing operation, 0 <= max <= len failed
   346  	BoundsSlice3AlenU                   // ... with unsigned max
   347  	BoundsSlice3Acap                    // 3-arg slicing operation, 0 <= max <= cap failed
   348  	BoundsSlice3AcapU                   // ... with unsigned max
   349  	BoundsSlice3B                       // 3-arg slicing operation, 0 <= high <= max failed
   350  	BoundsSlice3BU                      // ... with unsigned high
   351  	BoundsSlice3C                       // 3-arg slicing operation, 0 <= low <= high failed
   352  	BoundsSlice3CU                      // ... with unsigned low
   353  	BoundsKindCount
   354  )
   355  
   356  // boundsAPI determines which register arguments a bounds check call should use. For an [a:b:c] slice, we do:
   357  //   CMPQ c, cap
   358  //   JA   fail1
   359  //   CMPQ b, c
   360  //   JA   fail2
   361  //   CMPQ a, b
   362  //   JA   fail3
   363  //
   364  // fail1: CALL panicSlice3Acap (c, cap)
   365  // fail2: CALL panicSlice3B (b, c)
   366  // fail3: CALL panicSlice3C (a, b)
   367  //
   368  // When we register allocate that code, we want the same register to be used for
   369  // the first arg of panicSlice3Acap and the second arg to panicSlice3B. That way,
   370  // initializing that register once will satisfy both calls.
   371  // That desire ends up dividing the set of bounds check calls into 3 sets. This function
   372  // determines which set to use for a given panic call.
   373  // The first arg for set 0 should be the second arg for set 1.
   374  // The first arg for set 1 should be the second arg for set 2.
   375  func boundsABI(b int64) int {
   376  	switch BoundsKind(b) {
   377  	case BoundsSlice3Alen,
   378  		BoundsSlice3AlenU,
   379  		BoundsSlice3Acap,
   380  		BoundsSlice3AcapU:
   381  		return 0
   382  	case BoundsSliceAlen,
   383  		BoundsSliceAlenU,
   384  		BoundsSliceAcap,
   385  		BoundsSliceAcapU,
   386  		BoundsSlice3B,
   387  		BoundsSlice3BU:
   388  		return 1
   389  	case BoundsIndex,
   390  		BoundsIndexU,
   391  		BoundsSliceB,
   392  		BoundsSliceBU,
   393  		BoundsSlice3C,
   394  		BoundsSlice3CU:
   395  		return 2
   396  	default:
   397  		panic("bad BoundsKind")
   398  	}
   399  }
   400  
   401  // arm64BitFileld is the GO type of ARM64BitField auxInt.
   402  // if x is an ARM64BitField, then width=x&0xff, lsb=(x>>8)&0xff, and
   403  // width+lsb<64 for 64-bit variant, width+lsb<32 for 32-bit variant.
   404  // the meaning of width and lsb are instruction-dependent.
   405  type arm64BitField int16
   406  

View as plain text