Source file src/cmd/compile/internal/ssa/_gen/ARM64Ops.go

     1  // Copyright 2016 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 main
     6  
     7  import "strings"
     8  
     9  // Notes:
    10  //  - Integer types live in the low portion of registers. Upper portions are junk.
    11  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    12  //    Upper bytes are junk.
    13  //  - *const instructions may use a constant larger than the instruction can encode.
    14  //    In this case the assembler expands to multiple instructions and uses tmp
    15  //    register (R27).
    16  //  - All 32-bit Ops will zero the upper 32 bits of the destination register.
    17  
    18  // Suffixes encode the bit width of various instructions.
    19  // D (double word) = 64 bit
    20  // W (word)        = 32 bit
    21  // H (half word)   = 16 bit
    22  // HU              = 16 bit unsigned
    23  // B (byte)        = 8 bit
    24  // BU              = 8 bit unsigned
    25  // S (single)      = 32 bit float
    26  // D (double)      = 64 bit float
    27  
    28  // Note: registers not used in regalloc are not included in this list,
    29  // so that regmask stays within int64
    30  // Be careful when hand coding regmasks.
    31  var regNamesARM64 = []string{
    32  	"R0",
    33  	"R1",
    34  	"R2",
    35  	"R3",
    36  	"R4",
    37  	"R5",
    38  	"R6",
    39  	"R7",
    40  	"R8",
    41  	"R9",
    42  	"R10",
    43  	"R11",
    44  	"R12",
    45  	"R13",
    46  	"R14",
    47  	"R15",
    48  	"R16",
    49  	"R17",
    50  	// R18 = platform register, not used
    51  	"R19",
    52  	"R20",
    53  	"R21",
    54  	"R22",
    55  	"R23",
    56  	"R24",
    57  	"R25",
    58  	"R26",
    59  	// R27 = REGTMP not used in regalloc
    60  	"g",    // aka R28
    61  	"R29",  // frame pointer, not used
    62  	"R30",  // aka REGLINK
    63  	"ZERO", // zero register (aka R31)
    64  	"SP",   // stack pointer (aka R31)
    65  
    66  	// Note: both ZERO and SP are register number 31!
    67  	// What r31 means in a particular instruction depends on
    68  	// the instruction.  Generally, for arguments of instructions
    69  	// which are addresses to load or store from, r31 means SP.
    70  	// In other instructions, r31 means ZERO. But there are
    71  	// exceptions.
    72  	// See https://stackoverflow.com/questions/61532867
    73  	// This does not have much of an effect here, as the
    74  	// cmd/internal/obj/arm64 interface treats them as two
    75  	// different registers and picks the right instruction
    76  	// that encodes what r31 means. But see issue 71651.
    77  
    78  	"F0",
    79  	"F1",
    80  	"F2",
    81  	"F3",
    82  	"F4",
    83  	"F5",
    84  	"F6",
    85  	"F7",
    86  	"F8",
    87  	"F9",
    88  	"F10",
    89  	"F11",
    90  	"F12",
    91  	"F13",
    92  	"F14",
    93  	"F15",
    94  	"F16",
    95  	"F17",
    96  	"F18",
    97  	"F19",
    98  	"F20",
    99  	"F21",
   100  	"F22",
   101  	"F23",
   102  	"F24",
   103  	"F25",
   104  	"F26",
   105  	"F27",
   106  	"F28",
   107  	"F29",
   108  	"F30",
   109  	"F31",
   110  
   111  	"P0",
   112  	"P1",
   113  	"P2",
   114  	"P3",
   115  	"P4",
   116  	"P5",
   117  	"P6",
   118  	"P7",
   119  	"P8",
   120  	"P9",
   121  	"P10",
   122  	"P11",
   123  	"P12",
   124  	"P13",
   125  	"P14",
   126  	"P15",
   127  
   128  	// If you add registers, update asyncPreempt in runtime.
   129  
   130  	// pseudo-registers
   131  	"SB",
   132  }
   133  
   134  func init() {
   135  	// Make map from reg names to reg integers.
   136  	if len(regNamesARM64) > 128 {
   137  		panic("too many registers")
   138  	}
   139  	num := map[string]int{}
   140  	for i, name := range regNamesARM64 {
   141  		num[name] = i
   142  	}
   143  	buildReg := func(s string) regMask {
   144  		m := regMask{}
   145  		for _, r := range strings.Split(s, " ") {
   146  			if n, ok := num[r]; ok {
   147  				m = m.addReg(uint(n))
   148  				continue
   149  			}
   150  			panic("register " + r + " not found")
   151  		}
   152  		return m
   153  	}
   154  
   155  	// Common individual register masks
   156  	var (
   157  		gp         = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30")
   158  		gpg        = gp.union(buildReg("g"))
   159  		gpsp       = gp.union(buildReg("SP"))
   160  		gpspg      = gpg.union(buildReg("SP"))
   161  		gpspsbg    = gpspg.union(buildReg("SB"))
   162  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
   163  		pred       = buildReg("P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 P10 P11 P12 P13 P14 P15")
   164  		callerSave = gp.union(fp).union(pred).union(buildReg("g")) // runtime.setg (and anything calling it) may clobber g
   165  		r25        = buildReg("R25")
   166  		r24to25    = buildReg("R24 R25")
   167  		f16to17    = buildReg("F16 F17")
   168  		rz         = buildReg("ZERO")
   169  		first16    = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15")
   170  	)
   171  	// Common regInfo
   172  	var (
   173  		gp01           = regInfo{inputs: nil, outputs: []regMask{gp}}
   174  		gp0flags1      = regInfo{inputs: []regMask{regMask{}}, outputs: []regMask{gp}}
   175  		gp11           = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   176  		gp11sp         = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   177  		gp1flags       = regInfo{inputs: []regMask{gpg}}
   178  		gp1flagsflags  = regInfo{inputs: []regMask{gpg}}
   179  		gp1flags1      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   180  		gp11flags      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp, regMask{}}}
   181  		gp21           = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   182  		gp21nog        = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
   183  		gp21flags      = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp, regMask{}}}
   184  		gp2flags       = regInfo{inputs: []regMask{gpg, gpg}}
   185  		gp2flagsflags  = regInfo{inputs: []regMask{gpg, gpg}}
   186  		gp2flags1      = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
   187  		gp2flags1flags = regInfo{inputs: []regMask{gp, gp, regMask{}}, outputs: []regMask{gp, regMask{}}}
   188  		gp2load        = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   189  		gp31           = regInfo{inputs: []regMask{gpg, gpg, gpg}, outputs: []regMask{gp}}
   190  		gpload         = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   191  		gpload2        = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gpg, gpg}}
   192  		gpstore        = regInfo{inputs: []regMask{gpspsbg, gpg.union(rz)}}
   193  		gpstore2       = regInfo{inputs: []regMask{gpspsbg, gpg.union(rz), gpg.union(rz)}}
   194  		gpxchg         = regInfo{inputs: []regMask{gpspsbg, gpg.union(rz)}, outputs: []regMask{gp}}
   195  		gpcas          = regInfo{inputs: []regMask{gpspsbg, gpg.union(rz), gpg.union(rz)}, outputs: []regMask{gp}}
   196  		fp01           = regInfo{inputs: nil, outputs: []regMask{fp}}
   197  		pred01         = regInfo{inputs: nil, outputs: []regMask{pred}}
   198  		fp11           = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   199  		fpgp           = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   200  		fpgpfp         = regInfo{inputs: []regMask{fp, gp}, outputs: []regMask{fp}}
   201  		gpfp           = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   202  		fp21           = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   203  		fp31           = regInfo{inputs: []regMask{fp, fp, fp}, outputs: []regMask{fp}}
   204  		fp2flags       = regInfo{inputs: []regMask{fp, fp}}
   205  		fp1flags       = regInfo{inputs: []regMask{fp}}
   206  		fp1predfp      = regInfo{inputs: []regMask{fp, pred}, outputs: []regMask{fp}}
   207  		fp2predpred    = regInfo{inputs: []regMask{fp, fp, pred}, outputs: []regMask{pred}}
   208  		fp2predfp      = regInfo{inputs: []regMask{fp, fp, pred}, outputs: []regMask{fp}}
   209  		fp3predfp      = regInfo{inputs: []regMask{fp, fp, fp, pred}, outputs: []regMask{fp}}
   210  		predload       = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{pred}}
   211  		predstore      = regInfo{inputs: []regMask{gpspsbg, pred}}
   212  		fpload         = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   213  		fpload2        = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp, fp}}
   214  		fp2load        = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{fp}}
   215  		fpstore        = regInfo{inputs: []regMask{gpspsbg, fp}}
   216  		fpstoreidx     = regInfo{inputs: []regMask{gpspsbg, gpg, fp}}
   217  		gp2pred        = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{pred}}
   218  		fppredload     = regInfo{inputs: []regMask{gpspsbg, pred}, outputs: []regMask{fp}}
   219  		fppredstore    = regInfo{inputs: []regMask{gpspsbg, fp, pred}}
   220  		fpstore2       = regInfo{inputs: []regMask{gpspsbg, fp, fp}}
   221  		readflags      = regInfo{inputs: nil, outputs: []regMask{gp}}
   222  		prefreg        = regInfo{inputs: []regMask{gpspsbg}}
   223  	)
   224  	ops := []opData{
   225  		// binary ops
   226  		{name: "ADCSflags", argLength: 3, reg: gp2flags1flags, typ: "(UInt64,Flags)", asm: "ADCS", commutative: true},     // arg0+arg1+carry, set flags.
   227  		{name: "ADCzerocarry", argLength: 1, reg: gp0flags1, typ: "UInt64", asm: "ADC", earlyOk: true, zeroUpperBits: 56}, // ZR+ZR+carry
   228  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true, earlyOk: true},                              // arg0 + arg1
   229  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64", earlyOk: true},                            // arg0 + auxInt
   230  		{name: "ADDSconstflags", argLength: 1, reg: gp11flags, typ: "(UInt64,Flags)", asm: "ADDS", aux: "Int64"},          // arg0+auxint, set flags.
   231  		{name: "ADDSflags", argLength: 2, reg: gp21flags, typ: "(UInt64,Flags)", asm: "ADDS", commutative: true},          // arg0+arg1, set flags.
   232  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", earlyOk: true},                                                 // arg0 - arg1
   233  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64", earlyOk: true},                              // arg0 - auxInt
   234  		{name: "SBCSflags", argLength: 3, reg: gp2flags1flags, typ: "(UInt64,Flags)", asm: "SBCS"},                        // arg0-(arg1+borrowing), set flags.
   235  		{name: "SUBSflags", argLength: 2, reg: gp21flags, typ: "(UInt64,Flags)", asm: "SUBS"},                             // arg0 - arg1, set flags.
   236  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true, earlyOk: true},                              // arg0 * arg1
   237  		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true, earlyOk: true, zeroUpperBits: 32},         // arg0 * arg1, 32-bit
   238  		{name: "MNEG", argLength: 2, reg: gp21, asm: "MNEG", commutative: true, earlyOk: true},                            // -arg0 * arg1
   239  		{name: "MNEGW", argLength: 2, reg: gp21, asm: "MNEGW", commutative: true, earlyOk: true, zeroUpperBits: 32},       // -arg0 * arg1, 32-bit
   240  		{name: "MULH", argLength: 2, reg: gp21, asm: "SMULH", commutative: true, earlyOk: true},                           // (arg0 * arg1) >> 64, signed
   241  		{name: "UMULH", argLength: 2, reg: gp21, asm: "UMULH", commutative: true, earlyOk: true},                          // (arg0 * arg1) >> 64, unsigned
   242  		{name: "MULL", argLength: 2, reg: gp21, asm: "SMULL", commutative: true, earlyOk: true},                           // arg0 * arg1, signed, 32-bit mult results in 64-bit
   243  		{name: "UMULL", argLength: 2, reg: gp21, asm: "UMULL", commutative: true, earlyOk: true},                          // arg0 * arg1, unsigned, 32-bit mult results in 64-bit
   244  		{name: "DIV", argLength: 2, reg: gp21, asm: "SDIV", earlyOk: true},                                                // arg0 / arg1, signed
   245  		{name: "UDIV", argLength: 2, reg: gp21, asm: "UDIV", earlyOk: true},                                               // arg0 / arg1, unsigned
   246  		{name: "DIVW", argLength: 2, reg: gp21, asm: "SDIVW", earlyOk: true, zeroUpperBits: 32},                           // arg0 / arg1, signed, 32 bit
   247  		{name: "UDIVW", argLength: 2, reg: gp21, asm: "UDIVW", earlyOk: true, zeroUpperBits: 32},                          // arg0 / arg1, unsigned, 32 bit
   248  		{name: "MOD", argLength: 2, reg: gp21, asm: "REM", earlyOk: true},                                                 // arg0 % arg1, signed
   249  		{name: "UMOD", argLength: 2, reg: gp21, asm: "UREM", earlyOk: true},                                               // arg0 % arg1, unsigned
   250  		{name: "MODW", argLength: 2, reg: gp21, asm: "REMW", earlyOk: true, zeroUpperBits: 32},                            // arg0 % arg1, signed, 32 bit
   251  		{name: "UMODW", argLength: 2, reg: gp21, asm: "UREMW", earlyOk: true, zeroUpperBits: 32},                          // arg0 % arg1, unsigned, 32 bit
   252  
   253  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true, earlyOk: true},   // arg0 + arg1
   254  		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true, earlyOk: true},   // arg0 + arg1
   255  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS", earlyOk: true},                      // arg0 - arg1
   256  		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD", earlyOk: true},                      // arg0 - arg1
   257  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, earlyOk: true},   // arg0 * arg1
   258  		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true, earlyOk: true},   // arg0 * arg1
   259  		{name: "FNMULS", argLength: 2, reg: fp21, asm: "FNMULS", commutative: true, earlyOk: true}, // -(arg0 * arg1)
   260  		{name: "FNMULD", argLength: 2, reg: fp21, asm: "FNMULD", commutative: true, earlyOk: true}, // -(arg0 * arg1)
   261  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", earlyOk: true},                      // arg0 / arg1
   262  		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD", earlyOk: true},                      // arg0 / arg1
   263  
   264  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, earlyOk: true}, // arg0 & arg1
   265  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", earlyOk: true}, // arg0 & auxInt
   266  		{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true, earlyOk: true},  // arg0 | arg1
   267  		{name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int64", earlyOk: true},  // arg0 | auxInt
   268  		{name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true, earlyOk: true}, // arg0 ^ arg1
   269  		{name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int64", earlyOk: true}, // arg0 ^ auxInt
   270  		{name: "BIC", argLength: 2, reg: gp21, asm: "BIC", earlyOk: true},                    // arg0 &^ arg1
   271  		{name: "EON", argLength: 2, reg: gp21, asm: "EON", earlyOk: true},                    // arg0 ^ ^arg1
   272  		{name: "ORN", argLength: 2, reg: gp21, asm: "ORN", earlyOk: true},                    // arg0 | ^arg1
   273  
   274  		// unary ops
   275  		{name: "MVN", argLength: 1, reg: gp11, asm: "MVN", earlyOk: true},                              // ^arg0
   276  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", earlyOk: true},                              // -arg0
   277  		{name: "NEGSflags", argLength: 1, reg: gp11flags, typ: "(UInt64,Flags)", asm: "NEGS"},          // -arg0, set flags.
   278  		{name: "NGCzerocarry", argLength: 1, reg: gp0flags1, typ: "UInt64", asm: "NGC", earlyOk: true}, // -1 if borrowing, 0 otherwise.
   279  		{name: "FABSD", argLength: 1, reg: fp11, asm: "FABSD", earlyOk: true},                          // abs(arg0), float64
   280  		{name: "FABSS", argLength: 1, reg: fp11, asm: "FABSS", earlyOk: true},                          // abs(arg0), float32
   281  		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS", earlyOk: true},                          // -arg0, float32
   282  		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", earlyOk: true},                          // -arg0, float64
   283  		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", earlyOk: true},                        // sqrt(arg0), float64
   284  		{name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS", earlyOk: true},                        // sqrt(arg0), float32
   285  		{name: "FMIND", argLength: 2, reg: fp21, asm: "FMIND", earlyOk: true},                          // min(arg0, arg1)
   286  		{name: "FMINS", argLength: 2, reg: fp21, asm: "FMINS", earlyOk: true},                          // min(arg0, arg1)
   287  		{name: "FMAXD", argLength: 2, reg: fp21, asm: "FMAXD", earlyOk: true},                          // max(arg0, arg1)
   288  		{name: "FMAXS", argLength: 2, reg: fp21, asm: "FMAXS", earlyOk: true},                          // max(arg0, arg1)
   289  		{name: "REV", argLength: 1, reg: gp11, asm: "REV", earlyOk: true},                              // byte reverse, 64-bit
   290  		{name: "REVW", argLength: 1, reg: gp11, asm: "REVW", earlyOk: true, zeroUpperBits: 32},         // byte reverse, 32-bit
   291  		{name: "REV16", argLength: 1, reg: gp11, asm: "REV16", earlyOk: true},                          // byte reverse in each 16-bit halfword, 64-bit
   292  		{name: "REV16W", argLength: 1, reg: gp11, asm: "REV16W", earlyOk: true, zeroUpperBits: 32},     // byte reverse in each 16-bit halfword, 32-bit
   293  		{name: "RBIT", argLength: 1, reg: gp11, asm: "RBIT", earlyOk: true},                            // bit reverse, 64-bit
   294  		{name: "RBITW", argLength: 1, reg: gp11, asm: "RBITW", earlyOk: true, zeroUpperBits: 32},       // bit reverse, 32-bit
   295  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ", earlyOk: true, zeroUpperBits: 56},           // count leading zero, 64-bit
   296  		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW", earlyOk: true, zeroUpperBits: 56},         // count leading zero, 32-bit
   297  		{name: "VCNT", argLength: 1, reg: fp11, asm: "VCNT", earlyOk: true},                            // count set bits for each 8-bit unit and store the result in each 8-bit unit
   298  		{name: "VUADDLV", argLength: 1, reg: fp11, asm: "VUADDLV", earlyOk: true},                      // unsigned sum of eight bytes in a 64-bit value, zero extended to 64-bit.
   299  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true, earlyOk: true},
   300  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true, earlyOk: true},
   301  
   302  		// 3-operand, the addend comes first
   303  		{name: "FMADDS", argLength: 3, reg: fp31, asm: "FMADDS", earlyOk: true},                  // +arg0 + (arg1 * arg2)
   304  		{name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD", earlyOk: true},                  // +arg0 + (arg1 * arg2)
   305  		{name: "FNMADDS", argLength: 3, reg: fp31, asm: "FNMADDS", earlyOk: true},                // -arg0 - (arg1 * arg2)
   306  		{name: "FNMADDD", argLength: 3, reg: fp31, asm: "FNMADDD", earlyOk: true},                // -arg0 - (arg1 * arg2)
   307  		{name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS", earlyOk: true},                  // +arg0 - (arg1 * arg2)
   308  		{name: "FMSUBD", argLength: 3, reg: fp31, asm: "FMSUBD", earlyOk: true},                  // +arg0 - (arg1 * arg2)
   309  		{name: "FNMSUBS", argLength: 3, reg: fp31, asm: "FNMSUBS", earlyOk: true},                // -arg0 + (arg1 * arg2)
   310  		{name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", earlyOk: true},                // -arg0 + (arg1 * arg2)
   311  		{name: "MADD", argLength: 3, reg: gp31, asm: "MADD", earlyOk: true},                      // +arg0 + (arg1 * arg2)
   312  		{name: "MADDW", argLength: 3, reg: gp31, asm: "MADDW", earlyOk: true, zeroUpperBits: 32}, // +arg0 + (arg1 * arg2), 32-bit
   313  		{name: "MSUB", argLength: 3, reg: gp31, asm: "MSUB", earlyOk: true},                      // +arg0 - (arg1 * arg2)
   314  		{name: "MSUBW", argLength: 3, reg: gp31, asm: "MSUBW", earlyOk: true, zeroUpperBits: 32}, // +arg0 - (arg1 * arg2), 32-bit
   315  
   316  		// shifts
   317  		{name: "SLL", argLength: 2, reg: gp21, asm: "LSL", earlyOk: true},                                           // arg0 << arg1, shift amount is mod 64
   318  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "LSL", aux: "Int64", earlyOk: true},                        // arg0 << auxInt, auxInt should be in the range 0 to 63.
   319  		{name: "SRL", argLength: 2, reg: gp21, asm: "LSR", earlyOk: true},                                           // arg0 >> arg1, unsigned, shift amount is mod 64
   320  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "LSR", aux: "Int64", earlyOk: true},                        // arg0 >> auxInt, unsigned, auxInt should be in the range 0 to 63.
   321  		{name: "SRA", argLength: 2, reg: gp21, asm: "ASR", earlyOk: true},                                           // arg0 >> arg1, signed, shift amount is mod 64
   322  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "ASR", aux: "Int64", earlyOk: true},                        // arg0 >> auxInt, signed, auxInt should be in the range 0 to 63.
   323  		{name: "ROR", argLength: 2, reg: gp21, asm: "ROR", earlyOk: true},                                           // arg0 right rotate by (arg1 mod 64) bits
   324  		{name: "RORW", argLength: 2, reg: gp21, asm: "RORW", earlyOk: true, zeroUpperBits: 32},                      // arg0 right rotate by (arg1 mod 32) bits
   325  		{name: "RORconst", argLength: 1, reg: gp11, asm: "ROR", aux: "Int64", earlyOk: true},                        // arg0 right rotate by auxInt bits, auxInt should be in the range 0 to 63.
   326  		{name: "RORWconst", argLength: 1, reg: gp11, asm: "RORW", aux: "Int64", earlyOk: true, zeroUpperBits: 32},   // uint32(arg0) right rotate by auxInt bits, auxInt should be in the range 0 to 31.
   327  		{name: "EXTRconst", argLength: 2, reg: gp21, asm: "EXTR", aux: "Int64", earlyOk: true},                      // extract 64 bits from arg0:arg1 starting at lsb auxInt, auxInt should be in the range 0 to 63.
   328  		{name: "EXTRWconst", argLength: 2, reg: gp21, asm: "EXTRW", aux: "Int64", earlyOk: true, zeroUpperBits: 32}, // extract 32 bits from arg0[31:0]:arg1[31:0] starting at lsb auxInt and zero top 32 bits, auxInt should be in the range 0 to 31.
   329  
   330  		// comparisons
   331  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},                      // arg0 compare to arg1
   332  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"},   // arg0 compare to auxInt
   333  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"},                    // arg0 compare to arg1, 32 bit
   334  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit
   335  		{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true},   // arg0 compare to -arg1, provided arg1 is not 1<<63
   336  		{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"},   // arg0 compare to -auxInt
   337  		{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags", commutative: true}, // arg0 compare to -arg1, 32 bit, provided arg1 is not 1<<31
   338  		{name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit
   339  		{name: "TST", argLength: 2, reg: gp2flags, asm: "TST", typ: "Flags", commutative: true},   // arg0 & arg1 compare to 0
   340  		{name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int64", typ: "Flags"},   // arg0 & auxInt compare to 0
   341  		{name: "TSTW", argLength: 2, reg: gp2flags, asm: "TSTW", typ: "Flags", commutative: true}, // arg0 & arg1 compare to 0, 32 bit
   342  		{name: "TSTWconst", argLength: 1, reg: gp1flags, asm: "TSTW", aux: "Int32", typ: "Flags"}, // arg0 & auxInt compare to 0, 32 bit
   343  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "FCMPS", typ: "Flags"},                  // arg0 compare to arg1, float32
   344  		{name: "FCMPD", argLength: 2, reg: fp2flags, asm: "FCMPD", typ: "Flags"},                  // arg0 compare to arg1, float64
   345  		{name: "FCMPS0", argLength: 1, reg: fp1flags, asm: "FCMPS", typ: "Flags"},                 // arg0 compare to 0, float32
   346  		{name: "FCMPD0", argLength: 1, reg: fp1flags, asm: "FCMPD", typ: "Flags"},                 // arg0 compare to 0, float64
   347  
   348  		// shifted ops
   349  		{name: "MVNshiftLL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64", earlyOk: true},    // ^(arg0<<auxInt), auxInt should be in the range 0 to 63.
   350  		{name: "MVNshiftRL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64", earlyOk: true},    // ^(arg0>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
   351  		{name: "MVNshiftRA", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64", earlyOk: true},    // ^(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63.
   352  		{name: "MVNshiftRO", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64", earlyOk: true},    // ^(arg0 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
   353  		{name: "NEGshiftLL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64", earlyOk: true},    // -(arg0<<auxInt), auxInt should be in the range 0 to 63.
   354  		{name: "NEGshiftRL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64", earlyOk: true},    // -(arg0>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
   355  		{name: "NEGshiftRA", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64", earlyOk: true},    // -(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63.
   356  		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64", earlyOk: true},    // arg0 + arg1<<auxInt, auxInt should be in the range 0 to 63.
   357  		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64", earlyOk: true},    // arg0 + arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
   358  		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64", earlyOk: true},    // arg0 + arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
   359  		{name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64", earlyOk: true},    // arg0 - arg1<<auxInt, auxInt should be in the range 0 to 63.
   360  		{name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64", earlyOk: true},    // arg0 - arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
   361  		{name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64", earlyOk: true},    // arg0 - arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
   362  		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64", earlyOk: true},    // arg0 & (arg1<<auxInt), auxInt should be in the range 0 to 63.
   363  		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64", earlyOk: true},    // arg0 & (arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
   364  		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64", earlyOk: true},    // arg0 & (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
   365  		{name: "ANDshiftRO", argLength: 2, reg: gp21, asm: "AND", aux: "Int64", earlyOk: true},    // arg0 & (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
   366  		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64", earlyOk: true},     // arg0 | arg1<<auxInt, auxInt should be in the range 0 to 63.
   367  		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64", earlyOk: true},     // arg0 | arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
   368  		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64", earlyOk: true},     // arg0 | arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
   369  		{name: "ORshiftRO", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64", earlyOk: true},     // arg0 | arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63.
   370  		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64", earlyOk: true},    // arg0 ^ arg1<<auxInt, auxInt should be in the range 0 to 63.
   371  		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64", earlyOk: true},    // arg0 ^ arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
   372  		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64", earlyOk: true},    // arg0 ^ arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
   373  		{name: "XORshiftRO", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64", earlyOk: true},    // arg0 ^ arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63.
   374  		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64", earlyOk: true},    // arg0 &^ (arg1<<auxInt), auxInt should be in the range 0 to 63.
   375  		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64", earlyOk: true},    // arg0 &^ (arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
   376  		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64", earlyOk: true},    // arg0 &^ (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
   377  		{name: "BICshiftRO", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64", earlyOk: true},    // arg0 &^ (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
   378  		{name: "EONshiftLL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64", earlyOk: true},    // arg0 ^ ^(arg1<<auxInt), auxInt should be in the range 0 to 63.
   379  		{name: "EONshiftRL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64", earlyOk: true},    // arg0 ^ ^(arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
   380  		{name: "EONshiftRA", argLength: 2, reg: gp21, asm: "EON", aux: "Int64", earlyOk: true},    // arg0 ^ ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
   381  		{name: "EONshiftRO", argLength: 2, reg: gp21, asm: "EON", aux: "Int64", earlyOk: true},    // arg0 ^ ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
   382  		{name: "ORNshiftLL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64", earlyOk: true},    // arg0 | ^(arg1<<auxInt), auxInt should be in the range 0 to 63.
   383  		{name: "ORNshiftRL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64", earlyOk: true},    // arg0 | ^(arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
   384  		{name: "ORNshiftRA", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64", earlyOk: true},    // arg0 | ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
   385  		{name: "ORNshiftRO", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64", earlyOk: true},    // arg0 | ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
   386  		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt, auxInt should be in the range 0 to 63.
   387  		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
   388  		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
   389  		{name: "CMNshiftLL", argLength: 2, reg: gp2flags, asm: "CMN", aux: "Int64", typ: "Flags"}, // (arg0 + arg1<<auxInt) compare to 0, auxInt should be in the range 0 to 63.
   390  		{name: "CMNshiftRL", argLength: 2, reg: gp2flags, asm: "CMN", aux: "Int64", typ: "Flags"}, // (arg0 + arg1>>auxInt) compare to 0, unsigned shift, auxInt should be in the range 0 to 63.
   391  		{name: "CMNshiftRA", argLength: 2, reg: gp2flags, asm: "CMN", aux: "Int64", typ: "Flags"}, // (arg0 + arg1>>auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
   392  		{name: "TSTshiftLL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1<<auxInt) compare to 0, auxInt should be in the range 0 to 63.
   393  		{name: "TSTshiftRL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, unsigned shift, auxInt should be in the range 0 to 63.
   394  		{name: "TSTshiftRA", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
   395  		{name: "TSTshiftRO", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1 ROR auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
   396  
   397  		// bitfield ops
   398  		// for all bitfield ops lsb is auxInt>>8, width is auxInt&0xff
   399  		// insert low width bits of arg1 into the result starting at bit lsb, copy other bits from arg0
   400  		{name: "BFI", argLength: 2, reg: gp21nog, asm: "BFI", aux: "ARM64BitField", resultInArg0: true, earlyOk: true},
   401  		// extract width bits of arg1 starting at bit lsb and insert at low end of result, copy other bits from arg0
   402  		{name: "BFXIL", argLength: 2, reg: gp21nog, asm: "BFXIL", aux: "ARM64BitField", resultInArg0: true, earlyOk: true},
   403  		// insert low width bits of arg0 into the result starting at bit lsb, bits to the left of the inserted bit field are set to the high/sign bit of the inserted bit field, bits to the right are zeroed
   404  		{name: "SBFIZ", argLength: 1, reg: gp11, asm: "SBFIZ", aux: "ARM64BitField", earlyOk: true},
   405  		// extract width bits of arg0 starting at bit lsb and insert at low end of result, remaining high bits are set to the high/sign bit of the extracted bitfield
   406  		{name: "SBFX", argLength: 1, reg: gp11, asm: "SBFX", aux: "ARM64BitField", earlyOk: true},
   407  		// insert low width bits of arg0 into the result starting at bit lsb, bits to the left and right of the inserted bit field are zeroed
   408  		{name: "UBFIZ", argLength: 1, reg: gp11, asm: "UBFIZ", aux: "ARM64BitField", earlyOk: true},
   409  		// extract width bits of arg0 starting at bit lsb and insert at low end of result, remaining high bits are zeroed
   410  		{name: "UBFX", argLength: 1, reg: gp11, asm: "UBFX", aux: "ARM64BitField", earlyOk: true},
   411  
   412  		// moves
   413  		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "UInt64", rematerializeable: true, earlyOk: true},      // 64 bits from auxint
   414  		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVS", typ: "Float32", rematerializeable: true, earlyOk: true}, // auxint as 64-bit float, convert to 32-bit float
   415  		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", typ: "Float64", rematerializeable: true, earlyOk: true}, // auxint as 64-bit float
   416  
   417  		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP").union(buildReg("SB"))}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr", earlyOk: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   418  
   419  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                        // load from arg0 + auxInt + aux.  arg1=mem.
   420  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true, zeroUpperBits: 56},  // load from arg0 + auxInt + aux.  arg1=mem.
   421  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                       // load from arg0 + auxInt + aux.  arg1=mem.
   422  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true, zeroUpperBits: 48}, // load from arg0 + auxInt + aux.  arg1=mem.
   423  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                       // load from arg0 + auxInt + aux.  arg1=mem.
   424  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true, zeroUpperBits: 32}, // load from arg0 + auxInt + aux.  arg1=mem.
   425  		{name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                      // load from arg0 + auxInt + aux.  arg1=mem.
   426  		{name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                   // load from arg0 + auxInt + aux.  arg1=mem.
   427  		{name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                   // load from arg0 + auxInt + aux.  arg1=mem.
   428  		{name: "FMOVQload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVQ", typ: "Vec128", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                    // load from arg0 + auxInt + aux.  arg1=mem.
   429  
   430  		// LDP instructions load the contents of two adjacent locations in memory into registers.
   431  		// Address to start loading is addr = arg0 + auxInt + aux.
   432  		// x := *(*T)(addr)
   433  		// y := *(*T)(addr+sizeof(T))
   434  		// arg1=mem
   435  		// Returns the tuple <x,y>.
   436  		{name: "LDP", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDP", typ: "(UInt64,UInt64)", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                      // T=int64 (gp reg destination)
   437  		{name: "LDPW", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDPW", typ: "(UInt32,UInt32)", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true, zeroUpperBits: 32}, // T=int32 (gp reg destination) unsigned extension
   438  		{name: "LDPSW", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDPSW", typ: "(Int32,Int32)", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                    // T=int32 (gp reg destination) signed extension
   439  		{name: "FLDPD", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPD", typ: "(Float64,Float64)", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                // T=float64 (fp reg destination)
   440  		{name: "FLDPS", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPS", typ: "(Float32,Float32)", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                // T=float32 (fp reg destination)
   441  		{name: "FLDPQ", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPQ", typ: "(Vec128,Vec128)", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},                  // T=vec128 (fp reg destination)
   442  
   443  		// register indexed load
   444  		{name: "MOVDloadidx", argLength: 3, reg: gp2load, asm: "MOVD", typ: "UInt64", addrSinkArg0: true, addrSinkArg1: true},                      // load 64-bit dword from arg0 + arg1, arg2 = mem.
   445  		{name: "MOVWloadidx", argLength: 3, reg: gp2load, asm: "MOVW", typ: "Int32", addrSinkArg0: true, addrSinkArg1: true},                       // load 32-bit word from arg0 + arg1, sign-extended to 64-bit, arg2=mem.
   446  		{name: "MOVWUloadidx", argLength: 3, reg: gp2load, asm: "MOVWU", typ: "UInt32", addrSinkArg0: true, addrSinkArg1: true, zeroUpperBits: 32}, // load 32-bit word from arg0 + arg1, zero-extended to 64-bit, arg2=mem.
   447  		{name: "MOVHloadidx", argLength: 3, reg: gp2load, asm: "MOVH", typ: "Int16", addrSinkArg0: true, addrSinkArg1: true},                       // load 16-bit word from arg0 + arg1, sign-extended to 64-bit, arg2=mem.
   448  		{name: "MOVHUloadidx", argLength: 3, reg: gp2load, asm: "MOVHU", typ: "UInt16", addrSinkArg0: true, addrSinkArg1: true, zeroUpperBits: 48}, // load 16-bit word from arg0 + arg1, zero-extended to 64-bit, arg2=mem.
   449  		{name: "MOVBloadidx", argLength: 3, reg: gp2load, asm: "MOVB", typ: "Int8", addrSinkArg0: true, addrSinkArg1: true},                        // load 8-bit word from arg0 + arg1, sign-extended to 64-bit, arg2=mem.
   450  		{name: "MOVBUloadidx", argLength: 3, reg: gp2load, asm: "MOVBU", typ: "UInt8", addrSinkArg0: true, addrSinkArg1: true, zeroUpperBits: 56},  // load 8-bit word from arg0 + arg1, zero-extended to 64-bit, arg2=mem.
   451  		{name: "FMOVSloadidx", argLength: 3, reg: fp2load, asm: "FMOVS", typ: "Float32", addrSinkArg0: true, addrSinkArg1: true},                   // load 32-bit float from arg0 + arg1, arg2=mem.
   452  		{name: "FMOVDloadidx", argLength: 3, reg: fp2load, asm: "FMOVD", typ: "Float64", addrSinkArg0: true, addrSinkArg1: true},                   // load 64-bit float from arg0 + arg1, arg2=mem.
   453  
   454  		// shifted register indexed load
   455  		{name: "MOVHloadidx2", argLength: 3, reg: gp2load, asm: "MOVH", typ: "Int16", addrSinkArg0: true},                       // load 16-bit half-word from arg0 + arg1*2, sign-extended to 64-bit, arg2=mem.
   456  		{name: "MOVHUloadidx2", argLength: 3, reg: gp2load, asm: "MOVHU", typ: "UInt16", addrSinkArg0: true, zeroUpperBits: 48}, // load 16-bit half-word from arg0 + arg1*2, zero-extended to 64-bit, arg2=mem.
   457  		{name: "MOVWloadidx4", argLength: 3, reg: gp2load, asm: "MOVW", typ: "Int32", addrSinkArg0: true},                       // load 32-bit word from arg0 + arg1*4, sign-extended to 64-bit, arg2=mem.
   458  		{name: "MOVWUloadidx4", argLength: 3, reg: gp2load, asm: "MOVWU", typ: "UInt32", addrSinkArg0: true, zeroUpperBits: 32}, // load 32-bit word from arg0 + arg1*4, zero-extended to 64-bit, arg2=mem.
   459  		{name: "MOVDloadidx8", argLength: 3, reg: gp2load, asm: "MOVD", typ: "UInt64", addrSinkArg0: true},                      // load 64-bit double-word from arg0 + arg1*8, arg2 = mem.
   460  		{name: "FMOVSloadidx4", argLength: 3, reg: fp2load, asm: "FMOVS", typ: "Float32", addrSinkArg0: true},                   // load 32-bit float from arg0 + arg1*4, arg2 = mem.
   461  		{name: "FMOVDloadidx8", argLength: 3, reg: fp2load, asm: "FMOVD", typ: "Float64", addrSinkArg0: true},                   // load 64-bit float from arg0 + arg1*8, arg2 = mem.
   462  
   463  		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},   // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   464  		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},   // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   465  		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},   // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   466  		{name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},   // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   467  		{name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   468  		{name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   469  		{name: "FMOVQstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVQ", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 16 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   470  
   471  		// STP instructions store the contents of two registers to adjacent locations in memory.
   472  		// Address to start storing is addr = arg0 + auxInt + aux.
   473  		// *(*T)(addr) = arg1
   474  		// *(*T)(addr+sizeof(T)) = arg2
   475  		// arg3=mem. Returns mem.
   476  		{name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},     // T=int64 (gp reg source)
   477  		{name: "STPW", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STPW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},   // T=int32 (gp reg source)
   478  		{name: "FSTPD", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // T=float64 (fp reg source)
   479  		{name: "FSTPS", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // T=float32 (fp reg source)
   480  		{name: "FSTPQ", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPQ", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // T=vec128 (fp reg source)
   481  
   482  		// register indexed store
   483  		{name: "MOVBstoreidx", argLength: 4, reg: gpstore2, asm: "MOVB", typ: "Mem", addrSinkArg0: true, addrSinkArg1: true},     // store 1 byte of arg2 to arg0 + arg1, arg3 = mem.
   484  		{name: "MOVHstoreidx", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem", addrSinkArg0: true, addrSinkArg1: true},     // store 2 bytes of arg2 to arg0 + arg1, arg3 = mem.
   485  		{name: "MOVWstoreidx", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem", addrSinkArg0: true, addrSinkArg1: true},     // store 4 bytes of arg2 to arg0 + arg1, arg3 = mem.
   486  		{name: "MOVDstoreidx", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem", addrSinkArg0: true, addrSinkArg1: true},     // store 8 bytes of arg2 to arg0 + arg1, arg3 = mem.
   487  		{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", typ: "Mem", addrSinkArg0: true, addrSinkArg1: true}, // store 32-bit float of arg2 to arg0 + arg1, arg3=mem.
   488  		{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", typ: "Mem", addrSinkArg0: true, addrSinkArg1: true}, // store 64-bit float of arg2 to arg0 + arg1, arg3=mem.
   489  
   490  		// shifted register indexed store
   491  		{name: "MOVHstoreidx2", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem", addrSinkArg0: true},     // store 2 bytes of arg2 to arg0 + arg1*2, arg3 = mem.
   492  		{name: "MOVWstoreidx4", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem", addrSinkArg0: true},     // store 4 bytes of arg2 to arg0 + arg1*4, arg3 = mem.
   493  		{name: "MOVDstoreidx8", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem", addrSinkArg0: true},     // store 8 bytes of arg2 to arg0 + arg1*8, arg3 = mem.
   494  		{name: "FMOVSstoreidx4", argLength: 4, reg: fpstoreidx, asm: "FMOVS", typ: "Mem", addrSinkArg0: true}, // store 32-bit float of arg2 to arg0 + arg1*4, arg3=mem.
   495  		{name: "FMOVDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "FMOVD", typ: "Mem", addrSinkArg0: true}, // store 64-bit float of arg2 to arg0 + arg1*8, arg3=mem.
   496  
   497  		{name: "FMOVDgpfp", argLength: 1, reg: gpfp, asm: "FMOVD", earlyOk: true},                    // move int64 to float64 (no conversion)
   498  		{name: "FMOVDfpgp", argLength: 1, reg: fpgp, asm: "FMOVD", earlyOk: true},                    // move float64 to int64 (no conversion)
   499  		{name: "FMOVSgpfp", argLength: 1, reg: gpfp, asm: "FMOVS", earlyOk: true},                    // move 32bits from int to float reg (no conversion)
   500  		{name: "FMOVSfpgp", argLength: 1, reg: fpgp, asm: "FMOVS", earlyOk: true, zeroUpperBits: 32}, // move 32bits from float to int reg, zero extend (no conversion)
   501  
   502  		// conversions
   503  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB", earlyOk: true},                      // move from arg0, sign-extended from byte
   504  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU", earlyOk: true, zeroUpperBits: 56}, // move from arg0, unsign-extended from byte
   505  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH", earlyOk: true},                      // move from arg0, sign-extended from half
   506  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU", earlyOk: true, zeroUpperBits: 48}, // move from arg0, unsign-extended from half
   507  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW", earlyOk: true},                      // move from arg0, sign-extended from word
   508  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU", earlyOk: true, zeroUpperBits: 32}, // move from arg0, unsign-extended from word
   509  		{name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOVD", earlyOk: true},                      // move from arg0
   510  
   511  		{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true, earlyOk: true}, // nop, return arg0 in same register
   512  
   513  		{name: "SCVTFWS", argLength: 1, reg: gpfp, asm: "SCVTFWS", earlyOk: true},                      // int32 -> float32
   514  		{name: "SCVTFWD", argLength: 1, reg: gpfp, asm: "SCVTFWD", earlyOk: true},                      // int32 -> float64
   515  		{name: "UCVTFWS", argLength: 1, reg: gpfp, asm: "UCVTFWS", earlyOk: true},                      // uint32 -> float32
   516  		{name: "UCVTFWD", argLength: 1, reg: gpfp, asm: "UCVTFWD", earlyOk: true},                      // uint32 -> float64
   517  		{name: "SCVTFS", argLength: 1, reg: gpfp, asm: "SCVTFS", earlyOk: true},                        // int64 -> float32
   518  		{name: "SCVTFD", argLength: 1, reg: gpfp, asm: "SCVTFD", earlyOk: true},                        // int64 -> float64
   519  		{name: "UCVTFS", argLength: 1, reg: gpfp, asm: "UCVTFS", earlyOk: true},                        // uint64 -> float32
   520  		{name: "UCVTFD", argLength: 1, reg: gpfp, asm: "UCVTFD", earlyOk: true},                        // uint64 -> float64
   521  		{name: "FCVTZSSW", argLength: 1, reg: fpgp, asm: "FCVTZSSW", earlyOk: true, zeroUpperBits: 32}, // float32 -> int32
   522  		{name: "FCVTZSDW", argLength: 1, reg: fpgp, asm: "FCVTZSDW", earlyOk: true, zeroUpperBits: 32}, // float64 -> int32
   523  		{name: "FCVTZUSW", argLength: 1, reg: fpgp, asm: "FCVTZUSW", earlyOk: true, zeroUpperBits: 32}, // float32 -> uint32
   524  		{name: "FCVTZUDW", argLength: 1, reg: fpgp, asm: "FCVTZUDW", earlyOk: true, zeroUpperBits: 32}, // float64 -> uint32
   525  		{name: "FCVTZSS", argLength: 1, reg: fpgp, asm: "FCVTZSS", earlyOk: true},                      // float32 -> int64
   526  		{name: "FCVTZSD", argLength: 1, reg: fpgp, asm: "FCVTZSD", earlyOk: true},                      // float64 -> int64
   527  		{name: "FCVTZUS", argLength: 1, reg: fpgp, asm: "FCVTZUS", earlyOk: true},                      // float32 -> uint64
   528  		{name: "FCVTZUD", argLength: 1, reg: fpgp, asm: "FCVTZUD", earlyOk: true},                      // float64 -> uint64
   529  		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD", earlyOk: true},                        // float32 -> float64
   530  		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS", earlyOk: true},                        // float64 -> float32
   531  
   532  		// 64-bit floating-point round to integers in 64-bit FP format
   533  		{name: "FRINTAD", argLength: 1, reg: fp11, asm: "FRINTAD", earlyOk: true}, // Round (ties Away from zero; 0.5 -> 1, -0.5 -> -1)
   534  		{name: "FRINTMD", argLength: 1, reg: fp11, asm: "FRINTMD", earlyOk: true}, // Floor (towards Minus; 0.5 -> 0, -0.5 -> -1)
   535  		{name: "FRINTND", argLength: 1, reg: fp11, asm: "FRINTND", earlyOk: true}, // Round (ties to even; ; 0.5 -> 0, 1.5 -> 2)
   536  		{name: "FRINTPD", argLength: 1, reg: fp11, asm: "FRINTPD", earlyOk: true}, // Ceil (towards Positive; 0.5 -> 1, -0.5 -> 0)
   537  		{name: "FRINTZD", argLength: 1, reg: fp11, asm: "FRINTZD", earlyOk: true}, // Trunc (towards Zero; 0.5 -> 0, -0.5 -> 0))
   538  		// 32-bit floating-point round to integers in 32-bit FP format
   539  		{name: "FRINTAS", argLength: 1, reg: fp11, asm: "FRINTAS", earlyOk: true}, // Round (ties Away from zero; 0.5 -> 1, -0.5 -> -1)
   540  		{name: "FRINTMS", argLength: 1, reg: fp11, asm: "FRINTMS", earlyOk: true}, // Floor (towards Minus; 0.5 -> 0, -0.5 -> -1)
   541  		{name: "FRINTNS", argLength: 1, reg: fp11, asm: "FRINTNS", earlyOk: true}, // Round (ties to even; ; 0.5 -> 0, 1.5 -> 2)
   542  		{name: "FRINTPS", argLength: 1, reg: fp11, asm: "FRINTPS", earlyOk: true}, // Ceil (towards Positive; 0.5 -> 1, -0.5 -> 0)
   543  		{name: "FRINTZS", argLength: 1, reg: fp11, asm: "FRINTZS", earlyOk: true}, // Trunc (towards Zero; 0.5 -> 0, -0.5 -> 0))
   544  
   545  		// conditional instructions; auxint is
   546  		// one of the arm64 comparison pseudo-ops (LessThan, LessThanU, etc.)
   547  		{name: "CSEL", argLength: 3, reg: gp2flags1, asm: "CSEL", aux: "CCop", earlyOk: true},   // auxint(flags) ? arg0 : arg1
   548  		{name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop", earlyOk: true},  // auxint(flags) ? arg0 : 0
   549  		{name: "CSINC", argLength: 3, reg: gp2flags1, asm: "CSINC", aux: "CCop", earlyOk: true}, // auxint(flags) ? arg0 : arg1 + 1
   550  		{name: "CSINV", argLength: 3, reg: gp2flags1, asm: "CSINV", aux: "CCop", earlyOk: true}, // auxint(flags) ? arg0 : ^arg1
   551  		{name: "CSNEG", argLength: 3, reg: gp2flags1, asm: "CSNEG", aux: "CCop", earlyOk: true}, // auxint(flags) ? arg0 : -arg1
   552  		{name: "FCSELD", argLength: 3, reg: fp21, asm: "FCSELD", aux: "CCop", earlyOk: true},    // auxint(flags) ? arg0 : arg1, 64-bit float
   553  		{name: "FCSELS", argLength: 3, reg: fp21, asm: "FCSELS", aux: "CCop", earlyOk: true},    // auxint(flags) ? arg0 : arg1, 32-bit float
   554  		{name: "CSETM", argLength: 1, reg: readflags, asm: "CSETM", aux: "CCop", earlyOk: true}, // auxint(flags) ? -1 : 0
   555  
   556  		// conditional comparison instructions; auxint is
   557  		// combination of Cond, Nzcv and optional ConstValue
   558  		// Behavior:
   559  		//   If the condition 'Cond' evaluates to true against current flags,
   560  		//   flags are set to the result of the comparison operation.
   561  		//   Otherwise, flags are set to the fallback value 'Nzcv'.
   562  		{name: "CCMP", argLength: 3, reg: gp2flagsflags, asm: "CCMP", aux: "ARM64ConditionalParams", typ: "Flags"},      // If Cond then flags = CMP arg0 arg1 else flags = Nzcv
   563  		{name: "CCMN", argLength: 3, reg: gp2flagsflags, asm: "CCMN", aux: "ARM64ConditionalParams", typ: "Flags"},      // If Cond then flags = CMN arg0 arg1 else flags = Nzcv
   564  		{name: "CCMPconst", argLength: 2, reg: gp1flagsflags, asm: "CCMP", aux: "ARM64ConditionalParams", typ: "Flags"}, // If Cond then flags = CMPconst [ConstValue] arg0 else flags = Nzcv
   565  		{name: "CCMNconst", argLength: 2, reg: gp1flagsflags, asm: "CCMN", aux: "ARM64ConditionalParams", typ: "Flags"}, // If Cond then flags = CMNconst [ConstValue] arg0 else flags = Nzcv
   566  
   567  		{name: "CCMPW", argLength: 3, reg: gp2flagsflags, asm: "CCMPW", aux: "ARM64ConditionalParams", typ: "Flags"},      // If Cond then flags = CMPW arg0 arg1 else flags = Nzcv
   568  		{name: "CCMNW", argLength: 3, reg: gp2flagsflags, asm: "CCMNW", aux: "ARM64ConditionalParams", typ: "Flags"},      // If Cond then flags = CMNW arg0 arg1 else flags = Nzcv
   569  		{name: "CCMPWconst", argLength: 2, reg: gp1flagsflags, asm: "CCMPW", aux: "ARM64ConditionalParams", typ: "Flags"}, // If Cond then flags = CCMPWconst [ConstValue] arg0 else flags = Nzcv
   570  		{name: "CCMNWconst", argLength: 2, reg: gp1flagsflags, asm: "CCMNW", aux: "ARM64ConditionalParams", typ: "Flags"}, // If Cond then flags = CCMNWconst [ConstValue] arg0 else flags = Nzcv
   571  
   572  		// function calls
   573  		{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                       // call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
   574  		{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},                                         // tail call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
   575  		{name: "CALLtailinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},             // tail call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
   576  		{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), regMask{}}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
   577  		{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                 // call fn by pointer.  arg0=codeptr, last arg=mem, auxint=argsize, returns mem
   578  
   579  		// pseudo-ops
   580  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true},                                                                                                                                                      // panic if arg0 is nil.  arg1=mem.
   581  		{name: "LoweredMemEq", argLength: 4, reg: regInfo{inputs: []regMask{buildReg("R0"), buildReg("R1"), buildReg("R2")}, outputs: []regMask{buildReg("R0")}, clobbers: callerSave}, typ: "Bool", faultOnNilArg0: true, faultOnNilArg1: true, clobberFlags: true, call: true}, // arg0, arg1 - pointers to memory, arg2=size, arg3=mem.
   582  
   583  		{name: "Equal", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},            // bool, true flags encode x==y false otherwise.
   584  		{name: "NotEqual", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},         // bool, true flags encode x!=y false otherwise.
   585  		{name: "LessThan", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},         // bool, true flags encode signed x<y false otherwise.
   586  		{name: "LessEqual", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},        // bool, true flags encode signed x<=y false otherwise.
   587  		{name: "GreaterThan", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},      // bool, true flags encode signed x>y false otherwise.
   588  		{name: "GreaterEqual", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},     // bool, true flags encode signed x>=y false otherwise.
   589  		{name: "LessThanU", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},        // bool, true flags encode unsigned x<y false otherwise.
   590  		{name: "LessEqualU", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},       // bool, true flags encode unsigned x<=y false otherwise.
   591  		{name: "GreaterThanU", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},     // bool, true flags encode unsigned x>y false otherwise.
   592  		{name: "GreaterEqualU", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},    // bool, true flags encode unsigned x>=y false otherwise.
   593  		{name: "LessThanF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},        // bool, true flags encode floating-point x<y false otherwise.
   594  		{name: "LessEqualF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},       // bool, true flags encode floating-point x<=y false otherwise.
   595  		{name: "GreaterThanF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},     // bool, true flags encode floating-point x>y false otherwise.
   596  		{name: "GreaterEqualF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},    // bool, true flags encode floating-point x>=y false otherwise.
   597  		{name: "NotLessThanF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},     // bool, true flags encode floating-point x>=y || x is unordered with y, false otherwise.
   598  		{name: "NotLessEqualF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},    // bool, true flags encode floating-point x>y || x is unordered with y, false otherwise.
   599  		{name: "NotGreaterThanF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},  // bool, true flags encode floating-point x<=y || x is unordered with y, false otherwise.
   600  		{name: "NotGreaterEqualF", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56}, // bool, true flags encode floating-point x<y || x is unordered with y, false otherwise.
   601  		{name: "LessThanNoov", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56},     // bool, true flags encode signed x<y but without honoring overflow, false otherwise.
   602  		{name: "GreaterEqualNoov", argLength: 1, reg: readflags, earlyOk: true, zeroUpperBits: 56}, // bool, true flags encode signed x>=y but without honoring overflow, false otherwise.
   603  
   604  		// medium zeroing
   605  		// arg0 = address of memory to zero
   606  		// arg1 = mem
   607  		// auxint = # of bytes to zero
   608  		// returns mem
   609  		{
   610  			name:      "LoweredZero",
   611  			aux:       "Int64",
   612  			argLength: 2,
   613  			reg: regInfo{
   614  				inputs: []regMask{gp},
   615  			},
   616  			faultOnNilArg0: true,
   617  			addrSinkArg0:   true,
   618  		},
   619  
   620  		// large zeroing
   621  		// arg0 = address of memory to zero
   622  		// arg1 = mem
   623  		// auxint = # of bytes to zero
   624  		// returns mem
   625  		{
   626  			name:      "LoweredZeroLoop",
   627  			aux:       "Int64",
   628  			argLength: 2,
   629  			reg: regInfo{
   630  				inputs:       []regMask{gp},
   631  				clobbersArg0: true,
   632  			},
   633  			faultOnNilArg0: true,
   634  			addrSinkArg0:   true,
   635  			needIntTemp:    true,
   636  		},
   637  
   638  		// medium copying
   639  		// arg0 = address of dst memory
   640  		// arg1 = address of src memory
   641  		// arg2 = mem
   642  		// auxint = # of bytes to copy
   643  		// returns mem
   644  		{
   645  			name:      "LoweredMove",
   646  			aux:       "Int64",
   647  			argLength: 3,
   648  			reg: regInfo{
   649  				inputs:   []regMask{gp.minus(r25), gp.minus(r25)},
   650  				clobbers: r25.union(f16to17), // TODO: figure out needIntTemp + x2 for floats
   651  			},
   652  			faultOnNilArg0: true,
   653  			faultOnNilArg1: true,
   654  			addrSinkArg0:   true,
   655  			addrSinkArg1:   true,
   656  		},
   657  
   658  		// large copying
   659  		// arg0 = address of dst memory
   660  		// arg1 = address of src memory
   661  		// arg2 = mem
   662  		// auxint = # of bytes to copy
   663  		// returns mem
   664  		{
   665  			name:      "LoweredMoveLoop",
   666  			aux:       "Int64",
   667  			argLength: 3,
   668  			reg: regInfo{
   669  				inputs:       []regMask{gp.minus(r24to25), gp.minus(r24to25)},
   670  				clobbers:     r24to25.union(f16to17), // TODO: figure out needIntTemp x2 + x2 for floats
   671  				clobbersArg0: true,
   672  				clobbersArg1: true,
   673  			},
   674  			faultOnNilArg0: true,
   675  			faultOnNilArg1: true,
   676  			addrSinkArg0:   true,
   677  			addrSinkArg1:   true,
   678  		},
   679  
   680  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   681  		// and sorts it to the very beginning of the block to prevent other
   682  		// use of R26 (arm64.REGCTXT, the closure pointer)
   683  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}, zeroWidth: true},
   684  
   685  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem
   686  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   687  
   688  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   689  		// I.e., if f calls g "calls" sys.GetCallerPC,
   690  		// the result should be the PC within f that g will return to.
   691  		// See runtime/stubs.go for a more detailed discussion.
   692  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   693  
   694  		// Constant flag value.
   695  		// Note: there's an "unordered" outcome for floating-point
   696  		// comparisons, but we don't use such a beast yet.
   697  		// This op is for temporary use by rewrite rules. It
   698  		// cannot appear in the generated assembly.
   699  		{name: "FlagConstant", aux: "FlagConstant"},
   700  
   701  		// (InvertFlags (CMP a b)) == (CMP b a)
   702  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   703  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   704  
   705  		// atomic loads.
   706  		// load from arg0. arg1=mem. auxint must be zero.
   707  		// returns <value,memory> so they can be properly ordered with other loads.
   708  		{name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true},
   709  		{name: "LDARB", argLength: 2, reg: gpload, asm: "LDARB", faultOnNilArg0: true, zeroUpperBits: 56},
   710  		{name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true, zeroUpperBits: 32},
   711  
   712  		// atomic stores.
   713  		// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
   714  		{name: "STLRB", argLength: 3, reg: gpstore, asm: "STLRB", faultOnNilArg0: true, hasSideEffects: true},
   715  		{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true, hasSideEffects: true},
   716  		{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true, hasSideEffects: true},
   717  
   718  		// atomic exchange.
   719  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   720  		// LDAXR	(Rarg0), Rout
   721  		// STLXR	Rarg1, (Rarg0), Rtmp
   722  		// CBNZ		Rtmp, -2(PC)
   723  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   724  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 32},
   725  		{name: "LoweredAtomicExchange8", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   726  
   727  		// atomic exchange variant.
   728  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   729  		// SWPALD	Rarg1, (Rarg0), Rout
   730  		{name: "LoweredAtomicExchange64Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   731  		{name: "LoweredAtomicExchange32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, zeroUpperBits: 32},
   732  		{name: "LoweredAtomicExchange8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   733  
   734  		// atomic add.
   735  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
   736  		// LDAXR	(Rarg0), Rout
   737  		// ADD		Rarg1, Rout
   738  		// STLXR	Rout, (Rarg0), Rtmp
   739  		// CBNZ		Rtmp, -3(PC)
   740  		// Unlike the other 32-bit atomics, no zeroUpperBits: the final write
   741  		// to Rout is the 64-bit ADD.
   742  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   743  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   744  
   745  		// atomic add variant.
   746  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
   747  		// LDADDAL	(Rarg0), Rarg1, Rout
   748  		// ADD		Rarg1, Rout
   749  		{name: "LoweredAtomicAdd64Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   750  		{name: "LoweredAtomicAdd32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   751  
   752  		// atomic compare and swap.
   753  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   754  		// if *arg0 == arg1 {
   755  		//   *arg0 = arg2
   756  		//   return (true, memory)
   757  		// } else {
   758  		//   return (false, memory)
   759  		// }
   760  		// LDAXR	(Rarg0), Rtmp
   761  		// CMP		Rarg1, Rtmp
   762  		// BNE		3(PC)
   763  		// STLXR	Rarg2, (Rarg0), Rtmp
   764  		// CBNZ		Rtmp, -4(PC)
   765  		// CSET		EQ, Rout
   766  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   767  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   768  
   769  		// atomic compare and swap variant.
   770  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   771  		// if *arg0 == arg1 {
   772  		//   *arg0 = arg2
   773  		//   return (true, memory)
   774  		// } else {
   775  		//   return (false, memory)
   776  		// }
   777  		// MOV  	Rarg1, Rtmp
   778  		// CASAL	Rtmp, (Rarg0), Rarg2
   779  		// CMP  	Rarg1, Rtmp
   780  		// CSET 	EQ, Rout
   781  		{name: "LoweredAtomicCas64Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   782  		{name: "LoweredAtomicCas32Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   783  
   784  		// atomic and/or.
   785  		// *arg0 &= (|=) arg1. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   786  		// LDAXR	(Rarg0), Rout
   787  		// AND/OR	Rarg1, Rout, tempReg
   788  		// STLXR	tempReg, (Rarg0), Rtmp
   789  		// CBNZ		Rtmp, -3(PC)
   790  		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, needIntTemp: true, zeroUpperBits: 56},
   791  		{name: "LoweredAtomicOr8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, needIntTemp: true, zeroUpperBits: 56},
   792  		{name: "LoweredAtomicAnd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, needIntTemp: true},
   793  		{name: "LoweredAtomicOr64", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, needIntTemp: true},
   794  		{name: "LoweredAtomicAnd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, needIntTemp: true, zeroUpperBits: 32},
   795  		{name: "LoweredAtomicOr32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, needIntTemp: true, zeroUpperBits: 32},
   796  
   797  		// atomic and/or variant.
   798  		// *arg0 &= (|=) arg1. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   799  		//   AND:
   800  		// MNV       Rarg1, Rtemp
   801  		// LDANDALB  Rtemp, (Rarg0), Rout
   802  		//   OR:
   803  		// LDORALB  Rarg1, (Rarg0), Rout
   804  		{name: "LoweredAtomicAnd8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 56},
   805  		{name: "LoweredAtomicOr8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, zeroUpperBits: 56},
   806  		{name: "LoweredAtomicAnd64Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   807  		{name: "LoweredAtomicOr64Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   808  		{name: "LoweredAtomicAnd32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, zeroUpperBits: 32},
   809  		{name: "LoweredAtomicOr32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, zeroUpperBits: 32},
   810  
   811  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   812  		// It saves all GP registers if necessary,
   813  		// but clobbers R30 (LR) because it's a call.
   814  		// R16 and R17 may be clobbered by linker trampoline.
   815  		// Returns a pointer to a write barrier buffer in R25.
   816  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: callerSave.minus(gpg).union(buildReg("R16 R17 R30")), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
   817  
   818  		// LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
   819  		// the RC and CR versions are used when one of the arguments is a constant. CC is used
   820  		// when both are constant (normally both 0, as prove derives the fact that a [0] bounds
   821  		// failure means the length must have also been 0).
   822  		// AuxInt contains a report code (see PanicBounds in genericOps.go).
   823  		{name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{first16, first16}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
   824  		{name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=x, arg1=mem, returns memory.
   825  		{name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=y, arg1=mem, returns memory.
   826  		{name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true},                            // arg0=mem, returns memory.
   827  
   828  		// Prefetch instruction
   829  		// Do prefetch arg0 address with option aux. arg0=addr, arg1=memory, aux=option.
   830  		{name: "PRFM", argLength: 2, aux: "Int64", reg: prefreg, asm: "PRFM", hasSideEffects: true},
   831  
   832  		// Publication barrier
   833  		{name: "DMB", argLength: 1, aux: "Int64", asm: "DMB", hasSideEffects: true},       // Do data barrier. arg0=memory, aux=option.
   834  		{name: "ZERO", zeroWidth: true, fixedReg: true, earlyOk: true, zeroUpperBits: 56}, // reads-as-zero register
   835  
   836  		// Broadcast constant to each lane of a SIMD register. aux=constant.
   837  		// TODO: add the other arrangements after assembler supports them, to be used in simdgen-generated opt rules.
   838  		{name: "VMOVI16B", argLength: 0, reg: fp01, asm: "VMOVI", aux: "UInt8", commutative: false, typ: "Vec128", resultInArg0: false},
   839  
   840  		// SVE whole-register (unpredicated, VL-scaled) load/store of a scalable
   841  		// vector. These lower generic Load/Store of a 256-bit SIMD value; the
   842  		// scalable Z bank reuses the fp register masks.
   843  		{name: "ZLDRload", argLength: 2, reg: fpload, aux: "SymOff", asm: "ZLDR", typ: "Vec256", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   844  		{name: "ZSTRstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "ZSTR", faultOnNilArg0: true, symEffect: "Write"},             // store arg1 to arg0 + auxInt + aux.  arg2=mem.
   845  		{name: "ZSELB", argLength: 3, reg: fp2predfp, asm: "ZSEL", typ: "Vec256"},                                                         // arg0=x, arg1=y, arg2=predicate; per-element select, constructive.
   846  		{name: "ZSELH", argLength: 3, reg: fp2predfp, asm: "ZSEL", typ: "Vec256"},                                                         // arg0=x, arg1=y, arg2=predicate; per-element select, constructive.
   847  		{name: "ZSELS", argLength: 3, reg: fp2predfp, asm: "ZSEL", typ: "Vec256"},                                                         // arg0=x, arg1=y, arg2=predicate; per-element select, constructive.
   848  		{name: "ZSELD", argLength: 3, reg: fp2predfp, asm: "ZSEL", typ: "Vec256"},                                                         // arg0=x, arg1=y, arg2=predicate; per-element select, constructive.
   849  		{name: "PLDRload", argLength: 2, reg: predload, aux: "SymOff", asm: "PLDR", typ: "Mask", faultOnNilArg0: true, symEffect: "Read"}, // load a predicate from arg0 + auxInt + aux.  arg1=mem.
   850  		{name: "PSTRstore", argLength: 3, reg: predstore, aux: "SymOff", asm: "PSTR", faultOnNilArg0: true, symEffect: "Write"},           // store predicate arg1 to arg0 + auxInt + aux.  arg2=mem.
   851  		// PPFALSEB sets every bit of a predicate false, it's the zero value of a predicate.
   852  		{name: "PPFALSEB", argLength: 0, reg: pred01, asm: "PPFALSE", typ: "Mask"},
   853  		// ZDUPBconst broadcasts an 8-bit immediate to every byte lane; with [0] it
   854  		// zeroes a whole scalable vector, lowering ZeroSIMD for a 256-bit value.
   855  		{name: "ZDUPBconst", argLength: 0, aux: "Int8", reg: fp01, asm: "ZDUP", typ: "Vec256"},
   856  		// RDVL reads the architecture vector length in bytes (aux = scale). Used
   857  		// at package init to verify the hardware VL fits the fixed 256-bit model.
   858  		{name: "RDVL", argLength: 0, aux: "Int64", reg: gp01, asm: "RDVL", typ: "Int64"},
   859  
   860  		{name: "PWHILELTB", argLength: 2, reg: gp2pred, asm: "PWHILELT", typ: "(Mask,Flags)"},                                                       // arg0=lo, arg1=hi; predicate enabling byte (.B) lanes [lo,hi).
   861  		{name: "PWHILELTH", argLength: 2, reg: gp2pred, asm: "PWHILELT", typ: "(Mask,Flags)"},                                                       // as PWHILELTB but for .H (16-bit) lanes.
   862  		{name: "PWHILELTS", argLength: 2, reg: gp2pred, asm: "PWHILELT", typ: "(Mask,Flags)"},                                                       // as PWHILELTB but for .S (32-bit) lanes.
   863  		{name: "PWHILELTD", argLength: 2, reg: gp2pred, asm: "PWHILELT", typ: "(Mask,Flags)"},                                                       // as PWHILELTB but for .D (64-bit) lanes.
   864  		{name: "ZLD1BPredload", argLength: 3, reg: fppredload, aux: "SymOff", asm: "ZLD1B", typ: "Vec256", faultOnNilArg0: true, symEffect: "Read"}, // predicated load from arg0+auxInt+aux governed by arg1; arg2=mem.
   865  		{name: "ZST1BPredstore", argLength: 4, reg: fppredstore, aux: "SymOff", asm: "ZST1B", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // predicated store of arg1 to arg0+auxInt+aux governed by arg2; arg3=mem.
   866  	}
   867  
   868  	blocks := []blockData{
   869  		{name: "EQ", controls: 1},
   870  		{name: "NE", controls: 1},
   871  		{name: "LT", controls: 1},
   872  		{name: "LE", controls: 1},
   873  		{name: "GT", controls: 1},
   874  		{name: "GE", controls: 1},
   875  		{name: "ULT", controls: 1},
   876  		{name: "ULE", controls: 1},
   877  		{name: "UGT", controls: 1},
   878  		{name: "UGE", controls: 1},
   879  		{name: "Z", controls: 1},                  // Control == 0 (take a register instead of flags)
   880  		{name: "NZ", controls: 1},                 // Control != 0
   881  		{name: "ZW", controls: 1},                 // Control == 0, 32-bit
   882  		{name: "NZW", controls: 1},                // Control != 0, 32-bit
   883  		{name: "TBZ", controls: 1, aux: "Int64"},  // Control & (1 << AuxInt) == 0
   884  		{name: "TBNZ", controls: 1, aux: "Int64"}, // Control & (1 << AuxInt) != 0
   885  		{name: "FLT", controls: 1},
   886  		{name: "FLE", controls: 1},
   887  		{name: "FGT", controls: 1},
   888  		{name: "FGE", controls: 1},
   889  		{name: "LTnoov", controls: 1}, // 'LT' but without honoring overflow
   890  		{name: "LEnoov", controls: 1}, // 'LE' but without honoring overflow
   891  		{name: "GTnoov", controls: 1}, // 'GT' but without honoring overflow
   892  		{name: "GEnoov", controls: 1}, // 'GE' but without honoring overflow
   893  
   894  		// JUMPTABLE implements jump tables.
   895  		// Aux is the symbol (an *obj.LSym) for the jump table.
   896  		// control[0] is the index into the jump table.
   897  		// control[1] is the address of the jump table (the address of the symbol stored in Aux).
   898  		{name: "JUMPTABLE", controls: 2, aux: "Sym"},
   899  	}
   900  
   901  	archs = append(archs, arch{
   902  		name:               "ARM64",
   903  		pkg:                "cmd/internal/obj/arm64",
   904  		genfile:            "../../arm64/ssa.go",
   905  		genSIMDfile:        "../../arm64/simdssa.go ../../arm64/simdssa_sve.go",
   906  		ops:                append(append(ops, simdARM64Ops(fp11, fp21, fp31, fpgp, fpgpfp, fp21)...), simdARM64SVEOps(fp11, fp21, fp1predfp, fp2predpred, fp2predfp, fp2predfp, fp3predfp)...),
   907  		blocks:             blocks,
   908  		regnames:           regNamesARM64,
   909  		ParamIntRegNames:   "R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15",
   910  		ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
   911  		gpregmask:          gp,
   912  		fpregmask:          fp,
   913  		simdregmask:        fp,
   914  		specialregmask:     pred,
   915  		framepointerreg:    -1, // not used
   916  		linkreg:            int8(num["R30"]),
   917  	})
   918  }
   919  

View as plain text