1 // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
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.
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:
19 // The above copyright notice and this permission notice shall be included in
20 // all copies or substantial portions of the Software.
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
42 // Test if this value can encoded as a mask for
43 // li -1, rx; rlic rx,rx,sh,mb.
44 // Masks can also extend from the msb and wrap to
45 // the lsb too. That is, the valid masks are 32 bit strings
46 // of the form: 0..01..10..0 or 1..10..01..1 or 1...1
47 func isPPC64DoublewordRotateMask(v64 int64) bool {
48 // Isolate rightmost 1 (if none 0) and add.
51 // Likewise, for the wrapping case.
53 vpn := (vn & -vn) + vn
54 return (v&vp == 0 || vn&vpn == 0) && v != 0
57 // Encode a doubleword rotate mask into mb (mask begin) and
58 // me (mask end, inclusive). Note, POWER ISA labels bits in
60 func encodePPC64RLDCMask(mask int64) (mb, me int) {
61 // Determine boundaries and then decode them
62 mb = bits.LeadingZeros64(uint64(mask))
63 me = 64 - bits.TrailingZeros64(uint64(mask))
64 mbn := bits.LeadingZeros64(^uint64(mask))
65 men := 64 - bits.TrailingZeros64(^uint64(mask))
66 // Check for a wrapping mask (e.g bits at 0 and 63)
67 if mb == 0 && me == 64 {
68 // swap the inverted values
71 // Note, me is inclusive.
75 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
79 c := ctxt9{ctxt: ctxt, newprog: newprog}
81 // Rewrite BR/BL to symbol as TYPE_BRANCH.
89 p.To.Type = obj.TYPE_BRANCH
93 // Rewrite float constants to values stored in memory.
96 if p.From.Type == obj.TYPE_FCONST {
97 f32 := float32(p.From.Val.(float64))
98 p.From.Type = obj.TYPE_MEM
99 p.From.Sym = ctxt.Float32Sym(f32)
100 p.From.Name = obj.NAME_EXTERN
105 if p.From.Type == obj.TYPE_FCONST {
106 f64 := p.From.Val.(float64)
107 // Constant not needed in memory for float +/- 0
109 p.From.Type = obj.TYPE_MEM
110 p.From.Sym = ctxt.Float64Sym(f64)
111 p.From.Name = obj.NAME_EXTERN
117 // Note, for backwards compatibility, MOVW $const, Rx and MOVWZ $const, Rx are identical.
118 if p.From.Type == obj.TYPE_CONST && p.From.Offset != 0 && p.From.Offset&0xFFFF == 0 {
119 // This is a constant shifted 16 bits to the left, convert it to ADDIS/ORIS $const,...
121 // Use ORIS for large constants which should not be sign extended.
122 if p.From.Offset >= 0x80000000 {
130 // Skip this opcode if it is not a constant load.
131 if p.From.Type != obj.TYPE_CONST || p.From.Name != obj.NAME_NONE || p.From.Reg != 0 {
135 // 32b constants (signed and unsigned) can be generated via 1 or 2 instructions. They can be assembled directly.
136 isS32 := int64(int32(p.From.Offset)) == p.From.Offset
137 isU32 := uint64(uint32(p.From.Offset)) == uint64(p.From.Offset)
138 // If prefixed instructions are supported, a 34b signed constant can be generated by one pli instruction.
139 isS34 := pfxEnabled && (p.From.Offset<<30)>>30 == p.From.Offset
141 // Try converting MOVD $const,Rx into ADDIS/ORIS $s32>>16,R0,Rx
143 case isS32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
148 case isU32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
153 case isS32 || isU32 || isS34:
154 // The assembler can generate this opcode in 1 (on Power10) or 2 opcodes.
156 // Otherwise, see if the large constant can be generated with 2 instructions. If not, load it from memory.
158 // Is this a shifted 16b constant? If so, rewrite it to avoid a creating and loading a constant.
160 shift := bits.TrailingZeros64(uint64(val))
161 mask := 0xFFFF << shift
162 if val&int64(mask) == val || (val>>(shift+16) == -1 && (val>>shift)<<shift == val) {
163 // Rewrite this value into MOVD $const>>shift, Rto; SLD $shift, Rto
164 q := obj.Appendp(p, c.newprog)
166 q.From.SetConst(int64(shift))
168 p.From.Offset >>= shift
170 } else if isPPC64DoublewordRotateMask(val) {
171 // This constant is a mask value, generate MOVD $-1, Rto; RLDIC Rto, ^me, mb, Rto
172 mb, me := encodePPC64RLDCMask(val)
173 q := obj.Appendp(p, c.newprog)
175 q.AddRestSourceConst((^int64(me)) & 0x3F)
176 q.AddRestSourceConst(int64(mb))
182 // Load the constant from memory.
183 p.From.Type = obj.TYPE_MEM
184 p.From.Sym = ctxt.Int64Sym(p.From.Offset)
185 p.From.Name = obj.NAME_EXTERN
192 // Rewrite SUB constants into ADD.
194 if p.From.Type == obj.TYPE_CONST {
195 p.From.Offset = -p.From.Offset
200 if p.From.Type == obj.TYPE_CONST {
201 p.From.Offset = -p.From.Offset
206 if p.From.Type == obj.TYPE_CONST {
207 p.From.Offset = -p.From.Offset
211 // Rewrite ADD/OR/XOR/ANDCC $const,... forms into ADDIS/ORIS/XORIS/ANDISCC
213 // AADD can encode signed 34b values, ensure it is a valid signed 32b integer too.
214 if p.From.Type == obj.TYPE_CONST && p.From.Offset&0xFFFF == 0 && int64(int32(p.From.Offset)) == p.From.Offset && p.From.Offset != 0 {
219 if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
224 if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
229 if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
234 // To maintain backwards compatibility, we accept some 4 argument usage of
235 // several opcodes which was likely not intended, but did work. These are not
236 // added to optab to avoid the chance this behavior might be used with newer
239 // Rewrite argument ordering like "ADDEX R3, $3, R4, R5" into
240 // "ADDEX R3, R4, $3, R5"
241 case AVSHASIGMAW, AVSHASIGMAD, AADDEX, AXXSLDWI, AXXPERMDI:
242 if len(p.RestArgs) == 2 && p.Reg == 0 && p.RestArgs[0].Addr.Type == obj.TYPE_CONST && p.RestArgs[1].Addr.Type == obj.TYPE_REG {
243 p.Reg = p.RestArgs[1].Addr.Reg
244 p.RestArgs = p.RestArgs[:1]
248 if c.ctxt.Headtype == objabi.Haix {
250 } else if c.ctxt.Flag_dynlink {
255 // Rewrite p, if necessary, to access a symbol using its TOC anchor.
256 // This code is for AIX only.
257 func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) {
258 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
262 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
263 // ADUFFZERO/ADUFFCOPY is considered as an ABL except in dynamic
264 // link where it should be an indirect call.
265 if !c.ctxt.Flag_dynlink {
270 // MOVD runtime.duffxxx@TOC, R12
275 if p.As == obj.ADUFFZERO {
276 sym = c.ctxt.Lookup("runtime.duffzero")
278 sym = c.ctxt.Lookup("runtime.duffcopy")
280 // Retrieve or create the TOC anchor.
281 symtoc := c.ctxt.LookupInit("TOC."+sym.Name, func(s *obj.LSym) {
282 s.Type = objabi.SDATA
283 s.Set(obj.AttrDuplicateOK, true)
284 s.Set(obj.AttrStatic, true)
285 c.ctxt.Data = append(c.ctxt.Data, s)
286 s.WriteAddr(c.ctxt, 0, 8, sym, 0)
289 offset := p.To.Offset
291 p.From.Type = obj.TYPE_MEM
292 p.From.Name = obj.NAME_TOCREF
294 p.To.Type = obj.TYPE_REG
296 p.To.Name = obj.NAME_NONE
299 p1 := obj.Appendp(p, c.newprog)
301 p1.From.Type = obj.TYPE_CONST
302 p1.From.Offset = offset
303 p1.To.Type = obj.TYPE_REG
305 p2 := obj.Appendp(p1, c.newprog)
307 p2.From.Type = obj.TYPE_REG
308 p2.From.Reg = REG_R12
309 p2.To.Type = obj.TYPE_REG
311 p3 := obj.Appendp(p2, c.newprog)
313 p3.To.Type = obj.TYPE_REG
318 if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC {
319 if p.From.Type == obj.TYPE_ADDR {
321 // ADWORD $sym doesn't need TOC anchor
325 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p)
328 if p.To.Type != obj.TYPE_REG {
329 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p)
332 } else if p.From.Type != obj.TYPE_MEM {
333 c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
338 } else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC {
339 if p.To.Type != obj.TYPE_MEM {
340 c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
344 c.ctxt.Diag("cannot handle symbols on both sides in %v", p)
353 if source.Sym == nil {
354 c.ctxt.Diag("do not know how to handle nil symbol in %v", p)
358 if source.Sym.Type == objabi.STLSBSS {
362 // Retrieve or create the TOC anchor.
363 symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) {
364 s.Type = objabi.SDATA
365 s.Set(obj.AttrDuplicateOK, true)
366 s.Set(obj.AttrStatic, true)
367 c.ctxt.Data = append(c.ctxt.Data, s)
368 s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0)
371 if source.Type == obj.TYPE_ADDR {
372 // MOVD $sym, Rx becomes MOVD symtoc, Rx
373 // MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx
374 p.From.Type = obj.TYPE_MEM
376 p.From.Name = obj.NAME_TOCREF
378 if p.From.Offset != 0 {
379 q := obj.Appendp(p, c.newprog)
381 q.From.Type = obj.TYPE_CONST
382 q.From.Offset = p.From.Offset
390 // MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry
391 // MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP)
392 // An addition may be inserted between the two MOVs if there is an offset.
394 q := obj.Appendp(p, c.newprog)
396 q.From.Type = obj.TYPE_MEM
398 q.From.Name = obj.NAME_TOCREF
399 q.To.Type = obj.TYPE_REG
402 q = obj.Appendp(q, c.newprog)
406 if p.From.Name != obj.NAME_NONE {
407 q.From.Type = obj.TYPE_MEM
409 q.From.Name = obj.NAME_NONE
411 } else if p.To.Name != obj.NAME_NONE {
412 q.To.Type = obj.TYPE_MEM
414 q.To.Name = obj.NAME_NONE
417 c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p)
423 // Rewrite p, if necessary, to access global data via the global offset table.
424 func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
425 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
428 // MOVD runtime.duffxxx@GOT, R12
433 if p.As == obj.ADUFFZERO {
434 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
436 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
438 offset := p.To.Offset
440 p.From.Type = obj.TYPE_MEM
441 p.From.Name = obj.NAME_GOTREF
443 p.To.Type = obj.TYPE_REG
445 p.To.Name = obj.NAME_NONE
448 p1 := obj.Appendp(p, c.newprog)
450 p1.From.Type = obj.TYPE_CONST
451 p1.From.Offset = offset
452 p1.To.Type = obj.TYPE_REG
454 p2 := obj.Appendp(p1, c.newprog)
456 p2.From.Type = obj.TYPE_REG
457 p2.From.Reg = REG_R12
458 p2.To.Type = obj.TYPE_REG
460 p3 := obj.Appendp(p2, c.newprog)
462 p3.To.Type = obj.TYPE_REG
466 // We only care about global data: NAME_EXTERN means a global
467 // symbol in the Go sense, and p.Sym.Local is true for a few
468 // internally defined symbols.
469 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
470 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
471 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
473 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
475 if p.To.Type != obj.TYPE_REG {
476 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
478 p.From.Type = obj.TYPE_MEM
479 p.From.Name = obj.NAME_GOTREF
480 if p.From.Offset != 0 {
481 q := obj.Appendp(p, c.newprog)
483 q.From.Type = obj.TYPE_CONST
484 q.From.Offset = p.From.Offset
489 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
490 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
493 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
494 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
495 // An addition may be inserted between the two MOVs if there is an offset.
496 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
497 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
498 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
501 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
506 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
509 if source.Sym.Type == objabi.STLSBSS {
512 if source.Type != obj.TYPE_MEM {
513 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
515 p1 := obj.Appendp(p, c.newprog)
516 p2 := obj.Appendp(p1, c.newprog)
519 p1.From.Type = obj.TYPE_MEM
520 p1.From.Sym = source.Sym
521 p1.From.Name = obj.NAME_GOTREF
522 p1.To.Type = obj.TYPE_REG
528 if p.From.Name == obj.NAME_EXTERN {
530 p2.From.Name = obj.NAME_NONE
532 } else if p.To.Name == obj.NAME_EXTERN {
534 p2.To.Name = obj.NAME_NONE
542 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
543 // TODO(minux): add morestack short-cuts with small fixed frame-size.
544 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
548 c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
550 p := c.cursym.Func().Text
551 textstksiz := p.To.Offset
552 if textstksiz == -8 {
553 // Compatibility hack.
554 p.From.Sym.Set(obj.AttrNoFrame, true)
557 if textstksiz%8 != 0 {
558 c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
560 if p.From.Sym.NoFrame() {
562 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
566 c.cursym.Func().Args = p.To.Val.(int32)
567 c.cursym.Func().Locals = int32(textstksiz)
570 * find leaf subroutines
572 * expand BECOME pseudo
577 for p := c.cursym.Func().Text; p != nil; p = p.Link {
579 /* too hard, just leave alone */
583 p.Mark |= LABEL | LEAF | SYNC
590 if p.To.Type == obj.TYPE_REG {
591 if p.To.Reg == REGZERO {
592 p.Mark |= LABEL | SYNC
627 p.Mark |= LABEL | SYNC
630 case AMOVW, AMOVWZ, AMOVD:
632 if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
633 p.Mark |= LABEL | SYNC
681 c.cursym.Func().Text.Mark &^= LEAF
698 // NOPs are not removed due to #40689.
700 if q1.Mark&LEAF == 0 {
714 p.Mark |= FCMP | FLOAT
725 // NOPs are not removed due to
738 for p := c.cursym.Func().Text; p != nil; p = p.Link {
742 autosize = int32(textstksiz)
744 if p.Mark&LEAF != 0 && autosize == 0 {
745 // A leaf function with no locals has no frame.
746 p.From.Sym.Set(obj.AttrNoFrame, true)
749 if !p.From.Sym.NoFrame() {
750 // If there is a stack frame at all, it includes
751 // space to save the LR.
752 autosize += int32(c.ctxt.Arch.FixedFrameSize)
755 if p.Mark&LEAF != 0 && autosize < abi.StackSmall {
756 // A leaf function with a small stack can be marked
757 // NOSPLIT, avoiding a stack check.
758 p.From.Sym.Set(obj.AttrNoSplit, true)
761 p.To.Offset = int64(autosize)
765 if NeedTOCpointer(c.ctxt) && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
766 // When compiling Go into PIC, without PCrel support, all functions must start
767 // with instructions to load the TOC pointer into r2:
769 // addis r2, r12, .TOC.-func@ha
770 // addi r2, r2, .TOC.-func@l+4
772 // We could probably skip this prologue in some situations
773 // but it's a bit subtle. However, it is both safe and
774 // necessary to leave the prologue off duffzero and
775 // duffcopy as we rely on being able to jump to a specific
776 // instruction offset for them.
778 // These are AWORDS because there is no (afaict) way to
779 // generate the addis instruction except as part of the
780 // load of a large constant, and in that case there is no
781 // way to use r12 as the source.
783 // Note that the same condition is tested in
784 // putelfsym in cmd/link/internal/ld/symtab.go
785 // where we set the st_other field to indicate
786 // the presence of these instructions.
787 q = obj.Appendp(q, c.newprog)
790 q.From.Type = obj.TYPE_CONST
791 q.From.Offset = 0x3c4c0000
792 q = obj.Appendp(q, c.newprog)
795 q.From.Type = obj.TYPE_CONST
796 q.From.Offset = 0x38420000
797 rel := obj.Addrel(c.cursym)
800 rel.Sym = c.ctxt.Lookup(".TOC.")
801 rel.Type = objabi.R_ADDRPOWER_PCREL
804 if !c.cursym.Func().Text.From.Sym.NoSplit() {
805 q = c.stacksplit(q, autosize) // emit split check
809 var prologueEnd *obj.Prog
810 // Save the link register and update the SP. MOVDU is used unless
811 // the frame size is too large. The link register must be saved
812 // even for non-empty leaf functions so that traceback works.
813 if autosize >= -BIG && autosize <= BIG {
814 // Use MOVDU to adjust R1 when saving R31, if autosize is small.
815 q = obj.Appendp(q, c.newprog)
818 q.From.Type = obj.TYPE_REG
820 q.To.Type = obj.TYPE_REG
824 q = obj.Appendp(q, c.newprog)
827 q.From.Type = obj.TYPE_REG
829 q.To.Type = obj.TYPE_MEM
830 q.To.Offset = int64(-autosize)
834 // Frame size is too large for a MOVDU instruction.
835 // Store link register before decrementing SP, so if a signal comes
836 // during the execution of the function prologue, the traceback
837 // code will not see a half-updated stack frame.
838 // This sequence is not async preemptible, as if we open a frame
839 // at the current SP, it will clobber the saved LR.
840 q = obj.Appendp(q, c.newprog)
843 q.From.Type = obj.TYPE_REG
845 q.To.Type = obj.TYPE_REG
846 q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
848 q = c.ctxt.StartUnsafePoint(q, c.newprog)
850 q = obj.Appendp(q, c.newprog)
853 q.From.Type = obj.TYPE_REG
855 q.To.Type = obj.TYPE_MEM
856 q.To.Offset = int64(-autosize)
861 q = obj.Appendp(q, c.newprog)
864 q.From.Type = obj.TYPE_CONST
865 q.From.Offset = int64(-autosize)
866 q.To.Type = obj.TYPE_REG
870 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
872 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
873 } else if c.cursym.Func().Text.Mark&LEAF == 0 {
874 // A very few functions that do not return to their caller
875 // (e.g. gogo) are not identified as leaves but still have
877 c.cursym.Func().Text.Mark |= LEAF
880 if c.cursym.Func().Text.Mark&LEAF != 0 {
881 c.cursym.Set(obj.AttrLeaf, true)
885 if NeedTOCpointer(c.ctxt) {
886 q = obj.Appendp(q, c.newprog)
889 q.From.Type = obj.TYPE_REG
891 q.To.Type = obj.TYPE_MEM
896 if c.cursym.Func().Text.From.Sym.Wrapper() {
897 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
899 // MOVD g_panic(g), R3
902 // MOVD panic_argp(R3), R4
903 // ADD $(autosize+8), R1, R5
907 // MOVD R6, panic_argp(R3)
911 // The NOP is needed to give the jumps somewhere to land.
912 // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
914 q = obj.Appendp(q, c.newprog)
917 q.From.Type = obj.TYPE_MEM
919 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
920 q.To.Type = obj.TYPE_REG
923 q = obj.Appendp(q, c.newprog)
925 q.From.Type = obj.TYPE_REG
927 q.To.Type = obj.TYPE_REG
930 q = obj.Appendp(q, c.newprog)
932 q.To.Type = obj.TYPE_BRANCH
935 q = obj.Appendp(q, c.newprog)
937 q.From.Type = obj.TYPE_MEM
939 q.From.Offset = 0 // Panic.argp
940 q.To.Type = obj.TYPE_REG
943 q = obj.Appendp(q, c.newprog)
945 q.From.Type = obj.TYPE_CONST
946 q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize
948 q.To.Type = obj.TYPE_REG
951 q = obj.Appendp(q, c.newprog)
953 q.From.Type = obj.TYPE_REG
955 q.To.Type = obj.TYPE_REG
958 q = obj.Appendp(q, c.newprog)
960 q.To.Type = obj.TYPE_BRANCH
963 q = obj.Appendp(q, c.newprog)
965 q.From.Type = obj.TYPE_CONST
966 q.From.Offset = c.ctxt.Arch.FixedFrameSize
968 q.To.Type = obj.TYPE_REG
971 q = obj.Appendp(q, c.newprog)
973 q.From.Type = obj.TYPE_REG
975 q.To.Type = obj.TYPE_MEM
977 q.To.Offset = 0 // Panic.argp
979 q = obj.Appendp(q, c.newprog)
987 if p.From.Type == obj.TYPE_CONST {
988 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
992 retTarget := p.To.Sym
994 if c.cursym.Func().Text.Mark&LEAF != 0 {
998 if retTarget == nil {
999 p.To.Type = obj.TYPE_REG
1002 p.To.Type = obj.TYPE_BRANCH
1003 p.To.Sym = retTarget
1010 p.From.Type = obj.TYPE_CONST
1011 p.From.Offset = int64(autosize)
1012 p.To.Type = obj.TYPE_REG
1019 if retTarget == nil {
1020 q.To.Type = obj.TYPE_REG
1023 q.To.Type = obj.TYPE_BRANCH
1024 q.To.Sym = retTarget
1035 p.From.Type = obj.TYPE_MEM
1038 p.To.Type = obj.TYPE_REG
1044 q.From.Type = obj.TYPE_REG
1046 q.To.Type = obj.TYPE_REG
1054 // Debug bad returns
1059 q.From.Type = obj.TYPE_MEM
1062 q.To.Type = obj.TYPE_REG
1074 q.From.Type = obj.TYPE_CONST
1075 q.From.Offset = int64(autosize)
1076 q.To.Type = obj.TYPE_REG
1088 if retTarget == nil {
1089 q1.To.Type = obj.TYPE_REG
1092 q1.To.Type = obj.TYPE_BRANCH
1093 q1.To.Sym = retTarget
1096 q1.Spadj = +autosize
1101 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
1102 p.Spadj = int32(-p.From.Offset)
1105 if p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
1106 p.Spadj = int32(-p.To.Offset)
1108 if p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP {
1109 p.Spadj = int32(-p.From.Offset)
1111 case obj.AGETCALLERPC:
1115 p.From.Type = obj.TYPE_REG
1118 /* MOVD (RSP), Rd */
1120 p.From.Type = obj.TYPE_MEM
1125 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU {
1126 f := c.cursym.Func()
1127 if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
1128 c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
1129 if ctxt.Debugvlog || !ctxt.IsAsm {
1130 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1132 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1134 log.Fatalf("bad SPWRITE")
1143 // instruction scheduling
1150 q1 = firstp; // top of block
1151 o = 0; // count of instructions
1152 for(p = firstp; p != nil; p = p1) {
1155 if(p->mark & NOSCHED){
1159 for(; p != nil; p = p->link){
1160 if(!(p->mark & NOSCHED))
1169 if(p->mark & (LABEL|SYNC)) {
1175 if(p->mark & (BRANCH|SYNC)) {
1188 func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
1189 if c.ctxt.Flag_maymorestack != "" {
1190 if c.ctxt.Flag_shared || c.ctxt.Flag_dynlink {
1191 // See the call to morestack for why these are
1192 // complicated to support.
1193 c.ctxt.Diag("maymorestack with -shared or -dynlink is not supported")
1196 // Spill arguments. This has to happen before we open
1197 // any more frame space.
1198 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
1200 // Save LR and REGCTXT
1201 frameSize := 8 + c.ctxt.Arch.FixedFrameSize
1204 p = obj.Appendp(p, c.newprog)
1206 p.From.Type = obj.TYPE_REG
1208 p.To.Type = obj.TYPE_REG
1210 // MOVDU REGTMP, -16(SP)
1211 p = obj.Appendp(p, c.newprog)
1213 p.From.Type = obj.TYPE_REG
1215 p.To.Type = obj.TYPE_MEM
1216 p.To.Offset = -frameSize
1218 p.Spadj = int32(frameSize)
1220 // MOVD REGCTXT, 8(SP)
1221 p = obj.Appendp(p, c.newprog)
1223 p.From.Type = obj.TYPE_REG
1224 p.From.Reg = REGCTXT
1225 p.To.Type = obj.TYPE_MEM
1230 p = obj.Appendp(p, c.newprog)
1232 p.To.Type = obj.TYPE_BRANCH
1233 // See ../x86/obj6.go
1234 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
1236 // Restore LR and REGCTXT
1238 // MOVD 8(SP), REGCTXT
1239 p = obj.Appendp(p, c.newprog)
1241 p.From.Type = obj.TYPE_MEM
1244 p.To.Type = obj.TYPE_REG
1247 // MOVD 0(SP), REGTMP
1248 p = obj.Appendp(p, c.newprog)
1250 p.From.Type = obj.TYPE_MEM
1253 p.To.Type = obj.TYPE_REG
1257 p = obj.Appendp(p, c.newprog)
1259 p.From.Type = obj.TYPE_REG
1261 p.To.Type = obj.TYPE_REG
1265 p = obj.Appendp(p, c.newprog)
1267 p.From.Type = obj.TYPE_CONST
1268 p.From.Offset = frameSize
1269 p.To.Type = obj.TYPE_REG
1271 p.Spadj = -int32(frameSize)
1273 // Unspill arguments.
1274 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
1277 // save entry point, but skipping the two instructions setting R2 in shared mode and maymorestack
1280 // MOVD g_stackguard(g), R22
1281 p = obj.Appendp(p, c.newprog)
1284 p.From.Type = obj.TYPE_MEM
1286 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
1287 if c.cursym.CFunc() {
1288 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
1290 p.To.Type = obj.TYPE_REG
1293 // Mark the stack bound check and morestack call async nonpreemptible.
1294 // If we get preempted here, when resumed the preemption request is
1295 // cleared, but we'll still call morestack, which will double the stack
1296 // unnecessarily. See issue #35470.
1297 p = c.ctxt.StartUnsafePoint(p, c.newprog)
1300 if framesize <= abi.StackSmall {
1301 // small stack: SP < stackguard
1302 // CMP stackguard, SP
1303 p = obj.Appendp(p, c.newprog)
1306 p.From.Type = obj.TYPE_REG
1307 p.From.Reg = REG_R22
1308 p.To.Type = obj.TYPE_REG
1311 // large stack: SP-framesize < stackguard-StackSmall
1312 offset := int64(framesize) - abi.StackSmall
1313 if framesize > abi.StackBig {
1314 // Such a large stack we need to protect against underflow.
1315 // The runtime guarantees SP > objabi.StackBig, but
1316 // framesize is large enough that SP-framesize may
1317 // underflow, causing a direct comparison with the
1318 // stack guard to incorrectly succeed. We explicitly
1319 // guard against underflow.
1321 // CMPU SP, $(framesize-StackSmall)
1322 // BLT label-of-call-to-morestack
1323 if offset <= 0xffff {
1324 p = obj.Appendp(p, c.newprog)
1326 p.From.Type = obj.TYPE_REG
1328 p.To.Type = obj.TYPE_CONST
1329 p.To.Offset = offset
1331 // Constant is too big for CMPU.
1332 p = obj.Appendp(p, c.newprog)
1334 p.From.Type = obj.TYPE_CONST
1335 p.From.Offset = offset
1336 p.To.Type = obj.TYPE_REG
1339 p = obj.Appendp(p, c.newprog)
1341 p.From.Type = obj.TYPE_REG
1343 p.To.Type = obj.TYPE_REG
1347 p = obj.Appendp(p, c.newprog)
1350 p.To.Type = obj.TYPE_BRANCH
1353 // Check against the stack guard. We've ensured this won't underflow.
1354 // ADD $-(framesize-StackSmall), SP, R4
1355 // CMPU stackguard, R4
1356 p = obj.Appendp(p, c.newprog)
1359 p.From.Type = obj.TYPE_CONST
1360 p.From.Offset = -offset
1362 p.To.Type = obj.TYPE_REG
1365 p = obj.Appendp(p, c.newprog)
1367 p.From.Type = obj.TYPE_REG
1368 p.From.Reg = REG_R22
1369 p.To.Type = obj.TYPE_REG
1374 p = obj.Appendp(p, c.newprog)
1378 p.To.Type = obj.TYPE_BRANCH
1380 p = obj.Appendp(p, c.newprog)
1381 p.As = obj.ANOP // zero-width place holder
1387 // Spill the register args that could be clobbered by the
1390 spill := c.cursym.Func().SpillRegisterArgs(p, c.newprog)
1393 p = obj.Appendp(spill, c.newprog)
1395 p.From.Type = obj.TYPE_REG
1397 p.To.Type = obj.TYPE_REG
1400 p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
1402 var morestacksym *obj.LSym
1403 if c.cursym.CFunc() {
1404 morestacksym = c.ctxt.Lookup("runtime.morestackc")
1405 } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
1406 morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
1408 morestacksym = c.ctxt.Lookup("runtime.morestack")
1411 if NeedTOCpointer(c.ctxt) {
1412 // In PPC64 PIC code, R2 is used as TOC pointer derived from R12
1413 // which is the address of function entry point when entering
1414 // the function. We need to preserve R2 across call to morestack.
1415 // Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
1416 // the caller's frame, but not used (0(SP) is caller's saved LR,
1417 // 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
1419 p = obj.Appendp(p, c.newprog)
1421 p.From.Type = obj.TYPE_REG
1423 p.To.Type = obj.TYPE_MEM
1428 if c.ctxt.Flag_dynlink {
1429 // Avoid calling morestack via a PLT when dynamically linking. The
1430 // PLT stubs generated by the system linker on ppc64le when "std r2,
1431 // 24(r1)" to save the TOC pointer in their callers stack
1432 // frame. Unfortunately (and necessarily) morestack is called before
1433 // the function that calls it sets up its frame and so the PLT ends
1434 // up smashing the saved TOC pointer for its caller's caller.
1436 // According to the ABI documentation there is a mechanism to avoid
1437 // the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
1438 // relocation on the nop after the call to morestack) but at the time
1439 // of writing it is not supported at all by gold and my attempt to
1440 // use it with ld.bfd caused an internal linker error. So this hack
1441 // seems preferable.
1443 // MOVD $runtime.morestack(SB), R12
1444 p = obj.Appendp(p, c.newprog)
1446 p.From.Type = obj.TYPE_MEM
1447 p.From.Sym = morestacksym
1448 p.From.Name = obj.NAME_GOTREF
1449 p.To.Type = obj.TYPE_REG
1453 p = obj.Appendp(p, c.newprog)
1455 p.From.Type = obj.TYPE_REG
1456 p.From.Reg = REG_R12
1457 p.To.Type = obj.TYPE_REG
1461 p = obj.Appendp(p, c.newprog)
1463 p.To.Type = obj.TYPE_REG
1466 // BL runtime.morestack(SB)
1467 p = obj.Appendp(p, c.newprog)
1470 p.To.Type = obj.TYPE_BRANCH
1471 p.To.Sym = morestacksym
1474 if NeedTOCpointer(c.ctxt) {
1476 p = obj.Appendp(p, c.newprog)
1478 p.From.Type = obj.TYPE_MEM
1481 p.To.Type = obj.TYPE_REG
1485 // The instructions which unspill regs should be preemptible.
1486 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
1487 unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
1490 p = obj.Appendp(unspill, c.newprog)
1492 p.To.Type = obj.TYPE_BRANCH
1493 p.To.SetTarget(startPred.Link)
1495 // placeholder for q1's jump target
1496 p = obj.Appendp(p, c.newprog)
1498 p.As = obj.ANOP // zero-width place holder
1504 // MMA accumulator to/from instructions are slightly ambiguous since
1505 // the argument represents both source and destination, specified as
1506 // an accumulator. It is treated as a unary destination to simplify
1507 // the code generation in ppc64map.
1508 var unaryDst = map[obj.As]bool{
1514 var Linkppc64 = obj.LinkArch{
1515 Arch: sys.ArchPPC64,
1517 Preprocess: preprocess,
1521 DWARFRegisters: PPC64DWARFRegisters,
1524 var Linkppc64le = obj.LinkArch{
1525 Arch: sys.ArchPPC64LE,
1527 Preprocess: preprocess,
1531 DWARFRegisters: PPC64DWARFRegisters,