github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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}, // 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  }