github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/_gen/MIPS64Ops.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  //  - *const instructions may use a constant larger than the instruction can encode.
    14  //    In this case the assembler expands to multiple instructions and uses tmp
    15  //    register (R23).
    16  
    17  // Suffixes encode the bit width of various instructions.
    18  // V (vlong)     = 64 bit
    19  // WU (word)     = 32 bit unsigned
    20  // W (word)      = 32 bit
    21  // H (half word) = 16 bit
    22  // HU            = 16 bit unsigned
    23  // B (byte)      = 8 bit
    24  // BU            = 8 bit unsigned
    25  // F (float)     = 32 bit float
    26  // D (double)    = 64 bit float
    27  
    28  // Note: registers not used in regalloc are not included in this list,
    29  // so that regmask stays within int64
    30  // Be careful when hand coding regmasks.
    31  var regNamesMIPS64 = []string{
    32  	"R0", // constant 0
    33  	"R1",
    34  	"R2",
    35  	"R3",
    36  	"R4",
    37  	"R5",
    38  	"R6",
    39  	"R7",
    40  	"R8",
    41  	"R9",
    42  	"R10",
    43  	"R11",
    44  	"R12",
    45  	"R13",
    46  	"R14",
    47  	"R15",
    48  	"R16",
    49  	"R17",
    50  	"R18",
    51  	"R19",
    52  	"R20",
    53  	"R21",
    54  	"R22",
    55  	// R23 = REGTMP not used in regalloc
    56  	"R24",
    57  	"R25",
    58  	// R26 reserved by kernel
    59  	// R27 reserved by kernel
    60  	// R28 = REGSB not used in regalloc
    61  	"SP",  // aka R29
    62  	"g",   // aka R30
    63  	"R31", // aka REGLINK
    64  
    65  	"F0",
    66  	"F1",
    67  	"F2",
    68  	"F3",
    69  	"F4",
    70  	"F5",
    71  	"F6",
    72  	"F7",
    73  	"F8",
    74  	"F9",
    75  	"F10",
    76  	"F11",
    77  	"F12",
    78  	"F13",
    79  	"F14",
    80  	"F15",
    81  	"F16",
    82  	"F17",
    83  	"F18",
    84  	"F19",
    85  	"F20",
    86  	"F21",
    87  	"F22",
    88  	"F23",
    89  	"F24",
    90  	"F25",
    91  	"F26",
    92  	"F27",
    93  	"F28",
    94  	"F29",
    95  	"F30",
    96  	"F31",
    97  
    98  	"HI", // high bits of multiplication
    99  	"LO", // low bits of multiplication
   100  
   101  	// If you add registers, update asyncPreempt in runtime.
   102  
   103  	// pseudo-registers
   104  	"SB",
   105  }
   106  
   107  func init() {
   108  	// Make map from reg names to reg integers.
   109  	if len(regNamesMIPS64) > 64 {
   110  		panic("too many registers")
   111  	}
   112  	num := map[string]int{}
   113  	for i, name := range regNamesMIPS64 {
   114  		num[name] = i
   115  	}
   116  	buildReg := func(s string) regMask {
   117  		m := regMask(0)
   118  		for _, r := range strings.Split(s, " ") {
   119  			if n, ok := num[r]; ok {
   120  				m |= regMask(1) << uint(n)
   121  				continue
   122  			}
   123  			panic("register " + r + " not found")
   124  		}
   125  		return m
   126  	}
   127  
   128  	// Common individual register masks
   129  	var (
   130  		gp         = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31")
   131  		gpg        = gp | buildReg("g")
   132  		gpsp       = gp | buildReg("SP")
   133  		gpspg      = gpg | buildReg("SP")
   134  		gpspsbg    = gpspg | buildReg("SB")
   135  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
   136  		lo         = buildReg("LO")
   137  		hi         = buildReg("HI")
   138  		callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   139  		r1         = buildReg("R1")
   140  		r2         = buildReg("R2")
   141  		r3         = buildReg("R3")
   142  		r4         = buildReg("R4")
   143  	)
   144  	// Common regInfo
   145  	var (
   146  		gp01     = regInfo{inputs: nil, outputs: []regMask{gp}}
   147  		gp11     = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   148  		gp11sp   = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   149  		gp21     = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   150  		gp2hilo  = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
   151  		gpload   = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   152  		gpstore  = regInfo{inputs: []regMask{gpspsbg, gpg}}
   153  		gpstore0 = regInfo{inputs: []regMask{gpspsbg}}
   154  		gpxchg   = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   155  		gpcas    = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   156  		fp01     = regInfo{inputs: nil, outputs: []regMask{fp}}
   157  		fp11     = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   158  		//fp1flags  = regInfo{inputs: []regMask{fp}}
   159  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   160  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   161  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   162  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   163  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   164  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   165  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   166  	)
   167  	ops := []opData{
   168  		// binary ops
   169  		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},                             // arg0 + arg1
   170  		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"},                           // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
   171  		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                                                // arg0 - arg1
   172  		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},                             // arg0 - auxInt
   173  		{name: "MULV", argLength: 2, reg: gp2hilo, asm: "MULV", commutative: true, typ: "(Int64,Int64)"},     // arg0 * arg1, signed, results hi,lo
   174  		{name: "MULVU", argLength: 2, reg: gp2hilo, asm: "MULVU", commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned, results hi,lo
   175  		{name: "DIVV", argLength: 2, reg: gp2hilo, asm: "DIVV", typ: "(Int64,Int64)"},                        // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   176  		{name: "DIVVU", argLength: 2, reg: gp2hilo, asm: "DIVVU", typ: "(UInt64,UInt64)"},                    // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   177  
   178  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   179  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   180  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   181  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   182  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   183  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   184  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   185  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   186  
   187  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   188  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
   189  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   190  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
   191  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
   192  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
   193  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   194  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
   195  
   196  		{name: "NEGV", argLength: 1, reg: gp11},                // -arg0
   197  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   198  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   199  		{name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD"},   // abs(arg0), float64
   200  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   201  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
   202  
   203  		// shifts
   204  		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                    // arg0 << arg1, shift amount is mod 64
   205  		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt
   206  		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                    // arg0 >> arg1, unsigned, shift amount is mod 64
   207  		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned
   208  		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                    // arg0 >> arg1, signed, shift amount is mod 64
   209  		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed
   210  
   211  		// comparisons
   212  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   213  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   214  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   215  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   216  
   217  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   218  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   219  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   220  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   221  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   222  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   223  
   224  		// moves
   225  		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
   226  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   227  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   228  
   229  		{name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   230  
   231  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   232  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   233  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   234  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   235  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   236  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   237  		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   238  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   239  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   240  
   241  		{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.
   242  		{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.
   243  		{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.
   244  		{name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   245  		{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.
   246  		{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.
   247  
   248  		{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.
   249  		{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.
   250  		{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.
   251  		{name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
   252  
   253  		// moves (no conversion)
   254  		{name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW"}, // move float32 to int32 (no conversion). MIPS64 will perform sign-extend to 64-bit by default
   255  		{name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW"}, // move int32 to float32 (no conversion). MIPS64 will perform sign-extend to 64-bit by default
   256  		{name: "MOVVfpgp", argLength: 1, reg: fpgp, asm: "MOVV"}, // move float64 to int64 (no conversion).
   257  		{name: "MOVVgpfp", argLength: 1, reg: gpfp, asm: "MOVV"}, // move int64 to float64 (no conversion).
   258  
   259  		// conversions
   260  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   261  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   262  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   263  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   264  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   265  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   266  		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
   267  
   268  		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   269  
   270  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   271  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   272  		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
   273  		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
   274  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   275  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   276  		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
   277  		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
   278  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   279  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   280  
   281  		// function calls
   282  		{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
   283  		{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
   284  		{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
   285  		{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
   286  
   287  		// duffzero
   288  		// arg0 = address of memory to zero
   289  		// arg1 = mem
   290  		// auxint = offset into duffzero code to start executing
   291  		// returns mem
   292  		// R1 aka mips.REGRT1 changed as side effect
   293  		{
   294  			name:      "DUFFZERO",
   295  			aux:       "Int64",
   296  			argLength: 2,
   297  			reg: regInfo{
   298  				inputs:   []regMask{gp},
   299  				clobbers: buildReg("R1 R31"),
   300  			},
   301  			faultOnNilArg0: true,
   302  		},
   303  
   304  		// duffcopy
   305  		// arg0 = address of dst memory (in R2, changed as side effect)
   306  		// arg1 = address of src memory (in R1, changed as side effect)
   307  		// arg2 = mem
   308  		// auxint = offset into duffcopy code to start executing
   309  		// returns mem
   310  		{
   311  			name:      "DUFFCOPY",
   312  			aux:       "Int64",
   313  			argLength: 3,
   314  			reg: regInfo{
   315  				inputs:   []regMask{buildReg("R2"), buildReg("R1")},
   316  				clobbers: buildReg("R1 R2 R31"),
   317  			},
   318  			faultOnNilArg0: true,
   319  			faultOnNilArg1: true,
   320  		},
   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  		//	SUBV	$8, R1
   329  		//	MOVV	R0, 8(R1)
   330  		//	ADDV	$8, R1
   331  		//	BNE	Rarg1, R1, -2(PC)
   332  		{
   333  			name:      "LoweredZero",
   334  			aux:       "Int64",
   335  			argLength: 3,
   336  			reg: regInfo{
   337  				inputs:   []regMask{buildReg("R1"), gp},
   338  				clobbers: buildReg("R1"),
   339  			},
   340  			clobberFlags:   true,
   341  			faultOnNilArg0: true,
   342  		},
   343  
   344  		// large or unaligned move
   345  		// arg0 = address of dst memory (in R2, changed as side effect)
   346  		// arg1 = address of src memory (in R1, changed as side effect)
   347  		// arg2 = address of the last element of src
   348  		// arg3 = mem
   349  		// auxint = alignment
   350  		// returns mem
   351  		//	SUBV	$8, R1
   352  		//	MOVV	8(R1), Rtmp
   353  		//	MOVV	Rtmp, (R2)
   354  		//	ADDV	$8, R1
   355  		//	ADDV	$8, R2
   356  		//	BNE	Rarg2, R1, -4(PC)
   357  		{
   358  			name:      "LoweredMove",
   359  			aux:       "Int64",
   360  			argLength: 4,
   361  			reg: regInfo{
   362  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   363  				clobbers: buildReg("R1 R2"),
   364  			},
   365  			clobberFlags:   true,
   366  			faultOnNilArg0: true,
   367  			faultOnNilArg1: true,
   368  		},
   369  
   370  		// atomic and/or.
   371  		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
   372  		// SYNC
   373  		// LL	(Rarg0), Rtmp
   374  		// AND	Rarg1, Rtmp
   375  		// SC	Rtmp, (Rarg0)
   376  		// BEQ	Rtmp, -3(PC)
   377  		// SYNC
   378  		{name: "LoweredAtomicAnd32", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   379  		{name: "LoweredAtomicOr32", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   380  
   381  		// atomic loads.
   382  		// load from arg0. arg1=mem.
   383  		// returns <value,memory> so they can be properly ordered with other loads.
   384  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   385  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   386  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   387  
   388  		// atomic stores.
   389  		// store arg1 to arg0. arg2=mem. returns memory.
   390  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   391  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   392  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   393  		// store zero to arg0. arg1=mem. returns memory.
   394  		{name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   395  		{name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   396  
   397  		// atomic exchange.
   398  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   399  		// SYNC
   400  		// LL	(Rarg0), Rout
   401  		// MOVV Rarg1, Rtmp
   402  		// SC	Rtmp, (Rarg0)
   403  		// BEQ	Rtmp, -3(PC)
   404  		// SYNC
   405  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   406  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   407  
   408  		// atomic add.
   409  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   410  		// SYNC
   411  		// LL	(Rarg0), Rout
   412  		// ADDV Rarg1, Rout, Rtmp
   413  		// SC	Rtmp, (Rarg0)
   414  		// BEQ	Rtmp, -3(PC)
   415  		// SYNC
   416  		// ADDV Rarg1, Rout
   417  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   418  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   419  		// *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit.
   420  		{name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   421  		{name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   422  
   423  		// atomic compare and swap.
   424  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   425  		// if *arg0 == arg1 {
   426  		//   *arg0 = arg2
   427  		//   return (true, memory)
   428  		// } else {
   429  		//   return (false, memory)
   430  		// }
   431  		// SYNC
   432  		// MOVV $0, Rout
   433  		// LL	(Rarg0), Rtmp
   434  		// BNE	Rtmp, Rarg1, 4(PC)
   435  		// MOVV Rarg2, Rout
   436  		// SC	Rout, (Rarg0)
   437  		// BEQ	Rout, -4(PC)
   438  		// SYNC
   439  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   440  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   441  
   442  		// pseudo-ops
   443  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   444  
   445  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   446  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   447  
   448  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   449  		// and sorts it to the very beginning of the block to prevent other
   450  		// use of R22 (mips.REGCTXT, the closure pointer)
   451  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true},
   452  
   453  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   454  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   455  
   456  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   457  		// I.e., if f calls g "calls" getcallerpc,
   458  		// the result should be the PC within f that g will return to.
   459  		// See runtime/stubs.go for a more detailed discussion.
   460  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   461  
   462  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   463  		// It saves all GP registers if necessary,
   464  		// but clobbers R31 (LR) because it's a call
   465  		// and R23 (REGTMP).
   466  		// Returns a pointer to a write barrier buffer in R25.
   467  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
   468  
   469  		// There are three of these functions so that they can have three different register inputs.
   470  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   471  		// default registers to match so we don't need to copy registers around unnecessarily.
   472  		{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).
   473  		{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).
   474  		{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).
   475  	}
   476  
   477  	blocks := []blockData{
   478  		{name: "EQ", controls: 1},
   479  		{name: "NE", controls: 1},
   480  		{name: "LTZ", controls: 1}, // < 0
   481  		{name: "LEZ", controls: 1}, // <= 0
   482  		{name: "GTZ", controls: 1}, // > 0
   483  		{name: "GEZ", controls: 1}, // >= 0
   484  		{name: "FPT", controls: 1}, // FP flag is true
   485  		{name: "FPF", controls: 1}, // FP flag is false
   486  	}
   487  
   488  	archs = append(archs, arch{
   489  		name:            "MIPS64",
   490  		pkg:             "github.com/go-asm/go/cmd/obj/mips",
   491  		genfile:         "../../mips64/ssa.go",
   492  		ops:             ops,
   493  		blocks:          blocks,
   494  		regnames:        regNamesMIPS64,
   495  		gpregmask:       gp,
   496  		fpregmask:       fp,
   497  		specialregmask:  hi | lo,
   498  		framepointerreg: -1, // not used
   499  		linkreg:         int8(num["R31"]),
   500  	})
   501  }