Home | History | Annotate | Download | only in gen
      1 // Copyright 2016 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build ignore
      6 
      7 package main
      8 
      9 import "strings"
     10 
     11 // Notes:
     12 //  - Integer types live in the low portion of registers. Upper portions are junk.
     13 //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
     14 //    Upper bytes are junk.
     15 //  - 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