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