Home | History | Annotate | Download | only in gen
      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}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
    263 
    264 		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},      // load from arg0 + auxInt + aux.  arg1=mem.
    265 		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
    266 		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
    267 		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
    268 		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
    269 		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
    270 		{name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
    271 		{name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
    272 		{name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
    273 
    274 		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true},   // 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},   // 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},   // 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},   // 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}, // 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}, // 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}, // 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}, // 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}, // 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}, // 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},                                              // call static function aux.(*gc.Sym).  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: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                // call deferproc.  arg0=mem, auxint=argsize, returns mem
    324 		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                   // call newproc.  arg0=mem, auxint=argsize, returns mem
    325 		{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
    326 
    327 		// pseudo-ops
    328 		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
    329 
    330 		{name: "Equal", argLength: 1, reg: readflags},         // bool, true flags encode x==y false otherwise.
    331 		{name: "NotEqual", argLength: 1, reg: readflags},      // bool, true flags encode x!=y false otherwise.
    332 		{name: "LessThan", argLength: 1, reg: readflags},      // bool, true flags encode signed x<y false otherwise.
    333 		{name: "LessEqual", argLength: 1, reg: readflags},     // bool, true flags encode signed x<=y false otherwise.
    334 		{name: "GreaterThan", argLength: 1, reg: readflags},   // bool, true flags encode signed x>y false otherwise.
    335 		{name: "GreaterEqual", argLength: 1, reg: readflags},  // bool, true flags encode signed x>=y false otherwise.
    336 		{name: "LessThanU", argLength: 1, reg: readflags},     // bool, true flags encode unsigned x<y false otherwise.
    337 		{name: "LessEqualU", argLength: 1, reg: readflags},    // bool, true flags encode unsigned x<=y false otherwise.
    338 		{name: "GreaterThanU", argLength: 1, reg: readflags},  // bool, true flags encode unsigned x>y false otherwise.
    339 		{name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise.
    340 
    341 		// duffzero
    342 		// arg0 = address of memory to zero
    343 		// arg1 = mem
    344 		// auxint = offset into duffzero code to start executing
    345 		// returns mem
    346 		// R16 aka arm64.REGRT1 changed as side effect
    347 		{
    348 			name:      "DUFFZERO",
    349 			aux:       "Int64",
    350 			argLength: 2,
    351 			reg: regInfo{
    352 				inputs:   []regMask{gp},
    353 				clobbers: buildReg("R16 R30"),
    354 			},
    355 			faultOnNilArg0: true,
    356 		},
    357 
    358 		// large zeroing
    359 		// arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect)
    360 		// arg1 = address of the last element to zero
    361 		// arg2 = mem
    362 		// returns mem
    363 		//	MOVD.P	ZR, 8(R16)
    364 		//	CMP	Rarg1, R16
    365 		//	BLE	-2(PC)
    366 		// Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled.
    367 		// the-end-of-the-memory - 8 is with the area to zero, ok to spill.
    368 		{
    369 			name:      "LoweredZero",
    370 			argLength: 3,
    371 			reg: regInfo{
    372 				inputs:   []regMask{buildReg("R16"), gp},
    373 				clobbers: buildReg("R16"),
    374 			},
    375 			clobberFlags:   true,
    376 			faultOnNilArg0: true,
    377 		},
    378 
    379 		// duffcopy
    380 		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
    381 		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
    382 		// arg2 = mem
    383 		// auxint = offset into duffcopy code to start executing
    384 		// returns mem
    385 		// R16, R17 changed as side effect
    386 		{
    387 			name:      "DUFFCOPY",
    388 			aux:       "Int64",
    389 			argLength: 3,
    390 			reg: regInfo{
    391 				inputs:   []regMask{buildReg("R17"), buildReg("R16")},
    392 				clobbers: buildReg("R16 R17 R30"),
    393 			},
    394 			faultOnNilArg0: true,
    395 			faultOnNilArg1: true,
    396 		},
    397 
    398 		// large move
    399 		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
    400 		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
    401 		// arg2 = address of the last element of src
    402 		// arg3 = mem
    403 		// returns mem
    404 		//	MOVD.P	8(R16), Rtmp
    405 		//	MOVD.P	Rtmp, 8(R17)
    406 		//	CMP	Rarg2, R16
    407 		//	BLE	-3(PC)
    408 		// Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled.
    409 		// the-end-of-src - 8 is within the area to copy, ok to spill.
    410 		{
    411 			name:      "LoweredMove",
    412 			argLength: 4,
    413 			reg: regInfo{
    414 				inputs:   []regMask{buildReg("R17"), buildReg("R16"), gp},
    415 				clobbers: buildReg("R16 R17"),
    416 			},
    417 			clobberFlags:   true,
    418 			faultOnNilArg0: true,
    419 			faultOnNilArg1: true,
    420 		},
    421 
    422 		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
    423 		// and sorts it to the very beginning of the block to prevent other
    424 		// use of R26 (arm64.REGCTXT, the closure pointer)
    425 		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}},
    426 
    427 		// MOVDconvert converts between pointers and integers.
    428 		// We have a special op for this so as to not confuse GC
    429 		// (particularly stack maps).  It takes a memory arg so it
    430 		// gets correctly ordered with respect to GC safepoints.
    431 		// arg0=ptr/int arg1=mem, output=int/ptr
    432 		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
    433 
    434 		// Constant flag values. For any comparison, there are 5 possible
    435 		// outcomes: the three from the signed total order (<,==,>) and the
    436 		// three from the unsigned total order. The == cases overlap.
    437 		// Note: there's a sixth "unordered" outcome for floating-point
    438 		// comparisons, but we don't use such a beast yet.
    439 		// These ops are for temporary use by rewrite rules. They
    440 		// cannot appear in the generated assembly.
    441 		{name: "FlagEQ"},     // equal
    442 		{name: "FlagLT_ULT"}, // signed < and unsigned <
    443 		{name: "FlagLT_UGT"}, // signed < and unsigned >
    444 		{name: "FlagGT_UGT"}, // signed > and unsigned <
    445 		{name: "FlagGT_ULT"}, // signed > and unsigned >
    446 
    447 		// (InvertFlags (CMP a b)) == (CMP b a)
    448 		// InvertFlags is a pseudo-op which can't appear in assembly output.
    449 		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
    450 
    451 		// atomic loads.
    452 		// load from arg0. arg1=mem. auxint must be zero.
    453 		// returns <value,memory> so they can be properly ordered with other loads.
    454 		{name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true},
    455 		{name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true},
    456 
    457 		// atomic stores.
    458 		// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
    459 		{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true},
    460 		{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true},
    461 
    462 		// atomic exchange.
    463 		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
    464 		// LDAXR	(Rarg0), Rout
    465 		// STLXR	Rarg1, (Rarg0), Rtmp
    466 		// CBNZ		Rtmp, -2(PC)
    467 		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
    468 		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
    469 
    470 		// atomic add.
    471 		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
    472 		// LDAXR	(Rarg0), Rout
    473 		// ADD		Rarg1, Rout
    474 		// STLXR	Rout, (Rarg0), Rtmp
    475 		// CBNZ		Rtmp, -3(PC)
    476 		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
    477 		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
    478 
    479 		// atomic compare and swap.
    480 		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
    481 		// if *arg0 == arg1 {
    482 		//   *arg0 = arg2
    483 		//   return (true, memory)
    484 		// } else {
    485 		//   return (false, memory)
    486 		// }
    487 		// LDAXR	(Rarg0), Rtmp
    488 		// CMP		Rarg1, Rtmp
    489 		// BNE		3(PC)
    490 		// STLXR	Rarg2, (Rarg0), Rtmp
    491 		// CBNZ		Rtmp, -4(PC)
    492 		// CSET		EQ, Rout
    493 		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
    494 		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
    495 
    496 		// atomic and/or.
    497 		// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
    498 		// LDAXRB	(Rarg0), Rtmp
    499 		// AND/OR	Rarg1, Rtmp
    500 		// STLXRB	Rtmp, (Rarg0), Rtmp
    501 		// CBNZ		Rtmp, -3(PC)
    502 		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true},
    503 		{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true},
    504 	}
    505 
    506 	blocks := []blockData{
    507 		{name: "EQ"},
    508 		{name: "NE"},
    509 		{name: "LT"},
    510 		{name: "LE"},
    511 		{name: "GT"},
    512 		{name: "GE"},
    513 		{name: "ULT"},
    514 		{name: "ULE"},
    515 		{name: "UGT"},
    516 		{name: "UGE"},
    517 		{name: "Z"},   // Control == 0 (take a register instead of flags)
    518 		{name: "NZ"},  // Control != 0
    519 		{name: "ZW"},  // Control == 0, 32-bit
    520 		{name: "NZW"}, // Control != 0, 32-bit
    521 	}
    522 
    523 	archs = append(archs, arch{
    524 		name:            "ARM64",
    525 		pkg:             "cmd/internal/obj/arm64",
    526 		genfile:         "../../arm64/ssa.go",
    527 		ops:             ops,
    528 		blocks:          blocks,
    529 		regnames:        regNamesARM64,
    530 		gpregmask:       gp,
    531 		fpregmask:       fp,
    532 		framepointerreg: -1, // not used
    533 		linkreg:         int8(num["R30"]),
    534 	})
    535 }
    536