github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/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 gpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}} 148 gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} 149 gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}} 150 fp01 = regInfo{inputs: nil, outputs: []regMask{fp}} 151 fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}} 152 fpgp = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}} 153 gpfp = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}} 154 fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}} 155 fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: []regMask{fp}} 156 fp2flags = regInfo{inputs: []regMask{fp, fp}} 157 fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} 158 fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} 159 readflags = regInfo{inputs: nil, outputs: []regMask{gp}} 160 ) 161 ops := []opData{ 162 // binary ops 163 {name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1 164 {name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64"}, // arg0 + auxInt 165 {name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0 - arg1 166 {name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64"}, // arg0 - auxInt 167 {name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true}, // arg0 * arg1 168 {name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true}, // arg0 * arg1, 32-bit 169 {name: "MNEG", argLength: 2, reg: gp21, asm: "MNEG", commutative: true}, // -arg0 * arg1 170 {name: "MNEGW", argLength: 2, reg: gp21, asm: "MNEGW", commutative: true}, // -arg0 * arg1, 32-bit 171 {name: "MULH", argLength: 2, reg: gp21, asm: "SMULH", commutative: true}, // (arg0 * arg1) >> 64, signed 172 {name: "UMULH", argLength: 2, reg: gp21, asm: "UMULH", commutative: true}, // (arg0 * arg1) >> 64, unsigned 173 {name: "MULL", argLength: 2, reg: gp21, asm: "SMULL", commutative: true}, // arg0 * arg1, signed, 32-bit mult results in 64-bit 174 {name: "UMULL", argLength: 2, reg: gp21, asm: "UMULL", commutative: true}, // arg0 * arg1, unsigned, 32-bit mult results in 64-bit 175 {name: "DIV", argLength: 2, reg: gp21, asm: "SDIV"}, // arg0 / arg1, signed 176 {name: "UDIV", argLength: 2, reg: gp21, asm: "UDIV"}, // arg0 / arg1, unsighed 177 {name: "DIVW", argLength: 2, reg: gp21, asm: "SDIVW"}, // arg0 / arg1, signed, 32 bit 178 {name: "UDIVW", argLength: 2, reg: gp21, asm: "UDIVW"}, // arg0 / arg1, unsighed, 32 bit 179 {name: "MOD", argLength: 2, reg: gp21, asm: "REM"}, // arg0 % arg1, signed 180 {name: "UMOD", argLength: 2, reg: gp21, asm: "UREM"}, // arg0 % arg1, unsigned 181 {name: "MODW", argLength: 2, reg: gp21, asm: "REMW"}, // arg0 % arg1, signed, 32 bit 182 {name: "UMODW", argLength: 2, reg: gp21, asm: "UREMW"}, // arg0 % arg1, unsigned, 32 bit 183 184 {name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0 + arg1 185 {name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true}, // arg0 + arg1 186 {name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"}, // arg0 - arg1 187 {name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD"}, // arg0 - arg1 188 {name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0 * arg1 189 {name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true}, // arg0 * arg1 190 {name: "FNMULS", argLength: 2, reg: fp21, asm: "FNMULS", commutative: true}, // -(arg0 * arg1) 191 {name: "FNMULD", argLength: 2, reg: fp21, asm: "FNMULD", commutative: true}, // -(arg0 * arg1) 192 {name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"}, // arg0 / arg1 193 {name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD"}, // arg0 / arg1 194 195 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 196 {name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt 197 {name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true}, // arg0 | arg1 198 {name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int64"}, // arg0 | auxInt 199 {name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true}, // arg0 ^ arg1 200 {name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int64"}, // arg0 ^ auxInt 201 {name: "BIC", argLength: 2, reg: gp21, asm: "BIC"}, // arg0 &^ arg1 202 {name: "BICconst", argLength: 1, reg: gp11, asm: "BIC", aux: "Int64"}, // arg0 &^ auxInt 203 204 // unary ops 205 {name: "MVN", argLength: 1, reg: gp11, asm: "MVN"}, // ^arg0 206 {name: "NEG", argLength: 1, reg: gp11, asm: "NEG"}, // -arg0 207 {name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS"}, // -arg0, float32 208 {name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD"}, // -arg0, float64 209 {name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD"}, // sqrt(arg0), float64 210 {name: "REV", argLength: 1, reg: gp11, asm: "REV"}, // byte reverse, 64-bit 211 {name: "REVW", argLength: 1, reg: gp11, asm: "REVW"}, // byte reverse, 32-bit 212 {name: "REV16W", argLength: 1, reg: gp11, asm: "REV16W"}, // byte reverse in each 16-bit halfword, 32-bit 213 {name: "RBIT", argLength: 1, reg: gp11, asm: "RBIT"}, // bit reverse, 64-bit 214 {name: "RBITW", argLength: 1, reg: gp11, asm: "RBITW"}, // bit reverse, 32-bit 215 {name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"}, // count leading zero, 64-bit 216 {name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW"}, // count leading zero, 32-bit 217 {name: "VCNT", argLength: 1, reg: fp11, asm: "VCNT"}, // count set bits for each 8-bit unit and store the result in each 8-bit unit 218 {name: "VUADDLV", argLength: 1, reg: fp11, asm: "VUADDLV"}, // unsigned sum of eight bytes in a 64-bit value, zero extended to 64-bit. 219 {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true}, 220 {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true}, 221 222 // 3-operand, the addend comes first 223 {name: "FMADDS", argLength: 3, reg: fp31, asm: "FMADDS"}, // +arg0 + (arg1 * arg2) 224 {name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD"}, // +arg0 + (arg1 * arg2) 225 {name: "FNMADDS", argLength: 3, reg: fp31, asm: "FNMADDS"}, // -arg0 - (arg1 * arg2) 226 {name: "FNMADDD", argLength: 3, reg: fp31, asm: "FNMADDD"}, // -arg0 - (arg1 * arg2) 227 {name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS"}, // +arg0 - (arg1 * arg2) 228 {name: "FMSUBD", argLength: 3, reg: fp31, asm: "FMSUBD"}, // +arg0 - (arg1 * arg2) 229 {name: "FNMSUBS", argLength: 3, reg: fp31, asm: "FNMSUBS"}, // -arg0 + (arg1 * arg2) 230 {name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD"}, // -arg0 + (arg1 * arg2) 231 232 // shifts 233 {name: "SLL", argLength: 2, reg: gp21, asm: "LSL"}, // arg0 << arg1, shift amount is mod 64 234 {name: "SLLconst", argLength: 1, reg: gp11, asm: "LSL", aux: "Int64"}, // arg0 << auxInt 235 {name: "SRL", argLength: 2, reg: gp21, asm: "LSR"}, // arg0 >> arg1, unsigned, shift amount is mod 64 236 {name: "SRLconst", argLength: 1, reg: gp11, asm: "LSR", aux: "Int64"}, // arg0 >> auxInt, unsigned 237 {name: "SRA", argLength: 2, reg: gp21, asm: "ASR"}, // arg0 >> arg1, signed, shift amount is mod 64 238 {name: "SRAconst", argLength: 1, reg: gp11, asm: "ASR", aux: "Int64"}, // arg0 >> auxInt, signed 239 {name: "RORconst", argLength: 1, reg: gp11, asm: "ROR", aux: "Int64"}, // arg0 right rotate by auxInt bits 240 {name: "RORWconst", argLength: 1, reg: gp11, asm: "RORW", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits 241 242 // comparisons 243 {name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1 244 {name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to auxInt 245 {name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1, 32 bit 246 {name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit 247 {name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags"}, // arg0 compare to -arg1 248 {name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"}, // arg0 compare to -auxInt 249 {name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags"}, // arg0 compare to -arg1, 32 bit 250 {name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit 251 {name: "FCMPS", argLength: 2, reg: fp2flags, asm: "FCMPS", typ: "Flags"}, // arg0 compare to arg1, float32 252 {name: "FCMPD", argLength: 2, reg: fp2flags, asm: "FCMPD", typ: "Flags"}, // arg0 compare to arg1, float64 253 254 // shifted ops 255 {name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"}, // arg0 + arg1<<auxInt 256 {name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"}, // arg0 + arg1>>auxInt, unsigned shift 257 {name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"}, // arg0 + arg1>>auxInt, signed shift 258 {name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"}, // arg0 - arg1<<auxInt 259 {name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"}, // arg0 - arg1>>auxInt, unsigned shift 260 {name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"}, // arg0 - arg1>>auxInt, signed shift 261 {name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1<<auxInt) 262 {name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1>>auxInt), unsigned shift 263 {name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1>>auxInt), signed shift 264 {name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1<<auxInt 265 {name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1>>auxInt, unsigned shift 266 {name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1>>auxInt, signed shift 267 {name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1<<auxInt 268 {name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1>>auxInt, unsigned shift 269 {name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1>>auxInt, signed shift 270 {name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1<<auxInt) 271 {name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1>>auxInt), unsigned shift 272 {name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1>>auxInt), signed shift 273 {name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt 274 {name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift 275 {name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift 276 277 // moves 278 {name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "UInt64", rematerializeable: true}, // 32 low bits of auxint 279 {name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVS", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float 280 {name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float 281 282 {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 283 284 {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 285 {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 286 {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 287 {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 288 {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 289 {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 290 {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 291 {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 292 {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 293 294 {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. 295 {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. 296 {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. 297 {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. 298 {name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of arg1 and arg2 to arg0 + auxInt + aux. arg3=mem. 299 {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. 300 {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. 301 302 {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. 303 {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. 304 {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. 305 {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. arg1=mem. 306 {name: "MOVQstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of zero to arg0 + auxInt + aux. arg1=mem. 307 308 {name: "FMOVDgpfp", argLength: 1, reg: gpfp, asm: "FMOVD"}, // move int64 to float64 (no conversion) 309 {name: "FMOVDfpgp", argLength: 1, reg: fpgp, asm: "FMOVD"}, // move float64 to int64 (no conversion) 310 311 // conversions 312 {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"}, // move from arg0, sign-extended from byte 313 {name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte 314 {name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"}, // move from arg0, sign-extended from half 315 {name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half 316 {name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"}, // move from arg0, sign-extended from word 317 {name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word 318 {name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOVD"}, // move from arg0 319 320 {name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register 321 322 {name: "SCVTFWS", argLength: 1, reg: gpfp, asm: "SCVTFWS"}, // int32 -> float32 323 {name: "SCVTFWD", argLength: 1, reg: gpfp, asm: "SCVTFWD"}, // int32 -> float64 324 {name: "UCVTFWS", argLength: 1, reg: gpfp, asm: "UCVTFWS"}, // uint32 -> float32 325 {name: "UCVTFWD", argLength: 1, reg: gpfp, asm: "UCVTFWD"}, // uint32 -> float64 326 {name: "SCVTFS", argLength: 1, reg: gpfp, asm: "SCVTFS"}, // int64 -> float32 327 {name: "SCVTFD", argLength: 1, reg: gpfp, asm: "SCVTFD"}, // int64 -> float64 328 {name: "UCVTFS", argLength: 1, reg: gpfp, asm: "UCVTFS"}, // uint64 -> float32 329 {name: "UCVTFD", argLength: 1, reg: gpfp, asm: "UCVTFD"}, // uint64 -> float64 330 {name: "FCVTZSSW", argLength: 1, reg: fpgp, asm: "FCVTZSSW"}, // float32 -> int32 331 {name: "FCVTZSDW", argLength: 1, reg: fpgp, asm: "FCVTZSDW"}, // float64 -> int32 332 {name: "FCVTZUSW", argLength: 1, reg: fpgp, asm: "FCVTZUSW"}, // float32 -> uint32 333 {name: "FCVTZUDW", argLength: 1, reg: fpgp, asm: "FCVTZUDW"}, // float64 -> uint32 334 {name: "FCVTZSS", argLength: 1, reg: fpgp, asm: "FCVTZSS"}, // float32 -> int64 335 {name: "FCVTZSD", argLength: 1, reg: fpgp, asm: "FCVTZSD"}, // float64 -> int64 336 {name: "FCVTZUS", argLength: 1, reg: fpgp, asm: "FCVTZUS"}, // float32 -> uint64 337 {name: "FCVTZUD", argLength: 1, reg: fpgp, asm: "FCVTZUD"}, // float64 -> uint64 338 {name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD"}, // float32 -> float64 339 {name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS"}, // float64 -> float32 340 341 // floating-point round to integral 342 {name: "FRINTAD", argLength: 1, reg: fp11, asm: "FRINTAD"}, 343 {name: "FRINTMD", argLength: 1, reg: fp11, asm: "FRINTMD"}, 344 {name: "FRINTPD", argLength: 1, reg: fp11, asm: "FRINTPD"}, 345 {name: "FRINTZD", argLength: 1, reg: fp11, asm: "FRINTZD"}, 346 347 // conditional instructions; auxint is 348 // one of the arm64 comparison pseudo-ops (LessThan, LessThanU, etc.) 349 {name: "CSEL", argLength: 3, reg: gp2flags1, asm: "CSEL", aux: "CCop"}, // aux(flags) ? arg0 : arg1 350 {name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // aux(flags) ? arg0 : 0 351 352 // function calls 353 {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 354 {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 355 {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 356 357 // pseudo-ops 358 {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. 359 360 {name: "Equal", argLength: 1, reg: readflags}, // bool, true flags encode x==y false otherwise. 361 {name: "NotEqual", argLength: 1, reg: readflags}, // bool, true flags encode x!=y false otherwise. 362 {name: "LessThan", argLength: 1, reg: readflags}, // bool, true flags encode signed x<y false otherwise. 363 {name: "LessEqual", argLength: 1, reg: readflags}, // bool, true flags encode signed x<=y false otherwise. 364 {name: "GreaterThan", argLength: 1, reg: readflags}, // bool, true flags encode signed x>y false otherwise. 365 {name: "GreaterEqual", argLength: 1, reg: readflags}, // bool, true flags encode signed x>=y false otherwise. 366 {name: "LessThanU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x<y false otherwise. 367 {name: "LessEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x<=y false otherwise. 368 {name: "GreaterThanU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>y false otherwise. 369 {name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise. 370 371 // duffzero 372 // arg0 = address of memory to zero 373 // arg1 = mem 374 // auxint = offset into duffzero code to start executing 375 // returns mem 376 // R16 aka arm64.REGRT1 changed as side effect 377 { 378 name: "DUFFZERO", 379 aux: "Int64", 380 argLength: 2, 381 reg: regInfo{ 382 inputs: []regMask{buildReg("R16")}, 383 clobbers: buildReg("R16 R30"), 384 }, 385 faultOnNilArg0: true, 386 }, 387 388 // large zeroing 389 // arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect) 390 // arg1 = address of the last 16-byte unit to zero 391 // arg2 = mem 392 // returns mem 393 // STP.P (ZR,ZR), 16(R16) 394 // CMP Rarg1, R16 395 // BLE -2(PC) 396 // Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled. 397 // the-end-of-the-memory - 16 is with the area to zero, ok to spill. 398 { 399 name: "LoweredZero", 400 argLength: 3, 401 reg: regInfo{ 402 inputs: []regMask{buildReg("R16"), gp}, 403 clobbers: buildReg("R16"), 404 }, 405 clobberFlags: true, 406 faultOnNilArg0: true, 407 }, 408 409 // duffcopy 410 // arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect) 411 // arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect) 412 // arg2 = mem 413 // auxint = offset into duffcopy code to start executing 414 // returns mem 415 // R16, R17 changed as side effect 416 { 417 name: "DUFFCOPY", 418 aux: "Int64", 419 argLength: 3, 420 reg: regInfo{ 421 inputs: []regMask{buildReg("R17"), buildReg("R16")}, 422 clobbers: buildReg("R16 R17 R30"), 423 }, 424 faultOnNilArg0: true, 425 faultOnNilArg1: true, 426 }, 427 428 // large move 429 // arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect) 430 // arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect) 431 // arg2 = address of the last element of src 432 // arg3 = mem 433 // returns mem 434 // MOVD.P 8(R16), Rtmp 435 // MOVD.P Rtmp, 8(R17) 436 // CMP Rarg2, R16 437 // BLE -3(PC) 438 // Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled. 439 // the-end-of-src - 8 is within the area to copy, ok to spill. 440 { 441 name: "LoweredMove", 442 argLength: 4, 443 reg: regInfo{ 444 inputs: []regMask{buildReg("R17"), buildReg("R16"), gp}, 445 clobbers: buildReg("R16 R17"), 446 }, 447 clobberFlags: true, 448 faultOnNilArg0: true, 449 faultOnNilArg1: true, 450 }, 451 452 // Scheduler ensures LoweredGetClosurePtr occurs only in entry block, 453 // and sorts it to the very beginning of the block to prevent other 454 // use of R26 (arm64.REGCTXT, the closure pointer) 455 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}}, 456 457 // LoweredGetCallerSP returns the SP of the caller of the current function. 458 {name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true}, 459 460 // MOVDconvert converts between pointers and integers. 461 // We have a special op for this so as to not confuse GC 462 // (particularly stack maps). It takes a memory arg so it 463 // gets correctly ordered with respect to GC safepoints. 464 // arg0=ptr/int arg1=mem, output=int/ptr 465 {name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"}, 466 467 // Constant flag values. For any comparison, there are 5 possible 468 // outcomes: the three from the signed total order (<,==,>) and the 469 // three from the unsigned total order. The == cases overlap. 470 // Note: there's a sixth "unordered" outcome for floating-point 471 // comparisons, but we don't use such a beast yet. 472 // These ops are for temporary use by rewrite rules. They 473 // cannot appear in the generated assembly. 474 {name: "FlagEQ"}, // equal 475 {name: "FlagLT_ULT"}, // signed < and unsigned < 476 {name: "FlagLT_UGT"}, // signed < and unsigned > 477 {name: "FlagGT_UGT"}, // signed > and unsigned < 478 {name: "FlagGT_ULT"}, // signed > and unsigned > 479 480 // (InvertFlags (CMP a b)) == (CMP b a) 481 // InvertFlags is a pseudo-op which can't appear in assembly output. 482 {name: "InvertFlags", argLength: 1}, // reverse direction of arg0 483 484 // atomic loads. 485 // load from arg0. arg1=mem. auxint must be zero. 486 // returns <value,memory> so they can be properly ordered with other loads. 487 {name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true}, 488 {name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true}, 489 490 // atomic stores. 491 // store arg1 to arg0. arg2=mem. returns memory. auxint must be zero. 492 {name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true, hasSideEffects: true}, 493 {name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true, hasSideEffects: true}, 494 495 // atomic exchange. 496 // store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero. 497 // LDAXR (Rarg0), Rout 498 // STLXR Rarg1, (Rarg0), Rtmp 499 // CBNZ Rtmp, -2(PC) 500 {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true}, 501 {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true}, 502 503 // atomic add. 504 // *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero. 505 // LDAXR (Rarg0), Rout 506 // ADD Rarg1, Rout 507 // STLXR Rout, (Rarg0), Rtmp 508 // CBNZ Rtmp, -3(PC) 509 {name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true}, 510 {name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true}, 511 512 // atomic compare and swap. 513 // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero. 514 // if *arg0 == arg1 { 515 // *arg0 = arg2 516 // return (true, memory) 517 // } else { 518 // return (false, memory) 519 // } 520 // LDAXR (Rarg0), Rtmp 521 // CMP Rarg1, Rtmp 522 // BNE 3(PC) 523 // STLXR Rarg2, (Rarg0), Rtmp 524 // CBNZ Rtmp, -4(PC) 525 // CSET EQ, Rout 526 {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, 527 {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, 528 529 // atomic and/or. 530 // *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero. 531 // LDAXRB (Rarg0), Rtmp 532 // AND/OR Rarg1, Rtmp 533 // STLXRB Rtmp, (Rarg0), Rtmp 534 // CBNZ Rtmp, -3(PC) 535 {name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true}, 536 {name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true}, 537 538 // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier 539 // It saves all GP registers if necessary, 540 // but clobbers R30 (LR) because it's a call. 541 {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R30")}, clobberFlags: true, aux: "Sym", symEffect: "None"}, 542 } 543 544 blocks := []blockData{ 545 {name: "EQ"}, 546 {name: "NE"}, 547 {name: "LT"}, 548 {name: "LE"}, 549 {name: "GT"}, 550 {name: "GE"}, 551 {name: "ULT"}, 552 {name: "ULE"}, 553 {name: "UGT"}, 554 {name: "UGE"}, 555 {name: "Z"}, // Control == 0 (take a register instead of flags) 556 {name: "NZ"}, // Control != 0 557 {name: "ZW"}, // Control == 0, 32-bit 558 {name: "NZW"}, // Control != 0, 32-bit 559 {name: "TBZ"}, // Control & (1 << Aux.(int64)) == 0 560 {name: "TBNZ"}, // Control & (1 << Aux.(int64)) != 0 561 } 562 563 archs = append(archs, arch{ 564 name: "ARM64", 565 pkg: "cmd/internal/obj/arm64", 566 genfile: "../../arm64/ssa.go", 567 ops: ops, 568 blocks: blocks, 569 regnames: regNamesARM64, 570 gpregmask: gp, 571 fpregmask: fp, 572 framepointerreg: -1, // not used 573 linkreg: int8(num["R30"]), 574 }) 575 }