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/gc"
9	"cmd/compile/internal/ssa"
10	"cmd/compile/internal/types"
11	"cmd/internal/obj"
12	"cmd/internal/obj/riscv"
13)
14
15// ssaRegToReg maps ssa register numbers to obj register numbers.
16var ssaRegToReg = []int16{
17	riscv.REG_X0,
18	// X1 (LR): unused
19	riscv.REG_X2,
20	riscv.REG_X3,
21	riscv.REG_X4,
22	riscv.REG_X5,
23	riscv.REG_X6,
24	riscv.REG_X7,
25	riscv.REG_X8,
26	riscv.REG_X9,
27	riscv.REG_X10,
28	riscv.REG_X11,
29	riscv.REG_X12,
30	riscv.REG_X13,
31	riscv.REG_X14,
32	riscv.REG_X15,
33	riscv.REG_X16,
34	riscv.REG_X17,
35	riscv.REG_X18,
36	riscv.REG_X19,
37	riscv.REG_X20,
38	riscv.REG_X21,
39	riscv.REG_X22,
40	riscv.REG_X23,
41	riscv.REG_X24,
42	riscv.REG_X25,
43	riscv.REG_X26,
44	riscv.REG_X27,
45	riscv.REG_X28,
46	riscv.REG_X29,
47	riscv.REG_X30,
48	riscv.REG_X31,
49	riscv.REG_F0,
50	riscv.REG_F1,
51	riscv.REG_F2,
52	riscv.REG_F3,
53	riscv.REG_F4,
54	riscv.REG_F5,
55	riscv.REG_F6,
56	riscv.REG_F7,
57	riscv.REG_F8,
58	riscv.REG_F9,
59	riscv.REG_F10,
60	riscv.REG_F11,
61	riscv.REG_F12,
62	riscv.REG_F13,
63	riscv.REG_F14,
64	riscv.REG_F15,
65	riscv.REG_F16,
66	riscv.REG_F17,
67	riscv.REG_F18,
68	riscv.REG_F19,
69	riscv.REG_F20,
70	riscv.REG_F21,
71	riscv.REG_F22,
72	riscv.REG_F23,
73	riscv.REG_F24,
74	riscv.REG_F25,
75	riscv.REG_F26,
76	riscv.REG_F27,
77	riscv.REG_F28,
78	riscv.REG_F29,
79	riscv.REG_F30,
80	riscv.REG_F31,
81	0, // SB isn't a real register.  We fill an Addr.Reg field with 0 in this case.
82}
83
84func loadByType(t *types.Type) obj.As {
85	width := t.Size()
86
87	if t.IsFloat() {
88		switch width {
89		case 4:
90			return riscv.AMOVF
91		case 8:
92			return riscv.AMOVD
93		default:
94			gc.Fatalf("unknown float width for load %d in type %v", width, t)
95			return 0
96		}
97	}
98
99	switch width {
100	case 1:
101		if t.IsSigned() {
102			return riscv.AMOVB
103		} else {
104			return riscv.AMOVBU
105		}
106	case 2:
107		if t.IsSigned() {
108			return riscv.AMOVH
109		} else {
110			return riscv.AMOVHU
111		}
112	case 4:
113		if t.IsSigned() {
114			return riscv.AMOVW
115		} else {
116			return riscv.AMOVWU
117		}
118	case 8:
119		return riscv.AMOV
120	default:
121		gc.Fatalf("unknown width for load %d in type %v", width, t)
122		return 0
123	}
124}
125
126// storeByType returns the store instruction of the given type.
127func storeByType(t *types.Type) obj.As {
128	width := t.Size()
129
130	if t.IsFloat() {
131		switch width {
132		case 4:
133			return riscv.AMOVF
134		case 8:
135			return riscv.AMOVD
136		default:
137			gc.Fatalf("unknown float width for store %d in type %v", width, t)
138			return 0
139		}
140	}
141
142	switch width {
143	case 1:
144		return riscv.AMOVB
145	case 2:
146		return riscv.AMOVH
147	case 4:
148		return riscv.AMOVW
149	case 8:
150		return riscv.AMOV
151	default:
152		gc.Fatalf("unknown width for store %d in type %v", width, t)
153		return 0
154	}
155}
156
157// largestMove returns the largest move instruction possible and its size,
158// given the alignment of the total size of the move.
159//
160// e.g., a 16-byte move may use MOV, but an 11-byte move must use MOVB.
161//
162// Note that the moves may not be on naturally aligned addresses depending on
163// the source and destination.
164//
165// This matches the calculation in ssa.moveSize.
166func largestMove(alignment int64) (obj.As, int64) {
167	switch {
168	case alignment%8 == 0:
169		return riscv.AMOV, 8
170	case alignment%4 == 0:
171		return riscv.AMOVW, 4
172	case alignment%2 == 0:
173		return riscv.AMOVH, 2
174	default:
175		return riscv.AMOVB, 1
176	}
177}
178
179// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
180// RISC-V has no flags, so this is a no-op.
181func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {}
182
183func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
184	s.SetPos(v.Pos)
185
186	switch v.Op {
187	case ssa.OpInitMem:
188		// memory arg needs no code
189	case ssa.OpArg:
190		// input args need no code
191	case ssa.OpPhi:
192		gc.CheckLoweredPhi(v)
193	case ssa.OpCopy, ssa.OpRISCV64MOVconvert:
194		if v.Type.IsMemory() {
195			return
196		}
197		rs := v.Args[0].Reg()
198		rd := v.Reg()
199		if rs == rd {
200			return
201		}
202		as := riscv.AMOV
203		if v.Type.IsFloat() {
204			as = riscv.AMOVD
205		}
206		p := s.Prog(as)
207		p.From.Type = obj.TYPE_REG
208		p.From.Reg = rs
209		p.To.Type = obj.TYPE_REG
210		p.To.Reg = rd
211	case ssa.OpLoadReg:
212		if v.Type.IsFlags() {
213			v.Fatalf("load flags not implemented: %v", v.LongString())
214			return
215		}
216		p := s.Prog(loadByType(v.Type))
217		gc.AddrAuto(&p.From, v.Args[0])
218		p.To.Type = obj.TYPE_REG
219		p.To.Reg = v.Reg()
220	case ssa.OpStoreReg:
221		if v.Type.IsFlags() {
222			v.Fatalf("store flags not implemented: %v", v.LongString())
223			return
224		}
225		p := s.Prog(storeByType(v.Type))
226		p.From.Type = obj.TYPE_REG
227		p.From.Reg = v.Args[0].Reg()
228		gc.AddrAuto(&p.To, v)
229	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
230		// nothing to do
231	case ssa.OpRISCV64ADD, ssa.OpRISCV64SUB, ssa.OpRISCV64XOR, ssa.OpRISCV64OR, ssa.OpRISCV64AND,
232		ssa.OpRISCV64SLL, ssa.OpRISCV64SRA, ssa.OpRISCV64SRL,
233		ssa.OpRISCV64SLT, ssa.OpRISCV64SLTU, ssa.OpRISCV64MUL, ssa.OpRISCV64MULW, ssa.OpRISCV64MULH,
234		ssa.OpRISCV64MULHU, ssa.OpRISCV64DIV, ssa.OpRISCV64DIVU, ssa.OpRISCV64DIVW,
235		ssa.OpRISCV64DIVUW, ssa.OpRISCV64REM, ssa.OpRISCV64REMU, ssa.OpRISCV64REMW,
236		ssa.OpRISCV64REMUW,
237		ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS,
238		ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES,
239		ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD,
240		ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED:
241		r := v.Reg()
242		r1 := v.Args[0].Reg()
243		r2 := v.Args[1].Reg()
244		p := s.Prog(v.Op.Asm())
245		p.From.Type = obj.TYPE_REG
246		p.From.Reg = r2
247		p.Reg = r1
248		p.To.Type = obj.TYPE_REG
249		p.To.Reg = r
250	case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
251		ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX,
252		ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
253		ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD:
254		p := s.Prog(v.Op.Asm())
255		p.From.Type = obj.TYPE_REG
256		p.From.Reg = v.Args[0].Reg()
257		p.To.Type = obj.TYPE_REG
258		p.To.Reg = v.Reg()
259	case ssa.OpRISCV64ADDI, ssa.OpRISCV64XORI, ssa.OpRISCV64ORI, ssa.OpRISCV64ANDI,
260		ssa.OpRISCV64SLLI, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRLI, ssa.OpRISCV64SLTI,
261		ssa.OpRISCV64SLTIU:
262		p := s.Prog(v.Op.Asm())
263		p.From.Type = obj.TYPE_CONST
264		p.From.Offset = v.AuxInt
265		p.Reg = v.Args[0].Reg()
266		p.To.Type = obj.TYPE_REG
267		p.To.Reg = v.Reg()
268	case ssa.OpRISCV64MOVBconst, ssa.OpRISCV64MOVHconst, ssa.OpRISCV64MOVWconst, ssa.OpRISCV64MOVDconst:
269		p := s.Prog(v.Op.Asm())
270		p.From.Type = obj.TYPE_CONST
271		p.From.Offset = v.AuxInt
272		p.To.Type = obj.TYPE_REG
273		p.To.Reg = v.Reg()
274	case ssa.OpRISCV64MOVaddr:
275		p := s.Prog(v.Op.Asm())
276		p.From.Type = obj.TYPE_ADDR
277		p.To.Type = obj.TYPE_REG
278		p.To.Reg = v.Reg()
279
280		var wantreg string
281		// MOVW $sym+off(base), R
282		switch v.Aux.(type) {
283		default:
284			v.Fatalf("aux is of unknown type %T", v.Aux)
285		case *obj.LSym:
286			wantreg = "SB"
287			gc.AddAux(&p.From, v)
288		case *gc.Node:
289			wantreg = "SP"
290			gc.AddAux(&p.From, v)
291		case nil:
292			// No sym, just MOVW $off(SP), R
293			wantreg = "SP"
294			p.From.Reg = riscv.REG_SP
295			p.From.Offset = v.AuxInt
296		}
297		if reg := v.Args[0].RegName(); reg != wantreg {
298			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
299		}
300	case ssa.OpRISCV64MOVBload, ssa.OpRISCV64MOVHload, ssa.OpRISCV64MOVWload, ssa.OpRISCV64MOVDload,
301		ssa.OpRISCV64MOVBUload, ssa.OpRISCV64MOVHUload, ssa.OpRISCV64MOVWUload,
302		ssa.OpRISCV64FMOVWload, ssa.OpRISCV64FMOVDload:
303		p := s.Prog(v.Op.Asm())
304		p.From.Type = obj.TYPE_MEM
305		p.From.Reg = v.Args[0].Reg()
306		gc.AddAux(&p.From, v)
307		p.To.Type = obj.TYPE_REG
308		p.To.Reg = v.Reg()
309	case ssa.OpRISCV64MOVBstore, ssa.OpRISCV64MOVHstore, ssa.OpRISCV64MOVWstore, ssa.OpRISCV64MOVDstore,
310		ssa.OpRISCV64FMOVWstore, ssa.OpRISCV64FMOVDstore:
311		p := s.Prog(v.Op.Asm())
312		p.From.Type = obj.TYPE_REG
313		p.From.Reg = v.Args[1].Reg()
314		p.To.Type = obj.TYPE_MEM
315		p.To.Reg = v.Args[0].Reg()
316		gc.AddAux(&p.To, v)
317	case ssa.OpRISCV64SEQZ, ssa.OpRISCV64SNEZ:
318		p := s.Prog(v.Op.Asm())
319		p.From.Type = obj.TYPE_REG
320		p.From.Reg = v.Args[0].Reg()
321		p.To.Type = obj.TYPE_REG
322		p.To.Reg = v.Reg()
323	case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter:
324		s.Call(v)
325	case ssa.OpRISCV64LoweredWB:
326		p := s.Prog(obj.ACALL)
327		p.To.Type = obj.TYPE_MEM
328		p.To.Name = obj.NAME_EXTERN
329		p.To.Sym = v.Aux.(*obj.LSym)
330	case ssa.OpRISCV64LoweredPanicBoundsA, ssa.OpRISCV64LoweredPanicBoundsB, ssa.OpRISCV64LoweredPanicBoundsC:
331		p := s.Prog(obj.ACALL)
332		p.To.Type = obj.TYPE_MEM
333		p.To.Name = obj.NAME_EXTERN
334		p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
335		s.UseArgs(16) // space used in callee args area by assembly stubs
336	case ssa.OpRISCV64LoweredZero:
337		mov, sz := largestMove(v.AuxInt)
338
339		//	mov	ZERO, (Rarg0)
340		//	ADD	$sz, Rarg0
341		//	BGEU	Rarg1, Rarg0, -2(PC)
342
343		p := s.Prog(mov)
344		p.From.Type = obj.TYPE_REG
345		p.From.Reg = riscv.REG_ZERO
346		p.To.Type = obj.TYPE_MEM
347		p.To.Reg = v.Args[0].Reg()
348
349		p2 := s.Prog(riscv.AADD)
350		p2.From.Type = obj.TYPE_CONST
351		p2.From.Offset = sz
352		p2.To.Type = obj.TYPE_REG
353		p2.To.Reg = v.Args[0].Reg()
354
355		p3 := s.Prog(riscv.ABGEU)
356		p3.To.Type = obj.TYPE_BRANCH
357		p3.Reg = v.Args[0].Reg()
358		p3.From.Type = obj.TYPE_REG
359		p3.From.Reg = v.Args[1].Reg()
360		gc.Patch(p3, p)
361
362	case ssa.OpRISCV64LoweredMove:
363		mov, sz := largestMove(v.AuxInt)
364
365		//	mov	(Rarg1), T2
366		//	mov	T2, (Rarg0)
367		//	ADD	$sz, Rarg0
368		//	ADD	$sz, Rarg1
369		//	BGEU	Rarg2, Rarg0, -4(PC)
370
371		p := s.Prog(mov)
372		p.From.Type = obj.TYPE_MEM
373		p.From.Reg = v.Args[1].Reg()
374		p.To.Type = obj.TYPE_REG
375		p.To.Reg = riscv.REG_T2
376
377		p2 := s.Prog(mov)
378		p2.From.Type = obj.TYPE_REG
379		p2.From.Reg = riscv.REG_T2
380		p2.To.Type = obj.TYPE_MEM
381		p2.To.Reg = v.Args[0].Reg()
382
383		p3 := s.Prog(riscv.AADD)
384		p3.From.Type = obj.TYPE_CONST
385		p3.From.Offset = sz
386		p3.To.Type = obj.TYPE_REG
387		p3.To.Reg = v.Args[0].Reg()
388
389		p4 := s.Prog(riscv.AADD)
390		p4.From.Type = obj.TYPE_CONST
391		p4.From.Offset = sz
392		p4.To.Type = obj.TYPE_REG
393		p4.To.Reg = v.Args[1].Reg()
394
395		p5 := s.Prog(riscv.ABGEU)
396		p5.To.Type = obj.TYPE_BRANCH
397		p5.Reg = v.Args[1].Reg()
398		p5.From.Type = obj.TYPE_REG
399		p5.From.Reg = v.Args[2].Reg()
400		gc.Patch(p5, p)
401
402	case ssa.OpRISCV64LoweredNilCheck:
403		// Issue a load which will fault if arg is nil.
404		// TODO: optimizations. See arm and amd64 LoweredNilCheck.
405		p := s.Prog(riscv.AMOVB)
406		p.From.Type = obj.TYPE_MEM
407		p.From.Reg = v.Args[0].Reg()
408		gc.AddAux(&p.From, v)
409		p.To.Type = obj.TYPE_REG
410		p.To.Reg = riscv.REG_ZERO
411		if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers
412			gc.Warnl(v.Pos, "generated nil check")
413		}
414
415	case ssa.OpRISCV64LoweredGetClosurePtr:
416		// Closure pointer is S4 (riscv.REG_CTXT).
417		gc.CheckLoweredGetClosurePtr(v)
418
419	case ssa.OpRISCV64LoweredGetCallerSP:
420		// caller's SP is FixedFrameSize below the address of the first arg
421		p := s.Prog(riscv.AMOV)
422		p.From.Type = obj.TYPE_ADDR
423		p.From.Offset = -gc.Ctxt.FixedFrameSize()
424		p.From.Name = obj.NAME_PARAM
425		p.To.Type = obj.TYPE_REG
426		p.To.Reg = v.Reg()
427
428	case ssa.OpRISCV64LoweredGetCallerPC:
429		p := s.Prog(obj.AGETCALLERPC)
430		p.To.Type = obj.TYPE_REG
431		p.To.Reg = v.Reg()
432
433	default:
434		v.Fatalf("Unhandled op %v", v.Op)
435	}
436}
437
438func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
439	s.SetPos(b.Pos)
440
441	switch b.Kind {
442	case ssa.BlockDefer:
443		// defer returns in A0:
444		// 0 if we should continue executing
445		// 1 if we should jump to deferreturn call
446		p := s.Prog(riscv.ABNE)
447		p.To.Type = obj.TYPE_BRANCH
448		p.From.Type = obj.TYPE_REG
449		p.From.Reg = riscv.REG_ZERO
450		p.Reg = riscv.REG_A0
451		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
452		if b.Succs[0].Block() != next {
453			p := s.Prog(obj.AJMP)
454			p.To.Type = obj.TYPE_BRANCH
455			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
456		}
457	case ssa.BlockPlain:
458		if b.Succs[0].Block() != next {
459			p := s.Prog(obj.AJMP)
460			p.To.Type = obj.TYPE_BRANCH
461			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
462		}
463	case ssa.BlockExit:
464	case ssa.BlockRet:
465		s.Prog(obj.ARET)
466	case ssa.BlockRetJmp:
467		p := s.Prog(obj.AJMP)
468		p.To.Type = obj.TYPE_MEM
469		p.To.Name = obj.NAME_EXTERN
470		p.To.Sym = b.Aux.(*obj.LSym)
471	case ssa.BlockRISCV64BNE:
472		var p *obj.Prog
473		switch next {
474		case b.Succs[0].Block():
475			p = s.Br(riscv.ABNE, b.Succs[1].Block())
476			p.As = riscv.InvertBranch(p.As)
477		case b.Succs[1].Block():
478			p = s.Br(riscv.ABNE, b.Succs[0].Block())
479		default:
480			if b.Likely != ssa.BranchUnlikely {
481				p = s.Br(riscv.ABNE, b.Succs[0].Block())
482				s.Br(obj.AJMP, b.Succs[1].Block())
483			} else {
484				p = s.Br(riscv.ABNE, b.Succs[1].Block())
485				p.As = riscv.InvertBranch(p.As)
486				s.Br(obj.AJMP, b.Succs[0].Block())
487			}
488		}
489		p.Reg = b.Controls[0].Reg()
490		p.From.Type = obj.TYPE_REG
491		p.From.Reg = riscv.REG_ZERO
492
493	default:
494		b.Fatalf("Unhandled block: %s", b.LongString())
495	}
496}
497