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 loong64
6
7import (
8	"math"
9
10	"cmd/compile/internal/base"
11	"cmd/compile/internal/ir"
12	"cmd/compile/internal/logopt"
13	"cmd/compile/internal/objw"
14	"cmd/compile/internal/ssa"
15	"cmd/compile/internal/ssagen"
16	"cmd/compile/internal/types"
17	"cmd/internal/obj"
18	"cmd/internal/obj/loong64"
19)
20
21// isFPreg reports whether r is an FP register.
22func isFPreg(r int16) bool {
23	return loong64.REG_F0 <= r && r <= loong64.REG_F31
24}
25
26// loadByType returns the load instruction of the given type.
27func loadByType(t *types.Type, r int16) obj.As {
28	if isFPreg(r) {
29		if t.Size() == 4 {
30			return loong64.AMOVF
31		} else {
32			return loong64.AMOVD
33		}
34	} else {
35		switch t.Size() {
36		case 1:
37			if t.IsSigned() {
38				return loong64.AMOVB
39			} else {
40				return loong64.AMOVBU
41			}
42		case 2:
43			if t.IsSigned() {
44				return loong64.AMOVH
45			} else {
46				return loong64.AMOVHU
47			}
48		case 4:
49			if t.IsSigned() {
50				return loong64.AMOVW
51			} else {
52				return loong64.AMOVWU
53			}
54		case 8:
55			return loong64.AMOVV
56		}
57	}
58	panic("bad load type")
59}
60
61// storeByType returns the store instruction of the given type.
62func storeByType(t *types.Type, r int16) obj.As {
63	if isFPreg(r) {
64		if t.Size() == 4 {
65			return loong64.AMOVF
66		} else {
67			return loong64.AMOVD
68		}
69	} else {
70		switch t.Size() {
71		case 1:
72			return loong64.AMOVB
73		case 2:
74			return loong64.AMOVH
75		case 4:
76			return loong64.AMOVW
77		case 8:
78			return loong64.AMOVV
79		}
80	}
81	panic("bad store type")
82}
83
84// largestMove returns the largest move instruction possible and its size,
85// given the alignment of the total size of the move.
86//
87// e.g., a 16-byte move may use MOVV, but an 11-byte move must use MOVB.
88//
89// Note that the moves may not be on naturally aligned addresses depending on
90// the source and destination.
91//
92// This matches the calculation in ssa.moveSize.
93func largestMove(alignment int64) (obj.As, int64) {
94	switch {
95	case alignment%8 == 0:
96		return loong64.AMOVV, 8
97	case alignment%4 == 0:
98		return loong64.AMOVW, 4
99	case alignment%2 == 0:
100		return loong64.AMOVH, 2
101	default:
102		return loong64.AMOVB, 1
103	}
104}
105
106func ssaGenValue(s *ssagen.State, v *ssa.Value) {
107	switch v.Op {
108	case ssa.OpCopy, ssa.OpLOONG64MOVVreg:
109		if v.Type.IsMemory() {
110			return
111		}
112		x := v.Args[0].Reg()
113		y := v.Reg()
114		if x == y {
115			return
116		}
117		as := loong64.AMOVV
118		if isFPreg(x) && isFPreg(y) {
119			as = loong64.AMOVD
120		}
121		p := s.Prog(as)
122		p.From.Type = obj.TYPE_REG
123		p.From.Reg = x
124		p.To.Type = obj.TYPE_REG
125		p.To.Reg = y
126	case ssa.OpLOONG64MOVVnop:
127		// nothing to do
128	case ssa.OpLoadReg:
129		if v.Type.IsFlags() {
130			v.Fatalf("load flags not implemented: %v", v.LongString())
131			return
132		}
133		r := v.Reg()
134		p := s.Prog(loadByType(v.Type, r))
135		ssagen.AddrAuto(&p.From, v.Args[0])
136		p.To.Type = obj.TYPE_REG
137		p.To.Reg = r
138	case ssa.OpStoreReg:
139		if v.Type.IsFlags() {
140			v.Fatalf("store flags not implemented: %v", v.LongString())
141			return
142		}
143		r := v.Args[0].Reg()
144		p := s.Prog(storeByType(v.Type, r))
145		p.From.Type = obj.TYPE_REG
146		p.From.Reg = r
147		ssagen.AddrAuto(&p.To, v)
148	case ssa.OpArgIntReg, ssa.OpArgFloatReg:
149		// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
150		// The loop only runs once.
151		for _, a := range v.Block.Func.RegArgs {
152			// Pass the spill/unspill information along to the assembler, offset by size of
153			// the saved LR slot.
154			addr := ssagen.SpillSlotAddr(a, loong64.REGSP, base.Ctxt.Arch.FixedFrameSize)
155			s.FuncInfo().AddSpill(
156				obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type, a.Reg), Spill: storeByType(a.Type, a.Reg)})
157		}
158		v.Block.Func.RegArgs = nil
159		ssagen.CheckArgReg(v)
160	case ssa.OpLOONG64ADDV,
161		ssa.OpLOONG64SUBV,
162		ssa.OpLOONG64AND,
163		ssa.OpLOONG64OR,
164		ssa.OpLOONG64XOR,
165		ssa.OpLOONG64NOR,
166		ssa.OpLOONG64SLLV,
167		ssa.OpLOONG64SRLV,
168		ssa.OpLOONG64SRAV,
169		ssa.OpLOONG64ROTR,
170		ssa.OpLOONG64ROTRV,
171		ssa.OpLOONG64ADDF,
172		ssa.OpLOONG64ADDD,
173		ssa.OpLOONG64SUBF,
174		ssa.OpLOONG64SUBD,
175		ssa.OpLOONG64MULF,
176		ssa.OpLOONG64MULD,
177		ssa.OpLOONG64DIVF,
178		ssa.OpLOONG64DIVD,
179		ssa.OpLOONG64MULV, ssa.OpLOONG64MULHV, ssa.OpLOONG64MULHVU,
180		ssa.OpLOONG64DIVV, ssa.OpLOONG64REMV, ssa.OpLOONG64DIVVU, ssa.OpLOONG64REMVU:
181		p := s.Prog(v.Op.Asm())
182		p.From.Type = obj.TYPE_REG
183		p.From.Reg = v.Args[1].Reg()
184		p.Reg = v.Args[0].Reg()
185		p.To.Type = obj.TYPE_REG
186		p.To.Reg = v.Reg()
187	case ssa.OpLOONG64SGT,
188		ssa.OpLOONG64SGTU:
189		p := s.Prog(v.Op.Asm())
190		p.From.Type = obj.TYPE_REG
191		p.From.Reg = v.Args[0].Reg()
192		p.Reg = v.Args[1].Reg()
193		p.To.Type = obj.TYPE_REG
194		p.To.Reg = v.Reg()
195	case ssa.OpLOONG64ADDVconst,
196		ssa.OpLOONG64SUBVconst,
197		ssa.OpLOONG64ANDconst,
198		ssa.OpLOONG64ORconst,
199		ssa.OpLOONG64XORconst,
200		ssa.OpLOONG64NORconst,
201		ssa.OpLOONG64SLLVconst,
202		ssa.OpLOONG64SRLVconst,
203		ssa.OpLOONG64SRAVconst,
204		ssa.OpLOONG64ROTRconst,
205		ssa.OpLOONG64ROTRVconst,
206		ssa.OpLOONG64SGTconst,
207		ssa.OpLOONG64SGTUconst:
208		p := s.Prog(v.Op.Asm())
209		p.From.Type = obj.TYPE_CONST
210		p.From.Offset = v.AuxInt
211		p.Reg = v.Args[0].Reg()
212		p.To.Type = obj.TYPE_REG
213		p.To.Reg = v.Reg()
214	case ssa.OpLOONG64MOVVconst:
215		r := v.Reg()
216		p := s.Prog(v.Op.Asm())
217		p.From.Type = obj.TYPE_CONST
218		p.From.Offset = v.AuxInt
219		p.To.Type = obj.TYPE_REG
220		p.To.Reg = r
221		if isFPreg(r) {
222			// cannot move into FP or special registers, use TMP as intermediate
223			p.To.Reg = loong64.REGTMP
224			p = s.Prog(loong64.AMOVV)
225			p.From.Type = obj.TYPE_REG
226			p.From.Reg = loong64.REGTMP
227			p.To.Type = obj.TYPE_REG
228			p.To.Reg = r
229		}
230	case ssa.OpLOONG64MOVFconst,
231		ssa.OpLOONG64MOVDconst:
232		p := s.Prog(v.Op.Asm())
233		p.From.Type = obj.TYPE_FCONST
234		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
235		p.To.Type = obj.TYPE_REG
236		p.To.Reg = v.Reg()
237	case ssa.OpLOONG64CMPEQF,
238		ssa.OpLOONG64CMPEQD,
239		ssa.OpLOONG64CMPGEF,
240		ssa.OpLOONG64CMPGED,
241		ssa.OpLOONG64CMPGTF,
242		ssa.OpLOONG64CMPGTD:
243		p := s.Prog(v.Op.Asm())
244		p.From.Type = obj.TYPE_REG
245		p.From.Reg = v.Args[0].Reg()
246		p.Reg = v.Args[1].Reg()
247	case ssa.OpLOONG64MOVVaddr:
248		p := s.Prog(loong64.AMOVV)
249		p.From.Type = obj.TYPE_ADDR
250		p.From.Reg = v.Args[0].Reg()
251		var wantreg string
252		// MOVV $sym+off(base), R
253		// the assembler expands it as the following:
254		// - base is SP: add constant offset to SP (R3)
255		// when constant is large, tmp register (R30) may be used
256		// - base is SB: load external address with relocation
257		switch v.Aux.(type) {
258		default:
259			v.Fatalf("aux is of unknown type %T", v.Aux)
260		case *obj.LSym:
261			wantreg = "SB"
262			ssagen.AddAux(&p.From, v)
263		case *ir.Name:
264			wantreg = "SP"
265			ssagen.AddAux(&p.From, v)
266		case nil:
267			// No sym, just MOVV $off(SP), R
268			wantreg = "SP"
269			p.From.Offset = v.AuxInt
270		}
271		if reg := v.Args[0].RegName(); reg != wantreg {
272			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
273		}
274		p.To.Type = obj.TYPE_REG
275		p.To.Reg = v.Reg()
276	case ssa.OpLOONG64MOVBload,
277		ssa.OpLOONG64MOVBUload,
278		ssa.OpLOONG64MOVHload,
279		ssa.OpLOONG64MOVHUload,
280		ssa.OpLOONG64MOVWload,
281		ssa.OpLOONG64MOVWUload,
282		ssa.OpLOONG64MOVVload,
283		ssa.OpLOONG64MOVFload,
284		ssa.OpLOONG64MOVDload:
285		p := s.Prog(v.Op.Asm())
286		p.From.Type = obj.TYPE_MEM
287		p.From.Reg = v.Args[0].Reg()
288		ssagen.AddAux(&p.From, v)
289		p.To.Type = obj.TYPE_REG
290		p.To.Reg = v.Reg()
291	case ssa.OpLOONG64MOVBstore,
292		ssa.OpLOONG64MOVHstore,
293		ssa.OpLOONG64MOVWstore,
294		ssa.OpLOONG64MOVVstore,
295		ssa.OpLOONG64MOVFstore,
296		ssa.OpLOONG64MOVDstore:
297		p := s.Prog(v.Op.Asm())
298		p.From.Type = obj.TYPE_REG
299		p.From.Reg = v.Args[1].Reg()
300		p.To.Type = obj.TYPE_MEM
301		p.To.Reg = v.Args[0].Reg()
302		ssagen.AddAux(&p.To, v)
303	case ssa.OpLOONG64MOVBstorezero,
304		ssa.OpLOONG64MOVHstorezero,
305		ssa.OpLOONG64MOVWstorezero,
306		ssa.OpLOONG64MOVVstorezero:
307		p := s.Prog(v.Op.Asm())
308		p.From.Type = obj.TYPE_REG
309		p.From.Reg = loong64.REGZERO
310		p.To.Type = obj.TYPE_MEM
311		p.To.Reg = v.Args[0].Reg()
312		ssagen.AddAux(&p.To, v)
313	case ssa.OpLOONG64MOVBreg,
314		ssa.OpLOONG64MOVBUreg,
315		ssa.OpLOONG64MOVHreg,
316		ssa.OpLOONG64MOVHUreg,
317		ssa.OpLOONG64MOVWreg,
318		ssa.OpLOONG64MOVWUreg:
319		a := v.Args[0]
320		for a.Op == ssa.OpCopy || a.Op == ssa.OpLOONG64MOVVreg {
321			a = a.Args[0]
322		}
323		if a.Op == ssa.OpLoadReg && loong64.REG_R0 <= a.Reg() && a.Reg() <= loong64.REG_R31 {
324			// LoadReg from a narrower type does an extension, except loading
325			// to a floating point register. So only eliminate the extension
326			// if it is loaded to an integer register.
327
328			t := a.Type
329			switch {
330			case v.Op == ssa.OpLOONG64MOVBreg && t.Size() == 1 && t.IsSigned(),
331				v.Op == ssa.OpLOONG64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
332				v.Op == ssa.OpLOONG64MOVHreg && t.Size() == 2 && t.IsSigned(),
333				v.Op == ssa.OpLOONG64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
334				v.Op == ssa.OpLOONG64MOVWreg && t.Size() == 4 && t.IsSigned(),
335				v.Op == ssa.OpLOONG64MOVWUreg && t.Size() == 4 && !t.IsSigned():
336				// arg is a proper-typed load, already zero/sign-extended, don't extend again
337				if v.Reg() == v.Args[0].Reg() {
338					return
339				}
340				p := s.Prog(loong64.AMOVV)
341				p.From.Type = obj.TYPE_REG
342				p.From.Reg = v.Args[0].Reg()
343				p.To.Type = obj.TYPE_REG
344				p.To.Reg = v.Reg()
345				return
346			default:
347			}
348		}
349		fallthrough
350	case ssa.OpLOONG64MOVWF,
351		ssa.OpLOONG64MOVWD,
352		ssa.OpLOONG64TRUNCFW,
353		ssa.OpLOONG64TRUNCDW,
354		ssa.OpLOONG64MOVVF,
355		ssa.OpLOONG64MOVVD,
356		ssa.OpLOONG64TRUNCFV,
357		ssa.OpLOONG64TRUNCDV,
358		ssa.OpLOONG64MOVFD,
359		ssa.OpLOONG64MOVDF,
360		ssa.OpLOONG64NEGF,
361		ssa.OpLOONG64NEGD,
362		ssa.OpLOONG64SQRTD,
363		ssa.OpLOONG64SQRTF:
364		p := s.Prog(v.Op.Asm())
365		p.From.Type = obj.TYPE_REG
366		p.From.Reg = v.Args[0].Reg()
367		p.To.Type = obj.TYPE_REG
368		p.To.Reg = v.Reg()
369	case ssa.OpLOONG64NEGV:
370		// SUB from REGZERO
371		p := s.Prog(loong64.ASUBVU)
372		p.From.Type = obj.TYPE_REG
373		p.From.Reg = v.Args[0].Reg()
374		p.Reg = loong64.REGZERO
375		p.To.Type = obj.TYPE_REG
376		p.To.Reg = v.Reg()
377	case ssa.OpLOONG64DUFFZERO:
378		// runtime.duffzero expects start address in R20
379		p := s.Prog(obj.ADUFFZERO)
380		p.To.Type = obj.TYPE_MEM
381		p.To.Name = obj.NAME_EXTERN
382		p.To.Sym = ir.Syms.Duffzero
383		p.To.Offset = v.AuxInt
384	case ssa.OpLOONG64LoweredZero:
385		// MOVx	R0, (Rarg0)
386		// ADDV	$sz, Rarg0
387		// BGEU	Rarg1, Rarg0, -2(PC)
388		mov, sz := largestMove(v.AuxInt)
389		p := s.Prog(mov)
390		p.From.Type = obj.TYPE_REG
391		p.From.Reg = loong64.REGZERO
392		p.To.Type = obj.TYPE_MEM
393		p.To.Reg = v.Args[0].Reg()
394
395		p2 := s.Prog(loong64.AADDVU)
396		p2.From.Type = obj.TYPE_CONST
397		p2.From.Offset = sz
398		p2.To.Type = obj.TYPE_REG
399		p2.To.Reg = v.Args[0].Reg()
400
401		p3 := s.Prog(loong64.ABGEU)
402		p3.From.Type = obj.TYPE_REG
403		p3.From.Reg = v.Args[1].Reg()
404		p3.Reg = v.Args[0].Reg()
405		p3.To.Type = obj.TYPE_BRANCH
406		p3.To.SetTarget(p)
407
408	case ssa.OpLOONG64DUFFCOPY:
409		p := s.Prog(obj.ADUFFCOPY)
410		p.To.Type = obj.TYPE_MEM
411		p.To.Name = obj.NAME_EXTERN
412		p.To.Sym = ir.Syms.Duffcopy
413		p.To.Offset = v.AuxInt
414	case ssa.OpLOONG64LoweredMove:
415		// MOVx	(Rarg1), Rtmp
416		// MOVx	Rtmp, (Rarg0)
417		// ADDV	$sz, Rarg1
418		// ADDV	$sz, Rarg0
419		// BGEU	Rarg2, Rarg0, -4(PC)
420		mov, sz := largestMove(v.AuxInt)
421		p := s.Prog(mov)
422		p.From.Type = obj.TYPE_MEM
423		p.From.Reg = v.Args[1].Reg()
424		p.To.Type = obj.TYPE_REG
425		p.To.Reg = loong64.REGTMP
426
427		p2 := s.Prog(mov)
428		p2.From.Type = obj.TYPE_REG
429		p2.From.Reg = loong64.REGTMP
430		p2.To.Type = obj.TYPE_MEM
431		p2.To.Reg = v.Args[0].Reg()
432
433		p3 := s.Prog(loong64.AADDVU)
434		p3.From.Type = obj.TYPE_CONST
435		p3.From.Offset = sz
436		p3.To.Type = obj.TYPE_REG
437		p3.To.Reg = v.Args[1].Reg()
438
439		p4 := s.Prog(loong64.AADDVU)
440		p4.From.Type = obj.TYPE_CONST
441		p4.From.Offset = sz
442		p4.To.Type = obj.TYPE_REG
443		p4.To.Reg = v.Args[0].Reg()
444
445		p5 := s.Prog(loong64.ABGEU)
446		p5.From.Type = obj.TYPE_REG
447		p5.From.Reg = v.Args[2].Reg()
448		p5.Reg = v.Args[1].Reg()
449		p5.To.Type = obj.TYPE_BRANCH
450		p5.To.SetTarget(p)
451
452	case ssa.OpLOONG64CALLstatic, ssa.OpLOONG64CALLclosure, ssa.OpLOONG64CALLinter:
453		s.Call(v)
454	case ssa.OpLOONG64CALLtail:
455		s.TailCall(v)
456	case ssa.OpLOONG64LoweredWB:
457		p := s.Prog(obj.ACALL)
458		p.To.Type = obj.TYPE_MEM
459		p.To.Name = obj.NAME_EXTERN
460		// AuxInt encodes how many buffer entries we need.
461		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
462	case ssa.OpLOONG64LoweredPanicBoundsA, ssa.OpLOONG64LoweredPanicBoundsB, ssa.OpLOONG64LoweredPanicBoundsC:
463		p := s.Prog(obj.ACALL)
464		p.To.Type = obj.TYPE_MEM
465		p.To.Name = obj.NAME_EXTERN
466		p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
467		s.UseArgs(16) // space used in callee args area by assembly stubs
468	case ssa.OpLOONG64LoweredAtomicLoad8, ssa.OpLOONG64LoweredAtomicLoad32, ssa.OpLOONG64LoweredAtomicLoad64:
469		as := loong64.AMOVV
470		switch v.Op {
471		case ssa.OpLOONG64LoweredAtomicLoad8:
472			as = loong64.AMOVB
473		case ssa.OpLOONG64LoweredAtomicLoad32:
474			as = loong64.AMOVW
475		}
476		s.Prog(loong64.ADBAR)
477		p := s.Prog(as)
478		p.From.Type = obj.TYPE_MEM
479		p.From.Reg = v.Args[0].Reg()
480		p.To.Type = obj.TYPE_REG
481		p.To.Reg = v.Reg0()
482		s.Prog(loong64.ADBAR)
483	case ssa.OpLOONG64LoweredAtomicStore8, ssa.OpLOONG64LoweredAtomicStore32, ssa.OpLOONG64LoweredAtomicStore64:
484		as := loong64.AMOVV
485		switch v.Op {
486		case ssa.OpLOONG64LoweredAtomicStore8:
487			as = loong64.AMOVB
488		case ssa.OpLOONG64LoweredAtomicStore32:
489			as = loong64.AMOVW
490		}
491		s.Prog(loong64.ADBAR)
492		p := s.Prog(as)
493		p.From.Type = obj.TYPE_REG
494		p.From.Reg = v.Args[1].Reg()
495		p.To.Type = obj.TYPE_MEM
496		p.To.Reg = v.Args[0].Reg()
497		s.Prog(loong64.ADBAR)
498	case ssa.OpLOONG64LoweredAtomicStorezero32, ssa.OpLOONG64LoweredAtomicStorezero64:
499		as := loong64.AMOVV
500		if v.Op == ssa.OpLOONG64LoweredAtomicStorezero32 {
501			as = loong64.AMOVW
502		}
503		s.Prog(loong64.ADBAR)
504		p := s.Prog(as)
505		p.From.Type = obj.TYPE_REG
506		p.From.Reg = loong64.REGZERO
507		p.To.Type = obj.TYPE_MEM
508		p.To.Reg = v.Args[0].Reg()
509		s.Prog(loong64.ADBAR)
510	case ssa.OpLOONG64LoweredAtomicExchange32, ssa.OpLOONG64LoweredAtomicExchange64:
511		// DBAR
512		// MOVV	Rarg1, Rtmp
513		// LL	(Rarg0), Rout
514		// SC	Rtmp, (Rarg0)
515		// BEQ	Rtmp, -3(PC)
516		// DBAR
517		ll := loong64.ALLV
518		sc := loong64.ASCV
519		if v.Op == ssa.OpLOONG64LoweredAtomicExchange32 {
520			ll = loong64.ALL
521			sc = loong64.ASC
522		}
523		s.Prog(loong64.ADBAR)
524		p := s.Prog(loong64.AMOVV)
525		p.From.Type = obj.TYPE_REG
526		p.From.Reg = v.Args[1].Reg()
527		p.To.Type = obj.TYPE_REG
528		p.To.Reg = loong64.REGTMP
529		p1 := s.Prog(ll)
530		p1.From.Type = obj.TYPE_MEM
531		p1.From.Reg = v.Args[0].Reg()
532		p1.To.Type = obj.TYPE_REG
533		p1.To.Reg = v.Reg0()
534		p2 := s.Prog(sc)
535		p2.From.Type = obj.TYPE_REG
536		p2.From.Reg = loong64.REGTMP
537		p2.To.Type = obj.TYPE_MEM
538		p2.To.Reg = v.Args[0].Reg()
539		p3 := s.Prog(loong64.ABEQ)
540		p3.From.Type = obj.TYPE_REG
541		p3.From.Reg = loong64.REGTMP
542		p3.To.Type = obj.TYPE_BRANCH
543		p3.To.SetTarget(p)
544		s.Prog(loong64.ADBAR)
545	case ssa.OpLOONG64LoweredAtomicAdd32, ssa.OpLOONG64LoweredAtomicAdd64:
546		// DBAR
547		// LL	(Rarg0), Rout
548		// ADDV Rarg1, Rout, Rtmp
549		// SC	Rtmp, (Rarg0)
550		// BEQ	Rtmp, -3(PC)
551		// DBAR
552		// ADDV Rarg1, Rout
553		ll := loong64.ALLV
554		sc := loong64.ASCV
555		if v.Op == ssa.OpLOONG64LoweredAtomicAdd32 {
556			ll = loong64.ALL
557			sc = loong64.ASC
558		}
559		s.Prog(loong64.ADBAR)
560		p := s.Prog(ll)
561		p.From.Type = obj.TYPE_MEM
562		p.From.Reg = v.Args[0].Reg()
563		p.To.Type = obj.TYPE_REG
564		p.To.Reg = v.Reg0()
565		p1 := s.Prog(loong64.AADDVU)
566		p1.From.Type = obj.TYPE_REG
567		p1.From.Reg = v.Args[1].Reg()
568		p1.Reg = v.Reg0()
569		p1.To.Type = obj.TYPE_REG
570		p1.To.Reg = loong64.REGTMP
571		p2 := s.Prog(sc)
572		p2.From.Type = obj.TYPE_REG
573		p2.From.Reg = loong64.REGTMP
574		p2.To.Type = obj.TYPE_MEM
575		p2.To.Reg = v.Args[0].Reg()
576		p3 := s.Prog(loong64.ABEQ)
577		p3.From.Type = obj.TYPE_REG
578		p3.From.Reg = loong64.REGTMP
579		p3.To.Type = obj.TYPE_BRANCH
580		p3.To.SetTarget(p)
581		s.Prog(loong64.ADBAR)
582		p4 := s.Prog(loong64.AADDVU)
583		p4.From.Type = obj.TYPE_REG
584		p4.From.Reg = v.Args[1].Reg()
585		p4.Reg = v.Reg0()
586		p4.To.Type = obj.TYPE_REG
587		p4.To.Reg = v.Reg0()
588	case ssa.OpLOONG64LoweredAtomicAddconst32, ssa.OpLOONG64LoweredAtomicAddconst64:
589		// DBAR
590		// LL	(Rarg0), Rout
591		// ADDV $auxint, Rout, Rtmp
592		// SC	Rtmp, (Rarg0)
593		// BEQ	Rtmp, -3(PC)
594		// DBAR
595		// ADDV $auxint, Rout
596		ll := loong64.ALLV
597		sc := loong64.ASCV
598		if v.Op == ssa.OpLOONG64LoweredAtomicAddconst32 {
599			ll = loong64.ALL
600			sc = loong64.ASC
601		}
602		s.Prog(loong64.ADBAR)
603		p := s.Prog(ll)
604		p.From.Type = obj.TYPE_MEM
605		p.From.Reg = v.Args[0].Reg()
606		p.To.Type = obj.TYPE_REG
607		p.To.Reg = v.Reg0()
608		p1 := s.Prog(loong64.AADDVU)
609		p1.From.Type = obj.TYPE_CONST
610		p1.From.Offset = v.AuxInt
611		p1.Reg = v.Reg0()
612		p1.To.Type = obj.TYPE_REG
613		p1.To.Reg = loong64.REGTMP
614		p2 := s.Prog(sc)
615		p2.From.Type = obj.TYPE_REG
616		p2.From.Reg = loong64.REGTMP
617		p2.To.Type = obj.TYPE_MEM
618		p2.To.Reg = v.Args[0].Reg()
619		p3 := s.Prog(loong64.ABEQ)
620		p3.From.Type = obj.TYPE_REG
621		p3.From.Reg = loong64.REGTMP
622		p3.To.Type = obj.TYPE_BRANCH
623		p3.To.SetTarget(p)
624		s.Prog(loong64.ADBAR)
625		p4 := s.Prog(loong64.AADDVU)
626		p4.From.Type = obj.TYPE_CONST
627		p4.From.Offset = v.AuxInt
628		p4.Reg = v.Reg0()
629		p4.To.Type = obj.TYPE_REG
630		p4.To.Reg = v.Reg0()
631	case ssa.OpLOONG64LoweredAtomicCas32, ssa.OpLOONG64LoweredAtomicCas64:
632		// MOVV $0, Rout
633		// DBAR
634		// LL	(Rarg0), Rtmp
635		// BNE	Rtmp, Rarg1, 4(PC)
636		// MOVV Rarg2, Rout
637		// SC	Rout, (Rarg0)
638		// BEQ	Rout, -4(PC)
639		// DBAR
640		ll := loong64.ALLV
641		sc := loong64.ASCV
642		if v.Op == ssa.OpLOONG64LoweredAtomicCas32 {
643			ll = loong64.ALL
644			sc = loong64.ASC
645		}
646		p := s.Prog(loong64.AMOVV)
647		p.From.Type = obj.TYPE_REG
648		p.From.Reg = loong64.REGZERO
649		p.To.Type = obj.TYPE_REG
650		p.To.Reg = v.Reg0()
651		s.Prog(loong64.ADBAR)
652		p1 := s.Prog(ll)
653		p1.From.Type = obj.TYPE_MEM
654		p1.From.Reg = v.Args[0].Reg()
655		p1.To.Type = obj.TYPE_REG
656		p1.To.Reg = loong64.REGTMP
657		p2 := s.Prog(loong64.ABNE)
658		p2.From.Type = obj.TYPE_REG
659		p2.From.Reg = v.Args[1].Reg()
660		p2.Reg = loong64.REGTMP
661		p2.To.Type = obj.TYPE_BRANCH
662		p3 := s.Prog(loong64.AMOVV)
663		p3.From.Type = obj.TYPE_REG
664		p3.From.Reg = v.Args[2].Reg()
665		p3.To.Type = obj.TYPE_REG
666		p3.To.Reg = v.Reg0()
667		p4 := s.Prog(sc)
668		p4.From.Type = obj.TYPE_REG
669		p4.From.Reg = v.Reg0()
670		p4.To.Type = obj.TYPE_MEM
671		p4.To.Reg = v.Args[0].Reg()
672		p5 := s.Prog(loong64.ABEQ)
673		p5.From.Type = obj.TYPE_REG
674		p5.From.Reg = v.Reg0()
675		p5.To.Type = obj.TYPE_BRANCH
676		p5.To.SetTarget(p1)
677		p6 := s.Prog(loong64.ADBAR)
678		p2.To.SetTarget(p6)
679	case ssa.OpLOONG64LoweredNilCheck:
680		// Issue a load which will fault if arg is nil.
681		p := s.Prog(loong64.AMOVB)
682		p.From.Type = obj.TYPE_MEM
683		p.From.Reg = v.Args[0].Reg()
684		ssagen.AddAux(&p.From, v)
685		p.To.Type = obj.TYPE_REG
686		p.To.Reg = loong64.REGTMP
687		if logopt.Enabled() {
688			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
689		}
690		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
691			base.WarnfAt(v.Pos, "generated nil check")
692		}
693	case ssa.OpLOONG64FPFlagTrue,
694		ssa.OpLOONG64FPFlagFalse:
695		// MOVV	$0, r
696		// BFPF	2(PC)
697		// MOVV	$1, r
698		branch := loong64.ABFPF
699		if v.Op == ssa.OpLOONG64FPFlagFalse {
700			branch = loong64.ABFPT
701		}
702		p := s.Prog(loong64.AMOVV)
703		p.From.Type = obj.TYPE_REG
704		p.From.Reg = loong64.REGZERO
705		p.To.Type = obj.TYPE_REG
706		p.To.Reg = v.Reg()
707		p2 := s.Prog(branch)
708		p2.To.Type = obj.TYPE_BRANCH
709		p3 := s.Prog(loong64.AMOVV)
710		p3.From.Type = obj.TYPE_CONST
711		p3.From.Offset = 1
712		p3.To.Type = obj.TYPE_REG
713		p3.To.Reg = v.Reg()
714		p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land
715		p2.To.SetTarget(p4)
716	case ssa.OpLOONG64LoweredGetClosurePtr:
717		// Closure pointer is R22 (loong64.REGCTXT).
718		ssagen.CheckLoweredGetClosurePtr(v)
719	case ssa.OpLOONG64LoweredGetCallerSP:
720		// caller's SP is FixedFrameSize below the address of the first arg
721		p := s.Prog(loong64.AMOVV)
722		p.From.Type = obj.TYPE_ADDR
723		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
724		p.From.Name = obj.NAME_PARAM
725		p.To.Type = obj.TYPE_REG
726		p.To.Reg = v.Reg()
727	case ssa.OpLOONG64LoweredGetCallerPC:
728		p := s.Prog(obj.AGETCALLERPC)
729		p.To.Type = obj.TYPE_REG
730		p.To.Reg = v.Reg()
731	case ssa.OpLOONG64MASKEQZ, ssa.OpLOONG64MASKNEZ:
732		p := s.Prog(v.Op.Asm())
733		p.From.Type = obj.TYPE_REG
734		p.From.Reg = v.Args[1].Reg()
735		p.Reg = v.Args[0].Reg()
736		p.To.Type = obj.TYPE_REG
737		p.To.Reg = v.Reg()
738	case ssa.OpClobber, ssa.OpClobberReg:
739		// TODO: implement for clobberdead experiment. Nop is ok for now.
740	default:
741		v.Fatalf("genValue not implemented: %s", v.LongString())
742	}
743}
744
745var blockJump = map[ssa.BlockKind]struct {
746	asm, invasm obj.As
747}{
748	ssa.BlockLOONG64EQ:  {loong64.ABEQ, loong64.ABNE},
749	ssa.BlockLOONG64NE:  {loong64.ABNE, loong64.ABEQ},
750	ssa.BlockLOONG64LTZ: {loong64.ABLTZ, loong64.ABGEZ},
751	ssa.BlockLOONG64GEZ: {loong64.ABGEZ, loong64.ABLTZ},
752	ssa.BlockLOONG64LEZ: {loong64.ABLEZ, loong64.ABGTZ},
753	ssa.BlockLOONG64GTZ: {loong64.ABGTZ, loong64.ABLEZ},
754	ssa.BlockLOONG64FPT: {loong64.ABFPT, loong64.ABFPF},
755	ssa.BlockLOONG64FPF: {loong64.ABFPF, loong64.ABFPT},
756}
757
758func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
759	switch b.Kind {
760	case ssa.BlockPlain:
761		if b.Succs[0].Block() != next {
762			p := s.Prog(obj.AJMP)
763			p.To.Type = obj.TYPE_BRANCH
764			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
765		}
766	case ssa.BlockDefer:
767		// defer returns in R19:
768		// 0 if we should continue executing
769		// 1 if we should jump to deferreturn call
770		p := s.Prog(loong64.ABNE)
771		p.From.Type = obj.TYPE_REG
772		p.From.Reg = loong64.REGZERO
773		p.Reg = loong64.REG_R19
774		p.To.Type = obj.TYPE_BRANCH
775		s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
776		if b.Succs[0].Block() != next {
777			p := s.Prog(obj.AJMP)
778			p.To.Type = obj.TYPE_BRANCH
779			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
780		}
781	case ssa.BlockExit, ssa.BlockRetJmp:
782	case ssa.BlockRet:
783		s.Prog(obj.ARET)
784	case ssa.BlockLOONG64EQ, ssa.BlockLOONG64NE,
785		ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ,
786		ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ,
787		ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF:
788		jmp := blockJump[b.Kind]
789		var p *obj.Prog
790		switch next {
791		case b.Succs[0].Block():
792			p = s.Br(jmp.invasm, b.Succs[1].Block())
793		case b.Succs[1].Block():
794			p = s.Br(jmp.asm, b.Succs[0].Block())
795		default:
796			if b.Likely != ssa.BranchUnlikely {
797				p = s.Br(jmp.asm, b.Succs[0].Block())
798				s.Br(obj.AJMP, b.Succs[1].Block())
799			} else {
800				p = s.Br(jmp.invasm, b.Succs[1].Block())
801				s.Br(obj.AJMP, b.Succs[0].Block())
802			}
803		}
804		if !b.Controls[0].Type.IsFlags() {
805			p.From.Type = obj.TYPE_REG
806			p.From.Reg = b.Controls[0].Reg()
807		}
808	default:
809		b.Fatalf("branch not implemented: %s", b.LongString())
810	}
811}
812
813func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
814	p := s.Prog(loadByType(t, reg))
815	p.From.Type = obj.TYPE_MEM
816	p.From.Name = obj.NAME_AUTO
817	p.From.Sym = n.Linksym()
818	p.From.Offset = n.FrameOffset() + off
819	p.To.Type = obj.TYPE_REG
820	p.To.Reg = reg
821	return p
822}
823
824func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
825	p = pp.Append(p, storeByType(t, reg), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
826	p.To.Name = obj.NAME_PARAM
827	p.To.Sym = n.Linksym()
828	p.Pos = p.Pos.WithNotStmt()
829	return p
830}
831