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 mips
6
7import (
8	"math"
9
10	"cmd/compile/internal/base"
11	"cmd/compile/internal/ir"
12	"cmd/compile/internal/logopt"
13	"cmd/compile/internal/ssa"
14	"cmd/compile/internal/ssagen"
15	"cmd/compile/internal/types"
16	"cmd/internal/obj"
17	"cmd/internal/obj/mips"
18)
19
20// isFPreg reports whether r is an FP register.
21func isFPreg(r int16) bool {
22	return mips.REG_F0 <= r && r <= mips.REG_F31
23}
24
25// isHILO reports whether r is HI or LO register.
26func isHILO(r int16) bool {
27	return r == mips.REG_HI || r == mips.REG_LO
28}
29
30// loadByType returns the load instruction of the given type.
31func loadByType(t *types.Type, r int16) obj.As {
32	if isFPreg(r) {
33		if t.Size() == 4 { // float32 or int32
34			return mips.AMOVF
35		} else { // float64 or int64
36			return mips.AMOVD
37		}
38	} else {
39		switch t.Size() {
40		case 1:
41			if t.IsSigned() {
42				return mips.AMOVB
43			} else {
44				return mips.AMOVBU
45			}
46		case 2:
47			if t.IsSigned() {
48				return mips.AMOVH
49			} else {
50				return mips.AMOVHU
51			}
52		case 4:
53			return mips.AMOVW
54		}
55	}
56	panic("bad load type")
57}
58
59// storeByType returns the store instruction of the given type.
60func storeByType(t *types.Type, r int16) obj.As {
61	if isFPreg(r) {
62		if t.Size() == 4 { // float32 or int32
63			return mips.AMOVF
64		} else { // float64 or int64
65			return mips.AMOVD
66		}
67	} else {
68		switch t.Size() {
69		case 1:
70			return mips.AMOVB
71		case 2:
72			return mips.AMOVH
73		case 4:
74			return mips.AMOVW
75		}
76	}
77	panic("bad store type")
78}
79
80func ssaGenValue(s *ssagen.State, v *ssa.Value) {
81	switch v.Op {
82	case ssa.OpCopy, ssa.OpMIPSMOVWreg:
83		t := v.Type
84		if t.IsMemory() {
85			return
86		}
87		x := v.Args[0].Reg()
88		y := v.Reg()
89		if x == y {
90			return
91		}
92		as := mips.AMOVW
93		if isFPreg(x) && isFPreg(y) {
94			as = mips.AMOVF
95			if t.Size() == 8 {
96				as = mips.AMOVD
97			}
98		}
99
100		p := s.Prog(as)
101		p.From.Type = obj.TYPE_REG
102		p.From.Reg = x
103		p.To.Type = obj.TYPE_REG
104		p.To.Reg = y
105		if isHILO(x) && isHILO(y) || isHILO(x) && isFPreg(y) || isFPreg(x) && isHILO(y) {
106			// cannot move between special registers, use TMP as intermediate
107			p.To.Reg = mips.REGTMP
108			p = s.Prog(mips.AMOVW)
109			p.From.Type = obj.TYPE_REG
110			p.From.Reg = mips.REGTMP
111			p.To.Type = obj.TYPE_REG
112			p.To.Reg = y
113		}
114	case ssa.OpMIPSMOVWnop:
115		// nothing to do
116	case ssa.OpLoadReg:
117		if v.Type.IsFlags() {
118			v.Fatalf("load flags not implemented: %v", v.LongString())
119			return
120		}
121		r := v.Reg()
122		p := s.Prog(loadByType(v.Type, r))
123		ssagen.AddrAuto(&p.From, v.Args[0])
124		p.To.Type = obj.TYPE_REG
125		p.To.Reg = r
126		if isHILO(r) {
127			// cannot directly load, load to TMP and move
128			p.To.Reg = mips.REGTMP
129			p = s.Prog(mips.AMOVW)
130			p.From.Type = obj.TYPE_REG
131			p.From.Reg = mips.REGTMP
132			p.To.Type = obj.TYPE_REG
133			p.To.Reg = r
134		}
135	case ssa.OpStoreReg:
136		if v.Type.IsFlags() {
137			v.Fatalf("store flags not implemented: %v", v.LongString())
138			return
139		}
140		r := v.Args[0].Reg()
141		if isHILO(r) {
142			// cannot directly store, move to TMP and store
143			p := s.Prog(mips.AMOVW)
144			p.From.Type = obj.TYPE_REG
145			p.From.Reg = r
146			p.To.Type = obj.TYPE_REG
147			p.To.Reg = mips.REGTMP
148			r = mips.REGTMP
149		}
150		p := s.Prog(storeByType(v.Type, r))
151		p.From.Type = obj.TYPE_REG
152		p.From.Reg = r
153		ssagen.AddrAuto(&p.To, v)
154	case ssa.OpMIPSADD,
155		ssa.OpMIPSSUB,
156		ssa.OpMIPSAND,
157		ssa.OpMIPSOR,
158		ssa.OpMIPSXOR,
159		ssa.OpMIPSNOR,
160		ssa.OpMIPSSLL,
161		ssa.OpMIPSSRL,
162		ssa.OpMIPSSRA,
163		ssa.OpMIPSADDF,
164		ssa.OpMIPSADDD,
165		ssa.OpMIPSSUBF,
166		ssa.OpMIPSSUBD,
167		ssa.OpMIPSMULF,
168		ssa.OpMIPSMULD,
169		ssa.OpMIPSDIVF,
170		ssa.OpMIPSDIVD,
171		ssa.OpMIPSMUL:
172		p := s.Prog(v.Op.Asm())
173		p.From.Type = obj.TYPE_REG
174		p.From.Reg = v.Args[1].Reg()
175		p.Reg = v.Args[0].Reg()
176		p.To.Type = obj.TYPE_REG
177		p.To.Reg = v.Reg()
178	case ssa.OpMIPSSGT,
179		ssa.OpMIPSSGTU:
180		p := s.Prog(v.Op.Asm())
181		p.From.Type = obj.TYPE_REG
182		p.From.Reg = v.Args[0].Reg()
183		p.Reg = v.Args[1].Reg()
184		p.To.Type = obj.TYPE_REG
185		p.To.Reg = v.Reg()
186	case ssa.OpMIPSSGTzero,
187		ssa.OpMIPSSGTUzero:
188		p := s.Prog(v.Op.Asm())
189		p.From.Type = obj.TYPE_REG
190		p.From.Reg = v.Args[0].Reg()
191		p.Reg = mips.REGZERO
192		p.To.Type = obj.TYPE_REG
193		p.To.Reg = v.Reg()
194	case ssa.OpMIPSADDconst,
195		ssa.OpMIPSSUBconst,
196		ssa.OpMIPSANDconst,
197		ssa.OpMIPSORconst,
198		ssa.OpMIPSXORconst,
199		ssa.OpMIPSNORconst,
200		ssa.OpMIPSSLLconst,
201		ssa.OpMIPSSRLconst,
202		ssa.OpMIPSSRAconst,
203		ssa.OpMIPSSGTconst,
204		ssa.OpMIPSSGTUconst:
205		p := s.Prog(v.Op.Asm())
206		p.From.Type = obj.TYPE_CONST
207		p.From.Offset = v.AuxInt
208		p.Reg = v.Args[0].Reg()
209		p.To.Type = obj.TYPE_REG
210		p.To.Reg = v.Reg()
211	case ssa.OpMIPSMULT,
212		ssa.OpMIPSMULTU,
213		ssa.OpMIPSDIV,
214		ssa.OpMIPSDIVU:
215		// result in hi,lo
216		p := s.Prog(v.Op.Asm())
217		p.From.Type = obj.TYPE_REG
218		p.From.Reg = v.Args[1].Reg()
219		p.Reg = v.Args[0].Reg()
220	case ssa.OpMIPSMOVWconst:
221		r := v.Reg()
222		p := s.Prog(v.Op.Asm())
223		p.From.Type = obj.TYPE_CONST
224		p.From.Offset = v.AuxInt
225		p.To.Type = obj.TYPE_REG
226		p.To.Reg = r
227		if isFPreg(r) || isHILO(r) {
228			// cannot move into FP or special registers, use TMP as intermediate
229			p.To.Reg = mips.REGTMP
230			p = s.Prog(mips.AMOVW)
231			p.From.Type = obj.TYPE_REG
232			p.From.Reg = mips.REGTMP
233			p.To.Type = obj.TYPE_REG
234			p.To.Reg = r
235		}
236	case ssa.OpMIPSMOVFconst,
237		ssa.OpMIPSMOVDconst:
238		p := s.Prog(v.Op.Asm())
239		p.From.Type = obj.TYPE_FCONST
240		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
241		p.To.Type = obj.TYPE_REG
242		p.To.Reg = v.Reg()
243	case ssa.OpMIPSCMOVZ:
244		p := s.Prog(v.Op.Asm())
245		p.From.Type = obj.TYPE_REG
246		p.From.Reg = v.Args[2].Reg()
247		p.Reg = v.Args[1].Reg()
248		p.To.Type = obj.TYPE_REG
249		p.To.Reg = v.Reg()
250	case ssa.OpMIPSCMOVZzero:
251		p := s.Prog(v.Op.Asm())
252		p.From.Type = obj.TYPE_REG
253		p.From.Reg = v.Args[1].Reg()
254		p.Reg = mips.REGZERO
255		p.To.Type = obj.TYPE_REG
256		p.To.Reg = v.Reg()
257	case ssa.OpMIPSCMPEQF,
258		ssa.OpMIPSCMPEQD,
259		ssa.OpMIPSCMPGEF,
260		ssa.OpMIPSCMPGED,
261		ssa.OpMIPSCMPGTF,
262		ssa.OpMIPSCMPGTD:
263		p := s.Prog(v.Op.Asm())
264		p.From.Type = obj.TYPE_REG
265		p.From.Reg = v.Args[0].Reg()
266		p.Reg = v.Args[1].Reg()
267	case ssa.OpMIPSMOVWaddr:
268		p := s.Prog(mips.AMOVW)
269		p.From.Type = obj.TYPE_ADDR
270		p.From.Reg = v.Args[0].Reg()
271		var wantreg string
272		// MOVW $sym+off(base), R
273		// the assembler expands it as the following:
274		// - base is SP: add constant offset to SP (R29)
275		//               when constant is large, tmp register (R23) may be used
276		// - base is SB: load external address with relocation
277		switch v.Aux.(type) {
278		default:
279			v.Fatalf("aux is of unknown type %T", v.Aux)
280		case *obj.LSym:
281			wantreg = "SB"
282			ssagen.AddAux(&p.From, v)
283		case *ir.Name:
284			wantreg = "SP"
285			ssagen.AddAux(&p.From, v)
286		case nil:
287			// No sym, just MOVW $off(SP), R
288			wantreg = "SP"
289			p.From.Offset = v.AuxInt
290		}
291		if reg := v.Args[0].RegName(); reg != wantreg {
292			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
293		}
294		p.To.Type = obj.TYPE_REG
295		p.To.Reg = v.Reg()
296	case ssa.OpMIPSMOVBload,
297		ssa.OpMIPSMOVBUload,
298		ssa.OpMIPSMOVHload,
299		ssa.OpMIPSMOVHUload,
300		ssa.OpMIPSMOVWload,
301		ssa.OpMIPSMOVFload,
302		ssa.OpMIPSMOVDload:
303		p := s.Prog(v.Op.Asm())
304		p.From.Type = obj.TYPE_MEM
305		p.From.Reg = v.Args[0].Reg()
306		ssagen.AddAux(&p.From, v)
307		p.To.Type = obj.TYPE_REG
308		p.To.Reg = v.Reg()
309	case ssa.OpMIPSMOVBstore,
310		ssa.OpMIPSMOVHstore,
311		ssa.OpMIPSMOVWstore,
312		ssa.OpMIPSMOVFstore,
313		ssa.OpMIPSMOVDstore:
314		p := s.Prog(v.Op.Asm())
315		p.From.Type = obj.TYPE_REG
316		p.From.Reg = v.Args[1].Reg()
317		p.To.Type = obj.TYPE_MEM
318		p.To.Reg = v.Args[0].Reg()
319		ssagen.AddAux(&p.To, v)
320	case ssa.OpMIPSMOVBstorezero,
321		ssa.OpMIPSMOVHstorezero,
322		ssa.OpMIPSMOVWstorezero:
323		p := s.Prog(v.Op.Asm())
324		p.From.Type = obj.TYPE_REG
325		p.From.Reg = mips.REGZERO
326		p.To.Type = obj.TYPE_MEM
327		p.To.Reg = v.Args[0].Reg()
328		ssagen.AddAux(&p.To, v)
329	case ssa.OpMIPSMOVBreg,
330		ssa.OpMIPSMOVBUreg,
331		ssa.OpMIPSMOVHreg,
332		ssa.OpMIPSMOVHUreg:
333		a := v.Args[0]
334		for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPSMOVWreg || a.Op == ssa.OpMIPSMOVWnop {
335			a = a.Args[0]
336		}
337		if a.Op == ssa.OpLoadReg {
338			t := a.Type
339			switch {
340			case v.Op == ssa.OpMIPSMOVBreg && t.Size() == 1 && t.IsSigned(),
341				v.Op == ssa.OpMIPSMOVBUreg && t.Size() == 1 && !t.IsSigned(),
342				v.Op == ssa.OpMIPSMOVHreg && t.Size() == 2 && t.IsSigned(),
343				v.Op == ssa.OpMIPSMOVHUreg && t.Size() == 2 && !t.IsSigned():
344				// arg is a proper-typed load, already zero/sign-extended, don't extend again
345				if v.Reg() == v.Args[0].Reg() {
346					return
347				}
348				p := s.Prog(mips.AMOVW)
349				p.From.Type = obj.TYPE_REG
350				p.From.Reg = v.Args[0].Reg()
351				p.To.Type = obj.TYPE_REG
352				p.To.Reg = v.Reg()
353				return
354			default:
355			}
356		}
357		fallthrough
358	case ssa.OpMIPSMOVWF,
359		ssa.OpMIPSMOVWD,
360		ssa.OpMIPSTRUNCFW,
361		ssa.OpMIPSTRUNCDW,
362		ssa.OpMIPSMOVFD,
363		ssa.OpMIPSMOVDF,
364		ssa.OpMIPSMOVWfpgp,
365		ssa.OpMIPSMOVWgpfp,
366		ssa.OpMIPSNEGF,
367		ssa.OpMIPSNEGD,
368		ssa.OpMIPSABSD,
369		ssa.OpMIPSSQRTF,
370		ssa.OpMIPSSQRTD,
371		ssa.OpMIPSCLZ:
372		p := s.Prog(v.Op.Asm())
373		p.From.Type = obj.TYPE_REG
374		p.From.Reg = v.Args[0].Reg()
375		p.To.Type = obj.TYPE_REG
376		p.To.Reg = v.Reg()
377	case ssa.OpMIPSNEG:
378		// SUB from REGZERO
379		p := s.Prog(mips.ASUBU)
380		p.From.Type = obj.TYPE_REG
381		p.From.Reg = v.Args[0].Reg()
382		p.Reg = mips.REGZERO
383		p.To.Type = obj.TYPE_REG
384		p.To.Reg = v.Reg()
385	case ssa.OpMIPSLoweredZero:
386		// SUBU	$4, R1
387		// MOVW	R0, 4(R1)
388		// ADDU	$4, R1
389		// BNE	Rarg1, R1, -2(PC)
390		// arg1 is the address of the last element to zero
391		var sz int64
392		var mov obj.As
393		switch {
394		case v.AuxInt%4 == 0:
395			sz = 4
396			mov = mips.AMOVW
397		case v.AuxInt%2 == 0:
398			sz = 2
399			mov = mips.AMOVH
400		default:
401			sz = 1
402			mov = mips.AMOVB
403		}
404		p := s.Prog(mips.ASUBU)
405		p.From.Type = obj.TYPE_CONST
406		p.From.Offset = sz
407		p.To.Type = obj.TYPE_REG
408		p.To.Reg = mips.REG_R1
409		p2 := s.Prog(mov)
410		p2.From.Type = obj.TYPE_REG
411		p2.From.Reg = mips.REGZERO
412		p2.To.Type = obj.TYPE_MEM
413		p2.To.Reg = mips.REG_R1
414		p2.To.Offset = sz
415		p3 := s.Prog(mips.AADDU)
416		p3.From.Type = obj.TYPE_CONST
417		p3.From.Offset = sz
418		p3.To.Type = obj.TYPE_REG
419		p3.To.Reg = mips.REG_R1
420		p4 := s.Prog(mips.ABNE)
421		p4.From.Type = obj.TYPE_REG
422		p4.From.Reg = v.Args[1].Reg()
423		p4.Reg = mips.REG_R1
424		p4.To.Type = obj.TYPE_BRANCH
425		p4.To.SetTarget(p2)
426	case ssa.OpMIPSLoweredMove:
427		// SUBU	$4, R1
428		// MOVW	4(R1), Rtmp
429		// MOVW	Rtmp, (R2)
430		// ADDU	$4, R1
431		// ADDU	$4, R2
432		// BNE	Rarg2, R1, -4(PC)
433		// arg2 is the address of the last element of src
434		var sz int64
435		var mov obj.As
436		switch {
437		case v.AuxInt%4 == 0:
438			sz = 4
439			mov = mips.AMOVW
440		case v.AuxInt%2 == 0:
441			sz = 2
442			mov = mips.AMOVH
443		default:
444			sz = 1
445			mov = mips.AMOVB
446		}
447		p := s.Prog(mips.ASUBU)
448		p.From.Type = obj.TYPE_CONST
449		p.From.Offset = sz
450		p.To.Type = obj.TYPE_REG
451		p.To.Reg = mips.REG_R1
452		p2 := s.Prog(mov)
453		p2.From.Type = obj.TYPE_MEM
454		p2.From.Reg = mips.REG_R1
455		p2.From.Offset = sz
456		p2.To.Type = obj.TYPE_REG
457		p2.To.Reg = mips.REGTMP
458		p3 := s.Prog(mov)
459		p3.From.Type = obj.TYPE_REG
460		p3.From.Reg = mips.REGTMP
461		p3.To.Type = obj.TYPE_MEM
462		p3.To.Reg = mips.REG_R2
463		p4 := s.Prog(mips.AADDU)
464		p4.From.Type = obj.TYPE_CONST
465		p4.From.Offset = sz
466		p4.To.Type = obj.TYPE_REG
467		p4.To.Reg = mips.REG_R1
468		p5 := s.Prog(mips.AADDU)
469		p5.From.Type = obj.TYPE_CONST
470		p5.From.Offset = sz
471		p5.To.Type = obj.TYPE_REG
472		p5.To.Reg = mips.REG_R2
473		p6 := s.Prog(mips.ABNE)
474		p6.From.Type = obj.TYPE_REG
475		p6.From.Reg = v.Args[2].Reg()
476		p6.Reg = mips.REG_R1
477		p6.To.Type = obj.TYPE_BRANCH
478		p6.To.SetTarget(p2)
479	case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter:
480		s.Call(v)
481	case ssa.OpMIPSCALLtail:
482		s.TailCall(v)
483	case ssa.OpMIPSLoweredWB:
484		p := s.Prog(obj.ACALL)
485		p.To.Type = obj.TYPE_MEM
486		p.To.Name = obj.NAME_EXTERN
487		// AuxInt encodes how many buffer entries we need.
488		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
489	case ssa.OpMIPSLoweredPanicBoundsA, ssa.OpMIPSLoweredPanicBoundsB, ssa.OpMIPSLoweredPanicBoundsC:
490		p := s.Prog(obj.ACALL)
491		p.To.Type = obj.TYPE_MEM
492		p.To.Name = obj.NAME_EXTERN
493		p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
494		s.UseArgs(8) // space used in callee args area by assembly stubs
495	case ssa.OpMIPSLoweredPanicExtendA, ssa.OpMIPSLoweredPanicExtendB, ssa.OpMIPSLoweredPanicExtendC:
496		p := s.Prog(obj.ACALL)
497		p.To.Type = obj.TYPE_MEM
498		p.To.Name = obj.NAME_EXTERN
499		p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
500		s.UseArgs(12) // space used in callee args area by assembly stubs
501	case ssa.OpMIPSLoweredAtomicLoad8,
502		ssa.OpMIPSLoweredAtomicLoad32:
503		s.Prog(mips.ASYNC)
504
505		var op obj.As
506		switch v.Op {
507		case ssa.OpMIPSLoweredAtomicLoad8:
508			op = mips.AMOVB
509		case ssa.OpMIPSLoweredAtomicLoad32:
510			op = mips.AMOVW
511		}
512		p := s.Prog(op)
513		p.From.Type = obj.TYPE_MEM
514		p.From.Reg = v.Args[0].Reg()
515		p.To.Type = obj.TYPE_REG
516		p.To.Reg = v.Reg0()
517
518		s.Prog(mips.ASYNC)
519	case ssa.OpMIPSLoweredAtomicStore8,
520		ssa.OpMIPSLoweredAtomicStore32:
521		s.Prog(mips.ASYNC)
522
523		var op obj.As
524		switch v.Op {
525		case ssa.OpMIPSLoweredAtomicStore8:
526			op = mips.AMOVB
527		case ssa.OpMIPSLoweredAtomicStore32:
528			op = mips.AMOVW
529		}
530		p := s.Prog(op)
531		p.From.Type = obj.TYPE_REG
532		p.From.Reg = v.Args[1].Reg()
533		p.To.Type = obj.TYPE_MEM
534		p.To.Reg = v.Args[0].Reg()
535
536		s.Prog(mips.ASYNC)
537	case ssa.OpMIPSLoweredAtomicStorezero:
538		s.Prog(mips.ASYNC)
539
540		p := s.Prog(mips.AMOVW)
541		p.From.Type = obj.TYPE_REG
542		p.From.Reg = mips.REGZERO
543		p.To.Type = obj.TYPE_MEM
544		p.To.Reg = v.Args[0].Reg()
545
546		s.Prog(mips.ASYNC)
547	case ssa.OpMIPSLoweredAtomicExchange:
548		// SYNC
549		// MOVW Rarg1, Rtmp
550		// LL	(Rarg0), Rout
551		// SC	Rtmp, (Rarg0)
552		// BEQ	Rtmp, -3(PC)
553		// SYNC
554		s.Prog(mips.ASYNC)
555
556		p := s.Prog(mips.AMOVW)
557		p.From.Type = obj.TYPE_REG
558		p.From.Reg = v.Args[1].Reg()
559		p.To.Type = obj.TYPE_REG
560		p.To.Reg = mips.REGTMP
561
562		p1 := s.Prog(mips.ALL)
563		p1.From.Type = obj.TYPE_MEM
564		p1.From.Reg = v.Args[0].Reg()
565		p1.To.Type = obj.TYPE_REG
566		p1.To.Reg = v.Reg0()
567
568		p2 := s.Prog(mips.ASC)
569		p2.From.Type = obj.TYPE_REG
570		p2.From.Reg = mips.REGTMP
571		p2.To.Type = obj.TYPE_MEM
572		p2.To.Reg = v.Args[0].Reg()
573
574		p3 := s.Prog(mips.ABEQ)
575		p3.From.Type = obj.TYPE_REG
576		p3.From.Reg = mips.REGTMP
577		p3.To.Type = obj.TYPE_BRANCH
578		p3.To.SetTarget(p)
579
580		s.Prog(mips.ASYNC)
581	case ssa.OpMIPSLoweredAtomicAdd:
582		// SYNC
583		// LL	(Rarg0), Rout
584		// ADDU Rarg1, Rout, Rtmp
585		// SC	Rtmp, (Rarg0)
586		// BEQ	Rtmp, -3(PC)
587		// SYNC
588		// ADDU Rarg1, Rout
589		s.Prog(mips.ASYNC)
590
591		p := s.Prog(mips.ALL)
592		p.From.Type = obj.TYPE_MEM
593		p.From.Reg = v.Args[0].Reg()
594		p.To.Type = obj.TYPE_REG
595		p.To.Reg = v.Reg0()
596
597		p1 := s.Prog(mips.AADDU)
598		p1.From.Type = obj.TYPE_REG
599		p1.From.Reg = v.Args[1].Reg()
600		p1.Reg = v.Reg0()
601		p1.To.Type = obj.TYPE_REG
602		p1.To.Reg = mips.REGTMP
603
604		p2 := s.Prog(mips.ASC)
605		p2.From.Type = obj.TYPE_REG
606		p2.From.Reg = mips.REGTMP
607		p2.To.Type = obj.TYPE_MEM
608		p2.To.Reg = v.Args[0].Reg()
609
610		p3 := s.Prog(mips.ABEQ)
611		p3.From.Type = obj.TYPE_REG
612		p3.From.Reg = mips.REGTMP
613		p3.To.Type = obj.TYPE_BRANCH
614		p3.To.SetTarget(p)
615
616		s.Prog(mips.ASYNC)
617
618		p4 := s.Prog(mips.AADDU)
619		p4.From.Type = obj.TYPE_REG
620		p4.From.Reg = v.Args[1].Reg()
621		p4.Reg = v.Reg0()
622		p4.To.Type = obj.TYPE_REG
623		p4.To.Reg = v.Reg0()
624
625	case ssa.OpMIPSLoweredAtomicAddconst:
626		// SYNC
627		// LL	(Rarg0), Rout
628		// ADDU $auxInt, Rout, Rtmp
629		// SC	Rtmp, (Rarg0)
630		// BEQ	Rtmp, -3(PC)
631		// SYNC
632		// ADDU $auxInt, Rout
633		s.Prog(mips.ASYNC)
634
635		p := s.Prog(mips.ALL)
636		p.From.Type = obj.TYPE_MEM
637		p.From.Reg = v.Args[0].Reg()
638		p.To.Type = obj.TYPE_REG
639		p.To.Reg = v.Reg0()
640
641		p1 := s.Prog(mips.AADDU)
642		p1.From.Type = obj.TYPE_CONST
643		p1.From.Offset = v.AuxInt
644		p1.Reg = v.Reg0()
645		p1.To.Type = obj.TYPE_REG
646		p1.To.Reg = mips.REGTMP
647
648		p2 := s.Prog(mips.ASC)
649		p2.From.Type = obj.TYPE_REG
650		p2.From.Reg = mips.REGTMP
651		p2.To.Type = obj.TYPE_MEM
652		p2.To.Reg = v.Args[0].Reg()
653
654		p3 := s.Prog(mips.ABEQ)
655		p3.From.Type = obj.TYPE_REG
656		p3.From.Reg = mips.REGTMP
657		p3.To.Type = obj.TYPE_BRANCH
658		p3.To.SetTarget(p)
659
660		s.Prog(mips.ASYNC)
661
662		p4 := s.Prog(mips.AADDU)
663		p4.From.Type = obj.TYPE_CONST
664		p4.From.Offset = v.AuxInt
665		p4.Reg = v.Reg0()
666		p4.To.Type = obj.TYPE_REG
667		p4.To.Reg = v.Reg0()
668
669	case ssa.OpMIPSLoweredAtomicAnd,
670		ssa.OpMIPSLoweredAtomicOr:
671		// SYNC
672		// LL	(Rarg0), Rtmp
673		// AND/OR	Rarg1, Rtmp
674		// SC	Rtmp, (Rarg0)
675		// BEQ	Rtmp, -3(PC)
676		// SYNC
677		s.Prog(mips.ASYNC)
678
679		p := s.Prog(mips.ALL)
680		p.From.Type = obj.TYPE_MEM
681		p.From.Reg = v.Args[0].Reg()
682		p.To.Type = obj.TYPE_REG
683		p.To.Reg = mips.REGTMP
684
685		p1 := s.Prog(v.Op.Asm())
686		p1.From.Type = obj.TYPE_REG
687		p1.From.Reg = v.Args[1].Reg()
688		p1.Reg = mips.REGTMP
689		p1.To.Type = obj.TYPE_REG
690		p1.To.Reg = mips.REGTMP
691
692		p2 := s.Prog(mips.ASC)
693		p2.From.Type = obj.TYPE_REG
694		p2.From.Reg = mips.REGTMP
695		p2.To.Type = obj.TYPE_MEM
696		p2.To.Reg = v.Args[0].Reg()
697
698		p3 := s.Prog(mips.ABEQ)
699		p3.From.Type = obj.TYPE_REG
700		p3.From.Reg = mips.REGTMP
701		p3.To.Type = obj.TYPE_BRANCH
702		p3.To.SetTarget(p)
703
704		s.Prog(mips.ASYNC)
705
706	case ssa.OpMIPSLoweredAtomicCas:
707		// MOVW $0, Rout
708		// SYNC
709		// LL	(Rarg0), Rtmp
710		// BNE	Rtmp, Rarg1, 4(PC)
711		// MOVW Rarg2, Rout
712		// SC	Rout, (Rarg0)
713		// BEQ	Rout, -4(PC)
714		// SYNC
715		p := s.Prog(mips.AMOVW)
716		p.From.Type = obj.TYPE_REG
717		p.From.Reg = mips.REGZERO
718		p.To.Type = obj.TYPE_REG
719		p.To.Reg = v.Reg0()
720
721		s.Prog(mips.ASYNC)
722
723		p1 := s.Prog(mips.ALL)
724		p1.From.Type = obj.TYPE_MEM
725		p1.From.Reg = v.Args[0].Reg()
726		p1.To.Type = obj.TYPE_REG
727		p1.To.Reg = mips.REGTMP
728
729		p2 := s.Prog(mips.ABNE)
730		p2.From.Type = obj.TYPE_REG
731		p2.From.Reg = v.Args[1].Reg()
732		p2.Reg = mips.REGTMP
733		p2.To.Type = obj.TYPE_BRANCH
734
735		p3 := s.Prog(mips.AMOVW)
736		p3.From.Type = obj.TYPE_REG
737		p3.From.Reg = v.Args[2].Reg()
738		p3.To.Type = obj.TYPE_REG
739		p3.To.Reg = v.Reg0()
740
741		p4 := s.Prog(mips.ASC)
742		p4.From.Type = obj.TYPE_REG
743		p4.From.Reg = v.Reg0()
744		p4.To.Type = obj.TYPE_MEM
745		p4.To.Reg = v.Args[0].Reg()
746
747		p5 := s.Prog(mips.ABEQ)
748		p5.From.Type = obj.TYPE_REG
749		p5.From.Reg = v.Reg0()
750		p5.To.Type = obj.TYPE_BRANCH
751		p5.To.SetTarget(p1)
752
753		s.Prog(mips.ASYNC)
754
755		p6 := s.Prog(obj.ANOP)
756		p2.To.SetTarget(p6)
757
758	case ssa.OpMIPSLoweredNilCheck:
759		// Issue a load which will fault if arg is nil.
760		p := s.Prog(mips.AMOVB)
761		p.From.Type = obj.TYPE_MEM
762		p.From.Reg = v.Args[0].Reg()
763		ssagen.AddAux(&p.From, v)
764		p.To.Type = obj.TYPE_REG
765		p.To.Reg = mips.REGTMP
766		if logopt.Enabled() {
767			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
768		}
769		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
770			base.WarnfAt(v.Pos, "generated nil check")
771		}
772	case ssa.OpMIPSFPFlagTrue,
773		ssa.OpMIPSFPFlagFalse:
774		// MOVW		$1, r
775		// CMOVF	R0, r
776
777		cmov := mips.ACMOVF
778		if v.Op == ssa.OpMIPSFPFlagFalse {
779			cmov = mips.ACMOVT
780		}
781		p := s.Prog(mips.AMOVW)
782		p.From.Type = obj.TYPE_CONST
783		p.From.Offset = 1
784		p.To.Type = obj.TYPE_REG
785		p.To.Reg = v.Reg()
786		p1 := s.Prog(cmov)
787		p1.From.Type = obj.TYPE_REG
788		p1.From.Reg = mips.REGZERO
789		p1.To.Type = obj.TYPE_REG
790		p1.To.Reg = v.Reg()
791
792	case ssa.OpMIPSLoweredGetClosurePtr:
793		// Closure pointer is R22 (mips.REGCTXT).
794		ssagen.CheckLoweredGetClosurePtr(v)
795	case ssa.OpMIPSLoweredGetCallerSP:
796		// caller's SP is FixedFrameSize below the address of the first arg
797		p := s.Prog(mips.AMOVW)
798		p.From.Type = obj.TYPE_ADDR
799		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
800		p.From.Name = obj.NAME_PARAM
801		p.To.Type = obj.TYPE_REG
802		p.To.Reg = v.Reg()
803	case ssa.OpMIPSLoweredGetCallerPC:
804		p := s.Prog(obj.AGETCALLERPC)
805		p.To.Type = obj.TYPE_REG
806		p.To.Reg = v.Reg()
807	case ssa.OpClobber, ssa.OpClobberReg:
808		// TODO: implement for clobberdead experiment. Nop is ok for now.
809	default:
810		v.Fatalf("genValue not implemented: %s", v.LongString())
811	}
812}
813
814var blockJump = map[ssa.BlockKind]struct {
815	asm, invasm obj.As
816}{
817	ssa.BlockMIPSEQ:  {mips.ABEQ, mips.ABNE},
818	ssa.BlockMIPSNE:  {mips.ABNE, mips.ABEQ},
819	ssa.BlockMIPSLTZ: {mips.ABLTZ, mips.ABGEZ},
820	ssa.BlockMIPSGEZ: {mips.ABGEZ, mips.ABLTZ},
821	ssa.BlockMIPSLEZ: {mips.ABLEZ, mips.ABGTZ},
822	ssa.BlockMIPSGTZ: {mips.ABGTZ, mips.ABLEZ},
823	ssa.BlockMIPSFPT: {mips.ABFPT, mips.ABFPF},
824	ssa.BlockMIPSFPF: {mips.ABFPF, mips.ABFPT},
825}
826
827func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
828	switch b.Kind {
829	case ssa.BlockPlain:
830		if b.Succs[0].Block() != next {
831			p := s.Prog(obj.AJMP)
832			p.To.Type = obj.TYPE_BRANCH
833			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
834		}
835	case ssa.BlockDefer:
836		// defer returns in R1:
837		// 0 if we should continue executing
838		// 1 if we should jump to deferreturn call
839		p := s.Prog(mips.ABNE)
840		p.From.Type = obj.TYPE_REG
841		p.From.Reg = mips.REGZERO
842		p.Reg = mips.REG_R1
843		p.To.Type = obj.TYPE_BRANCH
844		s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
845		if b.Succs[0].Block() != next {
846			p := s.Prog(obj.AJMP)
847			p.To.Type = obj.TYPE_BRANCH
848			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
849		}
850	case ssa.BlockExit, ssa.BlockRetJmp:
851	case ssa.BlockRet:
852		s.Prog(obj.ARET)
853	case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
854		ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
855		ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,
856		ssa.BlockMIPSFPT, ssa.BlockMIPSFPF:
857		jmp := blockJump[b.Kind]
858		var p *obj.Prog
859		switch next {
860		case b.Succs[0].Block():
861			p = s.Br(jmp.invasm, b.Succs[1].Block())
862		case b.Succs[1].Block():
863			p = s.Br(jmp.asm, b.Succs[0].Block())
864		default:
865			if b.Likely != ssa.BranchUnlikely {
866				p = s.Br(jmp.asm, b.Succs[0].Block())
867				s.Br(obj.AJMP, b.Succs[1].Block())
868			} else {
869				p = s.Br(jmp.invasm, b.Succs[1].Block())
870				s.Br(obj.AJMP, b.Succs[0].Block())
871			}
872		}
873		if !b.Controls[0].Type.IsFlags() {
874			p.From.Type = obj.TYPE_REG
875			p.From.Reg = b.Controls[0].Reg()
876		}
877	default:
878		b.Fatalf("branch not implemented: %s", b.LongString())
879	}
880}
881