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 // - Unused portions of AuxInt are filled by sign-extending the used portion. 16 // - *const instructions may use a constant larger than the instruction can encode. 17 // In this case the assembler expands to multiple instructions and uses tmp 18 // register (R23). 19 20 // Suffixes encode the bit width of various instructions. 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 // F (float) = 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 regNamesMIPS = []string{ 33 "R0", // constant 0 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", 52 "R19", 53 "R20", 54 "R21", 55 "R22", 56 //REGTMP 57 "R24", 58 "R25", 59 // R26 reserved by kernel 60 // R27 reserved by kernel 61 "R28", 62 "SP", // aka R29 63 "g", // aka R30 64 "R31", // REGLINK 65 66 // odd FP registers contain high parts of 64-bit FP values 67 "F0", 68 "F2", 69 "F4", 70 "F6", 71 "F8", 72 "F10", 73 "F12", 74 "F14", 75 "F16", 76 "F18", 77 "F20", 78 "F22", 79 "F24", 80 "F26", 81 "F28", 82 "F30", 83 84 "HI", // high bits of multiplication 85 "LO", // low bits of multiplication 86 87 // pseudo-registers 88 "SB", 89 } 90 91 func init() { 92 // Make map from reg names to reg integers. 93 if len(regNamesMIPS) > 64 { 94 panic("too many registers") 95 } 96 num := map[string]int{} 97 for i, name := range regNamesMIPS { 98 num[name] = i 99 } 100 buildReg := func(s string) regMask { 101 m := regMask(0) 102 for _, r := range strings.Split(s, " ") { 103 if n, ok := num[r]; ok { 104 m |= regMask(1) << uint(n) 105 continue 106 } 107 panic("register " + r + " not found") 108 } 109 return m 110 } 111 112 // Common individual register masks 113 var ( 114 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 R28 R31") 115 gpg = gp | buildReg("g") 116 gpsp = gp | buildReg("SP") 117 gpspg = gpg | buildReg("SP") 118 gpspsbg = gpspg | buildReg("SB") 119 fp = buildReg("F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30") 120 lo = buildReg("LO") 121 hi = buildReg("HI") 122 callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg (and anything calling it) may clobber g 123 ) 124 // Common regInfo 125 var ( 126 gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} 127 gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} 128 gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}} 129 gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}} 130 gp31 = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}} 131 gp2hilo = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}} 132 gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}} 133 gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}} 134 gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} 135 gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}} 136 gpstore0 = regInfo{inputs: []regMask{gpspsbg}} 137 fp01 = regInfo{inputs: nil, outputs: []regMask{fp}} 138 fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}} 139 fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}} 140 fp2flags = regInfo{inputs: []regMask{fp, fp}} 141 fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} 142 fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} 143 readflags = regInfo{inputs: nil, outputs: []regMask{gp}} 144 ) 145 ops := []opData{ 146 {name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true}, // arg0 + arg1 147 {name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32"}, // arg0 + auxInt 148 {name: "SUB", argLength: 2, reg: gp21, asm: "SUBU"}, // arg0 - arg1 149 {name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32"}, // arg0 - auxInt 150 {name: "MUL", argLength: 2, reg: regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}, clobbers: hi | lo}, asm: "MUL", commutative: true}, // arg0 * arg1 151 {name: "MULT", argLength: 2, reg: gp2hilo, asm: "MUL", commutative: true, typ: "(Int32,Int32)"}, // arg0 * arg1, signed, results hi,lo 152 {name: "MULTU", argLength: 2, reg: gp2hilo, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)"}, // arg0 * arg1, unsigned, results hi,lo 153 {name: "DIV", argLength: 2, reg: gp2hilo, asm: "DIV", typ: "(Int32,Int32)"}, // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1 154 {name: "DIVU", argLength: 2, reg: gp2hilo, asm: "DIVU", typ: "(UInt32,UInt32)"}, // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1 155 156 {name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1 157 {name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1 158 {name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"}, // arg0 - arg1 159 {name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"}, // arg0 - arg1 160 {name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1 161 {name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1 162 {name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"}, // arg0 / arg1 163 {name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"}, // arg0 / arg1 164 165 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 166 {name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"}, // arg0 & auxInt 167 {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0 | arg1 168 {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32"}, // arg0 | auxInt 169 {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32"}, // arg0 ^ arg1 170 {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32"}, // arg0 ^ auxInt 171 {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0 | arg1) 172 {name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int32"}, // ^(arg0 | auxInt) 173 174 {name: "NEG", argLength: 1, reg: gp11}, // -arg0 175 {name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32 176 {name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64 177 {name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64 178 179 // shifts 180 {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 32 181 {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt 182 {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 32 183 {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned 184 {name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 32 185 {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed 186 187 {name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"}, 188 189 // comparisons 190 {name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"}, // 1 if arg0 > arg1 (signed), 0 otherwise 191 {name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool"}, // 1 if auxInt > arg0 (signed), 0 otherwise 192 {name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool"}, // 1 if arg0 > 0 (signed), 0 otherwise 193 {name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"}, // 1 if arg0 > arg1 (unsigned), 0 otherwise 194 {name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise 195 {name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool"}, // 1 if arg0 > 0 (unsigned), 0 otherwise 196 197 {name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32 198 {name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64 199 {name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32 200 {name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64 201 {name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32 202 {name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64 203 204 // moves 205 {name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true}, // auxint 206 {name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float 207 {name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float 208 209 {name: "MOVWaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVW", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB 210 211 {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 212 {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 213 {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 214 {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 215 {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 216 {name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 217 {name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true}, // load from arg0 + auxInt + aux. arg1=mem. 218 219 {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. 220 {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. 221 {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. 222 {name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 223 {name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 224 225 {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. 226 {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. 227 {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. 228 229 // conversions 230 {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"}, // move from arg0, sign-extended from byte 231 {name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte 232 {name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"}, // move from arg0, sign-extended from half 233 {name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half 234 {name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"}, // move from arg0 235 236 {name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register 237 238 // conditional move on zero (returns arg1 if arg2 is 0, otherwise arg0) 239 // order of parameters is reversed so we can use resultInArg0 (OpCMOVZ result arg1 arg2-> CMOVZ arg2reg, arg1reg, resultReg) 240 {name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true}, 241 {name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true}, 242 243 {name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"}, // int32 -> float32 244 {name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"}, // int32 -> float64 245 {name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32 246 {name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32 247 {name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"}, // float32 -> float64 248 {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 249 250 // function calls 251 {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 252 {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem 253 {name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true}, // call deferproc. arg0=mem, auxint=argsize, returns mem 254 {name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true}, // call newproc. arg0=mem, auxint=argsize, returns mem 255 {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem 256 257 // atomic ops 258 259 // load from arg0. arg1=mem. 260 // returns <value,memory> so they can be properly ordered with other loads. 261 // SYNC 262 // MOVW (Rarg0), Rout 263 // SYNC 264 {name: "LoweredAtomicLoad", argLength: 2, reg: gpload, faultOnNilArg0: true}, 265 266 // store arg1 to arg0. arg2=mem. returns memory. 267 // SYNC 268 // MOVW Rarg1, (Rarg0) 269 // SYNC 270 {name: "LoweredAtomicStore", argLength: 3, reg: gpstore, faultOnNilArg0: true}, 271 {name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true}, 272 273 // atomic exchange. 274 // store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. 275 // SYNC 276 // LL (Rarg0), Rout 277 // MOVW Rarg1, Rtmp 278 // SC Rtmp, (Rarg0) 279 // BEQ Rtmp, -3(PC) 280 // SYNC 281 {name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true}, 282 283 // atomic add. 284 // *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. 285 // SYNC 286 // LL (Rarg0), Rout 287 // ADDU Rarg1, Rout, Rtmp 288 // SC Rtmp, (Rarg0) 289 // BEQ Rtmp, -3(PC) 290 // SYNC 291 // ADDU Rarg1, Rout 292 {name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true}, 293 {name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true}, 294 295 // atomic compare and swap. 296 // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. 297 // if *arg0 == arg1 { 298 // *arg0 = arg2 299 // return (true, memory) 300 // } else { 301 // return (false, memory) 302 // } 303 // SYNC 304 // MOVW $0, Rout 305 // LL (Rarg0), Rtmp 306 // BNE Rtmp, Rarg1, 4(PC) 307 // MOVW Rarg2, Rout 308 // SC Rout, (Rarg0) 309 // BEQ Rout, -4(PC) 310 // SYNC 311 {name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true}, 312 313 // atomic and/or. 314 // *arg0 &= (|=) arg1. arg2=mem. returns memory. 315 // SYNC 316 // LL (Rarg0), Rtmp 317 // AND Rarg1, Rtmp 318 // SC Rtmp, (Rarg0) 319 // BEQ Rtmp, -3(PC) 320 // SYNC 321 {name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true}, 322 {name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true}, 323 324 // large or unaligned zeroing 325 // arg0 = address of memory to zero (in R1, changed as side effect) 326 // arg1 = address of the last element to zero 327 // arg2 = mem 328 // auxint = alignment 329 // returns mem 330 // SUBU $4, R1 331 // MOVW R0, 4(R1) 332 // ADDU $4, R1 333 // BNE Rarg1, R1, -2(PC) 334 { 335 name: "LoweredZero", 336 aux: "Int32", 337 argLength: 3, 338 reg: regInfo{ 339 inputs: []regMask{buildReg("R1"), gp}, 340 clobbers: buildReg("R1"), 341 }, 342 faultOnNilArg0: true, 343 }, 344 345 // large or unaligned move 346 // arg0 = address of dst memory (in R2, changed as side effect) 347 // arg1 = address of src memory (in R1, changed as side effect) 348 // arg2 = address of the last element of src 349 // arg3 = mem 350 // auxint = alignment 351 // returns mem 352 // SUBU $4, R1 353 // MOVW 4(R1), Rtmp 354 // MOVW Rtmp, (R2) 355 // ADDU $4, R1 356 // ADDU $4, R2 357 // BNE Rarg2, R1, -4(PC) 358 { 359 name: "LoweredMove", 360 aux: "Int32", 361 argLength: 4, 362 reg: regInfo{ 363 inputs: []regMask{buildReg("R2"), buildReg("R1"), gp}, 364 clobbers: buildReg("R1 R2"), 365 }, 366 faultOnNilArg0: true, 367 faultOnNilArg1: true, 368 }, 369 370 // pseudo-ops 371 {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. 372 373 {name: "FPFlagTrue", argLength: 1, reg: readflags}, // bool, true if FP flag is true 374 {name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false 375 376 // Scheduler ensures LoweredGetClosurePtr occurs only in entry block, 377 // and sorts it to the very beginning of the block to prevent other 378 // use of R22 (mips.REGCTXT, the closure pointer) 379 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}}, 380 381 // MOVWconvert converts between pointers and integers. 382 // We have a special op for this so as to not confuse GC 383 // (particularly stack maps). It takes a memory arg so it 384 // gets correctly ordered with respect to GC safepoints. 385 // arg0=ptr/int arg1=mem, output=int/ptr 386 {name: "MOVWconvert", argLength: 2, reg: gp11, asm: "MOVW"}, 387 } 388 389 blocks := []blockData{ 390 {name: "EQ"}, 391 {name: "NE"}, 392 {name: "LTZ"}, // < 0 393 {name: "LEZ"}, // <= 0 394 {name: "GTZ"}, // > 0 395 {name: "GEZ"}, // >= 0 396 {name: "FPT"}, // FP flag is true 397 {name: "FPF"}, // FP flag is false 398 } 399 400 archs = append(archs, arch{ 401 name: "MIPS", 402 pkg: "cmd/internal/obj/mips", 403 genfile: "../../mips/ssa.go", 404 ops: ops, 405 blocks: blocks, 406 regnames: regNamesMIPS, 407 gpregmask: gp, 408 fpregmask: fp, 409 specialregmask: hi | lo, 410 framepointerreg: -1, // not used 411 linkreg: int8(num["R31"]), 412 }) 413 } 414