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

View as plain text