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 //  - *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 (R23).
     18 
     19 // Suffixes encode the bit width of various instructions.
     20 // V (vlong)     = 64 bit
     21 // WU (word)     = 32 bit unsigned
     22 // W (word)      = 32 bit
     23 // H (half word) = 16 bit
     24 // HU            = 16 bit unsigned
     25 // B (byte)      = 8 bit
     26 // BU            = 8 bit unsigned
     27 // F (float)     = 32 bit float
     28 // D (double)    = 64 bit float
     29 
     30 // Note: registers not used in regalloc are not included in this list,
     31 // so that regmask stays within int64
     32 // Be careful when hand coding regmasks.
     33 var regNamesMIPS64 = []string{
     34 	"R0", // constant 0
     35 	"R1",
     36 	"R2",
     37 	"R3",
     38 	"R4",
     39 	"R5",
     40 	"R6",
     41 	"R7",
     42 	"R8",
     43 	"R9",
     44 	"R10",
     45 	"R11",
     46 	"R12",
     47 	"R13",
     48 	"R14",
     49 	"R15",
     50 	"R16",
     51 	"R17",
     52 	"R18",
     53 	"R19",
     54 	"R20",
     55 	"R21",
     56 	"R22",
     57 	// R23 = REGTMP not used in regalloc
     58 	"R24",
     59 	"R25",
     60 	// R26 reserved by kernel
     61 	// R27 reserved by kernel
     62 	// R28 = REGSB not used in regalloc
     63 	"SP",  // aka R29
     64 	"g",   // aka R30
     65 	"R31", // aka REGLINK
     66 
     67 	"F0",
     68 	"F1",
     69 	"F2",
     70 	"F3",
     71 	"F4",
     72 	"F5",
     73 	"F6",
     74 	"F7",
     75 	"F8",
     76 	"F9",
     77 	"F10",
     78 	"F11",
     79 	"F12",
     80 	"F13",
     81 	"F14",
     82 	"F15",
     83 	"F16",
     84 	"F17",
     85 	"F18",
     86 	"F19",
     87 	"F20",
     88 	"F21",
     89 	"F22",
     90 	"F23",
     91 	"F24",
     92 	"F25",
     93 	"F26",
     94 	"F27",
     95 	"F28",
     96 	"F29",
     97 	"F30",
     98 	"F31",
     99 
    100 	"HI", // high bits of multiplication
    101 	"LO", // low bits of multiplication
    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 	)
    140 	// Common regInfo
    141 	var (
    142 		gp01     = regInfo{inputs: nil, outputs: []regMask{gp}}
    143 		gp11     = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
    144 		gp11sp   = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
    145 		gp21     = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
    146 		gp2hilo  = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
    147 		gpload   = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
    148 		gpstore  = regInfo{inputs: []regMask{gpspsbg, gpg}}
    149 		gpstore0 = regInfo{inputs: []regMask{gpspsbg}}
    150 		fp01     = regInfo{inputs: nil, outputs: []regMask{fp}}
    151 		fp11     = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
    152 		//fp1flags  = regInfo{inputs: []regMask{fp}}
    153 		//fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
    154 		//gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
    155 		fp21      = regInfo{inputs: []regMask{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: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},                             // arg0 + arg1
    164 		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"},                           // arg0 + auxInt
    165 		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                                                // arg0 - arg1
    166 		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},                             // arg0 - auxInt
    167 		{name: "MULV", argLength: 2, reg: gp2hilo, asm: "MULV", commutative: true, typ: "(Int64,Int64)"},     // arg0 * arg1, signed, results hi,lo
    168 		{name: "MULVU", argLength: 2, reg: gp2hilo, asm: "MULVU", commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned, results hi,lo
    169 		{name: "DIVV", argLength: 2, reg: gp2hilo, asm: "DIVV", typ: "(Int64,Int64)"},                        // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
    170 		{name: "DIVVU", argLength: 2, reg: gp2hilo, asm: "DIVVU", typ: "(UInt64,UInt64)"},                    // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
    171 
    172 		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
    173 		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
    174 		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
    175 		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
    176 		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
    177 		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
    178 		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
    179 		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
    180 
    181 		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
    182 		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
    183 		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
    184 		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
    185 		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
    186 		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
    187 		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
    188 		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
    189 
    190 		{name: "NEGV", argLength: 1, reg: gp11},              // -arg0
    191 		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32
    192 		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64
    193 
    194 		// shifts
    195 		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                    // arg0 << arg1, shift amount is mod 64
    196 		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt
    197 		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                    // arg0 >> arg1, unsigned, shift amount is mod 64
    198 		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned
    199 		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                    // arg0 >> arg1, signed, shift amount is mod 64
    200 		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed
    201 
    202 		// comparisons
    203 		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
    204 		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
    205 		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
    206 		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
    207 
    208 		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
    209 		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
    210 		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
    211 		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
    212 		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
    213 		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
    214 
    215 		// moves
    216 		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
    217 		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
    218 		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
    219 
    220 		{name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
    221 
    222 		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
    223 		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
    224 		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
    225 		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
    226 		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
    227 		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
    228 		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
    229 		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
    230 		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
    231 
    232 		{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.
    233 		{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.
    234 		{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.
    235 		{name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
    236 		{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.
    237 		{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.
    238 
    239 		{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.
    240 		{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.
    241 		{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.
    242 		{name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
    243 
    244 		// conversions
    245 		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
    246 		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
    247 		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
    248 		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
    249 		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
    250 		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
    251 		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
    252 
    253 		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
    254 
    255 		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
    256 		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
    257 		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
    258 		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
    259 		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
    260 		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
    261 		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
    262 		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
    263 		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
    264 		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
    265 
    266 		// function calls
    267 		{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
    268 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
    269 		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                // call deferproc.  arg0=mem, auxint=argsize, returns mem
    270 		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                   // call newproc.  arg0=mem, auxint=argsize, returns mem
    271 		{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
    272 
    273 		// duffzero
    274 		// arg0 = address of memory to zero
    275 		// arg1 = mem
    276 		// auxint = offset into duffzero code to start executing
    277 		// returns mem
    278 		// R1 aka mips.REGRT1 changed as side effect
    279 		{
    280 			name:      "DUFFZERO",
    281 			aux:       "Int64",
    282 			argLength: 2,
    283 			reg: regInfo{
    284 				inputs:   []regMask{gp},
    285 				clobbers: buildReg("R1 R31"),
    286 			},
    287 			faultOnNilArg0: true,
    288 		},
    289 
    290 		// large or unaligned zeroing
    291 		// arg0 = address of memory to zero (in R1, changed as side effect)
    292 		// arg1 = address of the last element to zero
    293 		// arg2 = mem
    294 		// auxint = alignment
    295 		// returns mem
    296 		//	SUBV	$8, R1
    297 		//	MOVV	R0, 8(R1)
    298 		//	ADDV	$8, R1
    299 		//	BNE	Rarg1, R1, -2(PC)
    300 		{
    301 			name:      "LoweredZero",
    302 			aux:       "Int64",
    303 			argLength: 3,
    304 			reg: regInfo{
    305 				inputs:   []regMask{buildReg("R1"), gp},
    306 				clobbers: buildReg("R1"),
    307 			},
    308 			clobberFlags:   true,
    309 			faultOnNilArg0: true,
    310 		},
    311 
    312 		// large or unaligned move
    313 		// arg0 = address of dst memory (in R2, changed as side effect)
    314 		// arg1 = address of src memory (in R1, changed as side effect)
    315 		// arg2 = address of the last element of src
    316 		// arg3 = mem
    317 		// auxint = alignment
    318 		// returns mem
    319 		//	SUBV	$8, R1
    320 		//	MOVV	8(R1), Rtmp
    321 		//	MOVV	Rtmp, (R2)
    322 		//	ADDV	$8, R1
    323 		//	ADDV	$8, R2
    324 		//	BNE	Rarg2, R1, -4(PC)
    325 		{
    326 			name:      "LoweredMove",
    327 			aux:       "Int64",
    328 			argLength: 4,
    329 			reg: regInfo{
    330 				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
    331 				clobbers: buildReg("R1 R2"),
    332 			},
    333 			clobberFlags:   true,
    334 			faultOnNilArg0: true,
    335 			faultOnNilArg1: true,
    336 		},
    337 
    338 		// pseudo-ops
    339 		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
    340 
    341 		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
    342 		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
    343 
    344 		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
    345 		// and sorts it to the very beginning of the block to prevent other
    346 		// use of R22 (mips.REGCTXT, the closure pointer)
    347 		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}},
    348 
    349 		// MOVDconvert converts between pointers and integers.
    350 		// We have a special op for this so as to not confuse GC
    351 		// (particularly stack maps).  It takes a memory arg so it
    352 		// gets correctly ordered with respect to GC safepoints.
    353 		// arg0=ptr/int arg1=mem, output=int/ptr
    354 		{name: "MOVVconvert", argLength: 2, reg: gp11, asm: "MOVV"},
    355 	}
    356 
    357 	blocks := []blockData{
    358 		{name: "EQ"},
    359 		{name: "NE"},
    360 		{name: "LTZ"}, // < 0
    361 		{name: "LEZ"}, // <= 0
    362 		{name: "GTZ"}, // > 0
    363 		{name: "GEZ"}, // >= 0
    364 		{name: "FPT"}, // FP flag is true
    365 		{name: "FPF"}, // FP flag is false
    366 	}
    367 
    368 	archs = append(archs, arch{
    369 		name:            "MIPS64",
    370 		pkg:             "cmd/internal/obj/mips",
    371 		genfile:         "../../mips64/ssa.go",
    372 		ops:             ops,
    373 		blocks:          blocks,
    374 		regnames:        regNamesMIPS64,
    375 		gpregmask:       gp,
    376 		fpregmask:       fp,
    377 		specialregmask:  hi | lo,
    378 		framepointerreg: -1, // not used
    379 		linkreg:         int8(num["R31"]),
    380 	})
    381 }
    382