github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/_gen/MIPS64Ops.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 package main 6 7 import "strings" 8 9 // Notes: 10 // - Integer types live in the low portion of registers. Upper portions are junk. 11 // - Boolean types use the low-order byte of a register. 0=false, 1=true. 12 // Upper bytes are junk. 13 // - *const instructions may use a constant larger than the instruction can encode. 14 // In this case the assembler expands to multiple instructions and uses tmp 15 // register (R23). 16 17 // Suffixes encode the bit width of various instructions. 18 // V (vlong) = 64 bit 19 // WU (word) = 32 bit unsigned 20 // W (word) = 32 bit 21 // H (half word) = 16 bit 22 // HU = 16 bit unsigned 23 // B (byte) = 8 bit 24 // BU = 8 bit unsigned 25 // F (float) = 32 bit float 26 // D (double) = 64 bit float 27 28 // Note: registers not used in regalloc are not included in this list, 29 // so that regmask stays within int64 30 // Be careful when hand coding regmasks. 31 var regNamesMIPS64 = []string{ 32 "R0", // constant 0 33 "R1", 34 "R2", 35 "R3", 36 "R4", 37 "R5", 38 "R6", 39 "R7", 40 "R8", 41 "R9", 42 "R10", 43 "R11", 44 "R12", 45 "R13", 46 "R14", 47 "R15", 48 "R16", 49 "R17", 50 "R18", 51 "R19", 52 "R20", 53 "R21", 54 "R22", 55 // R23 = REGTMP not used in regalloc 56 "R24", 57 "R25", 58 // R26 reserved by kernel 59 // R27 reserved by kernel 60 // R28 = REGSB not used in regalloc 61 "SP", // aka R29 62 "g", // aka R30 63 "R31", // aka REGLINK 64 65 "F0", 66 "F1", 67 "F2", 68 "F3", 69 "F4", 70 "F5", 71 "F6", 72 "F7", 73 "F8", 74 "F9", 75 "F10", 76 "F11", 77 "F12", 78 "F13", 79 "F14", 80 "F15", 81 "F16", 82 "F17", 83 "F18", 84 "F19", 85 "F20", 86 "F21", 87 "F22", 88 "F23", 89 "F24", 90 "F25", 91 "F26", 92 "F27", 93 "F28", 94 "F29", 95 "F30", 96 "F31", 97 98 "HI", // high bits of multiplication 99 "LO", // low bits of multiplication 100 101 // If you add registers, update asyncPreempt in runtime. 102 103 // pseudo-registers 104 "SB", 105 } 106 107 func init() { 108 // Make map from reg names to reg integers. 109 if len(regNamesMIPS64) > 64 { 110 panic("too many registers") 111 } 112 num := map[string]int{} 113 for i, name := range regNamesMIPS64 { 114 num[name] = i 115 } 116 buildReg := func(s string) regMask { 117 m := regMask(0) 118 for _, r := range strings.Split(s, " ") { 119 if n, ok := num[r]; ok { 120 m |= regMask(1) << uint(n) 121 continue 122 } 123 panic("register " + r + " not found") 124 } 125 return m 126 } 127 128 // Common individual register masks 129 var ( 130 gp = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31") 131 gpg = gp | buildReg("g") 132 gpsp = gp | buildReg("SP") 133 gpspg = gpg | buildReg("SP") 134 gpspsbg = gpspg | buildReg("SB") 135 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") 136 lo = buildReg("LO") 137 hi = buildReg("HI") 138 callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg (and anything calling it) may clobber g 139 r1 = buildReg("R1") 140 r2 = buildReg("R2") 141 r3 = buildReg("R3") 142 r4 = buildReg("R4") 143 ) 144 // Common regInfo 145 var ( 146 gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} 147 gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} 148 gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}} 149 gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}} 150 gp2hilo = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}} 151 gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}} 152 gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}} 153 gpstore0 = regInfo{inputs: []regMask{gpspsbg}} 154 gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} 155 gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}} 156 fp01 = regInfo{inputs: nil, outputs: []regMask{fp}} 157 fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}} 158 //fp1flags = regInfo{inputs: []regMask{fp}} 159 fpgp = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}} 160 gpfp = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}} 161 fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}} 162 fp2flags = regInfo{inputs: []regMask{fp, fp}} 163 fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} 164 fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} 165 readflags = regInfo{inputs: nil, outputs: []regMask{gp}} 166 ) 167 ops := []opData{ 168 // binary ops 169 {name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true}, // arg0 + arg1 170 {name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops. 171 {name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"}, // arg0 - arg1 172 {name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"}, // arg0 - auxInt 173 {name: "MULV", argLength: 2, reg: gp2hilo, asm: "MULV", commutative: true, typ: "(Int64,Int64)"}, // arg0 * arg1, signed, results hi,lo 174 {name: "MULVU", argLength: 2, reg: gp2hilo, asm: "MULVU", commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned, results hi,lo 175 {name: "DIVV", argLength: 2, reg: gp2hilo, asm: "DIVV", typ: "(Int64,Int64)"}, // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1 176 {name: "DIVVU", argLength: 2, reg: gp2hilo, asm: "DIVVU", typ: "(UInt64,UInt64)"}, // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1 177 178 {name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1 179 {name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1 180 {name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"}, // arg0 - arg1 181 {name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"}, // arg0 - arg1 182 {name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1 183 {name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1 184 {name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"}, // arg0 / arg1 185 {name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"}, // arg0 / arg1 186 187 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 188 {name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt 189 {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0 | arg1 190 {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0 | auxInt 191 {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1 192 {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt 193 {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0 | arg1) 194 {name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"}, // ^(arg0 | auxInt) 195 196 {name: "NEGV", argLength: 1, reg: gp11}, // -arg0 197 {name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32 198 {name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64 199 {name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD"}, // abs(arg0), float64 200 {name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64 201 {name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32 202 203 // shifts 204 {name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"}, // arg0 << arg1, shift amount is mod 64 205 {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt 206 {name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"}, // arg0 >> arg1, unsigned, shift amount is mod 64 207 {name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned 208 {name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"}, // arg0 >> arg1, signed, shift amount is mod 64 209 {name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed 210 211 // comparisons 212 {name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"}, // 1 if arg0 > arg1 (signed), 0 otherwise 213 {name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (signed), 0 otherwise 214 {name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"}, // 1 if arg0 > arg1 (unsigned), 0 otherwise 215 {name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise 216 217 {name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32 218 {name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64 219 {name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32 220 {name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64 221 {name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32 222 {name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64 223 224 // moves 225 {name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true}, // auxint 226 {name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float 227 {name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float 228 229 {name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB 230 231 {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 232 {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 233 {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 234 {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 235 {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 236 {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 237 {name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 238 {name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 239 {name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 240 241 {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. 242 {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. 243 {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. 244 {name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 245 {name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 246 {name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 247 248 {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. 249 {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. 250 {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. 251 {name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux. ar12=mem. 252 253 // moves (no conversion) 254 {name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW"}, // move float32 to int32 (no conversion). MIPS64 will perform sign-extend to 64-bit by default 255 {name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW"}, // move int32 to float32 (no conversion). MIPS64 will perform sign-extend to 64-bit by default 256 {name: "MOVVfpgp", argLength: 1, reg: fpgp, asm: "MOVV"}, // move float64 to int64 (no conversion). 257 {name: "MOVVgpfp", argLength: 1, reg: gpfp, asm: "MOVV"}, // move int64 to float64 (no conversion). 258 259 // conversions 260 {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"}, // move from arg0, sign-extended from byte 261 {name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte 262 {name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"}, // move from arg0, sign-extended from half 263 {name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half 264 {name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"}, // move from arg0, sign-extended from word 265 {name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word 266 {name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"}, // move from arg0 267 268 {name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register 269 270 {name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"}, // int32 -> float32 271 {name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"}, // int32 -> float64 272 {name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"}, // int64 -> float32 273 {name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"}, // int64 -> float64 274 {name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32 275 {name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32 276 {name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64 277 {name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64 278 {name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"}, // float32 -> float64 279 {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 280 281 // function calls 282 {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem 283 {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem 284 {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem 285 {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem 286 287 // duffzero 288 // arg0 = address of memory to zero 289 // arg1 = mem 290 // auxint = offset into duffzero code to start executing 291 // returns mem 292 // R1 aka mips.REGRT1 changed as side effect 293 { 294 name: "DUFFZERO", 295 aux: "Int64", 296 argLength: 2, 297 reg: regInfo{ 298 inputs: []regMask{gp}, 299 clobbers: buildReg("R1 R31"), 300 }, 301 faultOnNilArg0: true, 302 }, 303 304 // duffcopy 305 // arg0 = address of dst memory (in R2, changed as side effect) 306 // arg1 = address of src memory (in R1, changed as side effect) 307 // arg2 = mem 308 // auxint = offset into duffcopy code to start executing 309 // returns mem 310 { 311 name: "DUFFCOPY", 312 aux: "Int64", 313 argLength: 3, 314 reg: regInfo{ 315 inputs: []regMask{buildReg("R2"), buildReg("R1")}, 316 clobbers: buildReg("R1 R2 R31"), 317 }, 318 faultOnNilArg0: true, 319 faultOnNilArg1: true, 320 }, 321 322 // large or unaligned zeroing 323 // arg0 = address of memory to zero (in R1, changed as side effect) 324 // arg1 = address of the last element to zero 325 // arg2 = mem 326 // auxint = alignment 327 // returns mem 328 // SUBV $8, R1 329 // MOVV R0, 8(R1) 330 // ADDV $8, R1 331 // BNE Rarg1, R1, -2(PC) 332 { 333 name: "LoweredZero", 334 aux: "Int64", 335 argLength: 3, 336 reg: regInfo{ 337 inputs: []regMask{buildReg("R1"), gp}, 338 clobbers: buildReg("R1"), 339 }, 340 clobberFlags: true, 341 faultOnNilArg0: true, 342 }, 343 344 // large or unaligned move 345 // arg0 = address of dst memory (in R2, changed as side effect) 346 // arg1 = address of src memory (in R1, changed as side effect) 347 // arg2 = address of the last element of src 348 // arg3 = mem 349 // auxint = alignment 350 // returns mem 351 // SUBV $8, R1 352 // MOVV 8(R1), Rtmp 353 // MOVV Rtmp, (R2) 354 // ADDV $8, R1 355 // ADDV $8, R2 356 // BNE Rarg2, R1, -4(PC) 357 { 358 name: "LoweredMove", 359 aux: "Int64", 360 argLength: 4, 361 reg: regInfo{ 362 inputs: []regMask{buildReg("R2"), buildReg("R1"), gp}, 363 clobbers: buildReg("R1 R2"), 364 }, 365 clobberFlags: true, 366 faultOnNilArg0: true, 367 faultOnNilArg1: true, 368 }, 369 370 // atomic and/or. 371 // *arg0 &= (|=) arg1. arg2=mem. returns memory. 372 // SYNC 373 // LL (Rarg0), Rtmp 374 // AND Rarg1, Rtmp 375 // SC Rtmp, (Rarg0) 376 // BEQ Rtmp, -3(PC) 377 // SYNC 378 {name: "LoweredAtomicAnd32", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 379 {name: "LoweredAtomicOr32", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 380 381 // atomic loads. 382 // load from arg0. arg1=mem. 383 // returns <value,memory> so they can be properly ordered with other loads. 384 {name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true}, 385 {name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true}, 386 {name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true}, 387 388 // atomic stores. 389 // store arg1 to arg0. arg2=mem. returns memory. 390 {name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 391 {name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 392 {name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 393 // store zero to arg0. arg1=mem. returns memory. 394 {name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, 395 {name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, 396 397 // atomic exchange. 398 // store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. 399 // SYNC 400 // LL (Rarg0), Rout 401 // MOVV Rarg1, Rtmp 402 // SC Rtmp, (Rarg0) 403 // BEQ Rtmp, -3(PC) 404 // SYNC 405 {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 406 {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 407 408 // atomic add. 409 // *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. 410 // SYNC 411 // LL (Rarg0), Rout 412 // ADDV Rarg1, Rout, Rtmp 413 // SC Rtmp, (Rarg0) 414 // BEQ Rtmp, -3(PC) 415 // SYNC 416 // ADDV Rarg1, Rout 417 {name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 418 {name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 419 // *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit. 420 {name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 421 {name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 422 423 // atomic compare and swap. 424 // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. 425 // if *arg0 == arg1 { 426 // *arg0 = arg2 427 // return (true, memory) 428 // } else { 429 // return (false, memory) 430 // } 431 // SYNC 432 // MOVV $0, Rout 433 // LL (Rarg0), Rtmp 434 // BNE Rtmp, Rarg1, 4(PC) 435 // MOVV Rarg2, Rout 436 // SC Rout, (Rarg0) 437 // BEQ Rout, -4(PC) 438 // SYNC 439 {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 440 {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 441 442 // pseudo-ops 443 {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. 444 445 {name: "FPFlagTrue", argLength: 1, reg: readflags}, // bool, true if FP flag is true 446 {name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false 447 448 // Scheduler ensures LoweredGetClosurePtr occurs only in entry block, 449 // and sorts it to the very beginning of the block to prevent other 450 // use of R22 (mips.REGCTXT, the closure pointer) 451 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true}, 452 453 // LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem. 454 {name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true}, 455 456 // LoweredGetCallerPC evaluates to the PC to which its "caller" will return. 457 // I.e., if f calls g "calls" getcallerpc, 458 // the result should be the PC within f that g will return to. 459 // See runtime/stubs.go for a more detailed discussion. 460 {name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true}, 461 462 // LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed 463 // It saves all GP registers if necessary, 464 // but clobbers R31 (LR) because it's a call 465 // and R23 (REGTMP). 466 // Returns a pointer to a write barrier buffer in R25. 467 {name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"}, 468 469 // There are three of these functions so that they can have three different register inputs. 470 // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the 471 // default registers to match so we don't need to copy registers around unnecessarily. 472 {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). 473 {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). 474 {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). 475 } 476 477 blocks := []blockData{ 478 {name: "EQ", controls: 1}, 479 {name: "NE", controls: 1}, 480 {name: "LTZ", controls: 1}, // < 0 481 {name: "LEZ", controls: 1}, // <= 0 482 {name: "GTZ", controls: 1}, // > 0 483 {name: "GEZ", controls: 1}, // >= 0 484 {name: "FPT", controls: 1}, // FP flag is true 485 {name: "FPF", controls: 1}, // FP flag is false 486 } 487 488 archs = append(archs, arch{ 489 name: "MIPS64", 490 pkg: "github.com/go-asm/go/cmd/obj/mips", 491 genfile: "../../mips64/ssa.go", 492 ops: ops, 493 blocks: blocks, 494 regnames: regNamesMIPS64, 495 gpregmask: gp, 496 fpregmask: fp, 497 specialregmask: hi | lo, 498 framepointerreg: -1, // not used 499 linkreg: int8(num["R31"]), 500 }) 501 }