github.com/bir3/gocompiler@v0.3.205/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  package main
     6  
     7  import "strings"
     8  
     9  // Notes:
    10  //  - Integer types live in the low portion of registers. Upper portions are junk.
    11  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    12  //    Upper bytes are junk.
    13  //  - Unused portions of AuxInt are filled by sign-extending the used portion.
    14  //  - *const instructions may use a constant larger than the instruction can encode.
    15  //    In this case the assembler expands to multiple instructions and uses tmp
    16  //    register (R23).
    17  
    18  // Suffixes encode the bit width of various instructions.
    19  // W (word)      = 32 bit
    20  // H (half word) = 16 bit
    21  // HU            = 16 bit unsigned
    22  // B (byte)      = 8 bit
    23  // BU            = 8 bit unsigned
    24  // F (float)     = 32 bit float
    25  // D (double)    = 64 bit float
    26  
    27  // Note: registers not used in regalloc are not included in this list,
    28  // so that regmask stays within int64
    29  // Be careful when hand coding regmasks.
    30  var regNamesMIPS = []string{
    31  	"R0", // constant 0
    32  	"R1",
    33  	"R2",
    34  	"R3",
    35  	"R4",
    36  	"R5",
    37  	"R6",
    38  	"R7",
    39  	"R8",
    40  	"R9",
    41  	"R10",
    42  	"R11",
    43  	"R12",
    44  	"R13",
    45  	"R14",
    46  	"R15",
    47  	"R16",
    48  	"R17",
    49  	"R18",
    50  	"R19",
    51  	"R20",
    52  	"R21",
    53  	"R22",
    54  	//REGTMP
    55  	"R24",
    56  	"R25",
    57  	// R26 reserved by kernel
    58  	// R27 reserved by kernel
    59  	"R28",
    60  	"SP",  // aka R29
    61  	"g",   // aka R30
    62  	"R31", // REGLINK
    63  
    64  	// odd FP registers contain high parts of 64-bit FP values
    65  	"F0",
    66  	"F2",
    67  	"F4",
    68  	"F6",
    69  	"F8",
    70  	"F10",
    71  	"F12",
    72  	"F14",
    73  	"F16",
    74  	"F18",
    75  	"F20",
    76  	"F22",
    77  	"F24",
    78  	"F26",
    79  	"F28",
    80  	"F30",
    81  
    82  	"HI", // high bits of multiplication
    83  	"LO", // low bits of multiplication
    84  
    85  	// If you add registers, update asyncPreempt in runtime.
    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  		r1         = buildReg("R1")
   124  		r2         = buildReg("R2")
   125  		r3         = buildReg("R3")
   126  		r4         = buildReg("R4")
   127  		r5         = buildReg("R5")
   128  	)
   129  	// Common regInfo
   130  	var (
   131  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   132  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   133  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   134  		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   135  		gp31      = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
   136  		gp2hilo   = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
   137  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   138  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   139  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   140  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   141  		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
   142  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   143  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   144  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   145  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   146  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   147  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   148  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   149  	)
   150  	ops := []opData{
   151  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true},                                                                           // arg0 + arg1
   152  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32"},                                                                         // arg0 + auxInt
   153  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUBU"},                                                                                              // arg0 - arg1
   154  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32"},                                                                           // arg0 - auxInt
   155  		{name: "MUL", argLength: 2, reg: regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}, clobbers: hi | lo}, asm: "MUL", commutative: true}, // arg0 * arg1
   156  		{name: "MULT", argLength: 2, reg: gp2hilo, asm: "MUL", commutative: true, typ: "(Int32,Int32)"},                                                  // arg0 * arg1, signed, results hi,lo
   157  		{name: "MULTU", argLength: 2, reg: gp2hilo, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)"},                                              // arg0 * arg1, unsigned, results hi,lo
   158  		{name: "DIV", argLength: 2, reg: gp2hilo, asm: "DIV", typ: "(Int32,Int32)"},                                                                      // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   159  		{name: "DIVU", argLength: 2, reg: gp2hilo, asm: "DIVU", typ: "(UInt32,UInt32)"},                                                                  // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   160  
   161  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   162  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   163  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   164  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   165  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   166  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   167  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   168  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   169  
   170  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   171  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"},                // arg0 & auxInt
   172  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   173  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32"},                  // arg0 | auxInt
   174  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32"}, // arg0 ^ arg1
   175  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32"}, // arg0 ^ auxInt
   176  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   177  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int32"},                // ^(arg0 | auxInt)
   178  
   179  		{name: "NEG", argLength: 1, reg: gp11},                 // -arg0
   180  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   181  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   182  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   183  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
   184  
   185  		// shifts
   186  		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0 << arg1, shift amount is mod 32
   187  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, shift amount must be 0 through 31 inclusive
   188  		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0 >> arg1, unsigned, shift amount is mod 32
   189  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, shift amount must be 0 through 31 inclusive
   190  		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0 >> arg1, signed, shift amount is mod 32
   191  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, shift amount must be 0 through 31 inclusive
   192  
   193  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},
   194  
   195  		// comparisons
   196  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   197  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   198  		{name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool"},                  // 1 if arg0 > 0 (signed), 0 otherwise
   199  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   200  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   201  		{name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool"},                // 1 if arg0 > 0 (unsigned), 0 otherwise
   202  
   203  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   204  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   205  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   206  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   207  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   208  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   209  
   210  		// moves
   211  		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true},    // auxint
   212  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   213  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   214  
   215  		{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
   216  
   217  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   218  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   219  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   220  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   221  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   222  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   223  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   224  
   225  		{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.
   226  		{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.
   227  		{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.
   228  		{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.
   229  		{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.
   230  
   231  		{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.
   232  		{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.
   233  		{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.
   234  
   235  		// conversions
   236  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   237  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   238  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   239  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   240  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0
   241  
   242  		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   243  
   244  		// conditional move on zero (returns arg1 if arg2 is 0, otherwise arg0)
   245  		// order of parameters is reversed so we can use resultInArg0 (OpCMOVZ result arg1 arg2-> CMOVZ arg2reg, arg1reg, resultReg)
   246  		{name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true},
   247  		{name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true},
   248  
   249  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   250  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   251  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   252  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   253  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   254  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   255  
   256  		// function calls
   257  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   258  		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},                                 //  tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   259  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   260  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   261  
   262  		// atomic ops
   263  
   264  		// load from arg0. arg1=mem.
   265  		// returns <value,memory> so they can be properly ordered with other loads.
   266  		// SYNC
   267  		// MOV(B|W)	(Rarg0), Rout
   268  		// SYNC
   269  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   270  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   271  
   272  		// store arg1 to arg0. arg2=mem. returns memory.
   273  		// SYNC
   274  		// MOV(B|W)	Rarg1, (Rarg0)
   275  		// SYNC
   276  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   277  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   278  		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   279  
   280  		// atomic exchange.
   281  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   282  		// SYNC
   283  		// LL	(Rarg0), Rout
   284  		// MOVW Rarg1, Rtmp
   285  		// SC	Rtmp, (Rarg0)
   286  		// BEQ	Rtmp, -3(PC)
   287  		// SYNC
   288  		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   289  
   290  		// atomic add.
   291  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   292  		// SYNC
   293  		// LL	(Rarg0), Rout
   294  		// ADDU Rarg1, Rout, Rtmp
   295  		// SC	Rtmp, (Rarg0)
   296  		// BEQ	Rtmp, -3(PC)
   297  		// SYNC
   298  		// ADDU Rarg1, Rout
   299  		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   300  		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   301  
   302  		// atomic compare and swap.
   303  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   304  		// if *arg0 == arg1 {
   305  		//   *arg0 = arg2
   306  		//   return (true, memory)
   307  		// } else {
   308  		//   return (false, memory)
   309  		// }
   310  		// SYNC
   311  		// MOVW $0, Rout
   312  		// LL	(Rarg0), Rtmp
   313  		// BNE	Rtmp, Rarg1, 4(PC)
   314  		// MOVW Rarg2, Rout
   315  		// SC	Rout, (Rarg0)
   316  		// BEQ	Rout, -4(PC)
   317  		// SYNC
   318  		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   319  
   320  		// atomic and/or.
   321  		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
   322  		// SYNC
   323  		// LL	(Rarg0), Rtmp
   324  		// AND	Rarg1, Rtmp
   325  		// SC	Rtmp, (Rarg0)
   326  		// BEQ	Rtmp, -3(PC)
   327  		// SYNC
   328  		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   329  		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   330  
   331  		// large or unaligned zeroing
   332  		// arg0 = address of memory to zero (in R1, changed as side effect)
   333  		// arg1 = address of the last element to zero
   334  		// arg2 = mem
   335  		// auxint = alignment
   336  		// returns mem
   337  		//	SUBU	$4, R1
   338  		//	MOVW	R0, 4(R1)
   339  		//	ADDU	$4, R1
   340  		//	BNE	Rarg1, R1, -2(PC)
   341  		{
   342  			name:      "LoweredZero",
   343  			aux:       "Int32",
   344  			argLength: 3,
   345  			reg: regInfo{
   346  				inputs:   []regMask{buildReg("R1"), gp},
   347  				clobbers: buildReg("R1"),
   348  			},
   349  			faultOnNilArg0: true,
   350  		},
   351  
   352  		// large or unaligned move
   353  		// arg0 = address of dst memory (in R2, changed as side effect)
   354  		// arg1 = address of src memory (in R1, changed as side effect)
   355  		// arg2 = address of the last element of src
   356  		// arg3 = mem
   357  		// auxint = alignment
   358  		// returns mem
   359  		//	SUBU	$4, R1
   360  		//	MOVW	4(R1), Rtmp
   361  		//	MOVW	Rtmp, (R2)
   362  		//	ADDU	$4, R1
   363  		//	ADDU	$4, R2
   364  		//	BNE	Rarg2, R1, -4(PC)
   365  		{
   366  			name:      "LoweredMove",
   367  			aux:       "Int32",
   368  			argLength: 4,
   369  			reg: regInfo{
   370  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   371  				clobbers: buildReg("R1 R2"),
   372  			},
   373  			faultOnNilArg0: true,
   374  			faultOnNilArg1: true,
   375  		},
   376  
   377  		// pseudo-ops
   378  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   379  
   380  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   381  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   382  
   383  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   384  		// and sorts it to the very beginning of the block to prevent other
   385  		// use of R22 (mips.REGCTXT, the closure pointer)
   386  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true},
   387  
   388  		// LoweredGetCallerSP returns the SP of the caller of the current function.
   389  		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   390  
   391  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   392  		// I.e., if f calls g "calls" getcallerpc,
   393  		// the result should be the PC within f that g will return to.
   394  		// See runtime/stubs.go for a more detailed discussion.
   395  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   396  
   397  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
   398  		// It saves all GP registers if necessary,
   399  		// but clobbers R31 (LR) because it's a call
   400  		// and R23 (REGTMP).
   401  		{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R20"), buildReg("R21")}, clobbers: (callerSave &^ gpg) | buildReg("R31")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
   402  
   403  		// There are three of these functions so that they can have three different register inputs.
   404  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   405  		// default registers to match so we don't need to copy registers around unnecessarily.
   406  		{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
   407  		{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
   408  		{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
   409  		// Extend ops are the same as Bounds ops except the indexes are 64-bit.
   410  		{name: "LoweredPanicExtendA", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r3, r4}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
   411  		{name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r2, r3}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
   412  		{name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
   413  	}
   414  
   415  	blocks := []blockData{
   416  		{name: "EQ", controls: 1},
   417  		{name: "NE", controls: 1},
   418  		{name: "LTZ", controls: 1}, // < 0
   419  		{name: "LEZ", controls: 1}, // <= 0
   420  		{name: "GTZ", controls: 1}, // > 0
   421  		{name: "GEZ", controls: 1}, // >= 0
   422  		{name: "FPT", controls: 1}, // FP flag is true
   423  		{name: "FPF", controls: 1}, // FP flag is false
   424  	}
   425  
   426  	archs = append(archs, arch{
   427  		name:            "MIPS",
   428  		pkg:             "cmd/internal/obj/mips",
   429  		genfile:         "../../mips/ssa.go",
   430  		ops:             ops,
   431  		blocks:          blocks,
   432  		regnames:        regNamesMIPS,
   433  		gpregmask:       gp,
   434  		fpregmask:       fp,
   435  		specialregmask:  hi | lo,
   436  		framepointerreg: -1, // not used
   437  		linkreg:         int8(num["R31"]),
   438  	})
   439  }