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 riscv64
6
7import (
8	"cmd/compile/internal/base"
9	"cmd/compile/internal/ir"
10	"cmd/compile/internal/objw"
11	"cmd/compile/internal/ssa"
12	"cmd/compile/internal/ssagen"
13	"cmd/compile/internal/types"
14	"cmd/internal/obj"
15	"cmd/internal/obj/riscv"
16)
17
18// ssaRegToReg maps ssa register numbers to obj register numbers.
19var ssaRegToReg = []int16{
20	riscv.REG_X0,
21	// X1 (LR): unused
22	riscv.REG_X2,
23	riscv.REG_X3,
24	riscv.REG_X4,
25	riscv.REG_X5,
26	riscv.REG_X6,
27	riscv.REG_X7,
28	riscv.REG_X8,
29	riscv.REG_X9,
30	riscv.REG_X10,
31	riscv.REG_X11,
32	riscv.REG_X12,
33	riscv.REG_X13,
34	riscv.REG_X14,
35	riscv.REG_X15,
36	riscv.REG_X16,
37	riscv.REG_X17,
38	riscv.REG_X18,
39	riscv.REG_X19,
40	riscv.REG_X20,
41	riscv.REG_X21,
42	riscv.REG_X22,
43	riscv.REG_X23,
44	riscv.REG_X24,
45	riscv.REG_X25,
46	riscv.REG_X26,
47	riscv.REG_X27,
48	riscv.REG_X28,
49	riscv.REG_X29,
50	riscv.REG_X30,
51	riscv.REG_X31,
52	riscv.REG_F0,
53	riscv.REG_F1,
54	riscv.REG_F2,
55	riscv.REG_F3,
56	riscv.REG_F4,
57	riscv.REG_F5,
58	riscv.REG_F6,
59	riscv.REG_F7,
60	riscv.REG_F8,
61	riscv.REG_F9,
62	riscv.REG_F10,
63	riscv.REG_F11,
64	riscv.REG_F12,
65	riscv.REG_F13,
66	riscv.REG_F14,
67	riscv.REG_F15,
68	riscv.REG_F16,
69	riscv.REG_F17,
70	riscv.REG_F18,
71	riscv.REG_F19,
72	riscv.REG_F20,
73	riscv.REG_F21,
74	riscv.REG_F22,
75	riscv.REG_F23,
76	riscv.REG_F24,
77	riscv.REG_F25,
78	riscv.REG_F26,
79	riscv.REG_F27,
80	riscv.REG_F28,
81	riscv.REG_F29,
82	riscv.REG_F30,
83	riscv.REG_F31,
84	0, // SB isn't a real register.  We fill an Addr.Reg field with 0 in this case.
85}
86
87func loadByType(t *types.Type) obj.As {
88	width := t.Size()
89
90	if t.IsFloat() {
91		switch width {
92		case 4:
93			return riscv.AMOVF
94		case 8:
95			return riscv.AMOVD
96		default:
97			base.Fatalf("unknown float width for load %d in type %v", width, t)
98			return 0
99		}
100	}
101
102	switch width {
103	case 1:
104		if t.IsSigned() {
105			return riscv.AMOVB
106		} else {
107			return riscv.AMOVBU
108		}
109	case 2:
110		if t.IsSigned() {
111			return riscv.AMOVH
112		} else {
113			return riscv.AMOVHU
114		}
115	case 4:
116		if t.IsSigned() {
117			return riscv.AMOVW
118		} else {
119			return riscv.AMOVWU
120		}
121	case 8:
122		return riscv.AMOV
123	default:
124		base.Fatalf("unknown width for load %d in type %v", width, t)
125		return 0
126	}
127}
128
129// storeByType returns the store instruction of the given type.
130func storeByType(t *types.Type) obj.As {
131	width := t.Size()
132
133	if t.IsFloat() {
134		switch width {
135		case 4:
136			return riscv.AMOVF
137		case 8:
138			return riscv.AMOVD
139		default:
140			base.Fatalf("unknown float width for store %d in type %v", width, t)
141			return 0
142		}
143	}
144
145	switch width {
146	case 1:
147		return riscv.AMOVB
148	case 2:
149		return riscv.AMOVH
150	case 4:
151		return riscv.AMOVW
152	case 8:
153		return riscv.AMOV
154	default:
155		base.Fatalf("unknown width for store %d in type %v", width, t)
156		return 0
157	}
158}
159
160// largestMove returns the largest move instruction possible and its size,
161// given the alignment of the total size of the move.
162//
163// e.g., a 16-byte move may use MOV, but an 11-byte move must use MOVB.
164//
165// Note that the moves may not be on naturally aligned addresses depending on
166// the source and destination.
167//
168// This matches the calculation in ssa.moveSize.
169func largestMove(alignment int64) (obj.As, int64) {
170	switch {
171	case alignment%8 == 0:
172		return riscv.AMOV, 8
173	case alignment%4 == 0:
174		return riscv.AMOVW, 4
175	case alignment%2 == 0:
176		return riscv.AMOVH, 2
177	default:
178		return riscv.AMOVB, 1
179	}
180}
181
182// ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
183// RISC-V has no flags, so this is a no-op.
184func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {}
185
186func ssaGenValue(s *ssagen.State, v *ssa.Value) {
187	s.SetPos(v.Pos)
188
189	switch v.Op {
190	case ssa.OpInitMem:
191		// memory arg needs no code
192	case ssa.OpArg:
193		// input args need no code
194	case ssa.OpPhi:
195		ssagen.CheckLoweredPhi(v)
196	case ssa.OpCopy, ssa.OpRISCV64MOVDreg:
197		if v.Type.IsMemory() {
198			return
199		}
200		rs := v.Args[0].Reg()
201		rd := v.Reg()
202		if rs == rd {
203			return
204		}
205		as := riscv.AMOV
206		if v.Type.IsFloat() {
207			as = riscv.AMOVD
208		}
209		p := s.Prog(as)
210		p.From.Type = obj.TYPE_REG
211		p.From.Reg = rs
212		p.To.Type = obj.TYPE_REG
213		p.To.Reg = rd
214	case ssa.OpRISCV64MOVDnop:
215		// nothing to do
216	case ssa.OpLoadReg:
217		if v.Type.IsFlags() {
218			v.Fatalf("load flags not implemented: %v", v.LongString())
219			return
220		}
221		p := s.Prog(loadByType(v.Type))
222		ssagen.AddrAuto(&p.From, v.Args[0])
223		p.To.Type = obj.TYPE_REG
224		p.To.Reg = v.Reg()
225	case ssa.OpStoreReg:
226		if v.Type.IsFlags() {
227			v.Fatalf("store flags not implemented: %v", v.LongString())
228			return
229		}
230		p := s.Prog(storeByType(v.Type))
231		p.From.Type = obj.TYPE_REG
232		p.From.Reg = v.Args[0].Reg()
233		ssagen.AddrAuto(&p.To, v)
234	case ssa.OpArgIntReg, ssa.OpArgFloatReg:
235		// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
236		// The loop only runs once.
237		for _, a := range v.Block.Func.RegArgs {
238			// Pass the spill/unspill information along to the assembler, offset by size of
239			// the saved LR slot.
240			addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.Arch.FixedFrameSize)
241			s.FuncInfo().AddSpill(
242				obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
243		}
244		v.Block.Func.RegArgs = nil
245
246		ssagen.CheckArgReg(v)
247	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
248		// nothing to do
249	case ssa.OpRISCV64MOVBreg, ssa.OpRISCV64MOVHreg, ssa.OpRISCV64MOVWreg,
250		ssa.OpRISCV64MOVBUreg, ssa.OpRISCV64MOVHUreg, ssa.OpRISCV64MOVWUreg:
251		a := v.Args[0]
252		for a.Op == ssa.OpCopy || a.Op == ssa.OpRISCV64MOVDreg {
253			a = a.Args[0]
254		}
255		as := v.Op.Asm()
256		rs := v.Args[0].Reg()
257		rd := v.Reg()
258		if a.Op == ssa.OpLoadReg {
259			t := a.Type
260			switch {
261			case v.Op == ssa.OpRISCV64MOVBreg && t.Size() == 1 && t.IsSigned(),
262				v.Op == ssa.OpRISCV64MOVHreg && t.Size() == 2 && t.IsSigned(),
263				v.Op == ssa.OpRISCV64MOVWreg && t.Size() == 4 && t.IsSigned(),
264				v.Op == ssa.OpRISCV64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
265				v.Op == ssa.OpRISCV64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
266				v.Op == ssa.OpRISCV64MOVWUreg && t.Size() == 4 && !t.IsSigned():
267				// arg is a proper-typed load and already sign/zero-extended
268				if rs == rd {
269					return
270				}
271				as = riscv.AMOV
272			default:
273			}
274		}
275		p := s.Prog(as)
276		p.From.Type = obj.TYPE_REG
277		p.From.Reg = rs
278		p.To.Type = obj.TYPE_REG
279		p.To.Reg = rd
280	case ssa.OpRISCV64ADD, ssa.OpRISCV64SUB, ssa.OpRISCV64SUBW, ssa.OpRISCV64XOR, ssa.OpRISCV64OR, ssa.OpRISCV64AND,
281		ssa.OpRISCV64SLL, ssa.OpRISCV64SLLW, ssa.OpRISCV64SRA, ssa.OpRISCV64SRAW, ssa.OpRISCV64SRL, ssa.OpRISCV64SRLW,
282		ssa.OpRISCV64SLT, ssa.OpRISCV64SLTU, ssa.OpRISCV64MUL, ssa.OpRISCV64MULW, ssa.OpRISCV64MULH,
283		ssa.OpRISCV64MULHU, ssa.OpRISCV64DIV, ssa.OpRISCV64DIVU, ssa.OpRISCV64DIVW,
284		ssa.OpRISCV64DIVUW, ssa.OpRISCV64REM, ssa.OpRISCV64REMU, ssa.OpRISCV64REMW,
285		ssa.OpRISCV64REMUW,
286		ssa.OpRISCV64ROL, ssa.OpRISCV64ROLW, ssa.OpRISCV64ROR, ssa.OpRISCV64RORW,
287		ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS,
288		ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES,
289		ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD,
290		ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED,
291		ssa.OpRISCV64FSGNJD:
292		r := v.Reg()
293		r1 := v.Args[0].Reg()
294		r2 := v.Args[1].Reg()
295		p := s.Prog(v.Op.Asm())
296		p.From.Type = obj.TYPE_REG
297		p.From.Reg = r2
298		p.Reg = r1
299		p.To.Type = obj.TYPE_REG
300		p.To.Reg = r
301
302	case ssa.OpRISCV64LoweredFMAXD, ssa.OpRISCV64LoweredFMIND, ssa.OpRISCV64LoweredFMAXS, ssa.OpRISCV64LoweredFMINS:
303		// Most of FMIN/FMAX result match Go's required behaviour, unless one of the
304		// inputs is a NaN. As such, we need to explicitly test for NaN
305		// before using FMIN/FMAX.
306
307		// FADD Rarg0, Rarg1, Rout // FADD is used to propagate a NaN to the result in these cases.
308		// FEQ  Rarg0, Rarg0, Rtmp
309		// BEQZ Rtmp, end
310		// FEQ  Rarg1, Rarg1, Rtmp
311		// BEQZ Rtmp, end
312		// F(MIN | MAX)
313
314		r0 := v.Args[0].Reg()
315		r1 := v.Args[1].Reg()
316		out := v.Reg()
317		add, feq := riscv.AFADDD, riscv.AFEQD
318		if v.Op == ssa.OpRISCV64LoweredFMAXS || v.Op == ssa.OpRISCV64LoweredFMINS {
319			add = riscv.AFADDS
320			feq = riscv.AFEQS
321		}
322
323		p1 := s.Prog(add)
324		p1.From.Type = obj.TYPE_REG
325		p1.From.Reg = r0
326		p1.Reg = r1
327		p1.To.Type = obj.TYPE_REG
328		p1.To.Reg = out
329
330		p2 := s.Prog(feq)
331		p2.From.Type = obj.TYPE_REG
332		p2.From.Reg = r0
333		p2.Reg = r0
334		p2.To.Type = obj.TYPE_REG
335		p2.To.Reg = riscv.REG_TMP
336
337		p3 := s.Prog(riscv.ABEQ)
338		p3.From.Type = obj.TYPE_REG
339		p3.From.Reg = riscv.REG_ZERO
340		p3.Reg = riscv.REG_TMP
341		p3.To.Type = obj.TYPE_BRANCH
342
343		p4 := s.Prog(feq)
344		p4.From.Type = obj.TYPE_REG
345		p4.From.Reg = r1
346		p4.Reg = r1
347		p4.To.Type = obj.TYPE_REG
348		p4.To.Reg = riscv.REG_TMP
349
350		p5 := s.Prog(riscv.ABEQ)
351		p5.From.Type = obj.TYPE_REG
352		p5.From.Reg = riscv.REG_ZERO
353		p5.Reg = riscv.REG_TMP
354		p5.To.Type = obj.TYPE_BRANCH
355
356		p6 := s.Prog(v.Op.Asm())
357		p6.From.Type = obj.TYPE_REG
358		p6.From.Reg = r1
359		p6.Reg = r0
360		p6.To.Type = obj.TYPE_REG
361		p6.To.Reg = out
362
363		nop := s.Prog(obj.ANOP)
364		p3.To.SetTarget(nop)
365		p5.To.SetTarget(nop)
366
367	case ssa.OpRISCV64LoweredMuluhilo:
368		r0 := v.Args[0].Reg()
369		r1 := v.Args[1].Reg()
370		p := s.Prog(riscv.AMULHU)
371		p.From.Type = obj.TYPE_REG
372		p.From.Reg = r1
373		p.Reg = r0
374		p.To.Type = obj.TYPE_REG
375		p.To.Reg = v.Reg0()
376		p1 := s.Prog(riscv.AMUL)
377		p1.From.Type = obj.TYPE_REG
378		p1.From.Reg = r1
379		p1.Reg = r0
380		p1.To.Type = obj.TYPE_REG
381		p1.To.Reg = v.Reg1()
382	case ssa.OpRISCV64LoweredMuluover:
383		r0 := v.Args[0].Reg()
384		r1 := v.Args[1].Reg()
385		p := s.Prog(riscv.AMULHU)
386		p.From.Type = obj.TYPE_REG
387		p.From.Reg = r1
388		p.Reg = r0
389		p.To.Type = obj.TYPE_REG
390		p.To.Reg = v.Reg1()
391		p1 := s.Prog(riscv.AMUL)
392		p1.From.Type = obj.TYPE_REG
393		p1.From.Reg = r1
394		p1.Reg = r0
395		p1.To.Type = obj.TYPE_REG
396		p1.To.Reg = v.Reg0()
397		p2 := s.Prog(riscv.ASNEZ)
398		p2.From.Type = obj.TYPE_REG
399		p2.From.Reg = v.Reg1()
400		p2.To.Type = obj.TYPE_REG
401		p2.To.Reg = v.Reg1()
402	case ssa.OpRISCV64FMADDD, ssa.OpRISCV64FMSUBD, ssa.OpRISCV64FNMADDD, ssa.OpRISCV64FNMSUBD,
403		ssa.OpRISCV64FMADDS, ssa.OpRISCV64FMSUBS, ssa.OpRISCV64FNMADDS, ssa.OpRISCV64FNMSUBS:
404		r := v.Reg()
405		r1 := v.Args[0].Reg()
406		r2 := v.Args[1].Reg()
407		r3 := v.Args[2].Reg()
408		p := s.Prog(v.Op.Asm())
409		p.From.Type = obj.TYPE_REG
410		p.From.Reg = r2
411		p.Reg = r1
412		p.AddRestSource(obj.Addr{Type: obj.TYPE_REG, Reg: r3})
413		p.To.Type = obj.TYPE_REG
414		p.To.Reg = r
415	case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
416		ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX,
417		ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
418		ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD,
419		ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW:
420		p := s.Prog(v.Op.Asm())
421		p.From.Type = obj.TYPE_REG
422		p.From.Reg = v.Args[0].Reg()
423		p.To.Type = obj.TYPE_REG
424		p.To.Reg = v.Reg()
425	case ssa.OpRISCV64ADDI, ssa.OpRISCV64ADDIW, ssa.OpRISCV64XORI, ssa.OpRISCV64ORI, ssa.OpRISCV64ANDI,
426		ssa.OpRISCV64SLLI, ssa.OpRISCV64SLLIW, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRAIW,
427		ssa.OpRISCV64SRLI, ssa.OpRISCV64SRLIW, ssa.OpRISCV64SLTI, ssa.OpRISCV64SLTIU,
428		ssa.OpRISCV64RORI, ssa.OpRISCV64RORIW:
429		p := s.Prog(v.Op.Asm())
430		p.From.Type = obj.TYPE_CONST
431		p.From.Offset = v.AuxInt
432		p.Reg = v.Args[0].Reg()
433		p.To.Type = obj.TYPE_REG
434		p.To.Reg = v.Reg()
435	case ssa.OpRISCV64MOVDconst:
436		p := s.Prog(v.Op.Asm())
437		p.From.Type = obj.TYPE_CONST
438		p.From.Offset = v.AuxInt
439		p.To.Type = obj.TYPE_REG
440		p.To.Reg = v.Reg()
441	case ssa.OpRISCV64MOVaddr:
442		p := s.Prog(v.Op.Asm())
443		p.From.Type = obj.TYPE_ADDR
444		p.To.Type = obj.TYPE_REG
445		p.To.Reg = v.Reg()
446
447		var wantreg string
448		// MOVW $sym+off(base), R
449		switch v.Aux.(type) {
450		default:
451			v.Fatalf("aux is of unknown type %T", v.Aux)
452		case *obj.LSym:
453			wantreg = "SB"
454			ssagen.AddAux(&p.From, v)
455		case *ir.Name:
456			wantreg = "SP"
457			ssagen.AddAux(&p.From, v)
458		case nil:
459			// No sym, just MOVW $off(SP), R
460			wantreg = "SP"
461			p.From.Reg = riscv.REG_SP
462			p.From.Offset = v.AuxInt
463		}
464		if reg := v.Args[0].RegName(); reg != wantreg {
465			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
466		}
467	case ssa.OpRISCV64MOVBload, ssa.OpRISCV64MOVHload, ssa.OpRISCV64MOVWload, ssa.OpRISCV64MOVDload,
468		ssa.OpRISCV64MOVBUload, ssa.OpRISCV64MOVHUload, ssa.OpRISCV64MOVWUload,
469		ssa.OpRISCV64FMOVWload, ssa.OpRISCV64FMOVDload:
470		p := s.Prog(v.Op.Asm())
471		p.From.Type = obj.TYPE_MEM
472		p.From.Reg = v.Args[0].Reg()
473		ssagen.AddAux(&p.From, v)
474		p.To.Type = obj.TYPE_REG
475		p.To.Reg = v.Reg()
476	case ssa.OpRISCV64MOVBstore, ssa.OpRISCV64MOVHstore, ssa.OpRISCV64MOVWstore, ssa.OpRISCV64MOVDstore,
477		ssa.OpRISCV64FMOVWstore, ssa.OpRISCV64FMOVDstore:
478		p := s.Prog(v.Op.Asm())
479		p.From.Type = obj.TYPE_REG
480		p.From.Reg = v.Args[1].Reg()
481		p.To.Type = obj.TYPE_MEM
482		p.To.Reg = v.Args[0].Reg()
483		ssagen.AddAux(&p.To, v)
484	case ssa.OpRISCV64MOVBstorezero, ssa.OpRISCV64MOVHstorezero, ssa.OpRISCV64MOVWstorezero, ssa.OpRISCV64MOVDstorezero:
485		p := s.Prog(v.Op.Asm())
486		p.From.Type = obj.TYPE_REG
487		p.From.Reg = riscv.REG_ZERO
488		p.To.Type = obj.TYPE_MEM
489		p.To.Reg = v.Args[0].Reg()
490		ssagen.AddAux(&p.To, v)
491	case ssa.OpRISCV64SEQZ, ssa.OpRISCV64SNEZ:
492		p := s.Prog(v.Op.Asm())
493		p.From.Type = obj.TYPE_REG
494		p.From.Reg = v.Args[0].Reg()
495		p.To.Type = obj.TYPE_REG
496		p.To.Reg = v.Reg()
497	case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter:
498		s.Call(v)
499	case ssa.OpRISCV64CALLtail:
500		s.TailCall(v)
501	case ssa.OpRISCV64LoweredWB:
502		p := s.Prog(obj.ACALL)
503		p.To.Type = obj.TYPE_MEM
504		p.To.Name = obj.NAME_EXTERN
505		// AuxInt encodes how many buffer entries we need.
506		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
507	case ssa.OpRISCV64LoweredPanicBoundsA, ssa.OpRISCV64LoweredPanicBoundsB, ssa.OpRISCV64LoweredPanicBoundsC:
508		p := s.Prog(obj.ACALL)
509		p.To.Type = obj.TYPE_MEM
510		p.To.Name = obj.NAME_EXTERN
511		p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
512		s.UseArgs(16) // space used in callee args area by assembly stubs
513
514	case ssa.OpRISCV64LoweredAtomicLoad8:
515		s.Prog(riscv.AFENCE)
516		p := s.Prog(riscv.AMOVBU)
517		p.From.Type = obj.TYPE_MEM
518		p.From.Reg = v.Args[0].Reg()
519		p.To.Type = obj.TYPE_REG
520		p.To.Reg = v.Reg0()
521		s.Prog(riscv.AFENCE)
522
523	case ssa.OpRISCV64LoweredAtomicLoad32, ssa.OpRISCV64LoweredAtomicLoad64:
524		as := riscv.ALRW
525		if v.Op == ssa.OpRISCV64LoweredAtomicLoad64 {
526			as = riscv.ALRD
527		}
528		p := s.Prog(as)
529		p.From.Type = obj.TYPE_MEM
530		p.From.Reg = v.Args[0].Reg()
531		p.To.Type = obj.TYPE_REG
532		p.To.Reg = v.Reg0()
533
534	case ssa.OpRISCV64LoweredAtomicStore8:
535		s.Prog(riscv.AFENCE)
536		p := s.Prog(riscv.AMOVB)
537		p.From.Type = obj.TYPE_REG
538		p.From.Reg = v.Args[1].Reg()
539		p.To.Type = obj.TYPE_MEM
540		p.To.Reg = v.Args[0].Reg()
541		s.Prog(riscv.AFENCE)
542
543	case ssa.OpRISCV64LoweredAtomicStore32, ssa.OpRISCV64LoweredAtomicStore64:
544		as := riscv.AAMOSWAPW
545		if v.Op == ssa.OpRISCV64LoweredAtomicStore64 {
546			as = riscv.AAMOSWAPD
547		}
548		p := s.Prog(as)
549		p.From.Type = obj.TYPE_REG
550		p.From.Reg = v.Args[1].Reg()
551		p.To.Type = obj.TYPE_MEM
552		p.To.Reg = v.Args[0].Reg()
553		p.RegTo2 = riscv.REG_ZERO
554
555	case ssa.OpRISCV64LoweredAtomicAdd32, ssa.OpRISCV64LoweredAtomicAdd64:
556		as := riscv.AAMOADDW
557		if v.Op == ssa.OpRISCV64LoweredAtomicAdd64 {
558			as = riscv.AAMOADDD
559		}
560		p := s.Prog(as)
561		p.From.Type = obj.TYPE_REG
562		p.From.Reg = v.Args[1].Reg()
563		p.To.Type = obj.TYPE_MEM
564		p.To.Reg = v.Args[0].Reg()
565		p.RegTo2 = riscv.REG_TMP
566
567		p2 := s.Prog(riscv.AADD)
568		p2.From.Type = obj.TYPE_REG
569		p2.From.Reg = riscv.REG_TMP
570		p2.Reg = v.Args[1].Reg()
571		p2.To.Type = obj.TYPE_REG
572		p2.To.Reg = v.Reg0()
573
574	case ssa.OpRISCV64LoweredAtomicExchange32, ssa.OpRISCV64LoweredAtomicExchange64:
575		as := riscv.AAMOSWAPW
576		if v.Op == ssa.OpRISCV64LoweredAtomicExchange64 {
577			as = riscv.AAMOSWAPD
578		}
579		p := s.Prog(as)
580		p.From.Type = obj.TYPE_REG
581		p.From.Reg = v.Args[1].Reg()
582		p.To.Type = obj.TYPE_MEM
583		p.To.Reg = v.Args[0].Reg()
584		p.RegTo2 = v.Reg0()
585
586	case ssa.OpRISCV64LoweredAtomicCas32, ssa.OpRISCV64LoweredAtomicCas64:
587		// MOV  ZERO, Rout
588		// LR	(Rarg0), Rtmp
589		// BNE	Rtmp, Rarg1, 3(PC)
590		// SC	Rarg2, (Rarg0), Rtmp
591		// BNE	Rtmp, ZERO, -3(PC)
592		// MOV	$1, Rout
593
594		lr := riscv.ALRW
595		sc := riscv.ASCW
596		if v.Op == ssa.OpRISCV64LoweredAtomicCas64 {
597			lr = riscv.ALRD
598			sc = riscv.ASCD
599		}
600
601		r0 := v.Args[0].Reg()
602		r1 := v.Args[1].Reg()
603		r2 := v.Args[2].Reg()
604		out := v.Reg0()
605
606		p := s.Prog(riscv.AMOV)
607		p.From.Type = obj.TYPE_REG
608		p.From.Reg = riscv.REG_ZERO
609		p.To.Type = obj.TYPE_REG
610		p.To.Reg = out
611
612		p1 := s.Prog(lr)
613		p1.From.Type = obj.TYPE_MEM
614		p1.From.Reg = r0
615		p1.To.Type = obj.TYPE_REG
616		p1.To.Reg = riscv.REG_TMP
617
618		p2 := s.Prog(riscv.ABNE)
619		p2.From.Type = obj.TYPE_REG
620		p2.From.Reg = r1
621		p2.Reg = riscv.REG_TMP
622		p2.To.Type = obj.TYPE_BRANCH
623
624		p3 := s.Prog(sc)
625		p3.From.Type = obj.TYPE_REG
626		p3.From.Reg = r2
627		p3.To.Type = obj.TYPE_MEM
628		p3.To.Reg = r0
629		p3.RegTo2 = riscv.REG_TMP
630
631		p4 := s.Prog(riscv.ABNE)
632		p4.From.Type = obj.TYPE_REG
633		p4.From.Reg = riscv.REG_TMP
634		p4.Reg = riscv.REG_ZERO
635		p4.To.Type = obj.TYPE_BRANCH
636		p4.To.SetTarget(p1)
637
638		p5 := s.Prog(riscv.AMOV)
639		p5.From.Type = obj.TYPE_CONST
640		p5.From.Offset = 1
641		p5.To.Type = obj.TYPE_REG
642		p5.To.Reg = out
643
644		p6 := s.Prog(obj.ANOP)
645		p2.To.SetTarget(p6)
646
647	case ssa.OpRISCV64LoweredAtomicAnd32, ssa.OpRISCV64LoweredAtomicOr32:
648		p := s.Prog(v.Op.Asm())
649		p.From.Type = obj.TYPE_REG
650		p.From.Reg = v.Args[1].Reg()
651		p.To.Type = obj.TYPE_MEM
652		p.To.Reg = v.Args[0].Reg()
653		p.RegTo2 = riscv.REG_ZERO
654
655	case ssa.OpRISCV64LoweredZero:
656		mov, sz := largestMove(v.AuxInt)
657
658		//	mov	ZERO, (Rarg0)
659		//	ADD	$sz, Rarg0
660		//	BGEU	Rarg1, Rarg0, -2(PC)
661
662		p := s.Prog(mov)
663		p.From.Type = obj.TYPE_REG
664		p.From.Reg = riscv.REG_ZERO
665		p.To.Type = obj.TYPE_MEM
666		p.To.Reg = v.Args[0].Reg()
667
668		p2 := s.Prog(riscv.AADD)
669		p2.From.Type = obj.TYPE_CONST
670		p2.From.Offset = sz
671		p2.To.Type = obj.TYPE_REG
672		p2.To.Reg = v.Args[0].Reg()
673
674		p3 := s.Prog(riscv.ABGEU)
675		p3.To.Type = obj.TYPE_BRANCH
676		p3.Reg = v.Args[0].Reg()
677		p3.From.Type = obj.TYPE_REG
678		p3.From.Reg = v.Args[1].Reg()
679		p3.To.SetTarget(p)
680
681	case ssa.OpRISCV64LoweredMove:
682		mov, sz := largestMove(v.AuxInt)
683
684		//	mov	(Rarg1), T2
685		//	mov	T2, (Rarg0)
686		//	ADD	$sz, Rarg0
687		//	ADD	$sz, Rarg1
688		//	BGEU	Rarg2, Rarg0, -4(PC)
689
690		p := s.Prog(mov)
691		p.From.Type = obj.TYPE_MEM
692		p.From.Reg = v.Args[1].Reg()
693		p.To.Type = obj.TYPE_REG
694		p.To.Reg = riscv.REG_T2
695
696		p2 := s.Prog(mov)
697		p2.From.Type = obj.TYPE_REG
698		p2.From.Reg = riscv.REG_T2
699		p2.To.Type = obj.TYPE_MEM
700		p2.To.Reg = v.Args[0].Reg()
701
702		p3 := s.Prog(riscv.AADD)
703		p3.From.Type = obj.TYPE_CONST
704		p3.From.Offset = sz
705		p3.To.Type = obj.TYPE_REG
706		p3.To.Reg = v.Args[0].Reg()
707
708		p4 := s.Prog(riscv.AADD)
709		p4.From.Type = obj.TYPE_CONST
710		p4.From.Offset = sz
711		p4.To.Type = obj.TYPE_REG
712		p4.To.Reg = v.Args[1].Reg()
713
714		p5 := s.Prog(riscv.ABGEU)
715		p5.To.Type = obj.TYPE_BRANCH
716		p5.Reg = v.Args[1].Reg()
717		p5.From.Type = obj.TYPE_REG
718		p5.From.Reg = v.Args[2].Reg()
719		p5.To.SetTarget(p)
720
721	case ssa.OpRISCV64LoweredNilCheck:
722		// Issue a load which will fault if arg is nil.
723		// TODO: optimizations. See arm and amd64 LoweredNilCheck.
724		p := s.Prog(riscv.AMOVB)
725		p.From.Type = obj.TYPE_MEM
726		p.From.Reg = v.Args[0].Reg()
727		ssagen.AddAux(&p.From, v)
728		p.To.Type = obj.TYPE_REG
729		p.To.Reg = riscv.REG_ZERO
730		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers
731			base.WarnfAt(v.Pos, "generated nil check")
732		}
733
734	case ssa.OpRISCV64LoweredGetClosurePtr:
735		// Closure pointer is S10 (riscv.REG_CTXT).
736		ssagen.CheckLoweredGetClosurePtr(v)
737
738	case ssa.OpRISCV64LoweredGetCallerSP:
739		// caller's SP is FixedFrameSize below the address of the first arg
740		p := s.Prog(riscv.AMOV)
741		p.From.Type = obj.TYPE_ADDR
742		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
743		p.From.Name = obj.NAME_PARAM
744		p.To.Type = obj.TYPE_REG
745		p.To.Reg = v.Reg()
746
747	case ssa.OpRISCV64LoweredGetCallerPC:
748		p := s.Prog(obj.AGETCALLERPC)
749		p.To.Type = obj.TYPE_REG
750		p.To.Reg = v.Reg()
751
752	case ssa.OpRISCV64DUFFZERO:
753		p := s.Prog(obj.ADUFFZERO)
754		p.To.Type = obj.TYPE_MEM
755		p.To.Name = obj.NAME_EXTERN
756		p.To.Sym = ir.Syms.Duffzero
757		p.To.Offset = v.AuxInt
758
759	case ssa.OpRISCV64DUFFCOPY:
760		p := s.Prog(obj.ADUFFCOPY)
761		p.To.Type = obj.TYPE_MEM
762		p.To.Name = obj.NAME_EXTERN
763		p.To.Sym = ir.Syms.Duffcopy
764		p.To.Offset = v.AuxInt
765
766	case ssa.OpRISCV64LoweredPubBarrier:
767		// FENCE
768		s.Prog(v.Op.Asm())
769
770	case ssa.OpRISCV64LoweredRound32F, ssa.OpRISCV64LoweredRound64F:
771		// input is already rounded
772
773	case ssa.OpClobber, ssa.OpClobberReg:
774		// TODO: implement for clobberdead experiment. Nop is ok for now.
775
776	default:
777		v.Fatalf("Unhandled op %v", v.Op)
778	}
779}
780
781var blockBranch = [...]obj.As{
782	ssa.BlockRISCV64BEQ:  riscv.ABEQ,
783	ssa.BlockRISCV64BEQZ: riscv.ABEQZ,
784	ssa.BlockRISCV64BGE:  riscv.ABGE,
785	ssa.BlockRISCV64BGEU: riscv.ABGEU,
786	ssa.BlockRISCV64BGEZ: riscv.ABGEZ,
787	ssa.BlockRISCV64BGTZ: riscv.ABGTZ,
788	ssa.BlockRISCV64BLEZ: riscv.ABLEZ,
789	ssa.BlockRISCV64BLT:  riscv.ABLT,
790	ssa.BlockRISCV64BLTU: riscv.ABLTU,
791	ssa.BlockRISCV64BLTZ: riscv.ABLTZ,
792	ssa.BlockRISCV64BNE:  riscv.ABNE,
793	ssa.BlockRISCV64BNEZ: riscv.ABNEZ,
794}
795
796func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
797	s.SetPos(b.Pos)
798
799	switch b.Kind {
800	case ssa.BlockDefer:
801		// defer returns in A0:
802		// 0 if we should continue executing
803		// 1 if we should jump to deferreturn call
804		p := s.Prog(riscv.ABNE)
805		p.To.Type = obj.TYPE_BRANCH
806		p.From.Type = obj.TYPE_REG
807		p.From.Reg = riscv.REG_ZERO
808		p.Reg = riscv.REG_A0
809		s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
810		if b.Succs[0].Block() != next {
811			p := s.Prog(obj.AJMP)
812			p.To.Type = obj.TYPE_BRANCH
813			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
814		}
815	case ssa.BlockPlain:
816		if b.Succs[0].Block() != next {
817			p := s.Prog(obj.AJMP)
818			p.To.Type = obj.TYPE_BRANCH
819			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
820		}
821	case ssa.BlockExit, ssa.BlockRetJmp:
822	case ssa.BlockRet:
823		s.Prog(obj.ARET)
824	case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ,
825		ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ,
826		ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:
827
828		as := blockBranch[b.Kind]
829		invAs := riscv.InvertBranch(as)
830
831		var p *obj.Prog
832		switch next {
833		case b.Succs[0].Block():
834			p = s.Br(invAs, b.Succs[1].Block())
835		case b.Succs[1].Block():
836			p = s.Br(as, b.Succs[0].Block())
837		default:
838			if b.Likely != ssa.BranchUnlikely {
839				p = s.Br(as, b.Succs[0].Block())
840				s.Br(obj.AJMP, b.Succs[1].Block())
841			} else {
842				p = s.Br(invAs, b.Succs[1].Block())
843				s.Br(obj.AJMP, b.Succs[0].Block())
844			}
845		}
846
847		p.From.Type = obj.TYPE_REG
848		switch b.Kind {
849		case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:
850			if b.NumControls() != 2 {
851				b.Fatalf("Unexpected number of controls (%d != 2): %s", b.NumControls(), b.LongString())
852			}
853			p.From.Reg = b.Controls[0].Reg()
854			p.Reg = b.Controls[1].Reg()
855
856		case ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ:
857			if b.NumControls() != 1 {
858				b.Fatalf("Unexpected number of controls (%d != 1): %s", b.NumControls(), b.LongString())
859			}
860			p.From.Reg = b.Controls[0].Reg()
861		}
862
863	default:
864		b.Fatalf("Unhandled block: %s", b.LongString())
865	}
866}
867
868func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
869	p := s.Prog(loadByType(t))
870	p.From.Type = obj.TYPE_MEM
871	p.From.Name = obj.NAME_AUTO
872	p.From.Sym = n.Linksym()
873	p.From.Offset = n.FrameOffset() + off
874	p.To.Type = obj.TYPE_REG
875	p.To.Reg = reg
876	return p
877}
878
879func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
880	p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
881	p.To.Name = obj.NAME_PARAM
882	p.To.Sym = n.Linksym()
883	p.Pos = p.Pos.WithNotStmt()
884	return p
885}
886