1// Based on cmd/internal/obj/ppc64/obj9.go.
2//
3//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
4//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
5//	Portions Copyright © 1997-1999 Vita Nuova Limited
6//	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
7//	Portions Copyright © 2004,2006 Bruce Ellis
8//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
9//	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
10//	Portions Copyright © 2009 The Go Authors. All rights reserved.
11//
12// Permission is hereby granted, free of charge, to any person obtaining a copy
13// of this software and associated documentation files (the "Software"), to deal
14// in the Software without restriction, including without limitation the rights
15// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16// copies of the Software, and to permit persons to whom the Software is
17// furnished to do so, subject to the following conditions:
18//
19// The above copyright notice and this permission notice shall be included in
20// all copies or substantial portions of the Software.
21//
22// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
25// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28// THE SOFTWARE.
29
30package s390x
31
32import (
33	"cmd/internal/obj"
34	"cmd/internal/objabi"
35	"cmd/internal/sys"
36	"log"
37	"math"
38)
39
40func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
41	p.From.Class = 0
42	p.To.Class = 0
43
44	c := ctxtz{ctxt: ctxt, newprog: newprog}
45
46	// Rewrite BR/BL to symbol as TYPE_BRANCH.
47	switch p.As {
48	case ABR, ABL, obj.ARET, obj.ADUFFZERO, obj.ADUFFCOPY:
49		if p.To.Sym != nil {
50			p.To.Type = obj.TYPE_BRANCH
51		}
52	}
53
54	// Rewrite float constants to values stored in memory unless they are +0.
55	switch p.As {
56	case AFMOVS:
57		if p.From.Type == obj.TYPE_FCONST {
58			f32 := float32(p.From.Val.(float64))
59			if math.Float32bits(f32) == 0 { // +0
60				break
61			}
62			p.From.Type = obj.TYPE_MEM
63			p.From.Sym = ctxt.Float32Sym(f32)
64			p.From.Name = obj.NAME_EXTERN
65			p.From.Offset = 0
66		}
67
68	case AFMOVD:
69		if p.From.Type == obj.TYPE_FCONST {
70			f64 := p.From.Val.(float64)
71			if math.Float64bits(f64) == 0 { // +0
72				break
73			}
74			p.From.Type = obj.TYPE_MEM
75			p.From.Sym = ctxt.Float64Sym(f64)
76			p.From.Name = obj.NAME_EXTERN
77			p.From.Offset = 0
78		}
79
80		// put constants not loadable by LOAD IMMEDIATE into memory
81	case AMOVD:
82		if p.From.Type == obj.TYPE_CONST {
83			val := p.From.Offset
84			if int64(int32(val)) != val &&
85				int64(uint32(val)) != val &&
86				int64(uint64(val)&(0xffffffff<<32)) != val {
87				p.From.Type = obj.TYPE_MEM
88				p.From.Sym = ctxt.Int64Sym(p.From.Offset)
89				p.From.Name = obj.NAME_EXTERN
90				p.From.Offset = 0
91			}
92		}
93	}
94
95	// Rewrite SUB constants into ADD.
96	switch p.As {
97	case ASUBC:
98		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
99			p.From.Offset = -p.From.Offset
100			p.As = AADDC
101		}
102
103	case ASUB:
104		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
105			p.From.Offset = -p.From.Offset
106			p.As = AADD
107		}
108	}
109
110	if c.ctxt.Flag_dynlink {
111		c.rewriteToUseGot(p)
112	}
113}
114
115// Rewrite p, if necessary, to access global data via the global offset table.
116func (c *ctxtz) rewriteToUseGot(p *obj.Prog) {
117	// At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
118	// assembly code.
119	if p.As == AEXRL {
120		return
121	}
122
123	// We only care about global data: NAME_EXTERN means a global
124	// symbol in the Go sense, and p.Sym.Local is true for a few
125	// internally defined symbols.
126	// Rewrites must not clobber flags and therefore cannot use the
127	// ADD instruction.
128	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
129		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
130		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx or REGTMP2; MOVD $<off>(Rx or REGTMP2), Rx
131		if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
132			c.ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
133		}
134		p.From.Type = obj.TYPE_MEM
135		p.From.Name = obj.NAME_GOTREF
136		q := p
137		if p.From.Offset != 0 {
138			target := p.To.Reg
139			if target == REG_R0 {
140				// Cannot use R0 as input to address calculation.
141				// REGTMP might be used by the assembler.
142				p.To.Reg = REGTMP2
143			}
144			q = obj.Appendp(q, c.newprog)
145			q.As = AMOVD
146			q.From.Type = obj.TYPE_ADDR
147			q.From.Offset = p.From.Offset
148			q.From.Reg = p.To.Reg
149			q.To.Type = obj.TYPE_REG
150			q.To.Reg = target
151			p.From.Offset = 0
152		}
153	}
154	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
155		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
156	}
157	var source *obj.Addr
158	// MOVD sym, Ry becomes MOVD sym@GOT, REGTMP2; MOVD (REGTMP2), Ry
159	// MOVD Ry, sym becomes MOVD sym@GOT, REGTMP2; MOVD Ry, (REGTMP2)
160	// An addition may be inserted between the two MOVs if there is an offset.
161	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
162		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
163			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
164		}
165		source = &p.From
166	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
167		source = &p.To
168	} else {
169		return
170	}
171	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
172		return
173	}
174	if source.Sym.Type == objabi.STLSBSS {
175		return
176	}
177	if source.Type != obj.TYPE_MEM {
178		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
179	}
180	p1 := obj.Appendp(p, c.newprog)
181	p2 := obj.Appendp(p1, c.newprog)
182
183	p1.As = AMOVD
184	p1.From.Type = obj.TYPE_MEM
185	p1.From.Sym = source.Sym
186	p1.From.Name = obj.NAME_GOTREF
187	p1.To.Type = obj.TYPE_REG
188	p1.To.Reg = REGTMP2
189
190	p2.As = p.As
191	p2.From = p.From
192	p2.To = p.To
193	if p.From.Name == obj.NAME_EXTERN {
194		p2.From.Reg = REGTMP2
195		p2.From.Name = obj.NAME_NONE
196		p2.From.Sym = nil
197	} else if p.To.Name == obj.NAME_EXTERN {
198		p2.To.Reg = REGTMP2
199		p2.To.Name = obj.NAME_NONE
200		p2.To.Sym = nil
201	} else {
202		return
203	}
204	obj.Nopout(p)
205}
206
207func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
208	// TODO(minux): add morestack short-cuts with small fixed frame-size.
209	if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
210		return
211	}
212
213	c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog}
214
215	p := c.cursym.Func().Text
216	textstksiz := p.To.Offset
217	if textstksiz == -8 {
218		// Compatibility hack.
219		p.From.Sym.Set(obj.AttrNoFrame, true)
220		textstksiz = 0
221	}
222	if textstksiz%8 != 0 {
223		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
224	}
225	if p.From.Sym.NoFrame() {
226		if textstksiz != 0 {
227			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
228		}
229	}
230
231	c.cursym.Func().Args = p.To.Val.(int32)
232	c.cursym.Func().Locals = int32(textstksiz)
233
234	/*
235	 * find leaf subroutines
236	 * strip NOPs
237	 * expand RET
238	 */
239
240	var q *obj.Prog
241	for p := c.cursym.Func().Text; p != nil; p = p.Link {
242		switch p.As {
243		case obj.ATEXT:
244			q = p
245			p.Mark |= LEAF
246
247		case ABL, ABCL:
248			q = p
249			c.cursym.Func().Text.Mark &^= LEAF
250			fallthrough
251
252		case ABC,
253			ABRC,
254			ABEQ,
255			ABGE,
256			ABGT,
257			ABLE,
258			ABLT,
259			ABLEU,
260			ABLTU,
261			ABNE,
262			ABR,
263			ABVC,
264			ABVS,
265			ACRJ,
266			ACGRJ,
267			ACLRJ,
268			ACLGRJ,
269			ACIJ,
270			ACGIJ,
271			ACLIJ,
272			ACLGIJ,
273			ACMPBEQ,
274			ACMPBGE,
275			ACMPBGT,
276			ACMPBLE,
277			ACMPBLT,
278			ACMPBNE,
279			ACMPUBEQ,
280			ACMPUBGE,
281			ACMPUBGT,
282			ACMPUBLE,
283			ACMPUBLT,
284			ACMPUBNE:
285			q = p
286			p.Mark |= BRANCH
287
288		default:
289			q = p
290		}
291	}
292
293	autosize := int32(0)
294	var pLast *obj.Prog
295	var pPre *obj.Prog
296	var pPreempt *obj.Prog
297	var pCheck *obj.Prog
298	wasSplit := false
299	for p := c.cursym.Func().Text; p != nil; p = p.Link {
300		pLast = p
301		switch p.As {
302		case obj.ATEXT:
303			autosize = int32(textstksiz)
304
305			if p.Mark&LEAF != 0 && autosize == 0 {
306				// A leaf function with no locals has no frame.
307				p.From.Sym.Set(obj.AttrNoFrame, true)
308			}
309
310			if !p.From.Sym.NoFrame() {
311				// If there is a stack frame at all, it includes
312				// space to save the LR.
313				autosize += int32(c.ctxt.FixedFrameSize())
314			}
315
316			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
317				// A leaf function with a small stack can be marked
318				// NOSPLIT, avoiding a stack check.
319				p.From.Sym.Set(obj.AttrNoSplit, true)
320			}
321
322			p.To.Offset = int64(autosize)
323
324			q := p
325
326			if !p.From.Sym.NoSplit() {
327				p, pPreempt, pCheck = c.stacksplitPre(p, autosize) // emit pre part of split check
328				pPre = p
329				p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
330				wasSplit = true //need post part of split
331			}
332
333			if autosize != 0 {
334				// Make sure to save link register for non-empty frame, even if
335				// it is a leaf function, so that traceback works.
336				// Store link register before decrementing SP, so if a signal comes
337				// during the execution of the function prologue, the traceback
338				// code will not see a half-updated stack frame.
339				// This sequence is not async preemptible, as if we open a frame
340				// at the current SP, it will clobber the saved LR.
341				q = c.ctxt.StartUnsafePoint(p, c.newprog)
342
343				q = obj.Appendp(q, c.newprog)
344				q.As = AMOVD
345				q.From.Type = obj.TYPE_REG
346				q.From.Reg = REG_LR
347				q.To.Type = obj.TYPE_MEM
348				q.To.Reg = REGSP
349				q.To.Offset = int64(-autosize)
350
351				q = obj.Appendp(q, c.newprog)
352				q.As = AMOVD
353				q.From.Type = obj.TYPE_ADDR
354				q.From.Offset = int64(-autosize)
355				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
356				q.To.Type = obj.TYPE_REG
357				q.To.Reg = REGSP
358				q.Spadj = autosize
359
360				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
361			} else if c.cursym.Func().Text.Mark&LEAF == 0 {
362				// A very few functions that do not return to their caller
363				// (e.g. gogo) are not identified as leaves but still have
364				// no frame.
365				c.cursym.Func().Text.Mark |= LEAF
366			}
367
368			if c.cursym.Func().Text.Mark&LEAF != 0 {
369				c.cursym.Set(obj.AttrLeaf, true)
370				break
371			}
372
373			if c.cursym.Func().Text.From.Sym.Wrapper() {
374				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
375				//
376				//	MOVD g_panic(g), R3
377				//	CMP R3, $0
378				//	BEQ end
379				//	MOVD panic_argp(R3), R4
380				//	ADD $(autosize+8), R1, R5
381				//	CMP R4, R5
382				//	BNE end
383				//	ADD $8, R1, R6
384				//	MOVD R6, panic_argp(R3)
385				// end:
386				//	NOP
387				//
388				// The NOP is needed to give the jumps somewhere to land.
389				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
390
391				q = obj.Appendp(q, c.newprog)
392
393				q.As = AMOVD
394				q.From.Type = obj.TYPE_MEM
395				q.From.Reg = REGG
396				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
397				q.To.Type = obj.TYPE_REG
398				q.To.Reg = REG_R3
399
400				q = obj.Appendp(q, c.newprog)
401				q.As = ACMP
402				q.From.Type = obj.TYPE_REG
403				q.From.Reg = REG_R3
404				q.To.Type = obj.TYPE_CONST
405				q.To.Offset = 0
406
407				q = obj.Appendp(q, c.newprog)
408				q.As = ABEQ
409				q.To.Type = obj.TYPE_BRANCH
410				p1 := q
411
412				q = obj.Appendp(q, c.newprog)
413				q.As = AMOVD
414				q.From.Type = obj.TYPE_MEM
415				q.From.Reg = REG_R3
416				q.From.Offset = 0 // Panic.argp
417				q.To.Type = obj.TYPE_REG
418				q.To.Reg = REG_R4
419
420				q = obj.Appendp(q, c.newprog)
421				q.As = AADD
422				q.From.Type = obj.TYPE_CONST
423				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
424				q.Reg = REGSP
425				q.To.Type = obj.TYPE_REG
426				q.To.Reg = REG_R5
427
428				q = obj.Appendp(q, c.newprog)
429				q.As = ACMP
430				q.From.Type = obj.TYPE_REG
431				q.From.Reg = REG_R4
432				q.To.Type = obj.TYPE_REG
433				q.To.Reg = REG_R5
434
435				q = obj.Appendp(q, c.newprog)
436				q.As = ABNE
437				q.To.Type = obj.TYPE_BRANCH
438				p2 := q
439
440				q = obj.Appendp(q, c.newprog)
441				q.As = AADD
442				q.From.Type = obj.TYPE_CONST
443				q.From.Offset = c.ctxt.FixedFrameSize()
444				q.Reg = REGSP
445				q.To.Type = obj.TYPE_REG
446				q.To.Reg = REG_R6
447
448				q = obj.Appendp(q, c.newprog)
449				q.As = AMOVD
450				q.From.Type = obj.TYPE_REG
451				q.From.Reg = REG_R6
452				q.To.Type = obj.TYPE_MEM
453				q.To.Reg = REG_R3
454				q.To.Offset = 0 // Panic.argp
455
456				q = obj.Appendp(q, c.newprog)
457
458				q.As = obj.ANOP
459				p1.To.SetTarget(q)
460				p2.To.SetTarget(q)
461			}
462
463		case obj.ARET:
464			retTarget := p.To.Sym
465
466			if c.cursym.Func().Text.Mark&LEAF != 0 {
467				if autosize == 0 {
468					p.As = ABR
469					p.From = obj.Addr{}
470					if retTarget == nil {
471						p.To.Type = obj.TYPE_REG
472						p.To.Reg = REG_LR
473					} else {
474						p.To.Type = obj.TYPE_BRANCH
475						p.To.Sym = retTarget
476					}
477					p.Mark |= BRANCH
478					break
479				}
480
481				p.As = AADD
482				p.From.Type = obj.TYPE_CONST
483				p.From.Offset = int64(autosize)
484				p.To.Type = obj.TYPE_REG
485				p.To.Reg = REGSP
486				p.Spadj = -autosize
487
488				q = obj.Appendp(p, c.newprog)
489				q.As = ABR
490				q.From = obj.Addr{}
491				if retTarget == nil {
492					q.To.Type = obj.TYPE_REG
493					q.To.Reg = REG_LR
494				} else {
495					q.To.Type = obj.TYPE_BRANCH
496					q.To.Sym = retTarget
497				}
498				q.Mark |= BRANCH
499				q.Spadj = autosize
500				break
501			}
502
503			p.As = AMOVD
504			p.From.Type = obj.TYPE_MEM
505			p.From.Reg = REGSP
506			p.From.Offset = 0
507			p.To = obj.Addr{
508				Type: obj.TYPE_REG,
509				Reg:  REG_LR,
510			}
511
512			q = p
513
514			if autosize != 0 {
515				q = obj.Appendp(q, c.newprog)
516				q.As = AADD
517				q.From.Type = obj.TYPE_CONST
518				q.From.Offset = int64(autosize)
519				q.To.Type = obj.TYPE_REG
520				q.To.Reg = REGSP
521				q.Spadj = -autosize
522			}
523
524			q = obj.Appendp(q, c.newprog)
525			q.As = ABR
526			q.From = obj.Addr{}
527			if retTarget == nil {
528				q.To.Type = obj.TYPE_REG
529				q.To.Reg = REG_LR
530			} else {
531				q.To.Type = obj.TYPE_BRANCH
532				q.To.Sym = retTarget
533			}
534			q.Mark |= BRANCH
535			q.Spadj = autosize
536
537		case AADD:
538			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
539				p.Spadj = int32(-p.From.Offset)
540			}
541
542		case obj.AGETCALLERPC:
543			if cursym.Leaf() {
544				/* MOVD LR, Rd */
545				p.As = AMOVD
546				p.From.Type = obj.TYPE_REG
547				p.From.Reg = REG_LR
548			} else {
549				/* MOVD (RSP), Rd */
550				p.As = AMOVD
551				p.From.Type = obj.TYPE_MEM
552				p.From.Reg = REGSP
553			}
554		}
555
556		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
557			f := c.cursym.Func()
558			if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
559				c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
560				if ctxt.Debugvlog || !ctxt.IsAsm {
561					ctxt.Logf("auto-SPWRITE: %s\n", c.cursym.Name)
562					if !ctxt.IsAsm {
563						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
564						ctxt.DiagFlush()
565						log.Fatalf("bad SPWRITE")
566					}
567				}
568			}
569		}
570	}
571	if wasSplit {
572		c.stacksplitPost(pLast, pPre, pPreempt, pCheck, autosize) // emit post part of split check
573	}
574}
575
576// stacksplitPre generates the function stack check prologue following
577// Prog p (which should be the TEXT Prog). It returns one or two
578// branch Progs that must be patched to jump to the morestack epilogue,
579// and the Prog that starts the morestack check.
580func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCheck *obj.Prog) {
581	if c.ctxt.Flag_maymorestack != "" {
582		// Save LR and REGCTXT
583		const frameSize = 16
584		p = c.ctxt.StartUnsafePoint(p, c.newprog)
585		// MOVD LR, -16(SP)
586		p = obj.Appendp(p, c.newprog)
587		p.As = AMOVD
588		p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
589		p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: -frameSize}
590		// MOVD $-16(SP), SP
591		p = obj.Appendp(p, c.newprog)
592		p.As = AMOVD
593		p.From = obj.Addr{Type: obj.TYPE_ADDR, Offset: -frameSize, Reg: REGSP}
594		p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
595		p.Spadj = frameSize
596		// MOVD REGCTXT, 8(SP)
597		p = obj.Appendp(p, c.newprog)
598		p.As = AMOVD
599		p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT}
600		p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8}
601
602		// BL maymorestack
603		p = obj.Appendp(p, c.newprog)
604		p.As = ABL
605		// See ../x86/obj6.go
606		sym := c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
607		p.To = obj.Addr{Type: obj.TYPE_BRANCH, Sym: sym}
608
609		// Restore LR and REGCTXT
610
611		// MOVD REGCTXT, 8(SP)
612		p = obj.Appendp(p, c.newprog)
613		p.As = AMOVD
614		p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8}
615		p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT}
616		// MOVD (SP), LR
617		p = obj.Appendp(p, c.newprog)
618		p.As = AMOVD
619		p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 0}
620		p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
621		// MOVD $16(SP), SP
622		p = obj.Appendp(p, c.newprog)
623		p.As = AMOVD
624		p.From = obj.Addr{Type: obj.TYPE_CONST, Reg: REGSP, Offset: frameSize}
625		p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
626		p.Spadj = -frameSize
627
628		p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
629	}
630
631	// MOVD	g_stackguard(g), R3
632	p = obj.Appendp(p, c.newprog)
633	// Jump back to here after morestack returns.
634	pCheck = p
635
636	p.As = AMOVD
637	p.From.Type = obj.TYPE_MEM
638	p.From.Reg = REGG
639	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
640	if c.cursym.CFunc() {
641		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
642	}
643	p.To.Type = obj.TYPE_REG
644	p.To.Reg = REG_R3
645
646	// Mark the stack bound check and morestack call async nonpreemptible.
647	// If we get preempted here, when resumed the preemption request is
648	// cleared, but we'll still call morestack, which will double the stack
649	// unnecessarily. See issue #35470.
650	p = c.ctxt.StartUnsafePoint(p, c.newprog)
651
652	if framesize <= objabi.StackSmall {
653		// small stack: SP < stackguard
654		//	CMPUBGE	stackguard, SP, label-of-call-to-morestack
655
656		p = obj.Appendp(p, c.newprog)
657		p.From.Type = obj.TYPE_REG
658		p.From.Reg = REG_R3
659		p.Reg = REGSP
660		p.As = ACMPUBGE
661		p.To.Type = obj.TYPE_BRANCH
662
663		return p, nil, pCheck
664	}
665
666	// large stack: SP-framesize < stackguard-StackSmall
667
668	offset := int64(framesize) - objabi.StackSmall
669	if framesize > objabi.StackBig {
670		// Such a large stack we need to protect against underflow.
671		// The runtime guarantees SP > objabi.StackBig, but
672		// framesize is large enough that SP-framesize may
673		// underflow, causing a direct comparison with the
674		// stack guard to incorrectly succeed. We explicitly
675		// guard against underflow.
676		//
677		//	MOVD	$(framesize-StackSmall), R4
678		//	CMPUBLT	SP, R4, label-of-call-to-morestack
679
680		p = obj.Appendp(p, c.newprog)
681		p.As = AMOVD
682		p.From.Type = obj.TYPE_CONST
683		p.From.Offset = offset
684		p.To.Type = obj.TYPE_REG
685		p.To.Reg = REG_R4
686
687		p = obj.Appendp(p, c.newprog)
688		pPreempt = p
689		p.As = ACMPUBLT
690		p.From.Type = obj.TYPE_REG
691		p.From.Reg = REGSP
692		p.Reg = REG_R4
693		p.To.Type = obj.TYPE_BRANCH
694	}
695
696	// Check against the stack guard. We've ensured this won't underflow.
697	//	ADD $-(framesize-StackSmall), SP, R4
698	//	CMPUBGE stackguard, R4, label-of-call-to-morestack
699	p = obj.Appendp(p, c.newprog)
700	p.As = AADD
701	p.From.Type = obj.TYPE_CONST
702	p.From.Offset = -offset
703	p.Reg = REGSP
704	p.To.Type = obj.TYPE_REG
705	p.To.Reg = REG_R4
706
707	p = obj.Appendp(p, c.newprog)
708	p.From.Type = obj.TYPE_REG
709	p.From.Reg = REG_R3
710	p.Reg = REG_R4
711	p.As = ACMPUBGE
712	p.To.Type = obj.TYPE_BRANCH
713
714	return p, pPreempt, pCheck
715}
716
717// stacksplitPost generates the function epilogue that calls morestack
718// and returns the new last instruction in the function.
719//
720// p is the last Prog in the function. pPre and pPreempt, if non-nil,
721// are the instructions that branch to the epilogue. This will fill in
722// their branch targets. pCheck is the Prog that begins the stack check.
723func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, framesize int32) *obj.Prog {
724	// Now we are at the end of the function, but logically
725	// we are still in function prologue. We need to fix the
726	// SP data and PCDATA.
727	spfix := obj.Appendp(p, c.newprog)
728	spfix.As = obj.ANOP
729	spfix.Spadj = -framesize
730
731	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
732	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
733
734	// MOVD	LR, R5
735	p = obj.Appendp(pcdata, c.newprog)
736	pPre.To.SetTarget(p)
737	p.As = AMOVD
738	p.From.Type = obj.TYPE_REG
739	p.From.Reg = REG_LR
740	p.To.Type = obj.TYPE_REG
741	p.To.Reg = REG_R5
742	if pPreempt != nil {
743		pPreempt.To.SetTarget(p)
744	}
745
746	// BL	runtime.morestack(SB)
747	p = obj.Appendp(p, c.newprog)
748
749	p.As = ABL
750	p.To.Type = obj.TYPE_BRANCH
751	if c.cursym.CFunc() {
752		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
753	} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
754		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
755	} else {
756		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
757	}
758
759	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
760
761	// BR	pCheck
762	p = obj.Appendp(p, c.newprog)
763
764	p.As = ABR
765	p.To.Type = obj.TYPE_BRANCH
766	p.To.SetTarget(pCheck)
767	return p
768}
769
770var unaryDst = map[obj.As]bool{
771	ASTCK:  true,
772	ASTCKC: true,
773	ASTCKE: true,
774	ASTCKF: true,
775	ANEG:   true,
776	ANEGW:  true,
777	AVONE:  true,
778	AVZERO: true,
779}
780
781var Links390x = obj.LinkArch{
782	Arch:           sys.ArchS390X,
783	Init:           buildop,
784	Preprocess:     preprocess,
785	Assemble:       spanz,
786	Progedit:       progedit,
787	UnaryDst:       unaryDst,
788	DWARFRegisters: S390XDWARFRegisters,
789}
790