Source file src/cmd/compile/internal/ssa/op.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/abi"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/types"
    11  	"cmd/internal/obj"
    12  	"fmt"
    13  	"strings"
    14  )
    15  
    16  // An Op encodes the specific operation that a Value performs.
    17  // Opcodes' semantics can be modified by the type and aux fields of the Value.
    18  // For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type.
    19  // Semantics of each op are described in the opcode files in _gen/*Ops.go.
    20  // There is one file for generic (architecture-independent) ops and one file
    21  // for each architecture.
    22  type Op int32
    23  
    24  type opInfo struct {
    25  	name              string
    26  	reg               regInfo
    27  	auxType           auxType
    28  	argLen            int32 // the number of arguments, -1 if variable length
    29  	asm               obj.As
    30  	generic           bool      // this is a generic (arch-independent) opcode
    31  	rematerializeable bool      // this op is rematerializeable
    32  	commutative       bool      // this operation is commutative (e.g. addition)
    33  	resultInArg0      bool      // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register
    34  	resultNotInArgs   bool      // outputs must not be allocated to the same registers as inputs
    35  	clobberFlags      bool      // this op clobbers flags register
    36  	needIntTemp       bool      // need a temporary free integer register
    37  	call              bool      // is a function call
    38  	tailCall          bool      // is a tail call
    39  	nilCheck          bool      // this op is a nil check on arg0
    40  	faultOnNilArg0    bool      // this op will fault if arg0 is nil (and aux encodes a small offset)
    41  	faultOnNilArg1    bool      // this op will fault if arg1 is nil (and aux encodes a small offset)
    42  	usesScratch       bool      // this op requires scratch memory space
    43  	hasSideEffects    bool      // for "reasons", not to be eliminated.  E.g., atomic store, #19182.
    44  	zeroWidth         bool      // op never translates into any machine code. example: copy, which may sometimes translate to machine code, is not zero-width.
    45  	unsafePoint       bool      // this op is an unsafe point, i.e. not safe for async preemption
    46  	symEffect         SymEffect // effect this op has on symbol in aux
    47  	scale             uint8     // amd64/386 indexed load scale
    48  }
    49  
    50  type inputInfo struct {
    51  	idx  int     // index in Args array
    52  	regs regMask // allowed input registers
    53  }
    54  
    55  type outputInfo struct {
    56  	idx  int     // index in output tuple
    57  	regs regMask // allowed output registers
    58  }
    59  
    60  type regInfo struct {
    61  	// inputs encodes the register restrictions for an instruction's inputs.
    62  	// Each entry specifies an allowed register set for a particular input.
    63  	// They are listed in the order in which regalloc should pick a register
    64  	// from the register set (most constrained first).
    65  	// Inputs which do not need registers are not listed.
    66  	inputs []inputInfo
    67  	// clobbers encodes the set of registers that are overwritten by
    68  	// the instruction (other than the output registers).
    69  	clobbers regMask
    70  	// outputs is the same as inputs, but for the outputs of the instruction.
    71  	outputs []outputInfo
    72  }
    73  
    74  func (r *regInfo) String() string {
    75  	s := ""
    76  	s += "INS:\n"
    77  	for _, i := range r.inputs {
    78  		mask := fmt.Sprintf("%64b", i.regs)
    79  		mask = strings.Replace(mask, "0", ".", -1)
    80  		s += fmt.Sprintf("%2d |%s|\n", i.idx, mask)
    81  	}
    82  	s += "OUTS:\n"
    83  	for _, i := range r.outputs {
    84  		mask := fmt.Sprintf("%64b", i.regs)
    85  		mask = strings.Replace(mask, "0", ".", -1)
    86  		s += fmt.Sprintf("%2d |%s|\n", i.idx, mask)
    87  	}
    88  	s += "CLOBBERS:\n"
    89  	mask := fmt.Sprintf("%64b", r.clobbers)
    90  	mask = strings.Replace(mask, "0", ".", -1)
    91  	s += fmt.Sprintf("   |%s|\n", mask)
    92  	return s
    93  }
    94  
    95  type auxType int8
    96  
    97  type AuxNameOffset struct {
    98  	Name   *ir.Name
    99  	Offset int64
   100  }
   101  
   102  func (a *AuxNameOffset) CanBeAnSSAAux() {}
   103  func (a *AuxNameOffset) String() string {
   104  	return fmt.Sprintf("%s+%d", a.Name.Sym().Name, a.Offset)
   105  }
   106  
   107  func (a *AuxNameOffset) FrameOffset() int64 {
   108  	return a.Name.FrameOffset() + a.Offset
   109  }
   110  
   111  type AuxCall struct {
   112  	Fn      *obj.LSym
   113  	reg     *regInfo // regInfo for this call
   114  	abiInfo *abi.ABIParamResultInfo
   115  }
   116  
   117  // Reg returns the regInfo for a given call, combining the derived in/out register masks
   118  // with the machine-specific register information in the input i.  (The machine-specific
   119  // regInfo is much handier at the call site than it is when the AuxCall is being constructed,
   120  // therefore do this lazily).
   121  //
   122  // TODO: there is a Clever Hack that allows pre-generation of a small-ish number of the slices
   123  // of inputInfo and outputInfo used here, provided that we are willing to reorder the inputs
   124  // and outputs from calls, so that all integer registers come first, then all floating registers.
   125  // At this point (active development of register ABI) that is very premature,
   126  // but if this turns out to be a cost, we could do it.
   127  func (a *AuxCall) Reg(i *regInfo, c *Config) *regInfo {
   128  	if a.reg.clobbers != 0 {
   129  		// Already updated
   130  		return a.reg
   131  	}
   132  	if a.abiInfo.InRegistersUsed()+a.abiInfo.OutRegistersUsed() == 0 {
   133  		// Shortcut for zero case, also handles old ABI.
   134  		a.reg = i
   135  		return a.reg
   136  	}
   137  
   138  	k := len(i.inputs)
   139  	for _, p := range a.abiInfo.InParams() {
   140  		for _, r := range p.Registers {
   141  			m := archRegForAbiReg(r, c)
   142  			a.reg.inputs = append(a.reg.inputs, inputInfo{idx: k, regs: (1 << m)})
   143  			k++
   144  		}
   145  	}
   146  	a.reg.inputs = append(a.reg.inputs, i.inputs...) // These are less constrained, thus should come last
   147  	k = len(i.outputs)
   148  	for _, p := range a.abiInfo.OutParams() {
   149  		for _, r := range p.Registers {
   150  			m := archRegForAbiReg(r, c)
   151  			a.reg.outputs = append(a.reg.outputs, outputInfo{idx: k, regs: (1 << m)})
   152  			k++
   153  		}
   154  	}
   155  	a.reg.outputs = append(a.reg.outputs, i.outputs...)
   156  	a.reg.clobbers = i.clobbers
   157  	return a.reg
   158  }
   159  func (a *AuxCall) ABI() *abi.ABIConfig {
   160  	return a.abiInfo.Config()
   161  }
   162  func (a *AuxCall) ABIInfo() *abi.ABIParamResultInfo {
   163  	return a.abiInfo
   164  }
   165  func (a *AuxCall) ResultReg(c *Config) *regInfo {
   166  	if a.abiInfo.OutRegistersUsed() == 0 {
   167  		return a.reg
   168  	}
   169  	if len(a.reg.inputs) > 0 {
   170  		return a.reg
   171  	}
   172  	k := 0
   173  	for _, p := range a.abiInfo.OutParams() {
   174  		for _, r := range p.Registers {
   175  			m := archRegForAbiReg(r, c)
   176  			a.reg.inputs = append(a.reg.inputs, inputInfo{idx: k, regs: (1 << m)})
   177  			k++
   178  		}
   179  	}
   180  	return a.reg
   181  }
   182  
   183  // For ABI register index r, returns the (dense) register number used in
   184  // SSA backend.
   185  func archRegForAbiReg(r abi.RegIndex, c *Config) uint8 {
   186  	var m int8
   187  	if int(r) < len(c.intParamRegs) {
   188  		m = c.intParamRegs[r]
   189  	} else {
   190  		m = c.floatParamRegs[int(r)-len(c.intParamRegs)]
   191  	}
   192  	return uint8(m)
   193  }
   194  
   195  // For ABI register index r, returns the register number used in the obj
   196  // package (assembler).
   197  func ObjRegForAbiReg(r abi.RegIndex, c *Config) int16 {
   198  	m := archRegForAbiReg(r, c)
   199  	return c.registers[m].objNum
   200  }
   201  
   202  // ArgWidth returns the amount of stack needed for all the inputs
   203  // and outputs of a function or method, including ABI-defined parameter
   204  // slots and ABI-defined spill slots for register-resident parameters.
   205  //
   206  // The name is taken from the types package's ArgWidth(<function type>),
   207  // which predated changes to the ABI; this version handles those changes.
   208  func (a *AuxCall) ArgWidth() int64 {
   209  	return a.abiInfo.ArgWidth()
   210  }
   211  
   212  // ParamAssignmentForResult returns the ABI Parameter assignment for result which (indexed 0, 1, etc).
   213  func (a *AuxCall) ParamAssignmentForResult(which int64) *abi.ABIParamAssignment {
   214  	return a.abiInfo.OutParam(int(which))
   215  }
   216  
   217  // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc).
   218  func (a *AuxCall) OffsetOfResult(which int64) int64 {
   219  	n := int64(a.abiInfo.OutParam(int(which)).Offset())
   220  	return n
   221  }
   222  
   223  // OffsetOfArg returns the SP offset of argument which (indexed 0, 1, etc).
   224  // If the call is to a method, the receiver is the first argument (i.e., index 0)
   225  func (a *AuxCall) OffsetOfArg(which int64) int64 {
   226  	n := int64(a.abiInfo.InParam(int(which)).Offset())
   227  	return n
   228  }
   229  
   230  // RegsOfResult returns the register(s) used for result which (indexed 0, 1, etc).
   231  func (a *AuxCall) RegsOfResult(which int64) []abi.RegIndex {
   232  	return a.abiInfo.OutParam(int(which)).Registers
   233  }
   234  
   235  // RegsOfArg returns the register(s) used for argument which (indexed 0, 1, etc).
   236  // If the call is to a method, the receiver is the first argument (i.e., index 0)
   237  func (a *AuxCall) RegsOfArg(which int64) []abi.RegIndex {
   238  	return a.abiInfo.InParam(int(which)).Registers
   239  }
   240  
   241  // NameOfResult returns the ir.Name of result which (indexed 0, 1, etc).
   242  func (a *AuxCall) NameOfResult(which int64) *ir.Name {
   243  	return a.abiInfo.OutParam(int(which)).Name
   244  }
   245  
   246  // TypeOfResult returns the type of result which (indexed 0, 1, etc).
   247  func (a *AuxCall) TypeOfResult(which int64) *types.Type {
   248  	return a.abiInfo.OutParam(int(which)).Type
   249  }
   250  
   251  // TypeOfArg returns the type of argument which (indexed 0, 1, etc).
   252  // If the call is to a method, the receiver is the first argument (i.e., index 0)
   253  func (a *AuxCall) TypeOfArg(which int64) *types.Type {
   254  	return a.abiInfo.InParam(int(which)).Type
   255  }
   256  
   257  // SizeOfResult returns the size of result which (indexed 0, 1, etc).
   258  func (a *AuxCall) SizeOfResult(which int64) int64 {
   259  	return a.TypeOfResult(which).Size()
   260  }
   261  
   262  // SizeOfArg returns the size of argument which (indexed 0, 1, etc).
   263  // If the call is to a method, the receiver is the first argument (i.e., index 0)
   264  func (a *AuxCall) SizeOfArg(which int64) int64 {
   265  	return a.TypeOfArg(which).Size()
   266  }
   267  
   268  // NResults returns the number of results.
   269  func (a *AuxCall) NResults() int64 {
   270  	return int64(len(a.abiInfo.OutParams()))
   271  }
   272  
   273  // LateExpansionResultType returns the result type (including trailing mem)
   274  // for a call that will be expanded later in the SSA phase.
   275  func (a *AuxCall) LateExpansionResultType() *types.Type {
   276  	var tys []*types.Type
   277  	for i := int64(0); i < a.NResults(); i++ {
   278  		tys = append(tys, a.TypeOfResult(i))
   279  	}
   280  	tys = append(tys, types.TypeMem)
   281  	return types.NewResults(tys)
   282  }
   283  
   284  // NArgs returns the number of arguments (including receiver, if there is one).
   285  func (a *AuxCall) NArgs() int64 {
   286  	return int64(len(a.abiInfo.InParams()))
   287  }
   288  
   289  // String returns "AuxCall{<fn>}"
   290  func (a *AuxCall) String() string {
   291  	var fn string
   292  	if a.Fn == nil {
   293  		fn = "AuxCall{nil" // could be interface/closure etc.
   294  	} else {
   295  		fn = fmt.Sprintf("AuxCall{%v", a.Fn)
   296  	}
   297  	// TODO how much of the ABI should be printed?
   298  
   299  	return fn + "}"
   300  }
   301  
   302  // StaticAuxCall returns an AuxCall for a static call.
   303  func StaticAuxCall(sym *obj.LSym, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
   304  	if paramResultInfo == nil {
   305  		panic(fmt.Errorf("Nil paramResultInfo, sym=%v", sym))
   306  	}
   307  	var reg *regInfo
   308  	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
   309  		reg = &regInfo{}
   310  	}
   311  	return &AuxCall{Fn: sym, abiInfo: paramResultInfo, reg: reg}
   312  }
   313  
   314  // InterfaceAuxCall returns an AuxCall for an interface call.
   315  func InterfaceAuxCall(paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
   316  	var reg *regInfo
   317  	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
   318  		reg = &regInfo{}
   319  	}
   320  	return &AuxCall{Fn: nil, abiInfo: paramResultInfo, reg: reg}
   321  }
   322  
   323  // ClosureAuxCall returns an AuxCall for a closure call.
   324  func ClosureAuxCall(paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
   325  	var reg *regInfo
   326  	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
   327  		reg = &regInfo{}
   328  	}
   329  	return &AuxCall{Fn: nil, abiInfo: paramResultInfo, reg: reg}
   330  }
   331  
   332  func (*AuxCall) CanBeAnSSAAux() {}
   333  
   334  // OwnAuxCall returns a function's own AuxCall.
   335  func OwnAuxCall(fn *obj.LSym, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
   336  	// TODO if this remains identical to ClosureAuxCall above after new ABI is done, should deduplicate.
   337  	var reg *regInfo
   338  	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
   339  		reg = &regInfo{}
   340  	}
   341  	return &AuxCall{Fn: fn, abiInfo: paramResultInfo, reg: reg}
   342  }
   343  
   344  const (
   345  	auxNone           auxType = iota
   346  	auxBool                   // auxInt is 0/1 for false/true
   347  	auxInt8                   // auxInt is an 8-bit integer
   348  	auxInt16                  // auxInt is a 16-bit integer
   349  	auxInt32                  // auxInt is a 32-bit integer
   350  	auxInt64                  // auxInt is a 64-bit integer
   351  	auxInt128                 // auxInt represents a 128-bit integer.  Always 0.
   352  	auxUInt8                  // auxInt is an 8-bit unsigned integer
   353  	auxFloat32                // auxInt is a float32 (encoded with math.Float64bits)
   354  	auxFloat64                // auxInt is a float64 (encoded with math.Float64bits)
   355  	auxFlagConstant           // auxInt is a flagConstant
   356  	auxCCop                   // auxInt is a ssa.Op that represents a flags-to-bool conversion (e.g. LessThan)
   357  	auxNameOffsetInt8         // aux is a &struct{Name ir.Name, Offset int64}; auxInt is index in parameter registers array
   358  	auxString                 // aux is a string
   359  	auxSym                    // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none)
   360  	auxSymOff                 // aux is a symbol, auxInt is an offset
   361  	auxSymValAndOff           // aux is a symbol, auxInt is a ValAndOff
   362  	auxTyp                    // aux is a type
   363  	auxTypSize                // aux is a type, auxInt is a size, must have Aux.(Type).Size() == AuxInt
   364  	auxCall                   // aux is a *ssa.AuxCall
   365  	auxCallOff                // aux is a *ssa.AuxCall, AuxInt is int64 param (in+out) size
   366  
   367  	// architecture specific aux types
   368  	auxARM64BitField     // aux is an arm64 bitfield lsb and width packed into auxInt
   369  	auxS390XRotateParams // aux is a s390x rotate parameters object encoding start bit, end bit and rotate amount
   370  	auxS390XCCMask       // aux is a s390x 4-bit condition code mask
   371  	auxS390XCCMaskInt8   // aux is a s390x 4-bit condition code mask, auxInt is an int8 immediate
   372  	auxS390XCCMaskUint8  // aux is a s390x 4-bit condition code mask, auxInt is a uint8 immediate
   373  )
   374  
   375  // A SymEffect describes the effect that an SSA Value has on the variable
   376  // identified by the symbol in its Aux field.
   377  type SymEffect int8
   378  
   379  const (
   380  	SymRead SymEffect = 1 << iota
   381  	SymWrite
   382  	SymAddr
   383  
   384  	SymRdWr = SymRead | SymWrite
   385  
   386  	SymNone SymEffect = 0
   387  )
   388  
   389  // A Sym represents a symbolic offset from a base register.
   390  // Currently a Sym can be one of 3 things:
   391  //   - a *gc.Node, for an offset from SP (the stack pointer)
   392  //   - a *obj.LSym, for an offset from SB (the global pointer)
   393  //   - nil, for no offset
   394  type Sym interface {
   395  	CanBeAnSSASym()
   396  	CanBeAnSSAAux()
   397  }
   398  
   399  // A ValAndOff is used by the several opcodes. It holds
   400  // both a value and a pointer offset.
   401  // A ValAndOff is intended to be encoded into an AuxInt field.
   402  // The zero ValAndOff encodes a value of 0 and an offset of 0.
   403  // The high 32 bits hold a value.
   404  // The low 32 bits hold a pointer offset.
   405  type ValAndOff int64
   406  
   407  func (x ValAndOff) Val() int32   { return int32(int64(x) >> 32) }
   408  func (x ValAndOff) Val64() int64 { return int64(x) >> 32 }
   409  func (x ValAndOff) Val16() int16 { return int16(int64(x) >> 32) }
   410  func (x ValAndOff) Val8() int8   { return int8(int64(x) >> 32) }
   411  
   412  func (x ValAndOff) Off64() int64 { return int64(int32(x)) }
   413  func (x ValAndOff) Off() int32   { return int32(x) }
   414  
   415  func (x ValAndOff) String() string {
   416  	return fmt.Sprintf("val=%d,off=%d", x.Val(), x.Off())
   417  }
   418  
   419  // validVal reports whether the value can be used
   420  // as an argument to makeValAndOff.
   421  func validVal(val int64) bool {
   422  	return val == int64(int32(val))
   423  }
   424  
   425  func makeValAndOff(val, off int32) ValAndOff {
   426  	return ValAndOff(int64(val)<<32 + int64(uint32(off)))
   427  }
   428  
   429  func (x ValAndOff) canAdd32(off int32) bool {
   430  	newoff := x.Off64() + int64(off)
   431  	return newoff == int64(int32(newoff))
   432  }
   433  func (x ValAndOff) canAdd64(off int64) bool {
   434  	newoff := x.Off64() + off
   435  	return newoff == int64(int32(newoff))
   436  }
   437  
   438  func (x ValAndOff) addOffset32(off int32) ValAndOff {
   439  	if !x.canAdd32(off) {
   440  		panic("invalid ValAndOff.addOffset32")
   441  	}
   442  	return makeValAndOff(x.Val(), x.Off()+off)
   443  }
   444  func (x ValAndOff) addOffset64(off int64) ValAndOff {
   445  	if !x.canAdd64(off) {
   446  		panic("invalid ValAndOff.addOffset64")
   447  	}
   448  	return makeValAndOff(x.Val(), x.Off()+int32(off))
   449  }
   450  
   451  // int128 is a type that stores a 128-bit constant.
   452  // The only allowed constant right now is 0, so we can cheat quite a bit.
   453  type int128 int64
   454  
   455  type BoundsKind uint8
   456  
   457  const (
   458  	BoundsIndex       BoundsKind = iota // indexing operation, 0 <= idx < len failed
   459  	BoundsIndexU                        // ... with unsigned idx
   460  	BoundsSliceAlen                     // 2-arg slicing operation, 0 <= high <= len failed
   461  	BoundsSliceAlenU                    // ... with unsigned high
   462  	BoundsSliceAcap                     // 2-arg slicing operation, 0 <= high <= cap failed
   463  	BoundsSliceAcapU                    // ... with unsigned high
   464  	BoundsSliceB                        // 2-arg slicing operation, 0 <= low <= high failed
   465  	BoundsSliceBU                       // ... with unsigned low
   466  	BoundsSlice3Alen                    // 3-arg slicing operation, 0 <= max <= len failed
   467  	BoundsSlice3AlenU                   // ... with unsigned max
   468  	BoundsSlice3Acap                    // 3-arg slicing operation, 0 <= max <= cap failed
   469  	BoundsSlice3AcapU                   // ... with unsigned max
   470  	BoundsSlice3B                       // 3-arg slicing operation, 0 <= high <= max failed
   471  	BoundsSlice3BU                      // ... with unsigned high
   472  	BoundsSlice3C                       // 3-arg slicing operation, 0 <= low <= high failed
   473  	BoundsSlice3CU                      // ... with unsigned low
   474  	BoundsConvert                       // conversion to array pointer failed
   475  	BoundsKindCount
   476  )
   477  
   478  // boundsABI determines which register arguments a bounds check call should use. For an [a:b:c] slice, we do:
   479  //
   480  //	CMPQ c, cap
   481  //	JA   fail1
   482  //	CMPQ b, c
   483  //	JA   fail2
   484  //	CMPQ a, b
   485  //	JA   fail3
   486  //
   487  // fail1: CALL panicSlice3Acap (c, cap)
   488  // fail2: CALL panicSlice3B (b, c)
   489  // fail3: CALL panicSlice3C (a, b)
   490  //
   491  // When we register allocate that code, we want the same register to be used for
   492  // the first arg of panicSlice3Acap and the second arg to panicSlice3B. That way,
   493  // initializing that register once will satisfy both calls.
   494  // That desire ends up dividing the set of bounds check calls into 3 sets. This function
   495  // determines which set to use for a given panic call.
   496  // The first arg for set 0 should be the second arg for set 1.
   497  // The first arg for set 1 should be the second arg for set 2.
   498  func boundsABI(b int64) int {
   499  	switch BoundsKind(b) {
   500  	case BoundsSlice3Alen,
   501  		BoundsSlice3AlenU,
   502  		BoundsSlice3Acap,
   503  		BoundsSlice3AcapU,
   504  		BoundsConvert:
   505  		return 0
   506  	case BoundsSliceAlen,
   507  		BoundsSliceAlenU,
   508  		BoundsSliceAcap,
   509  		BoundsSliceAcapU,
   510  		BoundsSlice3B,
   511  		BoundsSlice3BU:
   512  		return 1
   513  	case BoundsIndex,
   514  		BoundsIndexU,
   515  		BoundsSliceB,
   516  		BoundsSliceBU,
   517  		BoundsSlice3C,
   518  		BoundsSlice3CU:
   519  		return 2
   520  	default:
   521  		panic("bad BoundsKind")
   522  	}
   523  }
   524  
   525  // arm64BitField is the GO type of ARM64BitField auxInt.
   526  // if x is an ARM64BitField, then width=x&0xff, lsb=(x>>8)&0xff, and
   527  // width+lsb<64 for 64-bit variant, width+lsb<32 for 32-bit variant.
   528  // the meaning of width and lsb are instruction-dependent.
   529  type arm64BitField int16
   530  

View as plain text