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