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