Source file src/cmd/compile/internal/ssa/_gen/MIPSOps.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  //  - Unused portions of AuxInt are filled by sign-extending the used portion.
    14  //  - *const instructions may use a constant larger than the instruction can encode.
    15  //    In this case the assembler expands to multiple instructions and uses tmp
    16  //    register (R23).
    17  
    18  // Suffixes encode the bit width of various instructions.
    19  // W (word)      = 32 bit
    20  // H (half word) = 16 bit
    21  // HU            = 16 bit unsigned
    22  // B (byte)      = 8 bit
    23  // BU            = 8 bit unsigned
    24  // F (float)     = 32 bit float
    25  // D (double)    = 64 bit float
    26  
    27  // Note: registers not used in regalloc are not included in this list,
    28  // so that regmask stays within int64
    29  // Be careful when hand coding regmasks.
    30  var regNamesMIPS = []string{
    31  	"R0", // constant 0
    32  	"R1",
    33  	"R2",
    34  	"R3",
    35  	"R4",
    36  	"R5",
    37  	"R6",
    38  	"R7",
    39  	"R8",
    40  	"R9",
    41  	"R10",
    42  	"R11",
    43  	"R12",
    44  	"R13",
    45  	"R14",
    46  	"R15",
    47  	"R16",
    48  	"R17",
    49  	"R18",
    50  	"R19",
    51  	"R20",
    52  	"R21",
    53  	"R22",
    54  	//REGTMP
    55  	"R24",
    56  	"R25",
    57  	// R26 reserved by kernel
    58  	// R27 reserved by kernel
    59  	"R28",
    60  	"SP",  // aka R29
    61  	"g",   // aka R30
    62  	"R31", // REGLINK
    63  
    64  	// odd FP registers contain high parts of 64-bit FP values
    65  	"F0",
    66  	"F2",
    67  	"F4",
    68  	"F6",
    69  	"F8",
    70  	"F10",
    71  	"F12",
    72  	"F14",
    73  	"F16",
    74  	"F18",
    75  	"F20",
    76  	"F22",
    77  	"F24",
    78  	"F26",
    79  	"F28",
    80  	"F30",
    81  
    82  	// To avoid nested REGTMP issues we act as if HI LO does not exists.
    83  	// Operations using them output to GP and obj adds moves from HI LO to GP as needed.
    84  
    85  	// If you add registers, update asyncPreempt in runtime.
    86  
    87  	// pseudo-registers
    88  	"SB",
    89  }
    90  
    91  func init() {
    92  	// Make map from reg names to reg integers.
    93  	if len(regNamesMIPS) > 64 {
    94  		panic("too many registers")
    95  	}
    96  	num := map[string]int{}
    97  	for i, name := range regNamesMIPS {
    98  		num[name] = i
    99  	}
   100  	buildReg := func(s string) regMask {
   101  		m := regMask{}
   102  		for _, r := range strings.Split(s, " ") {
   103  			if n, ok := num[r]; ok {
   104  				m = m.addReg(uint(n))
   105  				continue
   106  			}
   107  			panic("register " + r + " not found")
   108  		}
   109  		return m
   110  	}
   111  
   112  	// Common individual register masks
   113  	var (
   114  		gp         = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31")
   115  		gpg        = gp.union(buildReg("g"))
   116  		gpsp       = gp.union(buildReg("SP"))
   117  		gpspg      = gpg.union(buildReg("SP"))
   118  		gpspsbg    = gpspg.union(buildReg("SB"))
   119  		fp         = buildReg("F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30")
   120  		callerSave = gp.union(fp).union(buildReg("g")) // runtime.setg (and anything calling it) may clobber g
   121  		first16    = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16")
   122  		first4     = buildReg("R1 R2 R3 R4")
   123  	)
   124  	// Common regInfo
   125  	var (
   126  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   127  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   128  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   129  		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   130  		gp31      = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
   131  		gp22      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp, gp}}
   132  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   133  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   134  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   135  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   136  		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
   137  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   138  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   139  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   140  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   141  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   142  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   143  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   144  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   145  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   146  	)
   147  	ops := []opData{
   148  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true, earlyOk: true},                           // arg0 + arg1
   149  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32", earlyOk: true},                         // arg0 + auxInt
   150  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUBU", earlyOk: true},                                              // arg0 - arg1
   151  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32", earlyOk: true},                           // arg0 - auxInt
   152  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true, earlyOk: true},                            // arg0 * arg1
   153  		{name: "MULT", argLength: 2, reg: gp22, asm: "MUL", commutative: true, typ: "(Int32,Int32)", earlyOk: true},     // arg0 * arg1, signed, results high,low
   154  		{name: "MULTU", argLength: 2, reg: gp22, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)", earlyOk: true}, // arg0 * arg1, unsigned, results high,low
   155  		{name: "DIV", argLength: 2, reg: gp22, asm: "DIV", typ: "(Int32,Int32)"},                                        // arg0 / arg1, signed, results arg0%arg1,arg0/arg1
   156  		{name: "DIVU", argLength: 2, reg: gp22, asm: "DIVU", typ: "(UInt32,UInt32)"},                                    // arg0 / arg1, signed, results arg0%arg1,arg0/arg1
   157  
   158  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true, earlyOk: true}, // arg0 + arg1
   159  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true, earlyOk: true}, // arg0 + arg1
   160  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF", earlyOk: true},                    // arg0 - arg1
   161  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD", earlyOk: true},                    // arg0 - arg1
   162  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true, earlyOk: true}, // arg0 * arg1
   163  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true, earlyOk: true}, // arg0 * arg1
   164  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF", earlyOk: true},                    // arg0 / arg1
   165  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD", earlyOk: true},                    // arg0 / arg1
   166  
   167  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, earlyOk: true},                // arg0 & arg1
   168  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32", earlyOk: true},                // arg0 & auxInt
   169  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, earlyOk: true},                  // arg0 | arg1
   170  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32", earlyOk: true},                  // arg0 | auxInt
   171  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32", earlyOk: true}, // arg0 ^ arg1
   172  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32", earlyOk: true}, // arg0 ^ auxInt
   173  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true, earlyOk: true},                // ^(arg0 | arg1)
   174  
   175  		{name: "NEG", argLength: 1, reg: gp11, earlyOk: true},                 // -arg0
   176  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF", earlyOk: true},   // -arg0, float32
   177  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD", earlyOk: true},   // -arg0, float64
   178  		{name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD", earlyOk: true},   // abs(arg0), float64
   179  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD", earlyOk: true}, // sqrt(arg0), float64
   180  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF", earlyOk: true}, // sqrt(arg0), float32
   181  
   182  		// shifts
   183  		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL", earlyOk: true},                    // arg0 << arg1, shift amount is mod 32
   184  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32", earlyOk: true}, // arg0 << auxInt, shift amount must be 0 through 31 inclusive
   185  		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL", earlyOk: true},                    // arg0 >> arg1, unsigned, shift amount is mod 32
   186  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32", earlyOk: true}, // arg0 >> auxInt, shift amount must be 0 through 31 inclusive
   187  		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA", earlyOk: true},                    // arg0 >> arg1, signed, shift amount is mod 32
   188  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32", earlyOk: true}, // arg0 >> auxInt, signed, shift amount must be 0 through 31 inclusive
   189  
   190  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ", earlyOk: true},
   191  
   192  		// comparisons
   193  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool", earlyOk: true},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   194  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool", earlyOk: true},   // 1 if auxInt > arg0 (signed), 0 otherwise
   195  		{name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool", earlyOk: true},                  // 1 if arg0 > 0 (signed), 0 otherwise
   196  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool", earlyOk: true},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   197  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool", earlyOk: true}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   198  		{name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool", earlyOk: true},                // 1 if arg0 > 0 (unsigned), 0 otherwise
   199  
   200  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   201  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   202  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   203  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   204  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   205  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   206  
   207  		// moves
   208  		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true, earlyOk: true},    // auxint
   209  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true, earlyOk: true}, // auxint as 64-bit float, convert to 32-bit float
   210  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true, earlyOk: true}, // auxint as 64-bit float
   211  
   212  		{name: "MOVWaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP").union(buildReg("SB"))}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVW", rematerializeable: true, symEffect: "Addr", earlyOk: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   213  
   214  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
   215  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   216  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
   217  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
   218  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
   219  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   220  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   221  
   222  		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   223  		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   224  		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   225  		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   226  		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   227  
   228  		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
   229  		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   230  		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   231  
   232  		// moves (no conversion)
   233  		{name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW", earlyOk: true}, // move float32 to int32 (no conversion)
   234  		{name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW", earlyOk: true}, // move int32 to float32 (no conversion)
   235  
   236  		// conversions
   237  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB", earlyOk: true},   // move from arg0, sign-extended from byte
   238  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU", earlyOk: true}, // move from arg0, unsign-extended from byte
   239  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH", earlyOk: true},   // move from arg0, sign-extended from half
   240  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU", earlyOk: true}, // move from arg0, unsign-extended from half
   241  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW", earlyOk: true},   // move from arg0
   242  
   243  		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true, earlyOk: true}, // nop, return arg0 in same register
   244  
   245  		// conditional move on zero (returns arg1 if arg2 is 0, otherwise arg0)
   246  		// order of parameters is reversed so we can use resultInArg0 (OpCMOVZ result arg1 arg2-> CMOVZ arg2reg, arg1reg, resultReg)
   247  		{name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true, earlyOk: true},
   248  		{name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true, earlyOk: true},
   249  
   250  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF", earlyOk: true},     // int32 -> float32
   251  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD", earlyOk: true},     // int32 -> float64
   252  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW", earlyOk: true}, // float32 -> int32
   253  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW", earlyOk: true}, // float64 -> int32
   254  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD", earlyOk: true},     // float32 -> float64
   255  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF", earlyOk: true},     // float64 -> float32
   256  
   257  		// function calls
   258  		{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
   259  		{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
   260  		{name: "CALLtailinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},             //  tail call fn by pointer.  arg0=cpdeptr, arg1=mem, auxint=argsize, returns mem
   261  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), regMask{}}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   262  		{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
   263  
   264  		// atomic ops
   265  
   266  		// load from arg0. arg1=mem.
   267  		// returns <value,memory> so they can be properly ordered with other loads.
   268  		// SYNC
   269  		// MOV(B|W)	(Rarg0), Rout
   270  		// SYNC
   271  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   272  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   273  
   274  		// store arg1 to arg0. arg2=mem. returns memory.
   275  		// SYNC
   276  		// MOV(B|W)	Rarg1, (Rarg0)
   277  		// SYNC
   278  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   279  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   280  		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   281  
   282  		// atomic exchange.
   283  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   284  		// SYNC
   285  		// LL	(Rarg0), Rout
   286  		// MOVW Rarg1, Rtmp
   287  		// SC	Rtmp, (Rarg0)
   288  		// BEQ	Rtmp, -3(PC)
   289  		// SYNC
   290  		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   291  
   292  		// atomic add.
   293  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   294  		// SYNC
   295  		// LL	(Rarg0), Rout
   296  		// ADDU Rarg1, Rout, Rtmp
   297  		// SC	Rtmp, (Rarg0)
   298  		// BEQ	Rtmp, -3(PC)
   299  		// SYNC
   300  		// ADDU Rarg1, Rout
   301  		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   302  		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   303  
   304  		// atomic compare and swap.
   305  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   306  		// if *arg0 == arg1 {
   307  		//   *arg0 = arg2
   308  		//   return (true, memory)
   309  		// } else {
   310  		//   return (false, memory)
   311  		// }
   312  		// SYNC
   313  		// MOVW $0, Rout
   314  		// LL	(Rarg0), Rtmp
   315  		// BNE	Rtmp, Rarg1, 4(PC)
   316  		// MOVW Rarg2, Rout
   317  		// SC	Rout, (Rarg0)
   318  		// BEQ	Rout, -4(PC)
   319  		// SYNC
   320  		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   321  
   322  		// atomic and/or.
   323  		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
   324  		// SYNC
   325  		// LL	(Rarg0), Rtmp
   326  		// AND	Rarg1, Rtmp
   327  		// SC	Rtmp, (Rarg0)
   328  		// BEQ	Rtmp, -3(PC)
   329  		// SYNC
   330  		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   331  		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   332  
   333  		// large or unaligned zeroing
   334  		// arg0 = address of memory to zero (in R1, changed as side effect)
   335  		// arg1 = address of the last element to zero
   336  		// arg2 = mem
   337  		// auxint = alignment
   338  		// returns mem
   339  		//	SUBU	$4, R1
   340  		//	MOVW	R0, 4(R1)
   341  		//	ADDU	$4, R1
   342  		//	BNE	Rarg1, R1, -2(PC)
   343  		{
   344  			name:      "LoweredZero",
   345  			aux:       "Int32",
   346  			argLength: 3,
   347  			reg: regInfo{
   348  				inputs:   []regMask{buildReg("R1"), gp},
   349  				clobbers: buildReg("R1"),
   350  			},
   351  			faultOnNilArg0: true,
   352  			addrSinkArg0:   true,
   353  			addrSinkArg1:   true,
   354  		},
   355  
   356  		// large or unaligned move
   357  		// arg0 = address of dst memory (in R2, changed as side effect)
   358  		// arg1 = address of src memory (in R1, changed as side effect)
   359  		// arg2 = address of the last element of src
   360  		// arg3 = mem
   361  		// auxint = alignment
   362  		// returns mem
   363  		//	SUBU	$4, R1
   364  		//	MOVW	4(R1), Rtmp
   365  		//	MOVW	Rtmp, (R2)
   366  		//	ADDU	$4, R1
   367  		//	ADDU	$4, R2
   368  		//	BNE	Rarg2, R1, -4(PC)
   369  		{
   370  			name:      "LoweredMove",
   371  			aux:       "Int32",
   372  			argLength: 4,
   373  			reg: regInfo{
   374  				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
   375  				clobbers: buildReg("R1 R2"),
   376  			},
   377  			faultOnNilArg0: true,
   378  			faultOnNilArg1: true,
   379  			addrSinkArg0:   true,
   380  			addrSinkArg1:   true,
   381  			// TODO: could use addrSinkArg2 here.
   382  		},
   383  
   384  		// pseudo-ops
   385  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   386  
   387  		{name: "FPFlagTrue", argLength: 1, reg: readflags, earlyOk: true},  // bool, true if FP flag is true
   388  		{name: "FPFlagFalse", argLength: 1, reg: readflags, earlyOk: true}, // bool, true if FP flag is false
   389  
   390  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   391  		// and sorts it to the very beginning of the block to prevent other
   392  		// use of R22 (mips.REGCTXT, the closure pointer)
   393  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true},
   394  
   395  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   396  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   397  
   398  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   399  		// I.e., if f calls g "calls" sys.GetCallerPC,
   400  		// the result should be the PC within f that g will return to.
   401  		// See runtime/stubs.go for a more detailed discussion.
   402  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   403  
   404  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   405  		// It saves all GP registers if necessary,
   406  		// but clobbers R31 (LR) because it's a call
   407  		// and R23 (REGTMP).
   408  		// Returns a pointer to a write barrier buffer in R25.
   409  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: callerSave.minus(gpg).union(buildReg("R31")), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
   410  
   411  		// Do data barrier. arg0=memorys
   412  		{name: "LoweredPubBarrier", argLength: 1, asm: "SYNC", hasSideEffects: true},
   413  
   414  		// LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
   415  		// the RC and CR versions are used when one of the arguments is a constant. CC is used
   416  		// when both are constant (normally both 0, as prove derives the fact that a [0] bounds
   417  		// failure means the length must have also been 0).
   418  		// AuxInt contains a report code (see PanicBounds in genericOps.go).
   419  		{name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{first16, first16}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
   420  		{name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=x, arg1=mem, returns memory.
   421  		{name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=y, arg1=mem, returns memory.
   422  		{name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true},                            // arg0=mem, returns memory.
   423  
   424  		// Same as above, but the x value is 64 bits.
   425  		{name: "LoweredPanicExtendRR", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{first4, first4, first16}}, typ: "Mem", call: true}, // arg0=x_hi, arg1=x_lo, arg2=y, arg3=mem, returns memory.
   426  		{name: "LoweredPanicExtendRC", argLength: 3, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first4, first4}}, typ: "Mem", call: true},   // arg0=x_hi, arg1=x_lo, arg2=mem, returns memory.
   427  	}
   428  
   429  	blocks := []blockData{
   430  		{name: "EQ", controls: 1},
   431  		{name: "NE", controls: 1},
   432  		{name: "LTZ", controls: 1}, // < 0
   433  		{name: "LEZ", controls: 1}, // <= 0
   434  		{name: "GTZ", controls: 1}, // > 0
   435  		{name: "GEZ", controls: 1}, // >= 0
   436  		{name: "FPT", controls: 1}, // FP flag is true
   437  		{name: "FPF", controls: 1}, // FP flag is false
   438  
   439  		// JUMPTABLE implements jump tables.
   440  		// Aux is the symbol (an *obj.LSym) for the jump table.
   441  		// control[0] is the index into the jump table.
   442  		// control[1] is the address of the jump table (the address of the symbol stored in Aux).
   443  		{name: "JUMPTABLE", controls: 2, aux: "Sym"},
   444  	}
   445  
   446  	archs = append(archs, arch{
   447  		name:            "MIPS",
   448  		pkg:             "cmd/internal/obj/mips",
   449  		genfile:         "../../mips/ssa.go",
   450  		ops:             ops,
   451  		blocks:          blocks,
   452  		regnames:        regNamesMIPS,
   453  		gpregmask:       gp,
   454  		fpregmask:       fp,
   455  		framepointerreg: -1, // not used
   456  		linkreg:         int8(num["R31"]),
   457  	})
   458  }
   459  

View as plain text