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