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 s390x
6
7import (
8	"math"
9
10	"cmd/compile/internal/gc"
11	"cmd/compile/internal/logopt"
12	"cmd/compile/internal/ssa"
13	"cmd/compile/internal/types"
14	"cmd/internal/obj"
15	"cmd/internal/obj/s390x"
16)
17
18// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
19func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
20	flive := b.FlagsLiveAtEnd
21	for _, c := range b.ControlValues() {
22		flive = c.Type.IsFlags() || flive
23	}
24	for i := len(b.Values) - 1; i >= 0; i-- {
25		v := b.Values[i]
26		if flive && v.Op == ssa.OpS390XMOVDconst {
27			// The "mark" is any non-nil Aux value.
28			v.Aux = v
29		}
30		if v.Type.IsFlags() {
31			flive = false
32		}
33		for _, a := range v.Args {
34			if a.Type.IsFlags() {
35				flive = true
36			}
37		}
38	}
39}
40
41// loadByType returns the load instruction of the given type.
42func loadByType(t *types.Type) obj.As {
43	if t.IsFloat() {
44		switch t.Size() {
45		case 4:
46			return s390x.AFMOVS
47		case 8:
48			return s390x.AFMOVD
49		}
50	} else {
51		switch t.Size() {
52		case 1:
53			if t.IsSigned() {
54				return s390x.AMOVB
55			} else {
56				return s390x.AMOVBZ
57			}
58		case 2:
59			if t.IsSigned() {
60				return s390x.AMOVH
61			} else {
62				return s390x.AMOVHZ
63			}
64		case 4:
65			if t.IsSigned() {
66				return s390x.AMOVW
67			} else {
68				return s390x.AMOVWZ
69			}
70		case 8:
71			return s390x.AMOVD
72		}
73	}
74	panic("bad load type")
75}
76
77// storeByType returns the store instruction of the given type.
78func storeByType(t *types.Type) obj.As {
79	width := t.Size()
80	if t.IsFloat() {
81		switch width {
82		case 4:
83			return s390x.AFMOVS
84		case 8:
85			return s390x.AFMOVD
86		}
87	} else {
88		switch width {
89		case 1:
90			return s390x.AMOVB
91		case 2:
92			return s390x.AMOVH
93		case 4:
94			return s390x.AMOVW
95		case 8:
96			return s390x.AMOVD
97		}
98	}
99	panic("bad store type")
100}
101
102// moveByType returns the reg->reg move instruction of the given type.
103func moveByType(t *types.Type) obj.As {
104	if t.IsFloat() {
105		return s390x.AFMOVD
106	} else {
107		switch t.Size() {
108		case 1:
109			if t.IsSigned() {
110				return s390x.AMOVB
111			} else {
112				return s390x.AMOVBZ
113			}
114		case 2:
115			if t.IsSigned() {
116				return s390x.AMOVH
117			} else {
118				return s390x.AMOVHZ
119			}
120		case 4:
121			if t.IsSigned() {
122				return s390x.AMOVW
123			} else {
124				return s390x.AMOVWZ
125			}
126		case 8:
127			return s390x.AMOVD
128		}
129	}
130	panic("bad load type")
131}
132
133// opregreg emits instructions for
134//     dest := dest(To) op src(From)
135// and also returns the created obj.Prog so it
136// may be further adjusted (offset, scale, etc).
137func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
138	p := s.Prog(op)
139	p.From.Type = obj.TYPE_REG
140	p.To.Type = obj.TYPE_REG
141	p.To.Reg = dest
142	p.From.Reg = src
143	return p
144}
145
146// opregregimm emits instructions for
147//	dest := src(From) op off
148// and also returns the created obj.Prog so it
149// may be further adjusted (offset, scale, etc).
150func opregregimm(s *gc.SSAGenState, op obj.As, dest, src int16, off int64) *obj.Prog {
151	p := s.Prog(op)
152	p.From.Type = obj.TYPE_CONST
153	p.From.Offset = off
154	p.Reg = src
155	p.To.Reg = dest
156	p.To.Type = obj.TYPE_REG
157	return p
158}
159
160func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
161	switch v.Op {
162	case ssa.OpS390XSLD, ssa.OpS390XSLW,
163		ssa.OpS390XSRD, ssa.OpS390XSRW,
164		ssa.OpS390XSRAD, ssa.OpS390XSRAW,
165		ssa.OpS390XRLLG, ssa.OpS390XRLL:
166		r := v.Reg()
167		r1 := v.Args[0].Reg()
168		r2 := v.Args[1].Reg()
169		if r2 == s390x.REG_R0 {
170			v.Fatalf("cannot use R0 as shift value %s", v.LongString())
171		}
172		p := opregreg(s, v.Op.Asm(), r, r2)
173		if r != r1 {
174			p.Reg = r1
175		}
176	case ssa.OpS390XRXSBG:
177		r1 := v.Reg()
178		if r1 != v.Args[0].Reg() {
179			v.Fatalf("input[0] and output not in same register %s", v.LongString())
180		}
181		r2 := v.Args[1].Reg()
182		i := v.Aux.(s390x.RotateParams)
183		p := s.Prog(v.Op.Asm())
184		p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(i.Start)}
185		p.RestArgs = []obj.Addr{
186			{Type: obj.TYPE_CONST, Offset: int64(i.End)},
187			{Type: obj.TYPE_CONST, Offset: int64(i.Amount)},
188			{Type: obj.TYPE_REG, Reg: r2},
189		}
190		p.To = obj.Addr{Type: obj.TYPE_REG, Reg: r1}
191	case ssa.OpS390XADD, ssa.OpS390XADDW,
192		ssa.OpS390XSUB, ssa.OpS390XSUBW,
193		ssa.OpS390XAND, ssa.OpS390XANDW,
194		ssa.OpS390XOR, ssa.OpS390XORW,
195		ssa.OpS390XXOR, ssa.OpS390XXORW:
196		r := v.Reg()
197		r1 := v.Args[0].Reg()
198		r2 := v.Args[1].Reg()
199		p := opregreg(s, v.Op.Asm(), r, r2)
200		if r != r1 {
201			p.Reg = r1
202		}
203	case ssa.OpS390XADDC:
204		r1 := v.Reg0()
205		r2 := v.Args[0].Reg()
206		r3 := v.Args[1].Reg()
207		if r1 == r2 {
208			r2, r3 = r3, r2
209		}
210		p := opregreg(s, v.Op.Asm(), r1, r2)
211		if r3 != r1 {
212			p.Reg = r3
213		}
214	case ssa.OpS390XSUBC:
215		r1 := v.Reg0()
216		r2 := v.Args[0].Reg()
217		r3 := v.Args[1].Reg()
218		p := opregreg(s, v.Op.Asm(), r1, r3)
219		if r1 != r2 {
220			p.Reg = r2
221		}
222	case ssa.OpS390XADDE, ssa.OpS390XSUBE:
223		r1 := v.Reg0()
224		if r1 != v.Args[0].Reg() {
225			v.Fatalf("input[0] and output not in same register %s", v.LongString())
226		}
227		r2 := v.Args[1].Reg()
228		opregreg(s, v.Op.Asm(), r1, r2)
229	case ssa.OpS390XADDCconst:
230		r1 := v.Reg0()
231		r3 := v.Args[0].Reg()
232		i2 := int64(int16(v.AuxInt))
233		opregregimm(s, v.Op.Asm(), r1, r3, i2)
234	// 2-address opcode arithmetic
235	case ssa.OpS390XMULLD, ssa.OpS390XMULLW,
236		ssa.OpS390XMULHD, ssa.OpS390XMULHDU,
237		ssa.OpS390XFADDS, ssa.OpS390XFADD, ssa.OpS390XFSUBS, ssa.OpS390XFSUB,
238		ssa.OpS390XFMULS, ssa.OpS390XFMUL, ssa.OpS390XFDIVS, ssa.OpS390XFDIV:
239		r := v.Reg()
240		if r != v.Args[0].Reg() {
241			v.Fatalf("input[0] and output not in same register %s", v.LongString())
242		}
243		opregreg(s, v.Op.Asm(), r, v.Args[1].Reg())
244	case ssa.OpS390XMLGR:
245		// MLGR Rx R3 -> R2:R3
246		r0 := v.Args[0].Reg()
247		r1 := v.Args[1].Reg()
248		if r1 != s390x.REG_R3 {
249			v.Fatalf("We require the multiplcand to be stored in R3 for MLGR %s", v.LongString())
250		}
251		p := s.Prog(s390x.AMLGR)
252		p.From.Type = obj.TYPE_REG
253		p.From.Reg = r0
254		p.To.Reg = s390x.REG_R2
255		p.To.Type = obj.TYPE_REG
256	case ssa.OpS390XFMADD, ssa.OpS390XFMADDS,
257		ssa.OpS390XFMSUB, ssa.OpS390XFMSUBS:
258		r := v.Reg()
259		if r != v.Args[0].Reg() {
260			v.Fatalf("input[0] and output not in same register %s", v.LongString())
261		}
262		r1 := v.Args[1].Reg()
263		r2 := v.Args[2].Reg()
264		p := s.Prog(v.Op.Asm())
265		p.From.Type = obj.TYPE_REG
266		p.From.Reg = r1
267		p.Reg = r2
268		p.To.Type = obj.TYPE_REG
269		p.To.Reg = r
270	case ssa.OpS390XFIDBR:
271		switch v.AuxInt {
272		case 0, 1, 3, 4, 5, 6, 7:
273			opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt)
274		default:
275			v.Fatalf("invalid FIDBR mask: %v", v.AuxInt)
276		}
277	case ssa.OpS390XCPSDR:
278		p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg())
279		p.Reg = v.Args[0].Reg()
280	case ssa.OpS390XDIVD, ssa.OpS390XDIVW,
281		ssa.OpS390XDIVDU, ssa.OpS390XDIVWU,
282		ssa.OpS390XMODD, ssa.OpS390XMODW,
283		ssa.OpS390XMODDU, ssa.OpS390XMODWU:
284
285		// TODO(mundaym): use the temp registers every time like x86 does with AX?
286		dividend := v.Args[0].Reg()
287		divisor := v.Args[1].Reg()
288
289		// CPU faults upon signed overflow, which occurs when most
290		// negative int is divided by -1.
291		var j *obj.Prog
292		if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW ||
293			v.Op == ssa.OpS390XMODD || v.Op == ssa.OpS390XMODW {
294
295			var c *obj.Prog
296			c = s.Prog(s390x.ACMP)
297			j = s.Prog(s390x.ABEQ)
298
299			c.From.Type = obj.TYPE_REG
300			c.From.Reg = divisor
301			c.To.Type = obj.TYPE_CONST
302			c.To.Offset = -1
303
304			j.To.Type = obj.TYPE_BRANCH
305
306		}
307
308		p := s.Prog(v.Op.Asm())
309		p.From.Type = obj.TYPE_REG
310		p.From.Reg = divisor
311		p.Reg = 0
312		p.To.Type = obj.TYPE_REG
313		p.To.Reg = dividend
314
315		// signed division, rest of the check for -1 case
316		if j != nil {
317			j2 := s.Prog(s390x.ABR)
318			j2.To.Type = obj.TYPE_BRANCH
319
320			var n *obj.Prog
321			if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW {
322				// n * -1 = -n
323				n = s.Prog(s390x.ANEG)
324				n.To.Type = obj.TYPE_REG
325				n.To.Reg = dividend
326			} else {
327				// n % -1 == 0
328				n = s.Prog(s390x.AXOR)
329				n.From.Type = obj.TYPE_REG
330				n.From.Reg = dividend
331				n.To.Type = obj.TYPE_REG
332				n.To.Reg = dividend
333			}
334
335			j.To.Val = n
336			j2.To.Val = s.Pc()
337		}
338	case ssa.OpS390XADDconst, ssa.OpS390XADDWconst:
339		opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt)
340	case ssa.OpS390XMULLDconst, ssa.OpS390XMULLWconst,
341		ssa.OpS390XSUBconst, ssa.OpS390XSUBWconst,
342		ssa.OpS390XANDconst, ssa.OpS390XANDWconst,
343		ssa.OpS390XORconst, ssa.OpS390XORWconst,
344		ssa.OpS390XXORconst, ssa.OpS390XXORWconst:
345		r := v.Reg()
346		if r != v.Args[0].Reg() {
347			v.Fatalf("input[0] and output not in same register %s", v.LongString())
348		}
349		p := s.Prog(v.Op.Asm())
350		p.From.Type = obj.TYPE_CONST
351		p.From.Offset = v.AuxInt
352		p.To.Type = obj.TYPE_REG
353		p.To.Reg = r
354	case ssa.OpS390XSLDconst, ssa.OpS390XSLWconst,
355		ssa.OpS390XSRDconst, ssa.OpS390XSRWconst,
356		ssa.OpS390XSRADconst, ssa.OpS390XSRAWconst,
357		ssa.OpS390XRLLGconst, ssa.OpS390XRLLconst:
358		p := s.Prog(v.Op.Asm())
359		p.From.Type = obj.TYPE_CONST
360		p.From.Offset = v.AuxInt
361		r := v.Reg()
362		r1 := v.Args[0].Reg()
363		if r != r1 {
364			p.Reg = r1
365		}
366		p.To.Type = obj.TYPE_REG
367		p.To.Reg = r
368	case ssa.OpS390XMOVDaddridx:
369		r := v.Args[0].Reg()
370		i := v.Args[1].Reg()
371		p := s.Prog(s390x.AMOVD)
372		p.From.Scale = 1
373		if i == s390x.REGSP {
374			r, i = i, r
375		}
376		p.From.Type = obj.TYPE_ADDR
377		p.From.Reg = r
378		p.From.Index = i
379		gc.AddAux(&p.From, v)
380		p.To.Type = obj.TYPE_REG
381		p.To.Reg = v.Reg()
382	case ssa.OpS390XMOVDaddr:
383		p := s.Prog(s390x.AMOVD)
384		p.From.Type = obj.TYPE_ADDR
385		p.From.Reg = v.Args[0].Reg()
386		gc.AddAux(&p.From, v)
387		p.To.Type = obj.TYPE_REG
388		p.To.Reg = v.Reg()
389	case ssa.OpS390XCMP, ssa.OpS390XCMPW, ssa.OpS390XCMPU, ssa.OpS390XCMPWU:
390		opregreg(s, v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
391	case ssa.OpS390XFCMPS, ssa.OpS390XFCMP:
392		opregreg(s, v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
393	case ssa.OpS390XCMPconst, ssa.OpS390XCMPWconst:
394		p := s.Prog(v.Op.Asm())
395		p.From.Type = obj.TYPE_REG
396		p.From.Reg = v.Args[0].Reg()
397		p.To.Type = obj.TYPE_CONST
398		p.To.Offset = v.AuxInt
399	case ssa.OpS390XCMPUconst, ssa.OpS390XCMPWUconst:
400		p := s.Prog(v.Op.Asm())
401		p.From.Type = obj.TYPE_REG
402		p.From.Reg = v.Args[0].Reg()
403		p.To.Type = obj.TYPE_CONST
404		p.To.Offset = int64(uint32(v.AuxInt))
405	case ssa.OpS390XMOVDconst:
406		x := v.Reg()
407		p := s.Prog(v.Op.Asm())
408		p.From.Type = obj.TYPE_CONST
409		p.From.Offset = v.AuxInt
410		p.To.Type = obj.TYPE_REG
411		p.To.Reg = x
412	case ssa.OpS390XFMOVSconst, ssa.OpS390XFMOVDconst:
413		x := v.Reg()
414		p := s.Prog(v.Op.Asm())
415		p.From.Type = obj.TYPE_FCONST
416		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
417		p.To.Type = obj.TYPE_REG
418		p.To.Reg = x
419	case ssa.OpS390XADDWload, ssa.OpS390XADDload,
420		ssa.OpS390XMULLWload, ssa.OpS390XMULLDload,
421		ssa.OpS390XSUBWload, ssa.OpS390XSUBload,
422		ssa.OpS390XANDWload, ssa.OpS390XANDload,
423		ssa.OpS390XORWload, ssa.OpS390XORload,
424		ssa.OpS390XXORWload, ssa.OpS390XXORload:
425		r := v.Reg()
426		if r != v.Args[0].Reg() {
427			v.Fatalf("input[0] and output not in same register %s", v.LongString())
428		}
429		p := s.Prog(v.Op.Asm())
430		p.From.Type = obj.TYPE_MEM
431		p.From.Reg = v.Args[1].Reg()
432		gc.AddAux(&p.From, v)
433		p.To.Type = obj.TYPE_REG
434		p.To.Reg = r
435	case ssa.OpS390XMOVDload,
436		ssa.OpS390XMOVWZload, ssa.OpS390XMOVHZload, ssa.OpS390XMOVBZload,
437		ssa.OpS390XMOVDBRload, ssa.OpS390XMOVWBRload, ssa.OpS390XMOVHBRload,
438		ssa.OpS390XMOVBload, ssa.OpS390XMOVHload, ssa.OpS390XMOVWload,
439		ssa.OpS390XFMOVSload, ssa.OpS390XFMOVDload:
440		p := s.Prog(v.Op.Asm())
441		p.From.Type = obj.TYPE_MEM
442		p.From.Reg = v.Args[0].Reg()
443		gc.AddAux(&p.From, v)
444		p.To.Type = obj.TYPE_REG
445		p.To.Reg = v.Reg()
446	case ssa.OpS390XMOVBZloadidx, ssa.OpS390XMOVHZloadidx, ssa.OpS390XMOVWZloadidx,
447		ssa.OpS390XMOVBloadidx, ssa.OpS390XMOVHloadidx, ssa.OpS390XMOVWloadidx, ssa.OpS390XMOVDloadidx,
448		ssa.OpS390XMOVHBRloadidx, ssa.OpS390XMOVWBRloadidx, ssa.OpS390XMOVDBRloadidx,
449		ssa.OpS390XFMOVSloadidx, ssa.OpS390XFMOVDloadidx:
450		r := v.Args[0].Reg()
451		i := v.Args[1].Reg()
452		if i == s390x.REGSP {
453			r, i = i, r
454		}
455		p := s.Prog(v.Op.Asm())
456		p.From.Type = obj.TYPE_MEM
457		p.From.Reg = r
458		p.From.Scale = 1
459		p.From.Index = i
460		gc.AddAux(&p.From, v)
461		p.To.Type = obj.TYPE_REG
462		p.To.Reg = v.Reg()
463	case ssa.OpS390XMOVBstore, ssa.OpS390XMOVHstore, ssa.OpS390XMOVWstore, ssa.OpS390XMOVDstore,
464		ssa.OpS390XMOVHBRstore, ssa.OpS390XMOVWBRstore, ssa.OpS390XMOVDBRstore,
465		ssa.OpS390XFMOVSstore, ssa.OpS390XFMOVDstore:
466		p := s.Prog(v.Op.Asm())
467		p.From.Type = obj.TYPE_REG
468		p.From.Reg = v.Args[1].Reg()
469		p.To.Type = obj.TYPE_MEM
470		p.To.Reg = v.Args[0].Reg()
471		gc.AddAux(&p.To, v)
472	case ssa.OpS390XMOVBstoreidx, ssa.OpS390XMOVHstoreidx, ssa.OpS390XMOVWstoreidx, ssa.OpS390XMOVDstoreidx,
473		ssa.OpS390XMOVHBRstoreidx, ssa.OpS390XMOVWBRstoreidx, ssa.OpS390XMOVDBRstoreidx,
474		ssa.OpS390XFMOVSstoreidx, ssa.OpS390XFMOVDstoreidx:
475		r := v.Args[0].Reg()
476		i := v.Args[1].Reg()
477		if i == s390x.REGSP {
478			r, i = i, r
479		}
480		p := s.Prog(v.Op.Asm())
481		p.From.Type = obj.TYPE_REG
482		p.From.Reg = v.Args[2].Reg()
483		p.To.Type = obj.TYPE_MEM
484		p.To.Reg = r
485		p.To.Scale = 1
486		p.To.Index = i
487		gc.AddAux(&p.To, v)
488	case ssa.OpS390XMOVDstoreconst, ssa.OpS390XMOVWstoreconst, ssa.OpS390XMOVHstoreconst, ssa.OpS390XMOVBstoreconst:
489		p := s.Prog(v.Op.Asm())
490		p.From.Type = obj.TYPE_CONST
491		sc := v.AuxValAndOff()
492		p.From.Offset = sc.Val()
493		p.To.Type = obj.TYPE_MEM
494		p.To.Reg = v.Args[0].Reg()
495		gc.AddAux2(&p.To, v, sc.Off())
496	case ssa.OpS390XMOVBreg, ssa.OpS390XMOVHreg, ssa.OpS390XMOVWreg,
497		ssa.OpS390XMOVBZreg, ssa.OpS390XMOVHZreg, ssa.OpS390XMOVWZreg,
498		ssa.OpS390XLDGR, ssa.OpS390XLGDR,
499		ssa.OpS390XCEFBRA, ssa.OpS390XCDFBRA, ssa.OpS390XCEGBRA, ssa.OpS390XCDGBRA,
500		ssa.OpS390XCFEBRA, ssa.OpS390XCFDBRA, ssa.OpS390XCGEBRA, ssa.OpS390XCGDBRA,
501		ssa.OpS390XLDEBR, ssa.OpS390XLEDBR,
502		ssa.OpS390XFNEG, ssa.OpS390XFNEGS,
503		ssa.OpS390XLPDFR, ssa.OpS390XLNDFR:
504		opregreg(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg())
505	case ssa.OpS390XCLEAR:
506		p := s.Prog(v.Op.Asm())
507		p.From.Type = obj.TYPE_CONST
508		sc := v.AuxValAndOff()
509		p.From.Offset = sc.Val()
510		p.To.Type = obj.TYPE_MEM
511		p.To.Reg = v.Args[0].Reg()
512		gc.AddAux2(&p.To, v, sc.Off())
513	case ssa.OpCopy:
514		if v.Type.IsMemory() {
515			return
516		}
517		x := v.Args[0].Reg()
518		y := v.Reg()
519		if x != y {
520			opregreg(s, moveByType(v.Type), y, x)
521		}
522	case ssa.OpLoadReg:
523		if v.Type.IsFlags() {
524			v.Fatalf("load flags not implemented: %v", v.LongString())
525			return
526		}
527		p := s.Prog(loadByType(v.Type))
528		gc.AddrAuto(&p.From, v.Args[0])
529		p.To.Type = obj.TYPE_REG
530		p.To.Reg = v.Reg()
531	case ssa.OpStoreReg:
532		if v.Type.IsFlags() {
533			v.Fatalf("store flags not implemented: %v", v.LongString())
534			return
535		}
536		p := s.Prog(storeByType(v.Type))
537		p.From.Type = obj.TYPE_REG
538		p.From.Reg = v.Args[0].Reg()
539		gc.AddrAuto(&p.To, v)
540	case ssa.OpS390XLoweredGetClosurePtr:
541		// Closure pointer is R12 (already)
542		gc.CheckLoweredGetClosurePtr(v)
543	case ssa.OpS390XLoweredRound32F, ssa.OpS390XLoweredRound64F:
544		// input is already rounded
545	case ssa.OpS390XLoweredGetG:
546		r := v.Reg()
547		p := s.Prog(s390x.AMOVD)
548		p.From.Type = obj.TYPE_REG
549		p.From.Reg = s390x.REGG
550		p.To.Type = obj.TYPE_REG
551		p.To.Reg = r
552	case ssa.OpS390XLoweredGetCallerSP:
553		// caller's SP is FixedFrameSize below the address of the first arg
554		p := s.Prog(s390x.AMOVD)
555		p.From.Type = obj.TYPE_ADDR
556		p.From.Offset = -gc.Ctxt.FixedFrameSize()
557		p.From.Name = obj.NAME_PARAM
558		p.To.Type = obj.TYPE_REG
559		p.To.Reg = v.Reg()
560	case ssa.OpS390XLoweredGetCallerPC:
561		p := s.Prog(obj.AGETCALLERPC)
562		p.To.Type = obj.TYPE_REG
563		p.To.Reg = v.Reg()
564	case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter:
565		s.Call(v)
566	case ssa.OpS390XLoweredWB:
567		p := s.Prog(obj.ACALL)
568		p.To.Type = obj.TYPE_MEM
569		p.To.Name = obj.NAME_EXTERN
570		p.To.Sym = v.Aux.(*obj.LSym)
571	case ssa.OpS390XLoweredPanicBoundsA, ssa.OpS390XLoweredPanicBoundsB, ssa.OpS390XLoweredPanicBoundsC:
572		p := s.Prog(obj.ACALL)
573		p.To.Type = obj.TYPE_MEM
574		p.To.Name = obj.NAME_EXTERN
575		p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
576		s.UseArgs(16) // space used in callee args area by assembly stubs
577	case ssa.OpS390XFLOGR, ssa.OpS390XPOPCNT,
578		ssa.OpS390XNEG, ssa.OpS390XNEGW,
579		ssa.OpS390XMOVWBR, ssa.OpS390XMOVDBR:
580		p := s.Prog(v.Op.Asm())
581		p.From.Type = obj.TYPE_REG
582		p.From.Reg = v.Args[0].Reg()
583		p.To.Type = obj.TYPE_REG
584		p.To.Reg = v.Reg()
585	case ssa.OpS390XNOT, ssa.OpS390XNOTW:
586		v.Fatalf("NOT/NOTW generated %s", v.LongString())
587	case ssa.OpS390XSumBytes2, ssa.OpS390XSumBytes4, ssa.OpS390XSumBytes8:
588		v.Fatalf("SumBytes generated %s", v.LongString())
589	case ssa.OpS390XLOCGR:
590		r := v.Reg()
591		if r != v.Args[0].Reg() {
592			v.Fatalf("input[0] and output not in same register %s", v.LongString())
593		}
594		p := s.Prog(v.Op.Asm())
595		p.From.Type = obj.TYPE_CONST
596		p.From.Offset = int64(v.Aux.(s390x.CCMask))
597		p.Reg = v.Args[1].Reg()
598		p.To.Type = obj.TYPE_REG
599		p.To.Reg = r
600	case ssa.OpS390XFSQRT:
601		p := s.Prog(v.Op.Asm())
602		p.From.Type = obj.TYPE_REG
603		p.From.Reg = v.Args[0].Reg()
604		p.To.Type = obj.TYPE_REG
605		p.To.Reg = v.Reg()
606	case ssa.OpS390XInvertFlags:
607		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
608	case ssa.OpS390XFlagEQ, ssa.OpS390XFlagLT, ssa.OpS390XFlagGT, ssa.OpS390XFlagOV:
609		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
610	case ssa.OpS390XAddTupleFirst32, ssa.OpS390XAddTupleFirst64:
611		v.Fatalf("AddTupleFirst* should never make it to codegen %v", v.LongString())
612	case ssa.OpS390XLoweredNilCheck:
613		// Issue a load which will fault if the input is nil.
614		p := s.Prog(s390x.AMOVBZ)
615		p.From.Type = obj.TYPE_MEM
616		p.From.Reg = v.Args[0].Reg()
617		gc.AddAux(&p.From, v)
618		p.To.Type = obj.TYPE_REG
619		p.To.Reg = s390x.REGTMP
620		if logopt.Enabled() {
621			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
622		}
623		if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
624			gc.Warnl(v.Pos, "generated nil check")
625		}
626	case ssa.OpS390XMVC:
627		vo := v.AuxValAndOff()
628		p := s.Prog(s390x.AMVC)
629		p.From.Type = obj.TYPE_CONST
630		p.From.Offset = vo.Val()
631		p.SetFrom3(obj.Addr{
632			Type:   obj.TYPE_MEM,
633			Reg:    v.Args[1].Reg(),
634			Offset: vo.Off(),
635		})
636		p.To.Type = obj.TYPE_MEM
637		p.To.Reg = v.Args[0].Reg()
638		p.To.Offset = vo.Off()
639	case ssa.OpS390XSTMG2, ssa.OpS390XSTMG3, ssa.OpS390XSTMG4,
640		ssa.OpS390XSTM2, ssa.OpS390XSTM3, ssa.OpS390XSTM4:
641		for i := 2; i < len(v.Args)-1; i++ {
642			if v.Args[i].Reg() != v.Args[i-1].Reg()+1 {
643				v.Fatalf("invalid store multiple %s", v.LongString())
644			}
645		}
646		p := s.Prog(v.Op.Asm())
647		p.From.Type = obj.TYPE_REG
648		p.From.Reg = v.Args[1].Reg()
649		p.Reg = v.Args[len(v.Args)-2].Reg()
650		p.To.Type = obj.TYPE_MEM
651		p.To.Reg = v.Args[0].Reg()
652		gc.AddAux(&p.To, v)
653	case ssa.OpS390XLoweredMove:
654		// Inputs must be valid pointers to memory,
655		// so adjust arg0 and arg1 as part of the expansion.
656		// arg2 should be src+size,
657		//
658		// mvc: MVC  $256, 0(R2), 0(R1)
659		//      MOVD $256(R1), R1
660		//      MOVD $256(R2), R2
661		//      CMP  R2, Rarg2
662		//      BNE  mvc
663		//      MVC  $rem, 0(R2), 0(R1) // if rem > 0
664		// arg2 is the last address to move in the loop + 256
665		mvc := s.Prog(s390x.AMVC)
666		mvc.From.Type = obj.TYPE_CONST
667		mvc.From.Offset = 256
668		mvc.SetFrom3(obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[1].Reg()})
669		mvc.To.Type = obj.TYPE_MEM
670		mvc.To.Reg = v.Args[0].Reg()
671
672		for i := 0; i < 2; i++ {
673			movd := s.Prog(s390x.AMOVD)
674			movd.From.Type = obj.TYPE_ADDR
675			movd.From.Reg = v.Args[i].Reg()
676			movd.From.Offset = 256
677			movd.To.Type = obj.TYPE_REG
678			movd.To.Reg = v.Args[i].Reg()
679		}
680
681		cmpu := s.Prog(s390x.ACMPU)
682		cmpu.From.Reg = v.Args[1].Reg()
683		cmpu.From.Type = obj.TYPE_REG
684		cmpu.To.Reg = v.Args[2].Reg()
685		cmpu.To.Type = obj.TYPE_REG
686
687		bne := s.Prog(s390x.ABLT)
688		bne.To.Type = obj.TYPE_BRANCH
689		gc.Patch(bne, mvc)
690
691		if v.AuxInt > 0 {
692			mvc := s.Prog(s390x.AMVC)
693			mvc.From.Type = obj.TYPE_CONST
694			mvc.From.Offset = v.AuxInt
695			mvc.SetFrom3(obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[1].Reg()})
696			mvc.To.Type = obj.TYPE_MEM
697			mvc.To.Reg = v.Args[0].Reg()
698		}
699	case ssa.OpS390XLoweredZero:
700		// Input must be valid pointers to memory,
701		// so adjust arg0 as part of the expansion.
702		// arg1 should be src+size,
703		//
704		// clear: CLEAR $256, 0(R1)
705		//        MOVD  $256(R1), R1
706		//        CMP   R1, Rarg1
707		//        BNE   clear
708		//        CLEAR $rem, 0(R1) // if rem > 0
709		// arg1 is the last address to zero in the loop + 256
710		clear := s.Prog(s390x.ACLEAR)
711		clear.From.Type = obj.TYPE_CONST
712		clear.From.Offset = 256
713		clear.To.Type = obj.TYPE_MEM
714		clear.To.Reg = v.Args[0].Reg()
715
716		movd := s.Prog(s390x.AMOVD)
717		movd.From.Type = obj.TYPE_ADDR
718		movd.From.Reg = v.Args[0].Reg()
719		movd.From.Offset = 256
720		movd.To.Type = obj.TYPE_REG
721		movd.To.Reg = v.Args[0].Reg()
722
723		cmpu := s.Prog(s390x.ACMPU)
724		cmpu.From.Reg = v.Args[0].Reg()
725		cmpu.From.Type = obj.TYPE_REG
726		cmpu.To.Reg = v.Args[1].Reg()
727		cmpu.To.Type = obj.TYPE_REG
728
729		bne := s.Prog(s390x.ABLT)
730		bne.To.Type = obj.TYPE_BRANCH
731		gc.Patch(bne, clear)
732
733		if v.AuxInt > 0 {
734			clear := s.Prog(s390x.ACLEAR)
735			clear.From.Type = obj.TYPE_CONST
736			clear.From.Offset = v.AuxInt
737			clear.To.Type = obj.TYPE_MEM
738			clear.To.Reg = v.Args[0].Reg()
739		}
740	case ssa.OpS390XMOVBZatomicload, ssa.OpS390XMOVWZatomicload, ssa.OpS390XMOVDatomicload:
741		p := s.Prog(v.Op.Asm())
742		p.From.Type = obj.TYPE_MEM
743		p.From.Reg = v.Args[0].Reg()
744		gc.AddAux(&p.From, v)
745		p.To.Type = obj.TYPE_REG
746		p.To.Reg = v.Reg0()
747	case ssa.OpS390XMOVBatomicstore, ssa.OpS390XMOVWatomicstore, ssa.OpS390XMOVDatomicstore:
748		p := s.Prog(v.Op.Asm())
749		p.From.Type = obj.TYPE_REG
750		p.From.Reg = v.Args[1].Reg()
751		p.To.Type = obj.TYPE_MEM
752		p.To.Reg = v.Args[0].Reg()
753		gc.AddAux(&p.To, v)
754	case ssa.OpS390XLANfloor, ssa.OpS390XLAOfloor:
755		r := v.Args[0].Reg() // clobbered, assumed R1 in comments
756
757		// Round ptr down to nearest multiple of 4.
758		// ANDW $~3, R1
759		ptr := s.Prog(s390x.AANDW)
760		ptr.From.Type = obj.TYPE_CONST
761		ptr.From.Offset = 0xfffffffc
762		ptr.To.Type = obj.TYPE_REG
763		ptr.To.Reg = r
764
765		// Redirect output of LA(N|O) into R1 since it is clobbered anyway.
766		// LA(N|O) Rx, R1, 0(R1)
767		op := s.Prog(v.Op.Asm())
768		op.From.Type = obj.TYPE_REG
769		op.From.Reg = v.Args[1].Reg()
770		op.Reg = r
771		op.To.Type = obj.TYPE_MEM
772		op.To.Reg = r
773	case ssa.OpS390XLAA, ssa.OpS390XLAAG:
774		p := s.Prog(v.Op.Asm())
775		p.Reg = v.Reg0()
776		p.From.Type = obj.TYPE_REG
777		p.From.Reg = v.Args[1].Reg()
778		p.To.Type = obj.TYPE_MEM
779		p.To.Reg = v.Args[0].Reg()
780		gc.AddAux(&p.To, v)
781	case ssa.OpS390XLoweredAtomicCas32, ssa.OpS390XLoweredAtomicCas64:
782		// Convert the flags output of CS{,G} into a bool.
783		//    CS{,G} arg1, arg2, arg0
784		//    MOVD   $0, ret
785		//    BNE    2(PC)
786		//    MOVD   $1, ret
787		//    NOP (so the BNE has somewhere to land)
788
789		// CS{,G} arg1, arg2, arg0
790		cs := s.Prog(v.Op.Asm())
791		cs.From.Type = obj.TYPE_REG
792		cs.From.Reg = v.Args[1].Reg() // old
793		cs.Reg = v.Args[2].Reg()      // new
794		cs.To.Type = obj.TYPE_MEM
795		cs.To.Reg = v.Args[0].Reg()
796		gc.AddAux(&cs.To, v)
797
798		// MOVD $0, ret
799		movd := s.Prog(s390x.AMOVD)
800		movd.From.Type = obj.TYPE_CONST
801		movd.From.Offset = 0
802		movd.To.Type = obj.TYPE_REG
803		movd.To.Reg = v.Reg0()
804
805		// BNE 2(PC)
806		bne := s.Prog(s390x.ABNE)
807		bne.To.Type = obj.TYPE_BRANCH
808
809		// MOVD $1, ret
810		movd = s.Prog(s390x.AMOVD)
811		movd.From.Type = obj.TYPE_CONST
812		movd.From.Offset = 1
813		movd.To.Type = obj.TYPE_REG
814		movd.To.Reg = v.Reg0()
815
816		// NOP (so the BNE has somewhere to land)
817		nop := s.Prog(obj.ANOP)
818		gc.Patch(bne, nop)
819	case ssa.OpS390XLoweredAtomicExchange32, ssa.OpS390XLoweredAtomicExchange64:
820		// Loop until the CS{,G} succeeds.
821		//     MOV{WZ,D} arg0, ret
822		// cs: CS{,G}    ret, arg1, arg0
823		//     BNE       cs
824
825		// MOV{WZ,D} arg0, ret
826		load := s.Prog(loadByType(v.Type.FieldType(0)))
827		load.From.Type = obj.TYPE_MEM
828		load.From.Reg = v.Args[0].Reg()
829		load.To.Type = obj.TYPE_REG
830		load.To.Reg = v.Reg0()
831		gc.AddAux(&load.From, v)
832
833		// CS{,G} ret, arg1, arg0
834		cs := s.Prog(v.Op.Asm())
835		cs.From.Type = obj.TYPE_REG
836		cs.From.Reg = v.Reg0()   // old
837		cs.Reg = v.Args[1].Reg() // new
838		cs.To.Type = obj.TYPE_MEM
839		cs.To.Reg = v.Args[0].Reg()
840		gc.AddAux(&cs.To, v)
841
842		// BNE cs
843		bne := s.Prog(s390x.ABNE)
844		bne.To.Type = obj.TYPE_BRANCH
845		gc.Patch(bne, cs)
846	case ssa.OpS390XSYNC:
847		s.Prog(s390x.ASYNC)
848	case ssa.OpClobber:
849		// TODO: implement for clobberdead experiment. Nop is ok for now.
850	default:
851		v.Fatalf("genValue not implemented: %s", v.LongString())
852	}
853}
854
855func blockAsm(b *ssa.Block) obj.As {
856	switch b.Kind {
857	case ssa.BlockS390XBRC:
858		return s390x.ABRC
859	case ssa.BlockS390XCRJ:
860		return s390x.ACRJ
861	case ssa.BlockS390XCGRJ:
862		return s390x.ACGRJ
863	case ssa.BlockS390XCLRJ:
864		return s390x.ACLRJ
865	case ssa.BlockS390XCLGRJ:
866		return s390x.ACLGRJ
867	case ssa.BlockS390XCIJ:
868		return s390x.ACIJ
869	case ssa.BlockS390XCGIJ:
870		return s390x.ACGIJ
871	case ssa.BlockS390XCLIJ:
872		return s390x.ACLIJ
873	case ssa.BlockS390XCLGIJ:
874		return s390x.ACLGIJ
875	}
876	b.Fatalf("blockAsm not implemented: %s", b.LongString())
877	panic("unreachable")
878}
879
880func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
881	// Handle generic blocks first.
882	switch b.Kind {
883	case ssa.BlockPlain:
884		if b.Succs[0].Block() != next {
885			p := s.Prog(s390x.ABR)
886			p.To.Type = obj.TYPE_BRANCH
887			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
888		}
889		return
890	case ssa.BlockDefer:
891		// defer returns in R3:
892		// 0 if we should continue executing
893		// 1 if we should jump to deferreturn call
894		p := s.Br(s390x.ACIJ, b.Succs[1].Block())
895		p.From.Type = obj.TYPE_CONST
896		p.From.Offset = int64(s390x.NotEqual & s390x.NotUnordered) // unordered is not possible
897		p.Reg = s390x.REG_R3
898		p.RestArgs = []obj.Addr{{Type: obj.TYPE_CONST, Offset: 0}}
899		if b.Succs[0].Block() != next {
900			s.Br(s390x.ABR, b.Succs[0].Block())
901		}
902		return
903	case ssa.BlockExit:
904		return
905	case ssa.BlockRet:
906		s.Prog(obj.ARET)
907		return
908	case ssa.BlockRetJmp:
909		p := s.Prog(s390x.ABR)
910		p.To.Type = obj.TYPE_MEM
911		p.To.Name = obj.NAME_EXTERN
912		p.To.Sym = b.Aux.(*obj.LSym)
913		return
914	}
915
916	// Handle s390x-specific blocks. These blocks all have a
917	// condition code mask in the Aux value and 2 successors.
918	succs := [...]*ssa.Block{b.Succs[0].Block(), b.Succs[1].Block()}
919	mask := b.Aux.(s390x.CCMask)
920
921	// TODO: take into account Likely property for forward/backward
922	// branches. We currently can't do this because we don't know
923	// whether a block has already been emitted. In general forward
924	// branches are assumed 'not taken' and backward branches are
925	// assumed 'taken'.
926	if next == succs[0] {
927		succs[0], succs[1] = succs[1], succs[0]
928		mask = mask.Inverse()
929	}
930
931	p := s.Br(blockAsm(b), succs[0])
932	switch b.Kind {
933	case ssa.BlockS390XBRC:
934		p.From.Type = obj.TYPE_CONST
935		p.From.Offset = int64(mask)
936	case ssa.BlockS390XCGRJ, ssa.BlockS390XCRJ,
937		ssa.BlockS390XCLGRJ, ssa.BlockS390XCLRJ:
938		p.From.Type = obj.TYPE_CONST
939		p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible
940		p.Reg = b.Controls[0].Reg()
941		p.RestArgs = []obj.Addr{{Type: obj.TYPE_REG, Reg: b.Controls[1].Reg()}}
942	case ssa.BlockS390XCGIJ, ssa.BlockS390XCIJ:
943		p.From.Type = obj.TYPE_CONST
944		p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible
945		p.Reg = b.Controls[0].Reg()
946		p.RestArgs = []obj.Addr{{Type: obj.TYPE_CONST, Offset: int64(int8(b.AuxInt))}}
947	case ssa.BlockS390XCLGIJ, ssa.BlockS390XCLIJ:
948		p.From.Type = obj.TYPE_CONST
949		p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible
950		p.Reg = b.Controls[0].Reg()
951		p.RestArgs = []obj.Addr{{Type: obj.TYPE_CONST, Offset: int64(uint8(b.AuxInt))}}
952	default:
953		b.Fatalf("branch not implemented: %s", b.LongString())
954	}
955	if next != succs[1] {
956		s.Br(s390x.ABR, succs[1])
957	}
958}
959