github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/cmd/compile/internal/ssa/gen/ARM64Ops.go (about)

     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  // +build ignore
     6  
     7  package main
     8  
     9  import "strings"
    10  
    11  // Notes:
    12  //  - Integer types live in the low portion of registers. Upper portions are junk.
    13  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    14  //    Upper bytes are junk.
    15  //  - *const instructions may use a constant larger than the instruction can encode.
    16  //    In this case the assembler expands to multiple instructions and uses tmp
    17  //    register (R27).
    18  
    19  // Suffixes encode the bit width of various instructions.
    20  // D (double word) = 64 bit
    21  // W (word)        = 32 bit
    22  // H (half word)   = 16 bit
    23  // HU              = 16 bit unsigned
    24  // B (byte)        = 8 bit
    25  // BU              = 8 bit unsigned
    26  // S (single)      = 32 bit float
    27  // D (double)      = 64 bit float
    28  
    29  // Note: registers not used in regalloc are not included in this list,
    30  // so that regmask stays within int64
    31  // Be careful when hand coding regmasks.
    32  var regNamesARM64 = []string{
    33  	"R0",
    34  	"R1",
    35  	"R2",
    36  	"R3",
    37  	"R4",
    38  	"R5",
    39  	"R6",
    40  	"R7",
    41  	"R8",
    42  	"R9",
    43  	"R10",
    44  	"R11",
    45  	"R12",
    46  	"R13",
    47  	"R14",
    48  	"R15",
    49  	"R16",
    50  	"R17",
    51  	"R18", // platform register, not used
    52  	"R19",
    53  	"R20",
    54  	"R21",
    55  	"R22",
    56  	"R23",
    57  	"R24",
    58  	"R25",
    59  	"R26",
    60  	// R27 = REGTMP not used in regalloc
    61  	"g",   // aka R28
    62  	"R29", // frame pointer, not used
    63  	"R30", // aka REGLINK
    64  	"SP",  // aka R31
    65  
    66  	"F0",
    67  	"F1",
    68  	"F2",
    69  	"F3",
    70  	"F4",
    71  	"F5",
    72  	"F6",
    73  	"F7",
    74  	"F8",
    75  	"F9",
    76  	"F10",
    77  	"F11",
    78  	"F12",
    79  	"F13",
    80  	"F14",
    81  	"F15",
    82  	"F16",
    83  	"F17",
    84  	"F18",
    85  	"F19",
    86  	"F20",
    87  	"F21",
    88  	"F22",
    89  	"F23",
    90  	"F24",
    91  	"F25",
    92  	"F26",
    93  	"F27",
    94  	"F28",
    95  	"F29",
    96  	"F30",
    97  	"F31",
    98  
    99  	// pseudo-registers
   100  	"SB",
   101  }
   102  
   103  func init() {
   104  	// Make map from reg names to reg integers.
   105  	if len(regNamesARM64) > 64 {
   106  		panic("too many registers")
   107  	}
   108  	num := map[string]int{}
   109  	for i, name := range regNamesARM64 {
   110  		num[name] = i
   111  	}
   112  	buildReg := func(s string) regMask {
   113  		m := regMask(0)
   114  		for _, r := range strings.Split(s, " ") {
   115  			if n, ok := num[r]; ok {
   116  				m |= regMask(1) << uint(n)
   117  				continue
   118  			}
   119  			panic("register " + r + " not found")
   120  		}
   121  		return m
   122  	}
   123  
   124  	// Common individual register masks
   125  	var (
   126  		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")
   127  		gpg        = gp | buildReg("g")
   128  		gpsp       = gp | buildReg("SP")
   129  		gpspg      = gpg | buildReg("SP")
   130  		gpspsbg    = gpspg | buildReg("SB")
   131  		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")
   132  		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   133  	)
   134  	// Common regInfo
   135  	var (
   136  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   137  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   138  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   139  		gp1flags  = regInfo{inputs: []regMask{gpg}}
   140  		gp1flags1 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   141  		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   142  		gp2flags  = regInfo{inputs: []regMask{gpg, gpg}}
   143  		gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
   144  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   145  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   146  		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
   147  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   148  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   149  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   150  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   151  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   152  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   153  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   154  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   155  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   156  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   157  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   158  	)
   159  	ops := []opData{
   160  		// binary ops
   161  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},     // arg0 + arg1
   162  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64"},   // arg0 + auxInt
   163  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                        // arg0 - arg1
   164  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64"},     // arg0 - auxInt
   165  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true},     // arg0 * arg1
   166  		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true},   // arg0 * arg1, 32-bit
   167  		{name: "MULH", argLength: 2, reg: gp21, asm: "SMULH", commutative: true},  // (arg0 * arg1) >> 64, signed
   168  		{name: "UMULH", argLength: 2, reg: gp21, asm: "UMULH", commutative: true}, // (arg0 * arg1) >> 64, unsigned
   169  		{name: "MULL", argLength: 2, reg: gp21, asm: "SMULL", commutative: true},  // arg0 * arg1, signed, 32-bit mult results in 64-bit
   170  		{name: "UMULL", argLength: 2, reg: gp21, asm: "UMULL", commutative: true}, // arg0 * arg1, unsigned, 32-bit mult results in 64-bit
   171  		{name: "DIV", argLength: 2, reg: gp21, asm: "SDIV"},                       // arg0 / arg1, signed
   172  		{name: "UDIV", argLength: 2, reg: gp21, asm: "UDIV"},                      // arg0 / arg1, unsighed
   173  		{name: "DIVW", argLength: 2, reg: gp21, asm: "SDIVW"},                     // arg0 / arg1, signed, 32 bit
   174  		{name: "UDIVW", argLength: 2, reg: gp21, asm: "UDIVW"},                    // arg0 / arg1, unsighed, 32 bit
   175  		{name: "MOD", argLength: 2, reg: gp21, asm: "REM"},                        // arg0 % arg1, signed
   176  		{name: "UMOD", argLength: 2, reg: gp21, asm: "UREM"},                      // arg0 % arg1, unsigned
   177  		{name: "MODW", argLength: 2, reg: gp21, asm: "REMW"},                      // arg0 % arg1, signed, 32 bit
   178  		{name: "UMODW", argLength: 2, reg: gp21, asm: "UREMW"},                    // arg0 % arg1, unsigned, 32 bit
   179  
   180  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0 + arg1
   181  		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true}, // arg0 + arg1
   182  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"},                    // arg0 - arg1
   183  		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD"},                    // arg0 - arg1
   184  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0 * arg1
   185  		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true}, // arg0 * arg1
   186  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"},                    // arg0 / arg1
   187  		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD"},                    // arg0 / arg1
   188  
   189  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
   190  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt
   191  		{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true},  // arg0 | arg1
   192  		{name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int64"},  // arg0 | auxInt
   193  		{name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true}, // arg0 ^ arg1
   194  		{name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int64"}, // arg0 ^ auxInt
   195  		{name: "BIC", argLength: 2, reg: gp21, asm: "BIC"},                    // arg0 &^ arg1
   196  		{name: "BICconst", argLength: 1, reg: gp11, asm: "BIC", aux: "Int64"}, // arg0 &^ auxInt
   197  
   198  		// unary ops
   199  		{name: "MVN", argLength: 1, reg: gp11, asm: "MVN"},       // ^arg0
   200  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},       // -arg0
   201  		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS"},   // -arg0, float32
   202  		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD"},   // -arg0, float64
   203  		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD"}, // sqrt(arg0), float64
   204  		{name: "REV", argLength: 1, reg: gp11, asm: "REV"},       // byte reverse, 64-bit
   205  		{name: "REVW", argLength: 1, reg: gp11, asm: "REVW"},     // byte reverse, 32-bit
   206  		{name: "REV16W", argLength: 1, reg: gp11, asm: "REV16W"}, // byte reverse in each 16-bit halfword, 32-bit
   207  		{name: "RBIT", argLength: 1, reg: gp11, asm: "RBIT"},     // bit reverse, 64-bit
   208  		{name: "RBITW", argLength: 1, reg: gp11, asm: "RBITW"},   // bit reverse, 32-bit
   209  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},       // count leading zero, 64-bit
   210  		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW"},     // count leading zero, 32-bit
   211  
   212  		// shifts
   213  		{name: "SLL", argLength: 2, reg: gp21, asm: "LSL"},                      // arg0 << arg1, shift amount is mod 64
   214  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "LSL", aux: "Int64"},   // arg0 << auxInt
   215  		{name: "SRL", argLength: 2, reg: gp21, asm: "LSR"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
   216  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "LSR", aux: "Int64"},   // arg0 >> auxInt, unsigned
   217  		{name: "SRA", argLength: 2, reg: gp21, asm: "ASR"},                      // arg0 >> arg1, signed, shift amount is mod 64
   218  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "ASR", aux: "Int64"},   // arg0 >> auxInt, signed
   219  		{name: "RORconst", argLength: 1, reg: gp11, asm: "ROR", aux: "Int64"},   // arg0 right rotate by auxInt bits
   220  		{name: "RORWconst", argLength: 1, reg: gp11, asm: "RORW", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits
   221  
   222  		// comparisons
   223  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},                      // arg0 compare to arg1
   224  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"},   // arg0 compare to auxInt
   225  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"},                    // arg0 compare to arg1, 32 bit
   226  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit
   227  		{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags"},                      // arg0 compare to -arg1
   228  		{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"},   // arg0 compare to -auxInt
   229  		{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags"},                    // arg0 compare to -arg1, 32 bit
   230  		{name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit
   231  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "FCMPS", typ: "Flags"},                  // arg0 compare to arg1, float32
   232  		{name: "FCMPD", argLength: 2, reg: fp2flags, asm: "FCMPD", typ: "Flags"},                  // arg0 compare to arg1, float64
   233  
   234  		// shifted ops
   235  		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1<<auxInt
   236  		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, unsigned shift
   237  		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, signed shift
   238  		{name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1<<auxInt
   239  		{name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, unsigned shift
   240  		{name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, signed shift
   241  		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1<<auxInt)
   242  		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), unsigned shift
   243  		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), signed shift
   244  		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1<<auxInt
   245  		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, unsigned shift
   246  		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, signed shift
   247  		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1<<auxInt
   248  		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, unsigned shift
   249  		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, signed shift
   250  		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1<<auxInt)
   251  		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), unsigned shift
   252  		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), signed shift
   253  		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt
   254  		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift
   255  		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift
   256  
   257  		// moves
   258  		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "UInt64", rematerializeable: true},      // 32 low bits of auxint
   259  		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVS", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   260  		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   261  
   262  		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   263  
   264  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},      // load from arg0 + auxInt + aux.  arg1=mem.
   265  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   266  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   267  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   268  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   269  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   270  		{name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   271  		{name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   272  		{name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   273  
   274  		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   275  		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   276  		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   277  		{name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   278  		{name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   279  		{name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   280  
   281  		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
   282  		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   283  		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   284  		{name: "MOVDstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
   285  
   286  		// conversions
   287  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   288  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   289  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   290  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   291  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   292  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   293  		{name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOVD"},   // move from arg0
   294  
   295  		{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   296  
   297  		{name: "SCVTFWS", argLength: 1, reg: gpfp, asm: "SCVTFWS"},   // int32 -> float32
   298  		{name: "SCVTFWD", argLength: 1, reg: gpfp, asm: "SCVTFWD"},   // int32 -> float64
   299  		{name: "UCVTFWS", argLength: 1, reg: gpfp, asm: "UCVTFWS"},   // uint32 -> float32
   300  		{name: "UCVTFWD", argLength: 1, reg: gpfp, asm: "UCVTFWD"},   // uint32 -> float64
   301  		{name: "SCVTFS", argLength: 1, reg: gpfp, asm: "SCVTFS"},     // int64 -> float32
   302  		{name: "SCVTFD", argLength: 1, reg: gpfp, asm: "SCVTFD"},     // int64 -> float64
   303  		{name: "UCVTFS", argLength: 1, reg: gpfp, asm: "UCVTFS"},     // uint64 -> float32
   304  		{name: "UCVTFD", argLength: 1, reg: gpfp, asm: "UCVTFD"},     // uint64 -> float64
   305  		{name: "FCVTZSSW", argLength: 1, reg: fpgp, asm: "FCVTZSSW"}, // float32 -> int32
   306  		{name: "FCVTZSDW", argLength: 1, reg: fpgp, asm: "FCVTZSDW"}, // float64 -> int32
   307  		{name: "FCVTZUSW", argLength: 1, reg: fpgp, asm: "FCVTZUSW"}, // float32 -> uint32
   308  		{name: "FCVTZUDW", argLength: 1, reg: fpgp, asm: "FCVTZUDW"}, // float64 -> uint32
   309  		{name: "FCVTZSS", argLength: 1, reg: fpgp, asm: "FCVTZSS"},   // float32 -> int64
   310  		{name: "FCVTZSD", argLength: 1, reg: fpgp, asm: "FCVTZSD"},   // float64 -> int64
   311  		{name: "FCVTZUS", argLength: 1, reg: fpgp, asm: "FCVTZUS"},   // float32 -> uint64
   312  		{name: "FCVTZUD", argLength: 1, reg: fpgp, asm: "FCVTZUD"},   // float64 -> uint64
   313  		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD"},     // float32 -> float64
   314  		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS"},     // float64 -> float32
   315  
   316  		// conditional instructions
   317  		{name: "CSELULT", argLength: 3, reg: gp2flags1, asm: "CSEL"},  // returns arg0 if flags indicates unsigned LT, arg1 otherwise, arg2=flags
   318  		{name: "CSELULT0", argLength: 2, reg: gp1flags1, asm: "CSEL"}, // returns arg0 if flags indicates unsigned LT, 0 otherwise, arg1=flags
   319  
   320  		// function calls
   321  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"},                           // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   322  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   323  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   324  
   325  		// pseudo-ops
   326  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   327  
   328  		{name: "Equal", argLength: 1, reg: readflags},         // bool, true flags encode x==y false otherwise.
   329  		{name: "NotEqual", argLength: 1, reg: readflags},      // bool, true flags encode x!=y false otherwise.
   330  		{name: "LessThan", argLength: 1, reg: readflags},      // bool, true flags encode signed x<y false otherwise.
   331  		{name: "LessEqual", argLength: 1, reg: readflags},     // bool, true flags encode signed x<=y false otherwise.
   332  		{name: "GreaterThan", argLength: 1, reg: readflags},   // bool, true flags encode signed x>y false otherwise.
   333  		{name: "GreaterEqual", argLength: 1, reg: readflags},  // bool, true flags encode signed x>=y false otherwise.
   334  		{name: "LessThanU", argLength: 1, reg: readflags},     // bool, true flags encode unsigned x<y false otherwise.
   335  		{name: "LessEqualU", argLength: 1, reg: readflags},    // bool, true flags encode unsigned x<=y false otherwise.
   336  		{name: "GreaterThanU", argLength: 1, reg: readflags},  // bool, true flags encode unsigned x>y false otherwise.
   337  		{name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise.
   338  
   339  		// duffzero
   340  		// arg0 = address of memory to zero
   341  		// arg1 = mem
   342  		// auxint = offset into duffzero code to start executing
   343  		// returns mem
   344  		// R16 aka arm64.REGRT1 changed as side effect
   345  		{
   346  			name:      "DUFFZERO",
   347  			aux:       "Int64",
   348  			argLength: 2,
   349  			reg: regInfo{
   350  				inputs:   []regMask{gp},
   351  				clobbers: buildReg("R16 R30"),
   352  			},
   353  			faultOnNilArg0: true,
   354  		},
   355  
   356  		// large zeroing
   357  		// arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect)
   358  		// arg1 = address of the last element to zero
   359  		// arg2 = mem
   360  		// returns mem
   361  		//	MOVD.P	ZR, 8(R16)
   362  		//	CMP	Rarg1, R16
   363  		//	BLE	-2(PC)
   364  		// Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled.
   365  		// the-end-of-the-memory - 8 is with the area to zero, ok to spill.
   366  		{
   367  			name:      "LoweredZero",
   368  			argLength: 3,
   369  			reg: regInfo{
   370  				inputs:   []regMask{buildReg("R16"), gp},
   371  				clobbers: buildReg("R16"),
   372  			},
   373  			clobberFlags:   true,
   374  			faultOnNilArg0: true,
   375  		},
   376  
   377  		// duffcopy
   378  		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
   379  		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
   380  		// arg2 = mem
   381  		// auxint = offset into duffcopy code to start executing
   382  		// returns mem
   383  		// R16, R17 changed as side effect
   384  		{
   385  			name:      "DUFFCOPY",
   386  			aux:       "Int64",
   387  			argLength: 3,
   388  			reg: regInfo{
   389  				inputs:   []regMask{buildReg("R17"), buildReg("R16")},
   390  				clobbers: buildReg("R16 R17 R30"),
   391  			},
   392  			faultOnNilArg0: true,
   393  			faultOnNilArg1: true,
   394  		},
   395  
   396  		// large move
   397  		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
   398  		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
   399  		// arg2 = address of the last element of src
   400  		// arg3 = mem
   401  		// returns mem
   402  		//	MOVD.P	8(R16), Rtmp
   403  		//	MOVD.P	Rtmp, 8(R17)
   404  		//	CMP	Rarg2, R16
   405  		//	BLE	-3(PC)
   406  		// Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled.
   407  		// the-end-of-src - 8 is within the area to copy, ok to spill.
   408  		{
   409  			name:      "LoweredMove",
   410  			argLength: 4,
   411  			reg: regInfo{
   412  				inputs:   []regMask{buildReg("R17"), buildReg("R16"), gp},
   413  				clobbers: buildReg("R16 R17"),
   414  			},
   415  			clobberFlags:   true,
   416  			faultOnNilArg0: true,
   417  			faultOnNilArg1: true,
   418  		},
   419  
   420  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   421  		// and sorts it to the very beginning of the block to prevent other
   422  		// use of R26 (arm64.REGCTXT, the closure pointer)
   423  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}},
   424  
   425  		// MOVDconvert converts between pointers and integers.
   426  		// We have a special op for this so as to not confuse GC
   427  		// (particularly stack maps).  It takes a memory arg so it
   428  		// gets correctly ordered with respect to GC safepoints.
   429  		// arg0=ptr/int arg1=mem, output=int/ptr
   430  		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
   431  
   432  		// Constant flag values. For any comparison, there are 5 possible
   433  		// outcomes: the three from the signed total order (<,==,>) and the
   434  		// three from the unsigned total order. The == cases overlap.
   435  		// Note: there's a sixth "unordered" outcome for floating-point
   436  		// comparisons, but we don't use such a beast yet.
   437  		// These ops are for temporary use by rewrite rules. They
   438  		// cannot appear in the generated assembly.
   439  		{name: "FlagEQ"},     // equal
   440  		{name: "FlagLT_ULT"}, // signed < and unsigned <
   441  		{name: "FlagLT_UGT"}, // signed < and unsigned >
   442  		{name: "FlagGT_UGT"}, // signed > and unsigned <
   443  		{name: "FlagGT_ULT"}, // signed > and unsigned >
   444  
   445  		// (InvertFlags (CMP a b)) == (CMP b a)
   446  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   447  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   448  
   449  		// atomic loads.
   450  		// load from arg0. arg1=mem. auxint must be zero.
   451  		// returns <value,memory> so they can be properly ordered with other loads.
   452  		{name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true},
   453  		{name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true},
   454  
   455  		// atomic stores.
   456  		// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
   457  		{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true, hasSideEffects: true},
   458  		{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true, hasSideEffects: true},
   459  
   460  		// atomic exchange.
   461  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   462  		// LDAXR	(Rarg0), Rout
   463  		// STLXR	Rarg1, (Rarg0), Rtmp
   464  		// CBNZ		Rtmp, -2(PC)
   465  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   466  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   467  
   468  		// atomic add.
   469  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
   470  		// LDAXR	(Rarg0), Rout
   471  		// ADD		Rarg1, Rout
   472  		// STLXR	Rout, (Rarg0), Rtmp
   473  		// CBNZ		Rtmp, -3(PC)
   474  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   475  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   476  
   477  		// atomic compare and swap.
   478  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   479  		// if *arg0 == arg1 {
   480  		//   *arg0 = arg2
   481  		//   return (true, memory)
   482  		// } else {
   483  		//   return (false, memory)
   484  		// }
   485  		// LDAXR	(Rarg0), Rtmp
   486  		// CMP		Rarg1, Rtmp
   487  		// BNE		3(PC)
   488  		// STLXR	Rarg2, (Rarg0), Rtmp
   489  		// CBNZ		Rtmp, -4(PC)
   490  		// CSET		EQ, Rout
   491  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   492  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   493  
   494  		// atomic and/or.
   495  		// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
   496  		// LDAXRB	(Rarg0), Rtmp
   497  		// AND/OR	Rarg1, Rtmp
   498  		// STLXRB	Rtmp, (Rarg0), Rtmp
   499  		// CBNZ		Rtmp, -3(PC)
   500  		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true},
   501  		{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true},
   502  	}
   503  
   504  	blocks := []blockData{
   505  		{name: "EQ"},
   506  		{name: "NE"},
   507  		{name: "LT"},
   508  		{name: "LE"},
   509  		{name: "GT"},
   510  		{name: "GE"},
   511  		{name: "ULT"},
   512  		{name: "ULE"},
   513  		{name: "UGT"},
   514  		{name: "UGE"},
   515  		{name: "Z"},   // Control == 0 (take a register instead of flags)
   516  		{name: "NZ"},  // Control != 0
   517  		{name: "ZW"},  // Control == 0, 32-bit
   518  		{name: "NZW"}, // Control != 0, 32-bit
   519  	}
   520  
   521  	archs = append(archs, arch{
   522  		name:            "ARM64",
   523  		pkg:             "cmd/internal/obj/arm64",
   524  		genfile:         "../../arm64/ssa.go",
   525  		ops:             ops,
   526  		blocks:          blocks,
   527  		regnames:        regNamesARM64,
   528  		gpregmask:       gp,
   529  		fpregmask:       fp,
   530  		framepointerreg: -1, // not used
   531  		linkreg:         int8(num["R30"]),
   532  	})
   533  }