github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/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  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   143  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   144  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   145  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   146  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   147  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   148  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   149  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   150  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   151  	)
   152  	ops := []opData{
   153  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true},                                                                           // arg0 + arg1
   154  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32"},                                                                         // arg0 + auxInt
   155  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUBU"},                                                                                              // arg0 - arg1
   156  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32"},                                                                           // arg0 - auxInt
   157  		{name: "MUL", argLength: 2, reg: regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}, clobbers: hi | lo}, asm: "MUL", commutative: true}, // arg0 * arg1
   158  		{name: "MULT", argLength: 2, reg: gp2hilo, asm: "MUL", commutative: true, typ: "(Int32,Int32)"},                                                  // arg0 * arg1, signed, results hi,lo
   159  		{name: "MULTU", argLength: 2, reg: gp2hilo, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)"},                                              // arg0 * arg1, unsigned, results hi,lo
   160  		{name: "DIV", argLength: 2, reg: gp2hilo, asm: "DIV", typ: "(Int32,Int32)"},                                                                      // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   161  		{name: "DIVU", argLength: 2, reg: gp2hilo, asm: "DIVU", typ: "(UInt32,UInt32)"},                                                                  // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   162  
   163  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   164  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   165  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   166  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   167  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   168  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   169  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   170  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   171  
   172  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   173  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"},                // arg0 & auxInt
   174  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   175  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32"},                  // arg0 | auxInt
   176  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32"}, // arg0 ^ arg1
   177  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32"}, // arg0 ^ auxInt
   178  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   179  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int32"},                // ^(arg0 | auxInt)
   180  
   181  		{name: "NEG", argLength: 1, reg: gp11},                 // -arg0
   182  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   183  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   184  		{name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD"},   // abs(arg0), float64
   185  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   186  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
   187  
   188  		// shifts
   189  		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0 << arg1, shift amount is mod 32
   190  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, shift amount must be 0 through 31 inclusive
   191  		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0 >> arg1, unsigned, shift amount is mod 32
   192  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, shift amount must be 0 through 31 inclusive
   193  		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0 >> arg1, signed, shift amount is mod 32
   194  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, shift amount must be 0 through 31 inclusive
   195  
   196  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},
   197  
   198  		// comparisons
   199  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   200  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   201  		{name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool"},                  // 1 if arg0 > 0 (signed), 0 otherwise
   202  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   203  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   204  		{name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool"},                // 1 if arg0 > 0 (unsigned), 0 otherwise
   205  
   206  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   207  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   208  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   209  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   210  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   211  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   212  
   213  		// moves
   214  		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true},    // auxint
   215  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   216  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   217  
   218  		{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
   219  
   220  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   221  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   222  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   223  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   224  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   225  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   226  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   227  
   228  		{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.
   229  		{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.
   230  		{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.
   231  		{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.
   232  		{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.
   233  
   234  		{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.
   235  		{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.
   236  		{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.
   237  
   238  		// moves (no conversion)
   239  		{name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW"}, // move float32 to int32 (no conversion)
   240  		{name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW"}, // move int32 to float32 (no conversion)
   241  
   242  		// conversions
   243  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   244  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   245  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   246  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   247  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0
   248  
   249  		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   250  
   251  		// conditional move on zero (returns arg1 if arg2 is 0, otherwise arg0)
   252  		// order of parameters is reversed so we can use resultInArg0 (OpCMOVZ result arg1 arg2-> CMOVZ arg2reg, arg1reg, resultReg)
   253  		{name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true},
   254  		{name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true},
   255  
   256  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   257  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   258  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   259  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   260  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   261  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   262  
   263  		// function calls
   264  		{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
   265  		{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
   266  		{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
   267  		{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
   268  
   269  		// atomic ops
   270  
   271  		// load from arg0. arg1=mem.
   272  		// returns <value,memory> so they can be properly ordered with other loads.
   273  		// SYNC
   274  		// MOV(B|W)	(Rarg0), Rout
   275  		// SYNC
   276  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   277  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   278  
   279  		// store arg1 to arg0. arg2=mem. returns memory.
   280  		// SYNC
   281  		// MOV(B|W)	Rarg1, (Rarg0)
   282  		// SYNC
   283  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   284  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   285  		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   286  
   287  		// atomic exchange.
   288  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   289  		// SYNC
   290  		// LL	(Rarg0), Rout
   291  		// MOVW Rarg1, Rtmp
   292  		// SC	Rtmp, (Rarg0)
   293  		// BEQ	Rtmp, -3(PC)
   294  		// SYNC
   295  		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   296  
   297  		// atomic add.
   298  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   299  		// SYNC
   300  		// LL	(Rarg0), Rout
   301  		// ADDU Rarg1, Rout, Rtmp
   302  		// SC	Rtmp, (Rarg0)
   303  		// BEQ	Rtmp, -3(PC)
   304  		// SYNC
   305  		// ADDU Rarg1, Rout
   306  		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   307  		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   308  
   309  		// atomic compare and swap.
   310  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   311  		// if *arg0 == arg1 {
   312  		//   *arg0 = arg2
   313  		//   return (true, memory)
   314  		// } else {
   315  		//   return (false, memory)
   316  		// }
   317  		// SYNC
   318  		// MOVW $0, Rout
   319  		// LL	(Rarg0), Rtmp
   320  		// BNE	Rtmp, Rarg1, 4(PC)
   321  		// MOVW Rarg2, Rout
   322  		// SC	Rout, (Rarg0)
   323  		// BEQ	Rout, -4(PC)
   324  		// SYNC
   325  		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   326  
   327  		// atomic and/or.
   328  		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
   329  		// SYNC
   330  		// LL	(Rarg0), Rtmp
   331  		// AND	Rarg1, Rtmp
   332  		// SC	Rtmp, (Rarg0)
   333  		// BEQ	Rtmp, -3(PC)
   334  		// SYNC
   335  		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   336  		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   337  
   338  		// large or unaligned zeroing
   339  		// arg0 = address of memory to zero (in R1, changed as side effect)
   340  		// arg1 = address of the last element to zero
   341  		// arg2 = mem
   342  		// auxint = alignment
   343  		// returns mem
   344  		//	SUBU	$4, R1
   345  		//	MOVW	R0, 4(R1)
   346  		//	ADDU	$4, R1
   347  		//	BNE	Rarg1, R1, -2(PC)
   348  		{
   349  			name:      "LoweredZero",
   350  			aux:       "Int32",
   351  			argLength: 3,
   352  			reg: regInfo{
   353  				inputs:   []regMask{buildReg("R1"), gp},
   354  				clobbers: buildReg("R1"),
   355  			},
   356  			faultOnNilArg0: true,
   357  		},
   358  
   359  		// large or unaligned move
   360  		// arg0 = address of dst memory (in R2, changed as side effect)
   361  		// arg1 = address of src memory (in R1, changed as side effect)
   362  		// arg2 = address of the last element of src
   363  		// arg3 = mem
   364  		// auxint = alignment
   365  		// returns mem
   366  		//	SUBU	$4, R1
   367  		//	MOVW	4(R1), Rtmp
   368  		//	MOVW	Rtmp, (R2)
   369  		//	ADDU	$4, R1
   370  		//	ADDU	$4, R2
   371  		//	BNE	Rarg2, R1, -4(PC)
   372  		{
   373  			name:      "LoweredMove",
   374  			aux:       "Int32",
   375  			argLength: 4,
   376  			reg: regInfo{
   377  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   378  				clobbers: buildReg("R1 R2"),
   379  			},
   380  			faultOnNilArg0: true,
   381  			faultOnNilArg1: true,
   382  		},
   383  
   384  		// pseudo-ops
   385  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   386  
   387  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   388  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   389  
   390  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   391  		// and sorts it to the very beginning of the block to prevent other
   392  		// use of R22 (mips.REGCTXT, the closure pointer)
   393  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true},
   394  
   395  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   396  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   397  
   398  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   399  		// I.e., if f calls g "calls" getcallerpc,
   400  		// the result should be the PC within f that g will return to.
   401  		// See runtime/stubs.go for a more detailed discussion.
   402  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   403  
   404  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   405  		// It saves all GP registers if necessary,
   406  		// but clobbers R31 (LR) because it's a call
   407  		// and R23 (REGTMP).
   408  		// Returns a pointer to a write barrier buffer in R25.
   409  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
   410  
   411  		// There are three of these functions so that they can have three different register inputs.
   412  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   413  		// default registers to match so we don't need to copy registers around unnecessarily.
   414  		{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).
   415  		{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).
   416  		{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).
   417  		// Extend ops are the same as Bounds ops except the indexes are 64-bit.
   418  		{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).
   419  		{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).
   420  		{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).
   421  	}
   422  
   423  	blocks := []blockData{
   424  		{name: "EQ", controls: 1},
   425  		{name: "NE", controls: 1},
   426  		{name: "LTZ", controls: 1}, // < 0
   427  		{name: "LEZ", controls: 1}, // <= 0
   428  		{name: "GTZ", controls: 1}, // > 0
   429  		{name: "GEZ", controls: 1}, // >= 0
   430  		{name: "FPT", controls: 1}, // FP flag is true
   431  		{name: "FPF", controls: 1}, // FP flag is false
   432  	}
   433  
   434  	archs = append(archs, arch{
   435  		name:            "MIPS",
   436  		pkg:             "github.com/go-asm/go/cmd/obj/mips",
   437  		genfile:         "../../mips/ssa.go",
   438  		ops:             ops,
   439  		blocks:          blocks,
   440  		regnames:        regNamesMIPS,
   441  		gpregmask:       gp,
   442  		fpregmask:       fp,
   443  		specialregmask:  hi | lo,
   444  		framepointerreg: -1, // not used
   445  		linkreg:         int8(num["R31"]),
   446  	})
   447  }