• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5package main
6
7import "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.
30var 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	"HI", // high bits of multiplication
83	"LO", // low bits of multiplication
84
85	// If you add registers, update asyncPreempt in runtime.
86
87	// pseudo-registers
88	"SB",
89}
90
91func 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(0)
102		for _, r := range strings.Split(s, " ") {
103			if n, ok := num[r]; ok {
104				m |= regMask(1) << 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 | buildReg("g")
116		gpsp       = gp | buildReg("SP")
117		gpspg      = gpg | buildReg("SP")
118		gpspsbg    = gpspg | buildReg("SB")
119		fp         = buildReg("F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30")
120		lo         = buildReg("LO")
121		hi         = buildReg("HI")
122		callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg (and anything calling it) may clobber g
123		r1         = buildReg("R1")
124		r2         = buildReg("R2")
125		r3         = buildReg("R3")
126		r4         = buildReg("R4")
127		r5         = buildReg("R5")
128	)
129	// Common regInfo
130	var (
131		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
132		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
133		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
134		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
135		gp31      = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
136		gp2hilo   = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
137		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
138		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
139		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
140		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
141		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
142		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
143		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
144		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
145		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
146		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
147		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
148		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
149		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
150		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
151	)
152	ops := []opData{
153		{name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true},                                                                           // arg0 + arg1
154		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32"},                                                                         // arg0 + auxInt
155		{name: "SUB", argLength: 2, reg: gp21, asm: "SUBU"},                                                                                              // arg0 - arg1
156		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32"},                                                                           // arg0 - auxInt
157		{name: "MUL", argLength: 2, reg: regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}, clobbers: hi | lo}, asm: "MUL", commutative: true}, // arg0 * arg1
158		{name: "MULT", argLength: 2, reg: gp2hilo, asm: "MUL", commutative: true, typ: "(Int32,Int32)"},                                                  // arg0 * arg1, signed, results hi,lo
159		{name: "MULTU", argLength: 2, reg: gp2hilo, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)"},                                              // arg0 * arg1, unsigned, results hi,lo
160		{name: "DIV", argLength: 2, reg: gp2hilo, asm: "DIV", typ: "(Int32,Int32)"},                                                                      // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
161		{name: "DIVU", argLength: 2, reg: gp2hilo, asm: "DIVU", typ: "(UInt32,UInt32)"},                                                                  // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
162
163		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
164		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
165		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
166		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
167		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
168		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
169		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
170		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
171
172		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
173		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"},                // arg0 & auxInt
174		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
175		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32"},                  // arg0 | auxInt
176		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32"}, // arg0 ^ arg1
177		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32"}, // arg0 ^ auxInt
178		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
179		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int32"},                // ^(arg0 | auxInt)
180
181		{name: "NEG", argLength: 1, reg: gp11},                 // -arg0
182		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
183		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
184		{name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD"},   // abs(arg0), float64
185		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
186		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
187
188		// shifts
189		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0 << arg1, shift amount is mod 32
190		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, shift amount must be 0 through 31 inclusive
191		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0 >> arg1, unsigned, shift amount is mod 32
192		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, shift amount must be 0 through 31 inclusive
193		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0 >> arg1, signed, shift amount is mod 32
194		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, shift amount must be 0 through 31 inclusive
195
196		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},
197
198		// comparisons
199		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
200		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
201		{name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool"},                  // 1 if arg0 > 0 (signed), 0 otherwise
202		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
203		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
204		{name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool"},                // 1 if arg0 > 0 (unsigned), 0 otherwise
205
206		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
207		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
208		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
209		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
210		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
211		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
212
213		// moves
214		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true},    // auxint
215		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
216		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
217
218		{name: "MOVWaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVW", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
219
220		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
221		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
222		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
223		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
224		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
225		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
226		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
227
228		{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.
229		{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.
230		{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.
231		{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.
232		{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.
233
234		{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.
235		{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.
236		{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.
237
238		// moves (no conversion)
239		{name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW"}, // move float32 to int32 (no conversion)
240		{name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW"}, // move int32 to float32 (no conversion)
241
242		// conversions
243		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
244		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
245		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
246		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
247		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0
248
249		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
250
251		// conditional move on zero (returns arg1 if arg2 is 0, otherwise arg0)
252		// order of parameters is reversed so we can use resultInArg0 (OpCMOVZ result arg1 arg2-> CMOVZ arg2reg, arg1reg, resultReg)
253		{name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true},
254		{name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true},
255
256		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
257		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
258		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
259		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
260		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
261		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
262
263		// function calls
264		{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
265		{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
266		{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
267		{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
268
269		// atomic ops
270
271		// load from arg0. arg1=mem.
272		// returns <value,memory> so they can be properly ordered with other loads.
273		// SYNC
274		// MOV(B|W)	(Rarg0), Rout
275		// SYNC
276		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
277		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
278
279		// store arg1 to arg0. arg2=mem. returns memory.
280		// SYNC
281		// MOV(B|W)	Rarg1, (Rarg0)
282		// SYNC
283		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
284		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
285		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
286
287		// atomic exchange.
288		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
289		// SYNC
290		// LL	(Rarg0), Rout
291		// MOVW Rarg1, Rtmp
292		// SC	Rtmp, (Rarg0)
293		// BEQ	Rtmp, -3(PC)
294		// SYNC
295		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
296
297		// atomic add.
298		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
299		// SYNC
300		// LL	(Rarg0), Rout
301		// ADDU Rarg1, Rout, Rtmp
302		// SC	Rtmp, (Rarg0)
303		// BEQ	Rtmp, -3(PC)
304		// SYNC
305		// ADDU Rarg1, Rout
306		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
307		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
308
309		// atomic compare and swap.
310		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
311		// if *arg0 == arg1 {
312		//   *arg0 = arg2
313		//   return (true, memory)
314		// } else {
315		//   return (false, memory)
316		// }
317		// SYNC
318		// MOVW $0, Rout
319		// LL	(Rarg0), Rtmp
320		// BNE	Rtmp, Rarg1, 4(PC)
321		// MOVW Rarg2, Rout
322		// SC	Rout, (Rarg0)
323		// BEQ	Rout, -4(PC)
324		// SYNC
325		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
326
327		// atomic and/or.
328		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
329		// SYNC
330		// LL	(Rarg0), Rtmp
331		// AND	Rarg1, Rtmp
332		// SC	Rtmp, (Rarg0)
333		// BEQ	Rtmp, -3(PC)
334		// SYNC
335		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
336		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
337
338		// large or unaligned zeroing
339		// arg0 = address of memory to zero (in R1, changed as side effect)
340		// arg1 = address of the last element to zero
341		// arg2 = mem
342		// auxint = alignment
343		// returns mem
344		//	SUBU	$4, R1
345		//	MOVW	R0, 4(R1)
346		//	ADDU	$4, R1
347		//	BNE	Rarg1, R1, -2(PC)
348		{
349			name:      "LoweredZero",
350			aux:       "Int32",
351			argLength: 3,
352			reg: regInfo{
353				inputs:   []regMask{buildReg("R1"), gp},
354				clobbers: buildReg("R1"),
355			},
356			faultOnNilArg0: true,
357		},
358
359		// large or unaligned move
360		// arg0 = address of dst memory (in R2, changed as side effect)
361		// arg1 = address of src memory (in R1, changed as side effect)
362		// arg2 = address of the last element of src
363		// arg3 = mem
364		// auxint = alignment
365		// returns mem
366		//	SUBU	$4, R1
367		//	MOVW	4(R1), Rtmp
368		//	MOVW	Rtmp, (R2)
369		//	ADDU	$4, R1
370		//	ADDU	$4, R2
371		//	BNE	Rarg2, R1, -4(PC)
372		{
373			name:      "LoweredMove",
374			aux:       "Int32",
375			argLength: 4,
376			reg: regInfo{
377				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
378				clobbers: buildReg("R1 R2"),
379			},
380			faultOnNilArg0: true,
381			faultOnNilArg1: true,
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},  // bool, true if FP flag is true
388		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // 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" 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 &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
410
411		// There are three of these functions so that they can have three different register inputs.
412		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
413		// default registers to match so we don't need to copy registers around unnecessarily.
414		{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
415		{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
416		{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
417		// Extend ops are the same as Bounds ops except the indexes are 64-bit.
418		{name: "LoweredPanicExtendA", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r3, r4}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
419		{name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r2, r3}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
420		{name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
421	}
422
423	blocks := []blockData{
424		{name: "EQ", controls: 1},
425		{name: "NE", controls: 1},
426		{name: "LTZ", controls: 1}, // < 0
427		{name: "LEZ", controls: 1}, // <= 0
428		{name: "GTZ", controls: 1}, // > 0
429		{name: "GEZ", controls: 1}, // >= 0
430		{name: "FPT", controls: 1}, // FP flag is true
431		{name: "FPF", controls: 1}, // FP flag is false
432	}
433
434	archs = append(archs, arch{
435		name:            "MIPS",
436		pkg:             "cmd/internal/obj/mips",
437		genfile:         "../../mips/ssa.go",
438		ops:             ops,
439		blocks:          blocks,
440		regnames:        regNamesMIPS,
441		gpregmask:       gp,
442		fpregmask:       fp,
443		specialregmask:  hi | lo,
444		framepointerreg: -1, // not used
445		linkreg:         int8(num["R31"]),
446	})
447}
448