github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/src/cmd/compile/internal/ssa/gen/MIPSOps.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build ignore
     6  
     7  package main
     8  
     9  import "strings"
    10  
    11  // Notes:
    12  //  - Integer types live in the low portion of registers. Upper portions are junk.
    13  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    14  //    Upper bytes are junk.
    15  //  - 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, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   210  
   211  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   212  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   213  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   214  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   215  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   216  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   217  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   218  
   219  		{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.
   220  		{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.
   221  		{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.
   222  		{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.
   223  		{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.
   224  
   225  		{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.
   226  		{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.
   227  		{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.
   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, symEffect: "None"},                           // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   252  		{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
   253  		{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
   254  
   255  		// atomic ops
   256  
   257  		// load from arg0. arg1=mem.
   258  		// returns <value,memory> so they can be properly ordered with other loads.
   259  		// SYNC
   260  		// MOVW	(Rarg0), Rout
   261  		// SYNC
   262  		{name: "LoweredAtomicLoad", argLength: 2, reg: gpload, faultOnNilArg0: true},
   263  
   264  		// store arg1 to arg0. arg2=mem. returns memory.
   265  		// SYNC
   266  		// MOVW	Rarg1, (Rarg0)
   267  		// SYNC
   268  		{name: "LoweredAtomicStore", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   269  		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   270  
   271  		// atomic exchange.
   272  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   273  		// SYNC
   274  		// LL	(Rarg0), Rout
   275  		// MOVW Rarg1, Rtmp
   276  		// SC	Rtmp, (Rarg0)
   277  		// BEQ	Rtmp, -3(PC)
   278  		// SYNC
   279  		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   280  
   281  		// atomic add.
   282  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   283  		// SYNC
   284  		// LL	(Rarg0), Rout
   285  		// ADDU Rarg1, Rout, Rtmp
   286  		// SC	Rtmp, (Rarg0)
   287  		// BEQ	Rtmp, -3(PC)
   288  		// SYNC
   289  		// ADDU Rarg1, Rout
   290  		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   291  		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   292  
   293  		// atomic compare and swap.
   294  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   295  		// if *arg0 == arg1 {
   296  		//   *arg0 = arg2
   297  		//   return (true, memory)
   298  		// } else {
   299  		//   return (false, memory)
   300  		// }
   301  		// SYNC
   302  		// MOVW $0, Rout
   303  		// LL	(Rarg0), Rtmp
   304  		// BNE	Rtmp, Rarg1, 4(PC)
   305  		// MOVW Rarg2, Rout
   306  		// SC	Rout, (Rarg0)
   307  		// BEQ	Rout, -4(PC)
   308  		// SYNC
   309  		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   310  
   311  		// atomic and/or.
   312  		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
   313  		// SYNC
   314  		// LL	(Rarg0), Rtmp
   315  		// AND	Rarg1, Rtmp
   316  		// SC	Rtmp, (Rarg0)
   317  		// BEQ	Rtmp, -3(PC)
   318  		// SYNC
   319  		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true},
   320  		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true},
   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  		//	SUBU	$4, R1
   329  		//	MOVW	R0, 4(R1)
   330  		//	ADDU	$4, R1
   331  		//	BNE	Rarg1, R1, -2(PC)
   332  		{
   333  			name:      "LoweredZero",
   334  			aux:       "Int32",
   335  			argLength: 3,
   336  			reg: regInfo{
   337  				inputs:   []regMask{buildReg("R1"), gp},
   338  				clobbers: buildReg("R1"),
   339  			},
   340  			faultOnNilArg0: true,
   341  		},
   342  
   343  		// large or unaligned move
   344  		// arg0 = address of dst memory (in R2, changed as side effect)
   345  		// arg1 = address of src memory (in R1, changed as side effect)
   346  		// arg2 = address of the last element of src
   347  		// arg3 = mem
   348  		// auxint = alignment
   349  		// returns mem
   350  		//	SUBU	$4, R1
   351  		//	MOVW	4(R1), Rtmp
   352  		//	MOVW	Rtmp, (R2)
   353  		//	ADDU	$4, R1
   354  		//	ADDU	$4, R2
   355  		//	BNE	Rarg2, R1, -4(PC)
   356  		{
   357  			name:      "LoweredMove",
   358  			aux:       "Int32",
   359  			argLength: 4,
   360  			reg: regInfo{
   361  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   362  				clobbers: buildReg("R1 R2"),
   363  			},
   364  			faultOnNilArg0: true,
   365  			faultOnNilArg1: true,
   366  		},
   367  
   368  		// pseudo-ops
   369  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   370  
   371  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   372  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   373  
   374  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   375  		// and sorts it to the very beginning of the block to prevent other
   376  		// use of R22 (mips.REGCTXT, the closure pointer)
   377  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}},
   378  
   379  		// MOVWconvert converts between pointers and integers.
   380  		// We have a special op for this so as to not confuse GC
   381  		// (particularly stack maps).  It takes a memory arg so it
   382  		// gets correctly ordered with respect to GC safepoints.
   383  		// arg0=ptr/int arg1=mem, output=int/ptr
   384  		{name: "MOVWconvert", argLength: 2, reg: gp11, asm: "MOVW"},
   385  	}
   386  
   387  	blocks := []blockData{
   388  		{name: "EQ"},
   389  		{name: "NE"},
   390  		{name: "LTZ"}, // < 0
   391  		{name: "LEZ"}, // <= 0
   392  		{name: "GTZ"}, // > 0
   393  		{name: "GEZ"}, // >= 0
   394  		{name: "FPT"}, // FP flag is true
   395  		{name: "FPF"}, // FP flag is false
   396  	}
   397  
   398  	archs = append(archs, arch{
   399  		name:            "MIPS",
   400  		pkg:             "cmd/internal/obj/mips",
   401  		genfile:         "../../mips/ssa.go",
   402  		ops:             ops,
   403  		blocks:          blocks,
   404  		regnames:        regNamesMIPS,
   405  		gpregmask:       gp,
   406  		fpregmask:       fp,
   407  		specialregmask:  hi | lo,
   408  		framepointerreg: -1, // not used
   409  		linkreg:         int8(num["R31"]),
   410  	})
   411  }