• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2022 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 regNamesLOONG64 = []string{
32	"R0", // constant 0
33	"R1",
34	"SP", // aka 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	"g", // aka R22
54	"R23",
55	"R24",
56	"R25",
57	"R26",
58	"R27",
59	"R28",
60	"R29",
61	// R30 is REGTMP not used in regalloc
62	"R31",
63
64	"F0",
65	"F1",
66	"F2",
67	"F3",
68	"F4",
69	"F5",
70	"F6",
71	"F7",
72	"F8",
73	"F9",
74	"F10",
75	"F11",
76	"F12",
77	"F13",
78	"F14",
79	"F15",
80	"F16",
81	"F17",
82	"F18",
83	"F19",
84	"F20",
85	"F21",
86	"F22",
87	"F23",
88	"F24",
89	"F25",
90	"F26",
91	"F27",
92	"F28",
93	"F29",
94	"F30",
95	"F31",
96
97	// If you add registers, update asyncPreempt in runtime.
98
99	// pseudo-registers
100	"SB",
101}
102
103func init() {
104	// Make map from reg names to reg integers.
105	if len(regNamesLOONG64) > 64 {
106		panic("too many registers")
107	}
108	num := map[string]int{}
109	for i, name := range regNamesLOONG64 {
110		num[name] = i
111	}
112	buildReg := func(s string) regMask {
113		m := regMask(0)
114		for _, r := range strings.Split(s, " ") {
115			if n, ok := num[r]; ok {
116				m |= regMask(1) << uint(n)
117				continue
118			}
119			panic("register " + r + " not found")
120		}
121		return m
122	}
123
124	// Common individual register masks
125	var (
126		gp         = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R22 is g, R30 is REGTMP
127		gpg        = gp | buildReg("g")
128		gpsp       = gp | buildReg("SP")
129		gpspg      = gpg | buildReg("SP")
130		gpspsbg    = gpspg | buildReg("SB")
131		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")
132		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
133		r1         = buildReg("R20")
134		r2         = buildReg("R21")
135		r3         = buildReg("R23")
136		r4         = buildReg("R24")
137	)
138	// Common regInfo
139	var (
140		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
141		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
142		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
143		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
144		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
145		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
146		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
147		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
148		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
149		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
150		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
151		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
152		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
153		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
154		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
155		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
156	)
157	ops := []opData{
158		// binary ops
159		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},   // arg0 + arg1
160		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
161		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                      // arg0 - arg1
162		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},   // arg0 - auxInt
163
164		{name: "MULV", argLength: 2, reg: gp21, asm: "MULV", commutative: true, typ: "Int64"},      // arg0 * arg1
165		{name: "MULHV", argLength: 2, reg: gp21, asm: "MULHV", commutative: true, typ: "Int64"},    // (arg0 * arg1) >> 64, signed
166		{name: "MULHVU", argLength: 2, reg: gp21, asm: "MULHVU", commutative: true, typ: "UInt64"}, // (arg0 * arg1) >> 64, unsigned
167		{name: "DIVV", argLength: 2, reg: gp21, asm: "DIVV", typ: "Int64"},                         // arg0 / arg1, signed
168		{name: "DIVVU", argLength: 2, reg: gp21, asm: "DIVVU", typ: "UInt64"},                      // arg0 / arg1, unsigned
169		{name: "REMV", argLength: 2, reg: gp21, asm: "REMV", typ: "Int64"},                         // arg0 / arg1, signed
170		{name: "REMVU", argLength: 2, reg: gp21, asm: "REMVU", typ: "UInt64"},                      // arg0 / arg1, unsigned
171
172		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
173		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
174		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
175		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
176		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
177		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
178		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
179		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
180
181		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
182		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
183		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
184		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
185		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
186		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
187		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
188		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
189
190		{name: "NEGV", argLength: 1, reg: gp11},                // -arg0
191		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
192		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
193		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
194		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
195
196		{name: "MASKEQZ", argLength: 2, reg: gp21, asm: "MASKEQZ"}, // returns 0 if arg1 == 0, otherwise returns arg0
197		{name: "MASKNEZ", argLength: 2, reg: gp21, asm: "MASKNEZ"}, // returns 0 if arg1 != 0, otherwise returns arg0
198
199		// shifts
200		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                      // arg0 << arg1, shift amount is mod 64
201		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"},   // arg0 << auxInt
202		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
203		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"},   // arg0 >> auxInt, unsigned
204		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                      // arg0 >> arg1, signed, shift amount is mod 64
205		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"},   // arg0 >> auxInt, signed
206		{name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"},                      // arg0 right rotate by (arg1 mod 32) bits
207		{name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"},                    // arg0 right rotate by (arg1 mod 64) bits
208		{name: "ROTRconst", argLength: 1, reg: gp11, asm: "ROTR", aux: "Int64"},   // uint32(arg0) right rotate by auxInt bits, auxInt should be in the range 0 to 31.
209		{name: "ROTRVconst", argLength: 1, reg: gp11, asm: "ROTRV", aux: "Int64"}, // arg0 right rotate by auxInt bits, auxInt should be in the range 0 to 63.
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		// conversions
254		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
255		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
256		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
257		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
258		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
259		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
260		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
261
262		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
263
264		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
265		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
266		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
267		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
268		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
269		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
270		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
271		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
272		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
273		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
274
275		// function calls
276		{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
277		{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},                                 // tail call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
278		{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
279		{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, last arg=mem, auxint=argsize, returns mem
280
281		// duffzero
282		// arg0 = address of memory to zero
283		// arg1 = mem
284		// auxint = offset into duffzero code to start executing
285		// returns mem
286		// R20 aka loong64.REGRT1 changed as side effect
287		{
288			name:      "DUFFZERO",
289			aux:       "Int64",
290			argLength: 2,
291			reg: regInfo{
292				inputs:   []regMask{buildReg("R20")},
293				clobbers: buildReg("R20 R1"),
294			},
295			typ:            "Mem",
296			faultOnNilArg0: true,
297		},
298
299		// duffcopy
300		// arg0 = address of dst memory (in R21, changed as side effect)
301		// arg1 = address of src memory (in R20, changed as side effect)
302		// arg2 = mem
303		// auxint = offset into duffcopy code to start executing
304		// returns mem
305		{
306			name:      "DUFFCOPY",
307			aux:       "Int64",
308			argLength: 3,
309			reg: regInfo{
310				inputs:   []regMask{buildReg("R21"), buildReg("R20")},
311				clobbers: buildReg("R20 R21 R1"),
312			},
313			typ:            "Mem",
314			faultOnNilArg0: true,
315			faultOnNilArg1: true,
316		},
317
318		// large or unaligned zeroing
319		// arg0 = address of memory to zero (in R20, changed as side effect)
320		// arg1 = address of the last element to zero
321		// arg2 = mem
322		// auxint = alignment
323		// returns mem
324		//	MOVx	R0, (R20)
325		//	ADDV	$sz, R20
326		//	BGEU	Rarg1, R20, -2(PC)
327		{
328			name:      "LoweredZero",
329			aux:       "Int64",
330			argLength: 3,
331			reg: regInfo{
332				inputs:   []regMask{buildReg("R20"), gp},
333				clobbers: buildReg("R20"),
334			},
335			typ:            "Mem",
336			faultOnNilArg0: true,
337		},
338
339		// large or unaligned move
340		// arg0 = address of dst memory (in R21, changed as side effect)
341		// arg1 = address of src memory (in R20, changed as side effect)
342		// arg2 = address of the last element of src
343		// arg3 = mem
344		// auxint = alignment
345		// returns mem
346		//	MOVx	(R20), Rtmp
347		//	MOVx	Rtmp, (R21)
348		//	ADDV	$sz, R20
349		//	ADDV	$sz, R21
350		//	BGEU	Rarg2, R20, -4(PC)
351		{
352			name:      "LoweredMove",
353			aux:       "Int64",
354			argLength: 4,
355			reg: regInfo{
356				inputs:   []regMask{buildReg("R21"), buildReg("R20"), gp},
357				clobbers: buildReg("R20 R21"),
358			},
359			typ:            "Mem",
360			faultOnNilArg0: true,
361			faultOnNilArg1: true,
362		},
363
364		// atomic loads.
365		// load from arg0. arg1=mem.
366		// returns <value,memory> so they can be properly ordered with other loads.
367		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
368		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
369		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
370
371		// atomic stores.
372		// store arg1 to arg0. arg2=mem. returns memory.
373		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
374		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
375		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
376		// store zero to arg0. arg1=mem. returns memory.
377		{name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
378		{name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
379
380		// atomic exchange.
381		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
382		// DBAR
383		// LL	(Rarg0), Rout
384		// MOVV Rarg1, Rtmp
385		// SC	Rtmp, (Rarg0)
386		// BEQ	Rtmp, -3(PC)
387		// DBAR
388		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
389		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
390
391		// atomic add.
392		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
393		// DBAR
394		// LL	(Rarg0), Rout
395		// ADDV Rarg1, Rout, Rtmp
396		// SC	Rtmp, (Rarg0)
397		// BEQ	Rtmp, -3(PC)
398		// DBAR
399		// ADDV Rarg1, Rout
400		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
401		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
402		// *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit.
403		{name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
404		{name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
405
406		// atomic compare and swap.
407		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
408		// if *arg0 == arg1 {
409		//   *arg0 = arg2
410		//   return (true, memory)
411		// } else {
412		//   return (false, memory)
413		// }
414		// DBAR
415		// MOVV $0, Rout
416		// LL	(Rarg0), Rtmp
417		// BNE	Rtmp, Rarg1, 4(PC)
418		// MOVV Rarg2, Rout
419		// SC	Rout, (Rarg0)
420		// BEQ	Rout, -4(PC)
421		// DBAR
422		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
423		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
424
425		// pseudo-ops
426		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
427
428		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
429		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
430
431		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
432		// and sorts it to the very beginning of the block to prevent other
433		// use of R22 (loong64.REGCTXT, the closure pointer)
434		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true},
435
436		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
437		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
438
439		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
440		// I.e., if f calls g "calls" getcallerpc,
441		// the result should be the PC within f that g will return to.
442		// See runtime/stubs.go for a more detailed discussion.
443		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
444
445		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
446		// It saves all GP registers if necessary,
447		// but clobbers R1 (LR) because it's a call
448		// and R30 (REGTMP).
449		// Returns a pointer to a write barrier buffer in R29.
450		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R1"), outputs: []regMask{buildReg("R29")}}, clobberFlags: true, aux: "Int64"},
451
452		// There are three of these functions so that they can have three different register inputs.
453		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
454		// default registers to match so we don't need to copy registers around unnecessarily.
455		{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).
456		{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).
457		{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).
458	}
459
460	blocks := []blockData{
461		{name: "EQ", controls: 1},
462		{name: "NE", controls: 1},
463		{name: "LTZ", controls: 1}, // < 0
464		{name: "LEZ", controls: 1}, // <= 0
465		{name: "GTZ", controls: 1}, // > 0
466		{name: "GEZ", controls: 1}, // >= 0
467		{name: "FPT", controls: 1}, // FP flag is true
468		{name: "FPF", controls: 1}, // FP flag is false
469	}
470
471	archs = append(archs, arch{
472		name:     "LOONG64",
473		pkg:      "cmd/internal/obj/loong64",
474		genfile:  "../../loong64/ssa.go",
475		ops:      ops,
476		blocks:   blocks,
477		regnames: regNamesLOONG64,
478		// TODO: support register ABI on loong64
479		ParamIntRegNames:   "R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19",
480		ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
481		gpregmask:          gp,
482		fpregmask:          fp,
483		framepointerreg:    -1, // not used
484		linkreg:            int8(num["R1"]),
485	})
486}
487