github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/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  // +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  //  - *const instructions may use a constant larger than the instruction can encode.
    16  //    In this case the assembler expands to multiple instructions and uses tmp
    17  //    register (R23).
    18  
    19  // Suffixes encode the bit width of various instructions.
    20  // V (vlong)     = 64 bit
    21  // WU (word)     = 32 bit unsigned
    22  // W (word)      = 32 bit
    23  // H (half word) = 16 bit
    24  // HU            = 16 bit unsigned
    25  // B (byte)      = 8 bit
    26  // BU            = 8 bit unsigned
    27  // F (float)     = 32 bit float
    28  // D (double)    = 64 bit float
    29  
    30  // Note: registers not used in regalloc are not included in this list,
    31  // so that regmask stays within int64
    32  // Be careful when hand coding regmasks.
    33  var regNamesMIPS64 = []string{
    34  	"R0", // constant 0
    35  	"R1",
    36  	"R2",
    37  	"R3",
    38  	"R4",
    39  	"R5",
    40  	"R6",
    41  	"R7",
    42  	"R8",
    43  	"R9",
    44  	"R10",
    45  	"R11",
    46  	"R12",
    47  	"R13",
    48  	"R14",
    49  	"R15",
    50  	"R16",
    51  	"R17",
    52  	"R18",
    53  	"R19",
    54  	"R20",
    55  	"R21",
    56  	"R22",
    57  	// R23 = REGTMP not used in regalloc
    58  	"R24",
    59  	"R25",
    60  	// R26 reserved by kernel
    61  	// R27 reserved by kernel
    62  	// R28 = REGSB not used in regalloc
    63  	"SP",  // aka R29
    64  	"g",   // aka R30
    65  	"R31", // aka REGLINK
    66  
    67  	"F0",
    68  	"F1",
    69  	"F2",
    70  	"F3",
    71  	"F4",
    72  	"F5",
    73  	"F6",
    74  	"F7",
    75  	"F8",
    76  	"F9",
    77  	"F10",
    78  	"F11",
    79  	"F12",
    80  	"F13",
    81  	"F14",
    82  	"F15",
    83  	"F16",
    84  	"F17",
    85  	"F18",
    86  	"F19",
    87  	"F20",
    88  	"F21",
    89  	"F22",
    90  	"F23",
    91  	"F24",
    92  	"F25",
    93  	"F26",
    94  	"F27",
    95  	"F28",
    96  	"F29",
    97  	"F30",
    98  	"F31",
    99  
   100  	"HI", // high bits of multiplication
   101  	"LO", // low bits of multiplication
   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  	)
   140  	// Common regInfo
   141  	var (
   142  		gp01     = regInfo{inputs: nil, outputs: []regMask{gp}}
   143  		gp11     = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   144  		gp11sp   = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   145  		gp21     = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   146  		gp2hilo  = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
   147  		gpload   = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   148  		gpstore  = regInfo{inputs: []regMask{gpspsbg, gpg}}
   149  		gpstore0 = regInfo{inputs: []regMask{gpspsbg}}
   150  		gpxchg   = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   151  		gpcas    = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   152  		fp01     = regInfo{inputs: nil, outputs: []regMask{fp}}
   153  		fp11     = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   154  		//fp1flags  = regInfo{inputs: []regMask{fp}}
   155  		//fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   156  		//gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   157  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   158  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   159  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   160  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   161  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   162  	)
   163  	ops := []opData{
   164  		// binary ops
   165  		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},                             // arg0 + arg1
   166  		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"},                           // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
   167  		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                                                // arg0 - arg1
   168  		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},                             // arg0 - auxInt
   169  		{name: "MULV", argLength: 2, reg: gp2hilo, asm: "MULV", commutative: true, typ: "(Int64,Int64)"},     // arg0 * arg1, signed, results hi,lo
   170  		{name: "MULVU", argLength: 2, reg: gp2hilo, asm: "MULVU", commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned, results hi,lo
   171  		{name: "DIVV", argLength: 2, reg: gp2hilo, asm: "DIVV", typ: "(Int64,Int64)"},                        // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   172  		{name: "DIVVU", argLength: 2, reg: gp2hilo, asm: "DIVVU", typ: "(UInt64,UInt64)"},                    // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   173  
   174  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   175  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   176  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   177  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   178  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   179  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   180  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   181  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   182  
   183  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   184  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
   185  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   186  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
   187  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
   188  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
   189  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   190  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
   191  
   192  		{name: "NEGV", argLength: 1, reg: gp11},                // -arg0
   193  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   194  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   195  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   196  
   197  		// shifts
   198  		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                    // arg0 << arg1, shift amount is mod 64
   199  		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt
   200  		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                    // arg0 >> arg1, unsigned, shift amount is mod 64
   201  		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned
   202  		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                    // arg0 >> arg1, signed, shift amount is mod 64
   203  		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed
   204  
   205  		// comparisons
   206  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   207  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   208  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   209  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   210  
   211  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   212  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   213  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   214  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   215  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   216  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   217  
   218  		// moves
   219  		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
   220  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   221  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   222  
   223  		{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
   224  
   225  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   226  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   227  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   228  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   229  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   230  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   231  		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   232  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   233  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   234  
   235  		{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.
   236  		{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.
   237  		{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.
   238  		{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.
   239  		{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.
   240  		{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.
   241  
   242  		{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.
   243  		{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.
   244  		{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.
   245  		{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.
   246  
   247  		// conversions
   248  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   249  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   250  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   251  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   252  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   253  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   254  		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
   255  
   256  		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   257  
   258  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   259  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   260  		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
   261  		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
   262  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   263  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   264  		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
   265  		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
   266  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   267  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   268  
   269  		// function calls
   270  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"},                           // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   271  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   272  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   273  
   274  		// duffzero
   275  		// arg0 = address of memory to zero
   276  		// arg1 = mem
   277  		// auxint = offset into duffzero code to start executing
   278  		// returns mem
   279  		// R1 aka mips.REGRT1 changed as side effect
   280  		{
   281  			name:      "DUFFZERO",
   282  			aux:       "Int64",
   283  			argLength: 2,
   284  			reg: regInfo{
   285  				inputs:   []regMask{gp},
   286  				clobbers: buildReg("R1 R31"),
   287  			},
   288  			faultOnNilArg0: true,
   289  		},
   290  
   291  		// large or unaligned zeroing
   292  		// arg0 = address of memory to zero (in R1, changed as side effect)
   293  		// arg1 = address of the last element to zero
   294  		// arg2 = mem
   295  		// auxint = alignment
   296  		// returns mem
   297  		//	SUBV	$8, R1
   298  		//	MOVV	R0, 8(R1)
   299  		//	ADDV	$8, R1
   300  		//	BNE	Rarg1, R1, -2(PC)
   301  		{
   302  			name:      "LoweredZero",
   303  			aux:       "Int64",
   304  			argLength: 3,
   305  			reg: regInfo{
   306  				inputs:   []regMask{buildReg("R1"), gp},
   307  				clobbers: buildReg("R1"),
   308  			},
   309  			clobberFlags:   true,
   310  			faultOnNilArg0: true,
   311  		},
   312  
   313  		// large or unaligned move
   314  		// arg0 = address of dst memory (in R2, changed as side effect)
   315  		// arg1 = address of src memory (in R1, changed as side effect)
   316  		// arg2 = address of the last element of src
   317  		// arg3 = mem
   318  		// auxint = alignment
   319  		// returns mem
   320  		//	SUBV	$8, R1
   321  		//	MOVV	8(R1), Rtmp
   322  		//	MOVV	Rtmp, (R2)
   323  		//	ADDV	$8, R1
   324  		//	ADDV	$8, R2
   325  		//	BNE	Rarg2, R1, -4(PC)
   326  		{
   327  			name:      "LoweredMove",
   328  			aux:       "Int64",
   329  			argLength: 4,
   330  			reg: regInfo{
   331  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   332  				clobbers: buildReg("R1 R2"),
   333  			},
   334  			clobberFlags:   true,
   335  			faultOnNilArg0: true,
   336  			faultOnNilArg1: true,
   337  		},
   338  
   339  		// atomic loads.
   340  		// load from arg0. arg1=mem.
   341  		// returns <value,memory> so they can be properly ordered with other loads.
   342  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   343  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   344  
   345  		// atomic stores.
   346  		// store arg1 to arg0. arg2=mem. returns memory.
   347  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   348  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   349  		// store zero to arg0. arg1=mem. returns memory.
   350  		{name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   351  		{name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   352  
   353  		// atomic exchange.
   354  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   355  		// SYNC
   356  		// LL	(Rarg0), Rout
   357  		// MOVV Rarg1, Rtmp
   358  		// SC	Rtmp, (Rarg0)
   359  		// BEQ	Rtmp, -3(PC)
   360  		// SYNC
   361  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   362  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   363  
   364  		// atomic add.
   365  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   366  		// SYNC
   367  		// LL	(Rarg0), Rout
   368  		// ADDV Rarg1, Rout, Rtmp
   369  		// SC	Rtmp, (Rarg0)
   370  		// BEQ	Rtmp, -3(PC)
   371  		// SYNC
   372  		// ADDV Rarg1, Rout
   373  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   374  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   375  		// *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit.
   376  		{name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   377  		{name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   378  
   379  		// atomic compare and swap.
   380  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   381  		// if *arg0 == arg1 {
   382  		//   *arg0 = arg2
   383  		//   return (true, memory)
   384  		// } else {
   385  		//   return (false, memory)
   386  		// }
   387  		// SYNC
   388  		// MOVV $0, Rout
   389  		// LL	(Rarg0), Rtmp
   390  		// BNE	Rtmp, Rarg1, 4(PC)
   391  		// MOVV Rarg2, Rout
   392  		// SC	Rout, (Rarg0)
   393  		// BEQ	Rout, -4(PC)
   394  		// SYNC
   395  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   396  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   397  
   398  		// pseudo-ops
   399  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   400  
   401  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   402  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   403  
   404  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   405  		// and sorts it to the very beginning of the block to prevent other
   406  		// use of R22 (mips.REGCTXT, the closure pointer)
   407  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}},
   408  
   409  		// LoweredGetCallerSP returns the SP of the caller of the current function.
   410  		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   411  
   412  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
   413  		// It saves all GP registers if necessary,
   414  		// but clobbers R31 (LR) because it's a call
   415  		// and R23 (REGTMP).
   416  		{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R20"), buildReg("R21")}, clobbers: (callerSave &^ gpg) | buildReg("R31")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
   417  
   418  		// MOVDconvert converts between pointers and integers.
   419  		// We have a special op for this so as to not confuse GC
   420  		// (particularly stack maps).  It takes a memory arg so it
   421  		// gets correctly ordered with respect to GC safepoints.
   422  		// arg0=ptr/int arg1=mem, output=int/ptr
   423  		{name: "MOVVconvert", argLength: 2, reg: gp11, asm: "MOVV"},
   424  	}
   425  
   426  	blocks := []blockData{
   427  		{name: "EQ"},
   428  		{name: "NE"},
   429  		{name: "LTZ"}, // < 0
   430  		{name: "LEZ"}, // <= 0
   431  		{name: "GTZ"}, // > 0
   432  		{name: "GEZ"}, // >= 0
   433  		{name: "FPT"}, // FP flag is true
   434  		{name: "FPF"}, // FP flag is false
   435  	}
   436  
   437  	archs = append(archs, arch{
   438  		name:            "MIPS64",
   439  		pkg:             "cmd/internal/obj/mips",
   440  		genfile:         "../../mips64/ssa.go",
   441  		ops:             ops,
   442  		blocks:          blocks,
   443  		regnames:        regNamesMIPS64,
   444  		gpregmask:       gp,
   445  		fpregmask:       fp,
   446  		specialregmask:  hi | lo,
   447  		framepointerreg: -1, // not used
   448  		linkreg:         int8(num["R31"]),
   449  	})
   450  }