1// cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
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 ppc64
31
32import (
33	"cmd/internal/obj"
34	"cmd/internal/objabi"
35	"cmd/internal/sys"
36)
37
38func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
39	p.From.Class = 0
40	p.To.Class = 0
41
42	c := ctxt9{ctxt: ctxt, newprog: newprog}
43
44	// Rewrite BR/BL to symbol as TYPE_BRANCH.
45	switch p.As {
46	case ABR,
47		ABL,
48		obj.ARET,
49		obj.ADUFFZERO,
50		obj.ADUFFCOPY:
51		if p.To.Sym != nil {
52			p.To.Type = obj.TYPE_BRANCH
53		}
54	}
55
56	// Rewrite float constants to values stored in memory.
57	switch p.As {
58	case AFMOVS:
59		if p.From.Type == obj.TYPE_FCONST {
60			f32 := float32(p.From.Val.(float64))
61			p.From.Type = obj.TYPE_MEM
62			p.From.Sym = ctxt.Float32Sym(f32)
63			p.From.Name = obj.NAME_EXTERN
64			p.From.Offset = 0
65		}
66
67	case AFMOVD:
68		if p.From.Type == obj.TYPE_FCONST {
69			f64 := p.From.Val.(float64)
70			p.From.Type = obj.TYPE_MEM
71			p.From.Sym = ctxt.Float64Sym(f64)
72			p.From.Name = obj.NAME_EXTERN
73			p.From.Offset = 0
74		}
75
76		// Put >32-bit constants in memory and load them
77	case AMOVD:
78		if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
79			p.From.Type = obj.TYPE_MEM
80			p.From.Sym = ctxt.Int64Sym(p.From.Offset)
81			p.From.Name = obj.NAME_EXTERN
82			p.From.Offset = 0
83		}
84	}
85
86	// Rewrite SUB constants into ADD.
87	switch p.As {
88	case ASUBC:
89		if p.From.Type == obj.TYPE_CONST {
90			p.From.Offset = -p.From.Offset
91			p.As = AADDC
92		}
93
94	case ASUBCCC:
95		if p.From.Type == obj.TYPE_CONST {
96			p.From.Offset = -p.From.Offset
97			p.As = AADDCCC
98		}
99
100	case ASUB:
101		if p.From.Type == obj.TYPE_CONST {
102			p.From.Offset = -p.From.Offset
103			p.As = AADD
104		}
105	}
106	if c.ctxt.Flag_dynlink {
107		c.rewriteToUseGot(p)
108	}
109}
110
111// Rewrite p, if necessary, to access global data via the global offset table.
112func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
113	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
114		//     ADUFFxxx $offset
115		// becomes
116		//     MOVD runtime.duffxxx@GOT, R12
117		//     ADD $offset, R12
118		//     MOVD R12, CTR
119		//     BL (CTR)
120		var sym *obj.LSym
121		if p.As == obj.ADUFFZERO {
122			sym = c.ctxt.Lookup("runtime.duffzero")
123		} else {
124			sym = c.ctxt.Lookup("runtime.duffcopy")
125		}
126		offset := p.To.Offset
127		p.As = AMOVD
128		p.From.Type = obj.TYPE_MEM
129		p.From.Name = obj.NAME_GOTREF
130		p.From.Sym = sym
131		p.To.Type = obj.TYPE_REG
132		p.To.Reg = REG_R12
133		p.To.Name = obj.NAME_NONE
134		p.To.Offset = 0
135		p.To.Sym = nil
136		p1 := obj.Appendp(p, c.newprog)
137		p1.As = AADD
138		p1.From.Type = obj.TYPE_CONST
139		p1.From.Offset = offset
140		p1.To.Type = obj.TYPE_REG
141		p1.To.Reg = REG_R12
142		p2 := obj.Appendp(p1, c.newprog)
143		p2.As = AMOVD
144		p2.From.Type = obj.TYPE_REG
145		p2.From.Reg = REG_R12
146		p2.To.Type = obj.TYPE_REG
147		p2.To.Reg = REG_CTR
148		p3 := obj.Appendp(p2, c.newprog)
149		p3.As = obj.ACALL
150		p3.From.Type = obj.TYPE_REG
151		p3.From.Reg = REG_R12
152		p3.To.Type = obj.TYPE_REG
153		p3.To.Reg = REG_CTR
154	}
155
156	// We only care about global data: NAME_EXTERN means a global
157	// symbol in the Go sense, and p.Sym.Local is true for a few
158	// internally defined symbols.
159	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
160		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
161		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
162		if p.As != AMOVD {
163			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
164		}
165		if p.To.Type != obj.TYPE_REG {
166			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
167		}
168		p.From.Type = obj.TYPE_MEM
169		p.From.Name = obj.NAME_GOTREF
170		if p.From.Offset != 0 {
171			q := obj.Appendp(p, c.newprog)
172			q.As = AADD
173			q.From.Type = obj.TYPE_CONST
174			q.From.Offset = p.From.Offset
175			q.To = p.To
176			p.From.Offset = 0
177		}
178	}
179	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
180		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
181	}
182	var source *obj.Addr
183	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
184	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
185	// An addition may be inserted between the two MOVs if there is an offset.
186	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
187		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
188			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
189		}
190		source = &p.From
191	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
192		source = &p.To
193	} else {
194		return
195	}
196	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
197		return
198	}
199	if source.Sym.Type == objabi.STLSBSS {
200		return
201	}
202	if source.Type != obj.TYPE_MEM {
203		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
204	}
205	p1 := obj.Appendp(p, c.newprog)
206	p2 := obj.Appendp(p1, c.newprog)
207
208	p1.As = AMOVD
209	p1.From.Type = obj.TYPE_MEM
210	p1.From.Sym = source.Sym
211	p1.From.Name = obj.NAME_GOTREF
212	p1.To.Type = obj.TYPE_REG
213	p1.To.Reg = REGTMP
214
215	p2.As = p.As
216	p2.From = p.From
217	p2.To = p.To
218	if p.From.Name == obj.NAME_EXTERN {
219		p2.From.Reg = REGTMP
220		p2.From.Name = obj.NAME_NONE
221		p2.From.Sym = nil
222	} else if p.To.Name == obj.NAME_EXTERN {
223		p2.To.Reg = REGTMP
224		p2.To.Name = obj.NAME_NONE
225		p2.To.Sym = nil
226	} else {
227		return
228	}
229	obj.Nopout(p)
230}
231
232func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
233	// TODO(minux): add morestack short-cuts with small fixed frame-size.
234	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
235		return
236	}
237
238	c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
239
240	p := c.cursym.Func.Text
241	textstksiz := p.To.Offset
242	if textstksiz == -8 {
243		// Compatibility hack.
244		p.From.Sym.Set(obj.AttrNoFrame, true)
245		textstksiz = 0
246	}
247	if textstksiz%8 != 0 {
248		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
249	}
250	if p.From.Sym.NoFrame() {
251		if textstksiz != 0 {
252			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
253		}
254	}
255
256	c.cursym.Func.Args = p.To.Val.(int32)
257	c.cursym.Func.Locals = int32(textstksiz)
258
259	/*
260	 * find leaf subroutines
261	 * strip NOPs
262	 * expand RET
263	 * expand BECOME pseudo
264	 */
265
266	var q *obj.Prog
267	var q1 *obj.Prog
268	for p := c.cursym.Func.Text; p != nil; p = p.Link {
269		switch p.As {
270		/* too hard, just leave alone */
271		case obj.ATEXT:
272			q = p
273
274			p.Mark |= LABEL | LEAF | SYNC
275			if p.Link != nil {
276				p.Link.Mark |= LABEL
277			}
278
279		case ANOR:
280			q = p
281			if p.To.Type == obj.TYPE_REG {
282				if p.To.Reg == REGZERO {
283					p.Mark |= LABEL | SYNC
284				}
285			}
286
287		case ALWAR,
288			ALBAR,
289			ASTBCCC,
290			ASTWCCC,
291			AECIWX,
292			AECOWX,
293			AEIEIO,
294			AICBI,
295			AISYNC,
296			ATLBIE,
297			ATLBIEL,
298			ASLBIA,
299			ASLBIE,
300			ASLBMFEE,
301			ASLBMFEV,
302			ASLBMTE,
303			ADCBF,
304			ADCBI,
305			ADCBST,
306			ADCBT,
307			ADCBTST,
308			ADCBZ,
309			ASYNC,
310			ATLBSYNC,
311			APTESYNC,
312			ALWSYNC,
313			ATW,
314			AWORD,
315			ARFI,
316			ARFCI,
317			ARFID,
318			AHRFID:
319			q = p
320			p.Mark |= LABEL | SYNC
321			continue
322
323		case AMOVW, AMOVWZ, AMOVD:
324			q = p
325			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
326				p.Mark |= LABEL | SYNC
327			}
328			continue
329
330		case AFABS,
331			AFABSCC,
332			AFADD,
333			AFADDCC,
334			AFCTIW,
335			AFCTIWCC,
336			AFCTIWZ,
337			AFCTIWZCC,
338			AFDIV,
339			AFDIVCC,
340			AFMADD,
341			AFMADDCC,
342			AFMOVD,
343			AFMOVDU,
344			/* case AFMOVDS: */
345			AFMOVS,
346			AFMOVSU,
347
348			/* case AFMOVSD: */
349			AFMSUB,
350			AFMSUBCC,
351			AFMUL,
352			AFMULCC,
353			AFNABS,
354			AFNABSCC,
355			AFNEG,
356			AFNEGCC,
357			AFNMADD,
358			AFNMADDCC,
359			AFNMSUB,
360			AFNMSUBCC,
361			AFRSP,
362			AFRSPCC,
363			AFSUB,
364			AFSUBCC:
365			q = p
366
367			p.Mark |= FLOAT
368			continue
369
370		case ABL,
371			ABCL,
372			obj.ADUFFZERO,
373			obj.ADUFFCOPY:
374			c.cursym.Func.Text.Mark &^= LEAF
375			fallthrough
376
377		case ABC,
378			ABEQ,
379			ABGE,
380			ABGT,
381			ABLE,
382			ABLT,
383			ABNE,
384			ABR,
385			ABVC,
386			ABVS:
387			p.Mark |= BRANCH
388			q = p
389			q1 = p.Pcond
390			if q1 != nil {
391				for q1.As == obj.ANOP {
392					q1 = q1.Link
393					p.Pcond = q1
394				}
395
396				if q1.Mark&LEAF == 0 {
397					q1.Mark |= LABEL
398				}
399			} else {
400				p.Mark |= LABEL
401			}
402			q1 = p.Link
403			if q1 != nil {
404				q1.Mark |= LABEL
405			}
406			continue
407
408		case AFCMPO, AFCMPU:
409			q = p
410			p.Mark |= FCMP | FLOAT
411			continue
412
413		case obj.ARET:
414			q = p
415			if p.Link != nil {
416				p.Link.Mark |= LABEL
417			}
418			continue
419
420		case obj.ANOP:
421			q1 = p.Link
422			q.Link = q1 /* q is non-nop */
423			q1.Mark |= p.Mark
424			continue
425
426		default:
427			q = p
428			continue
429		}
430	}
431
432	autosize := int32(0)
433	var p1 *obj.Prog
434	var p2 *obj.Prog
435	for p := c.cursym.Func.Text; p != nil; p = p.Link {
436		o := p.As
437		switch o {
438		case obj.ATEXT:
439			autosize = int32(textstksiz)
440
441			if p.Mark&LEAF != 0 && autosize == 0 {
442				// A leaf function with no locals has no frame.
443				p.From.Sym.Set(obj.AttrNoFrame, true)
444			}
445
446			if !p.From.Sym.NoFrame() {
447				// If there is a stack frame at all, it includes
448				// space to save the LR.
449				autosize += int32(c.ctxt.FixedFrameSize())
450			}
451
452			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
453				// A leaf function with a small stack can be marked
454				// NOSPLIT, avoiding a stack check.
455				p.From.Sym.Set(obj.AttrNoSplit, true)
456			}
457
458			p.To.Offset = int64(autosize)
459
460			q = p
461
462			if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
463				// When compiling Go into PIC, all functions must start
464				// with instructions to load the TOC pointer into r2:
465				//
466				//	addis r2, r12, .TOC.-func@ha
467				//	addi r2, r2, .TOC.-func@l+4
468				//
469				// We could probably skip this prologue in some situations
470				// but it's a bit subtle. However, it is both safe and
471				// necessary to leave the prologue off duffzero and
472				// duffcopy as we rely on being able to jump to a specific
473				// instruction offset for them.
474				//
475				// These are AWORDS because there is no (afaict) way to
476				// generate the addis instruction except as part of the
477				// load of a large constant, and in that case there is no
478				// way to use r12 as the source.
479				//
480				// Note that the same condition is tested in
481				// putelfsym in cmd/link/internal/ld/symtab.go
482				// where we set the st_other field to indicate
483				// the presence of these instructions.
484				q = obj.Appendp(q, c.newprog)
485				q.As = AWORD
486				q.Pos = p.Pos
487				q.From.Type = obj.TYPE_CONST
488				q.From.Offset = 0x3c4c0000
489				q = obj.Appendp(q, c.newprog)
490				q.As = AWORD
491				q.Pos = p.Pos
492				q.From.Type = obj.TYPE_CONST
493				q.From.Offset = 0x38420000
494				rel := obj.Addrel(c.cursym)
495				rel.Off = 0
496				rel.Siz = 8
497				rel.Sym = c.ctxt.Lookup(".TOC.")
498				rel.Type = objabi.R_ADDRPOWER_PCREL
499			}
500
501			if !c.cursym.Func.Text.From.Sym.NoSplit() {
502				q = c.stacksplit(q, autosize) // emit split check
503			}
504
505			if autosize != 0 {
506				// Save the link register and update the SP.  MOVDU is used unless
507				// the frame size is too large.  The link register must be saved
508				// even for non-empty leaf functions so that traceback works.
509				if autosize >= -BIG && autosize <= BIG {
510					// Use MOVDU to adjust R1 when saving R31, if autosize is small.
511					q = obj.Appendp(q, c.newprog)
512					q.As = AMOVD
513					q.Pos = p.Pos
514					q.From.Type = obj.TYPE_REG
515					q.From.Reg = REG_LR
516					q.To.Type = obj.TYPE_REG
517					q.To.Reg = REGTMP
518
519					q = obj.Appendp(q, c.newprog)
520					q.As = AMOVDU
521					q.Pos = p.Pos
522					q.From.Type = obj.TYPE_REG
523					q.From.Reg = REGTMP
524					q.To.Type = obj.TYPE_MEM
525					q.To.Offset = int64(-autosize)
526					q.To.Reg = REGSP
527					q.Spadj = int32(autosize)
528				} else {
529					// Frame size is too large for a MOVDU instruction.
530					// Store link register before decrementing SP, so if a signal comes
531					// during the execution of the function prologue, the traceback
532					// code will not see a half-updated stack frame.
533					q = obj.Appendp(q, c.newprog)
534					q.As = AMOVD
535					q.Pos = p.Pos
536					q.From.Type = obj.TYPE_REG
537					q.From.Reg = REG_LR
538					q.To.Type = obj.TYPE_REG
539					q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
540
541					q = obj.Appendp(q, c.newprog)
542					q.As = AMOVD
543					q.Pos = p.Pos
544					q.From.Type = obj.TYPE_REG
545					q.From.Reg = REG_R29
546					q.To.Type = obj.TYPE_MEM
547					q.To.Offset = int64(-autosize)
548					q.To.Reg = REGSP
549
550					q = obj.Appendp(q, c.newprog)
551					q.As = AADD
552					q.Pos = p.Pos
553					q.From.Type = obj.TYPE_CONST
554					q.From.Offset = int64(-autosize)
555					q.To.Type = obj.TYPE_REG
556					q.To.Reg = REGSP
557					q.Spadj = +autosize
558				}
559			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
560				// A very few functions that do not return to their caller
561				// (e.g. gogo) are not identified as leaves but still have
562				// no frame.
563				c.cursym.Func.Text.Mark |= LEAF
564			}
565
566			if c.cursym.Func.Text.Mark&LEAF != 0 {
567				c.cursym.Set(obj.AttrLeaf, true)
568				break
569			}
570
571			if c.ctxt.Flag_shared {
572				q = obj.Appendp(q, c.newprog)
573				q.As = AMOVD
574				q.Pos = p.Pos
575				q.From.Type = obj.TYPE_REG
576				q.From.Reg = REG_R2
577				q.To.Type = obj.TYPE_MEM
578				q.To.Reg = REGSP
579				q.To.Offset = 24
580			}
581
582			if c.cursym.Func.Text.From.Sym.Wrapper() {
583				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
584				//
585				//	MOVD g_panic(g), R3
586				//	CMP R0, R3
587				//	BEQ end
588				//	MOVD panic_argp(R3), R4
589				//	ADD $(autosize+8), R1, R5
590				//	CMP R4, R5
591				//	BNE end
592				//	ADD $8, R1, R6
593				//	MOVD R6, panic_argp(R3)
594				// end:
595				//	NOP
596				//
597				// The NOP is needed to give the jumps somewhere to land.
598				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
599
600				q = obj.Appendp(q, c.newprog)
601
602				q.As = AMOVD
603				q.From.Type = obj.TYPE_MEM
604				q.From.Reg = REGG
605				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
606				q.To.Type = obj.TYPE_REG
607				q.To.Reg = REG_R3
608
609				q = obj.Appendp(q, c.newprog)
610				q.As = ACMP
611				q.From.Type = obj.TYPE_REG
612				q.From.Reg = REG_R0
613				q.To.Type = obj.TYPE_REG
614				q.To.Reg = REG_R3
615
616				q = obj.Appendp(q, c.newprog)
617				q.As = ABEQ
618				q.To.Type = obj.TYPE_BRANCH
619				p1 = q
620
621				q = obj.Appendp(q, c.newprog)
622				q.As = AMOVD
623				q.From.Type = obj.TYPE_MEM
624				q.From.Reg = REG_R3
625				q.From.Offset = 0 // Panic.argp
626				q.To.Type = obj.TYPE_REG
627				q.To.Reg = REG_R4
628
629				q = obj.Appendp(q, c.newprog)
630				q.As = AADD
631				q.From.Type = obj.TYPE_CONST
632				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
633				q.Reg = REGSP
634				q.To.Type = obj.TYPE_REG
635				q.To.Reg = REG_R5
636
637				q = obj.Appendp(q, c.newprog)
638				q.As = ACMP
639				q.From.Type = obj.TYPE_REG
640				q.From.Reg = REG_R4
641				q.To.Type = obj.TYPE_REG
642				q.To.Reg = REG_R5
643
644				q = obj.Appendp(q, c.newprog)
645				q.As = ABNE
646				q.To.Type = obj.TYPE_BRANCH
647				p2 = q
648
649				q = obj.Appendp(q, c.newprog)
650				q.As = AADD
651				q.From.Type = obj.TYPE_CONST
652				q.From.Offset = c.ctxt.FixedFrameSize()
653				q.Reg = REGSP
654				q.To.Type = obj.TYPE_REG
655				q.To.Reg = REG_R6
656
657				q = obj.Appendp(q, c.newprog)
658				q.As = AMOVD
659				q.From.Type = obj.TYPE_REG
660				q.From.Reg = REG_R6
661				q.To.Type = obj.TYPE_MEM
662				q.To.Reg = REG_R3
663				q.To.Offset = 0 // Panic.argp
664
665				q = obj.Appendp(q, c.newprog)
666
667				q.As = obj.ANOP
668				p1.Pcond = q
669				p2.Pcond = q
670			}
671
672		case obj.ARET:
673			if p.From.Type == obj.TYPE_CONST {
674				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
675				break
676			}
677
678			retTarget := p.To.Sym
679
680			if c.cursym.Func.Text.Mark&LEAF != 0 {
681				if autosize == 0 {
682					p.As = ABR
683					p.From = obj.Addr{}
684					if retTarget == nil {
685						p.To.Type = obj.TYPE_REG
686						p.To.Reg = REG_LR
687					} else {
688						p.To.Type = obj.TYPE_BRANCH
689						p.To.Sym = retTarget
690					}
691					p.Mark |= BRANCH
692					break
693				}
694
695				p.As = AADD
696				p.From.Type = obj.TYPE_CONST
697				p.From.Offset = int64(autosize)
698				p.To.Type = obj.TYPE_REG
699				p.To.Reg = REGSP
700				p.Spadj = -autosize
701
702				q = c.newprog()
703				q.As = ABR
704				q.Pos = p.Pos
705				q.To.Type = obj.TYPE_REG
706				q.To.Reg = REG_LR
707				q.Mark |= BRANCH
708				q.Spadj = +autosize
709
710				q.Link = p.Link
711				p.Link = q
712				break
713			}
714
715			p.As = AMOVD
716			p.From.Type = obj.TYPE_MEM
717			p.From.Offset = 0
718			p.From.Reg = REGSP
719			p.To.Type = obj.TYPE_REG
720			p.To.Reg = REGTMP
721
722			q = c.newprog()
723			q.As = AMOVD
724			q.Pos = p.Pos
725			q.From.Type = obj.TYPE_REG
726			q.From.Reg = REGTMP
727			q.To.Type = obj.TYPE_REG
728			q.To.Reg = REG_LR
729
730			q.Link = p.Link
731			p.Link = q
732			p = q
733
734			if false {
735				// Debug bad returns
736				q = c.newprog()
737
738				q.As = AMOVD
739				q.Pos = p.Pos
740				q.From.Type = obj.TYPE_MEM
741				q.From.Offset = 0
742				q.From.Reg = REGTMP
743				q.To.Type = obj.TYPE_REG
744				q.To.Reg = REGTMP
745
746				q.Link = p.Link
747				p.Link = q
748				p = q
749			}
750
751			if autosize != 0 {
752				q = c.newprog()
753				q.As = AADD
754				q.Pos = p.Pos
755				q.From.Type = obj.TYPE_CONST
756				q.From.Offset = int64(autosize)
757				q.To.Type = obj.TYPE_REG
758				q.To.Reg = REGSP
759				q.Spadj = -autosize
760
761				q.Link = p.Link
762				p.Link = q
763			}
764
765			q1 = c.newprog()
766			q1.As = ABR
767			q1.Pos = p.Pos
768			if retTarget == nil {
769				q1.To.Type = obj.TYPE_REG
770				q1.To.Reg = REG_LR
771			} else {
772				q1.To.Type = obj.TYPE_BRANCH
773				q1.To.Sym = retTarget
774			}
775			q1.Mark |= BRANCH
776			q1.Spadj = +autosize
777
778			q1.Link = q.Link
779			q.Link = q1
780		case AADD:
781			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
782				p.Spadj = int32(-p.From.Offset)
783			}
784		}
785	}
786}
787
788/*
789// instruction scheduling
790	if(debug['Q'] == 0)
791		return;
792
793	curtext = nil;
794	q = nil;	// p - 1
795	q1 = firstp;	// top of block
796	o = 0;		// count of instructions
797	for(p = firstp; p != nil; p = p1) {
798		p1 = p->link;
799		o++;
800		if(p->mark & NOSCHED){
801			if(q1 != p){
802				sched(q1, q);
803			}
804			for(; p != nil; p = p->link){
805				if(!(p->mark & NOSCHED))
806					break;
807				q = p;
808			}
809			p1 = p;
810			q1 = p;
811			o = 0;
812			continue;
813		}
814		if(p->mark & (LABEL|SYNC)) {
815			if(q1 != p)
816				sched(q1, q);
817			q1 = p;
818			o = 1;
819		}
820		if(p->mark & (BRANCH|SYNC)) {
821			sched(q1, p);
822			q1 = p1;
823			o = 0;
824		}
825		if(o >= NSCHED) {
826			sched(q1, p);
827			q1 = p1;
828			o = 0;
829		}
830		q = p;
831	}
832*/
833func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
834	p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
835
836	// MOVD	g_stackguard(g), R3
837	p = obj.Appendp(p, c.newprog)
838
839	p.As = AMOVD
840	p.From.Type = obj.TYPE_MEM
841	p.From.Reg = REGG
842	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
843	if c.cursym.CFunc() {
844		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
845	}
846	p.To.Type = obj.TYPE_REG
847	p.To.Reg = REG_R3
848
849	var q *obj.Prog
850	if framesize <= objabi.StackSmall {
851		// small stack: SP < stackguard
852		//	CMP	stackguard, SP
853		p = obj.Appendp(p, c.newprog)
854
855		p.As = ACMPU
856		p.From.Type = obj.TYPE_REG
857		p.From.Reg = REG_R3
858		p.To.Type = obj.TYPE_REG
859		p.To.Reg = REGSP
860	} else if framesize <= objabi.StackBig {
861		// large stack: SP-framesize < stackguard-StackSmall
862		//	ADD $-(framesize-StackSmall), SP, R4
863		//	CMP stackguard, R4
864		p = obj.Appendp(p, c.newprog)
865
866		p.As = AADD
867		p.From.Type = obj.TYPE_CONST
868		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
869		p.Reg = REGSP
870		p.To.Type = obj.TYPE_REG
871		p.To.Reg = REG_R4
872
873		p = obj.Appendp(p, c.newprog)
874		p.As = ACMPU
875		p.From.Type = obj.TYPE_REG
876		p.From.Reg = REG_R3
877		p.To.Type = obj.TYPE_REG
878		p.To.Reg = REG_R4
879	} else {
880		// Such a large stack we need to protect against wraparound.
881		// If SP is close to zero:
882		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
883		// The +StackGuard on both sides is required to keep the left side positive:
884		// SP is allowed to be slightly below stackguard. See stack.h.
885		//
886		// Preemption sets stackguard to StackPreempt, a very large value.
887		// That breaks the math above, so we have to check for that explicitly.
888		//	// stackguard is R3
889		//	CMP	R3, $StackPreempt
890		//	BEQ	label-of-call-to-morestack
891		//	ADD	$StackGuard, SP, R4
892		//	SUB	R3, R4
893		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
894		//	CMPU	R31, R4
895		p = obj.Appendp(p, c.newprog)
896
897		p.As = ACMP
898		p.From.Type = obj.TYPE_REG
899		p.From.Reg = REG_R3
900		p.To.Type = obj.TYPE_CONST
901		p.To.Offset = objabi.StackPreempt
902
903		p = obj.Appendp(p, c.newprog)
904		q = p
905		p.As = ABEQ
906		p.To.Type = obj.TYPE_BRANCH
907
908		p = obj.Appendp(p, c.newprog)
909		p.As = AADD
910		p.From.Type = obj.TYPE_CONST
911		p.From.Offset = objabi.StackGuard
912		p.Reg = REGSP
913		p.To.Type = obj.TYPE_REG
914		p.To.Reg = REG_R4
915
916		p = obj.Appendp(p, c.newprog)
917		p.As = ASUB
918		p.From.Type = obj.TYPE_REG
919		p.From.Reg = REG_R3
920		p.To.Type = obj.TYPE_REG
921		p.To.Reg = REG_R4
922
923		p = obj.Appendp(p, c.newprog)
924		p.As = AMOVD
925		p.From.Type = obj.TYPE_CONST
926		p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall
927		p.To.Type = obj.TYPE_REG
928		p.To.Reg = REGTMP
929
930		p = obj.Appendp(p, c.newprog)
931		p.As = ACMPU
932		p.From.Type = obj.TYPE_REG
933		p.From.Reg = REGTMP
934		p.To.Type = obj.TYPE_REG
935		p.To.Reg = REG_R4
936	}
937
938	// q1: BLT	done
939	p = obj.Appendp(p, c.newprog)
940	q1 := p
941
942	p.As = ABLT
943	p.To.Type = obj.TYPE_BRANCH
944
945	// MOVD	LR, R5
946	p = obj.Appendp(p, c.newprog)
947
948	p.As = AMOVD
949	p.From.Type = obj.TYPE_REG
950	p.From.Reg = REG_LR
951	p.To.Type = obj.TYPE_REG
952	p.To.Reg = REG_R5
953	if q != nil {
954		q.Pcond = p
955	}
956
957	var morestacksym *obj.LSym
958	if c.cursym.CFunc() {
959		morestacksym = c.ctxt.Lookup("runtime.morestackc")
960	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
961		morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
962	} else {
963		morestacksym = c.ctxt.Lookup("runtime.morestack")
964	}
965
966	if c.ctxt.Flag_shared {
967		// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
968		// which is the address of function entry point when entering
969		// the function. We need to preserve R2 across call to morestack.
970		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
971		// the caller's frame, but not used (0(SP) is caller's saved LR,
972		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
973
974		// MOVD R12, 8(SP)
975		p = obj.Appendp(p, c.newprog)
976		p.As = AMOVD
977		p.From.Type = obj.TYPE_REG
978		p.From.Reg = REG_R2
979		p.To.Type = obj.TYPE_MEM
980		p.To.Reg = REGSP
981		p.To.Offset = 8
982	}
983
984	if c.ctxt.Flag_dynlink {
985		// Avoid calling morestack via a PLT when dynamically linking. The
986		// PLT stubs generated by the system linker on ppc64le when "std r2,
987		// 24(r1)" to save the TOC pointer in their callers stack
988		// frame. Unfortunately (and necessarily) morestack is called before
989		// the function that calls it sets up its frame and so the PLT ends
990		// up smashing the saved TOC pointer for its caller's caller.
991		//
992		// According to the ABI documentation there is a mechanism to avoid
993		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
994		// relocation on the nop after the call to morestack) but at the time
995		// of writing it is not supported at all by gold and my attempt to
996		// use it with ld.bfd caused an internal linker error. So this hack
997		// seems preferable.
998
999		// MOVD $runtime.morestack(SB), R12
1000		p = obj.Appendp(p, c.newprog)
1001		p.As = AMOVD
1002		p.From.Type = obj.TYPE_MEM
1003		p.From.Sym = morestacksym
1004		p.From.Name = obj.NAME_GOTREF
1005		p.To.Type = obj.TYPE_REG
1006		p.To.Reg = REG_R12
1007
1008		// MOVD R12, CTR
1009		p = obj.Appendp(p, c.newprog)
1010		p.As = AMOVD
1011		p.From.Type = obj.TYPE_REG
1012		p.From.Reg = REG_R12
1013		p.To.Type = obj.TYPE_REG
1014		p.To.Reg = REG_CTR
1015
1016		// BL CTR
1017		p = obj.Appendp(p, c.newprog)
1018		p.As = obj.ACALL
1019		p.From.Type = obj.TYPE_REG
1020		p.From.Reg = REG_R12
1021		p.To.Type = obj.TYPE_REG
1022		p.To.Reg = REG_CTR
1023	} else {
1024		// BL	runtime.morestack(SB)
1025		p = obj.Appendp(p, c.newprog)
1026
1027		p.As = ABL
1028		p.To.Type = obj.TYPE_BRANCH
1029		p.To.Sym = morestacksym
1030	}
1031
1032	if c.ctxt.Flag_shared {
1033		// MOVD 8(SP), R2
1034		p = obj.Appendp(p, c.newprog)
1035		p.As = AMOVD
1036		p.From.Type = obj.TYPE_MEM
1037		p.From.Reg = REGSP
1038		p.From.Offset = 8
1039		p.To.Type = obj.TYPE_REG
1040		p.To.Reg = REG_R2
1041	}
1042
1043	// BR	start
1044	p = obj.Appendp(p, c.newprog)
1045	p.As = ABR
1046	p.To.Type = obj.TYPE_BRANCH
1047	p.Pcond = p0.Link
1048
1049	// placeholder for q1's jump target
1050	p = obj.Appendp(p, c.newprog)
1051
1052	p.As = obj.ANOP // zero-width place holder
1053	q1.Pcond = p
1054
1055	return p
1056}
1057
1058var Linkppc64 = obj.LinkArch{
1059	Arch:       sys.ArchPPC64,
1060	Init:       buildop,
1061	Preprocess: preprocess,
1062	Assemble:   span9,
1063	Progedit:   progedit,
1064}
1065
1066var Linkppc64le = obj.LinkArch{
1067	Arch:       sys.ArchPPC64LE,
1068	Init:       buildop,
1069	Preprocess: preprocess,
1070	Assemble:   span9,
1071	Progedit:   progedit,
1072}
1073