Source file src/cmd/compile/internal/ssa/_gen/MIPS64Ops.go

     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  	"ZERO", // 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  		first16    = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16")
   140  		rz         = buildReg("ZERO")
   141  	)
   142  	// Common regInfo
   143  	var (
   144  		gp01     = regInfo{inputs: nil, outputs: []regMask{gp}}
   145  		gp11     = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   146  		gp11sp   = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   147  		gp21     = regInfo{inputs: []regMask{gpg, gpg | rz}, outputs: []regMask{gp}}
   148  		gp2hilo  = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
   149  		gpload   = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   150  		gpstore  = regInfo{inputs: []regMask{gpspsbg, gpg | rz}}
   151  		gpstore0 = regInfo{inputs: []regMask{gpspsbg}}
   152  		gpxchg   = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   153  		gpcas    = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   154  		fp01     = regInfo{inputs: nil, outputs: []regMask{fp}}
   155  		fp11     = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   156  		//fp1flags  = regInfo{inputs: []regMask{fp}}
   157  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   158  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   159  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   160  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   161  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   162  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   163  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   164  	)
   165  	ops := []opData{
   166  		// binary ops
   167  		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},                             // arg0 + arg1
   168  		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"},                           // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
   169  		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                                                // arg0 - arg1
   170  		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},                             // arg0 - auxInt
   171  		{name: "MULV", argLength: 2, reg: gp2hilo, asm: "MULV", commutative: true, typ: "(Int64,Int64)"},     // arg0 * arg1, signed, results hi,lo
   172  		{name: "MULVU", argLength: 2, reg: gp2hilo, asm: "MULVU", commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned, results hi,lo
   173  		{name: "DIVV", argLength: 2, reg: gp2hilo, asm: "DIVV", typ: "(Int64,Int64)"},                        // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   174  		{name: "DIVVU", argLength: 2, reg: gp2hilo, asm: "DIVVU", typ: "(UInt64,UInt64)"},                    // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
   175  
   176  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   177  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   178  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   179  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   180  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   181  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   182  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   183  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   184  
   185  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   186  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
   187  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   188  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
   189  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
   190  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
   191  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   192  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
   193  
   194  		{name: "NEGV", argLength: 1, reg: gp11},                // -arg0
   195  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   196  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   197  		{name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD"},   // abs(arg0), float64
   198  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   199  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
   200  
   201  		// shifts
   202  		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                    // arg0 << arg1, shift amount is mod 64
   203  		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt
   204  		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                    // arg0 >> arg1, unsigned, shift amount is mod 64
   205  		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned
   206  		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                    // arg0 >> arg1, signed, shift amount is mod 64
   207  		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed
   208  
   209  		// comparisons
   210  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   211  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   212  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   213  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   214  
   215  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   216  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   217  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   218  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   219  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   220  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   221  
   222  		// moves
   223  		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
   224  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   225  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   226  
   227  		{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
   228  
   229  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   230  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   231  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   232  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   233  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   234  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   235  		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   236  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   237  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   238  
   239  		{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.
   240  		{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.
   241  		{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.
   242  		{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.
   243  		{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.
   244  		{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.
   245  
   246  		{name: "ZERO", zeroWidth: true, fixedReg: true},
   247  
   248  		// moves (no conversion)
   249  		{name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW"}, // move float32 to int32 (no conversion). MIPS64 will perform sign-extend to 64-bit by default
   250  		{name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW"}, // move int32 to float32 (no conversion). MIPS64 will perform sign-extend to 64-bit by default
   251  		{name: "MOVVfpgp", argLength: 1, reg: fpgp, asm: "MOVV"}, // move float64 to int64 (no conversion).
   252  		{name: "MOVVgpfp", argLength: 1, reg: gpfp, asm: "MOVV"}, // move int64 to float64 (no conversion).
   253  
   254  		// conversions
   255  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   256  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   257  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   258  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   259  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   260  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   261  		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
   262  
   263  		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   264  
   265  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   266  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   267  		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
   268  		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
   269  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   270  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   271  		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
   272  		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
   273  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   274  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   275  
   276  		// function calls
   277  		{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
   278  		{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
   279  		{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
   280  		{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
   281  
   282  		// duffzero
   283  		// arg0 = address of memory to zero
   284  		// arg1 = mem
   285  		// auxint = offset into duffzero code to start executing
   286  		// returns mem
   287  		// R1 aka mips.REGRT1 changed as side effect
   288  		{
   289  			name:      "DUFFZERO",
   290  			aux:       "Int64",
   291  			argLength: 2,
   292  			reg: regInfo{
   293  				inputs:   []regMask{gp},
   294  				clobbers: buildReg("R1 R31"),
   295  			},
   296  			faultOnNilArg0: true,
   297  		},
   298  
   299  		// duffcopy
   300  		// arg0 = address of dst memory (in R2, changed as side effect)
   301  		// arg1 = address of src memory (in R1, changed as side effect)
   302  		// arg2 = mem
   303  		// auxint = offset into duffcopy code to start executing
   304  		// returns mem
   305  		{
   306  			name:      "DUFFCOPY",
   307  			aux:       "Int64",
   308  			argLength: 3,
   309  			reg: regInfo{
   310  				inputs:   []regMask{buildReg("R2"), buildReg("R1")},
   311  				clobbers: buildReg("R1 R2 R31"),
   312  			},
   313  			faultOnNilArg0: true,
   314  			faultOnNilArg1: true,
   315  		},
   316  
   317  		// large or unaligned zeroing
   318  		// arg0 = address of memory to zero (in R1, changed as side effect)
   319  		// arg1 = address of the last element to zero
   320  		// arg2 = mem
   321  		// auxint = alignment
   322  		// returns mem
   323  		//	SUBV	$8, R1
   324  		//	MOVV	R0, 8(R1)
   325  		//	ADDV	$8, R1
   326  		//	BNE	Rarg1, R1, -2(PC)
   327  		{
   328  			name:      "LoweredZero",
   329  			aux:       "Int64",
   330  			argLength: 3,
   331  			reg: regInfo{
   332  				inputs:   []regMask{buildReg("R1"), gp},
   333  				clobbers: buildReg("R1"),
   334  			},
   335  			clobberFlags:   true,
   336  			faultOnNilArg0: true,
   337  		},
   338  
   339  		// large or unaligned move
   340  		// arg0 = address of dst memory (in R2, changed as side effect)
   341  		// arg1 = address of src memory (in R1, changed as side effect)
   342  		// arg2 = address of the last element of src
   343  		// arg3 = mem
   344  		// auxint = alignment
   345  		// returns mem
   346  		//	SUBV	$8, R1
   347  		//	MOVV	8(R1), Rtmp
   348  		//	MOVV	Rtmp, (R2)
   349  		//	ADDV	$8, R1
   350  		//	ADDV	$8, R2
   351  		//	BNE	Rarg2, R1, -4(PC)
   352  		{
   353  			name:      "LoweredMove",
   354  			aux:       "Int64",
   355  			argLength: 4,
   356  			reg: regInfo{
   357  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   358  				clobbers: buildReg("R1 R2"),
   359  			},
   360  			clobberFlags:   true,
   361  			faultOnNilArg0: true,
   362  			faultOnNilArg1: true,
   363  		},
   364  
   365  		// atomic and/or.
   366  		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
   367  		// SYNC
   368  		// LL	(Rarg0), Rtmp
   369  		// AND	Rarg1, Rtmp
   370  		// SC	Rtmp, (Rarg0)
   371  		// BEQ	Rtmp, -3(PC)
   372  		// SYNC
   373  		{name: "LoweredAtomicAnd32", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   374  		{name: "LoweredAtomicOr32", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   375  
   376  		// atomic loads.
   377  		// load from arg0. arg1=mem.
   378  		// returns <value,memory> so they can be properly ordered with other loads.
   379  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   380  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   381  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   382  
   383  		// atomic stores.
   384  		// store arg1 to arg0. arg2=mem. returns memory.
   385  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   386  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   387  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   388  		// store zero to arg0. arg1=mem. returns memory.
   389  		{name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   390  		{name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   391  
   392  		// atomic exchange.
   393  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   394  		// SYNC
   395  		// LL	(Rarg0), Rout
   396  		// MOVV Rarg1, Rtmp
   397  		// SC	Rtmp, (Rarg0)
   398  		// BEQ	Rtmp, -3(PC)
   399  		// SYNC
   400  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   401  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   402  
   403  		// atomic add.
   404  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   405  		// SYNC
   406  		// LL	(Rarg0), Rout
   407  		// ADDV Rarg1, Rout, Rtmp
   408  		// SC	Rtmp, (Rarg0)
   409  		// BEQ	Rtmp, -3(PC)
   410  		// SYNC
   411  		// ADDV Rarg1, Rout
   412  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   413  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   414  		// *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit.
   415  		{name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   416  		{name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   417  
   418  		// atomic compare and swap.
   419  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   420  		// if *arg0 == arg1 {
   421  		//   *arg0 = arg2
   422  		//   return (true, memory)
   423  		// } else {
   424  		//   return (false, memory)
   425  		// }
   426  		// SYNC
   427  		// MOVV $0, Rout
   428  		// LL	(Rarg0), Rtmp
   429  		// BNE	Rtmp, Rarg1, 4(PC)
   430  		// MOVV Rarg2, Rout
   431  		// SC	Rout, (Rarg0)
   432  		// BEQ	Rout, -4(PC)
   433  		// SYNC
   434  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   435  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   436  
   437  		// pseudo-ops
   438  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   439  
   440  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   441  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   442  
   443  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   444  		// and sorts it to the very beginning of the block to prevent other
   445  		// use of R22 (mips.REGCTXT, the closure pointer)
   446  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true},
   447  
   448  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   449  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   450  
   451  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   452  		// I.e., if f calls g "calls" sys.GetCallerPC,
   453  		// the result should be the PC within f that g will return to.
   454  		// See runtime/stubs.go for a more detailed discussion.
   455  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   456  
   457  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   458  		// It saves all GP registers if necessary,
   459  		// but clobbers R31 (LR) because it's a call
   460  		// and R23 (REGTMP).
   461  		// Returns a pointer to a write barrier buffer in R25.
   462  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
   463  
   464  		// Do data barrier. arg0=memorys
   465  		{name: "LoweredPubBarrier", argLength: 1, asm: "SYNC", hasSideEffects: true},
   466  
   467  		// LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
   468  		// the RC and CR versions are used when one of the arguments is a constant. CC is used
   469  		// when both are constant (normally both 0, as prove derives the fact that a [0] bounds
   470  		// failure means the length must have also been 0).
   471  		// AuxInt contains a report code (see PanicBounds in genericOps.go).
   472  		{name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{first16, first16}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
   473  		{name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=x, arg1=mem, returns memory.
   474  		{name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=y, arg1=mem, returns memory.
   475  		{name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true},                            // arg0=mem, returns memory.
   476  	}
   477  
   478  	blocks := []blockData{
   479  		{name: "EQ", controls: 1},
   480  		{name: "NE", controls: 1},
   481  		{name: "LTZ", controls: 1}, // < 0
   482  		{name: "LEZ", controls: 1}, // <= 0
   483  		{name: "GTZ", controls: 1}, // > 0
   484  		{name: "GEZ", controls: 1}, // >= 0
   485  		{name: "FPT", controls: 1}, // FP flag is true
   486  		{name: "FPF", controls: 1}, // FP flag is false
   487  	}
   488  
   489  	archs = append(archs, arch{
   490  		name:            "MIPS64",
   491  		pkg:             "cmd/internal/obj/mips",
   492  		genfile:         "../../mips64/ssa.go",
   493  		ops:             ops,
   494  		blocks:          blocks,
   495  		regnames:        regNamesMIPS64,
   496  		gpregmask:       gp,
   497  		fpregmask:       fp,
   498  		specialregmask:  hi | lo,
   499  		framepointerreg: -1, // not used
   500  		linkreg:         int8(num["R31"]),
   501  	})
   502  }
   503  

View as plain text