1 // cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova.
2 // https://code.google.com/p/ken-cc/source/browse/
4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6 // Portions Copyright © 1997-1999 Vita Nuova Limited
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8 // Portions Copyright © 2004,2006 Bruce Ellis
9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11 // Portions Copyright © 2009 The Go Authors. All rights reserved.
13 // Permission is hereby granted, free of charge, to any person obtaining a copy
14 // of this software and associated documentation files (the "Software"), to deal
15 // in the Software without restriction, including without limitation the rights
16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 // copies of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 var complements = []obj.As{
54 // noZRreplace is the set of instructions for which $0 in the To operand
55 // should NOT be replaced with REGZERO.
56 var noZRreplace = map[obj.As]bool{
60 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
61 if c.ctxt.Flag_maymorestack != "" {
62 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
64 // Save LR and make room for FP, REGCTXT. Leave room
65 // for caller's saved FP.
67 p = obj.Appendp(p, c.newprog)
69 p.From.Type = obj.TYPE_REG
71 p.To.Type = obj.TYPE_MEM
73 p.To.Offset = -frameSize
78 p = obj.Appendp(p, c.newprog)
80 p.From.Type = obj.TYPE_REG
82 p.To.Type = obj.TYPE_MEM
86 p = obj.Appendp(p, c.newprog)
88 p.From.Type = obj.TYPE_CONST
91 p.To.Type = obj.TYPE_REG
94 // Save REGCTXT (for simplicity we do this whether or
96 p = obj.Appendp(p, c.newprog)
98 p.From.Type = obj.TYPE_REG
100 p.To.Type = obj.TYPE_MEM
105 p = obj.Appendp(p, c.newprog)
107 p.To.Type = obj.TYPE_BRANCH
108 // See ../x86/obj6.go
109 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
112 p = obj.Appendp(p, c.newprog)
114 p.From.Type = obj.TYPE_MEM
117 p.To.Type = obj.TYPE_REG
121 p = obj.Appendp(p, c.newprog)
123 p.From.Type = obj.TYPE_MEM
126 p.To.Type = obj.TYPE_REG
129 // Restore LR and SP.
130 p = obj.Appendp(p, c.newprog)
132 p.From.Type = obj.TYPE_MEM
134 p.From.Offset = frameSize
136 p.To.Type = obj.TYPE_REG
140 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
143 // Jump back to here after morestack returns.
146 // MOV g_stackguard(g), RT1
147 p = obj.Appendp(p, c.newprog)
150 p.From.Type = obj.TYPE_MEM
152 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
153 if c.cursym.CFunc() {
154 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
156 p.To.Type = obj.TYPE_REG
159 // Mark the stack bound check and morestack call async nonpreemptible.
160 // If we get preempted here, when resumed the preemption request is
161 // cleared, but we'll still call morestack, which will double the stack
162 // unnecessarily. See issue #35470.
163 p = c.ctxt.StartUnsafePoint(p, c.newprog)
165 q := (*obj.Prog)(nil)
166 if framesize <= objabi.StackSmall {
167 // small stack: SP < stackguard
168 // CMP stackguard, SP
170 p = obj.Appendp(p, c.newprog)
172 p.From.Type = obj.TYPE_REG
175 } else if framesize <= objabi.StackBig {
176 // large stack: SP-framesize < stackguard-StackSmall
177 // SUB $(framesize-StackSmall), SP, RT2
178 // CMP stackguard, RT2
179 p = obj.Appendp(p, c.newprog)
182 p.From.Type = obj.TYPE_CONST
183 p.From.Offset = int64(framesize) - objabi.StackSmall
185 p.To.Type = obj.TYPE_REG
188 p = obj.Appendp(p, c.newprog)
190 p.From.Type = obj.TYPE_REG
194 // Such a large stack we need to protect against underflow.
195 // The runtime guarantees SP > objabi.StackBig, but
196 // framesize is large enough that SP-framesize may
197 // underflow, causing a direct comparison with the
198 // stack guard to incorrectly succeed. We explicitly
199 // guard against underflow.
201 // SUBS $(framesize-StackSmall), SP, RT2
202 // // On underflow, jump to morestack
203 // BLO label_of_call_to_morestack
204 // CMP stackguard, RT2
206 p = obj.Appendp(p, c.newprog)
208 p.From.Type = obj.TYPE_CONST
209 p.From.Offset = int64(framesize) - objabi.StackSmall
211 p.To.Type = obj.TYPE_REG
214 p = obj.Appendp(p, c.newprog)
217 p.To.Type = obj.TYPE_BRANCH
219 p = obj.Appendp(p, c.newprog)
221 p.From.Type = obj.TYPE_REG
227 bls := obj.Appendp(p, c.newprog)
229 bls.To.Type = obj.TYPE_BRANCH
231 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
234 for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
237 // Now we are at the end of the function, but logically
238 // we are still in function prologue. We need to fix the
239 // SP data and PCDATA.
240 spfix := obj.Appendp(last, c.newprog)
242 spfix.Spadj = -framesize
244 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
245 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
248 q.To.SetTarget(pcdata)
250 bls.To.SetTarget(pcdata)
252 spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
255 movlr := obj.Appendp(spill, c.newprog)
257 movlr.From.Type = obj.TYPE_REG
258 movlr.From.Reg = REGLINK
259 movlr.To.Type = obj.TYPE_REG
260 movlr.To.Reg = REG_R3
264 debug = obj.Appendp(debug, c.newprog)
266 debug.From.Type = obj.TYPE_CONST
267 debug.From.Offset = int64(framesize)
268 debug.To.Type = obj.TYPE_REG
269 debug.To.Reg = REGTMP
272 // BL runtime.morestack(SB)
273 call := obj.Appendp(debug, c.newprog)
275 call.To.Type = obj.TYPE_BRANCH
276 morestack := "runtime.morestack"
278 case c.cursym.CFunc():
279 morestack = "runtime.morestackc"
280 case !c.cursym.Func().Text.From.Sym.NeedCtxt():
281 morestack = "runtime.morestack_noctxt"
283 call.To.Sym = c.ctxt.Lookup(morestack)
285 unspill := c.cursym.Func().UnspillRegisterArgs(call, c.newprog)
286 pcdata = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1)
289 jmp := obj.Appendp(pcdata, c.newprog)
291 jmp.To.Type = obj.TYPE_BRANCH
292 jmp.To.SetTarget(startPred.Link)
293 jmp.Spadj = +framesize
298 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
299 c := ctxt7{ctxt: ctxt, newprog: newprog}
304 // $0 results in C_ZCON, which matches both C_REG and various
305 // C_xCON, however the C_REG cases in asmout don't expect a
306 // constant, so they will use the register fields and assemble
307 // a R0. To prevent that, rewrite $0 as ZR.
308 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
309 p.From.Type = obj.TYPE_REG
312 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 && !noZRreplace[p.As] {
313 p.To.Type = obj.TYPE_REG
317 // Rewrite BR/BL to symbol as TYPE_BRANCH.
325 p.To.Type = obj.TYPE_BRANCH
330 // Rewrite float constants to values stored in memory.
333 if p.From.Type == obj.TYPE_FCONST {
334 f64 := p.From.Val.(float64)
336 if c.chipfloat7(f64) > 0 {
339 if math.Float32bits(f32) == 0 {
340 p.From.Type = obj.TYPE_REG
344 p.From.Type = obj.TYPE_MEM
345 p.From.Sym = c.ctxt.Float32Sym(f32)
346 p.From.Name = obj.NAME_EXTERN
351 if p.From.Type == obj.TYPE_FCONST {
352 f64 := p.From.Val.(float64)
353 if c.chipfloat7(f64) > 0 {
356 if math.Float64bits(f64) == 0 {
357 p.From.Type = obj.TYPE_REG
361 p.From.Type = obj.TYPE_MEM
362 p.From.Sym = c.ctxt.Float64Sym(f64)
363 p.From.Name = obj.NAME_EXTERN
370 // Rewrite negative immediates as positive immediates with
371 // complementary instruction.
373 case AADD, ASUB, ACMP, ACMN:
374 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
375 p.From.Offset = -p.From.Offset
376 p.As = complements[p.As]
378 case AADDW, ASUBW, ACMPW, ACMNW:
379 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
380 p.From.Offset = -p.From.Offset
381 p.As = complements[p.As]
385 if c.ctxt.Flag_dynlink {
390 // Rewrite p, if necessary, to access global data via the global offset table.
391 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
392 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
395 // MOVD runtime.duffxxx@GOT, REGTMP
396 // ADD $offset, REGTMP
399 if p.As == obj.ADUFFZERO {
400 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
402 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
404 offset := p.To.Offset
406 p.From.Type = obj.TYPE_MEM
407 p.From.Name = obj.NAME_GOTREF
409 p.To.Type = obj.TYPE_REG
411 p.To.Name = obj.NAME_NONE
414 p1 := obj.Appendp(p, c.newprog)
416 p1.From.Type = obj.TYPE_CONST
417 p1.From.Offset = offset
418 p1.To.Type = obj.TYPE_REG
420 p2 := obj.Appendp(p1, c.newprog)
422 p2.To.Type = obj.TYPE_REG
426 // We only care about global data: NAME_EXTERN means a global
427 // symbol in the Go sense, and p.Sym.Local is true for a few
428 // internally defined symbols.
429 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
430 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
431 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
433 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
435 if p.To.Type != obj.TYPE_REG {
436 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
438 p.From.Type = obj.TYPE_MEM
439 p.From.Name = obj.NAME_GOTREF
440 if p.From.Offset != 0 {
441 q := obj.Appendp(p, c.newprog)
443 q.From.Type = obj.TYPE_CONST
444 q.From.Offset = p.From.Offset
449 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
450 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
453 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
454 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
455 // An addition may be inserted between the two MOVs if there is an offset.
456 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
457 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
458 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
461 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
466 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
469 if source.Sym.Type == objabi.STLSBSS {
472 if source.Type != obj.TYPE_MEM {
473 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
475 p1 := obj.Appendp(p, c.newprog)
476 p2 := obj.Appendp(p1, c.newprog)
478 p1.From.Type = obj.TYPE_MEM
479 p1.From.Sym = source.Sym
480 p1.From.Name = obj.NAME_GOTREF
481 p1.To.Type = obj.TYPE_REG
487 if p.From.Name == obj.NAME_EXTERN {
489 p2.From.Name = obj.NAME_NONE
491 } else if p.To.Name == obj.NAME_EXTERN {
493 p2.To.Name = obj.NAME_NONE
501 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
502 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
506 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
508 p := c.cursym.Func().Text
509 textstksiz := p.To.Offset
510 if textstksiz == -8 {
511 // Historical way to mark NOFRAME.
512 p.From.Sym.Set(obj.AttrNoFrame, true)
516 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
518 if p.From.Sym.NoFrame() {
520 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
524 c.cursym.Func().Args = p.To.Val.(int32)
525 c.cursym.Func().Locals = int32(textstksiz)
528 * find leaf subroutines
530 for p := c.cursym.Func().Text; p != nil; p = p.Link {
538 c.cursym.Func().Text.Mark &^= LEAF
545 for p := c.cursym.Func().Text; p != nil; p = p.Link {
549 c.cursym.Func().Text = p
550 c.autosize = int32(textstksiz)
552 if p.Mark&LEAF != 0 && c.autosize == 0 {
553 // A leaf function with no locals has no frame.
554 p.From.Sym.Set(obj.AttrNoFrame, true)
557 if !p.From.Sym.NoFrame() {
558 // If there is a stack frame at all, it includes
559 // space to save the LR.
564 extrasize := int32(0)
565 if c.autosize%16 == 8 {
566 // Allocate extra 8 bytes on the frame top to save FP
568 } else if c.autosize&(16-1) == 0 {
569 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
572 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
574 c.autosize += extrasize
575 c.cursym.Func().Locals += extrasize
577 // low 32 bits for autosize
578 // high 32 bits for extrasize
579 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
585 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
586 if c.ctxt.Debugvlog {
587 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
589 c.cursym.Func().Text.Mark |= LEAF
592 if cursym.Func().Text.Mark&LEAF != 0 {
593 cursym.Set(obj.AttrLeaf, true)
594 if p.From.Sym.NoFrame() {
599 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
600 // A leaf function with a small stack can be marked
601 // NOSPLIT, avoiding a stack check.
602 p.From.Sym.Set(obj.AttrNoSplit, true)
605 if !p.From.Sym.NoSplit() {
606 p = c.stacksplit(p, c.autosize) // emit split check
609 var prologueEnd *obj.Prog
611 aoffset := c.autosize
613 // LDP offset variant range is -512 to 504, SP should be 16-byte aligned,
614 // so the maximum aoffset value is 496.
618 // Frame is non-empty. Make sure to save link register, even if
619 // it is a leaf function, so that traceback works.
621 if c.autosize > aoffset {
622 // Frame size is too large for a STP instruction. Store the frame pointer
623 // register and link register before decrementing SP, so if a signal comes
624 // during the execution of the function prologue, the traceback code will
625 // not see a half-updated stack frame.
627 // SUB $autosize, RSP, R20
628 q1 = obj.Appendp(q, c.newprog)
631 q1.From.Type = obj.TYPE_CONST
632 q1.From.Offset = int64(c.autosize)
634 q1.To.Type = obj.TYPE_REG
639 // STP (R29, R30), -8(R20)
640 q1 = obj.Appendp(q1, c.newprog)
643 q1.From.Type = obj.TYPE_REGREG
645 q1.From.Offset = REGLINK
646 q1.To.Type = obj.TYPE_MEM
650 // This is not async preemptible, as if we open a frame
651 // at the current SP, it will clobber the saved LR.
652 q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
655 q1 = obj.Appendp(q1, c.newprog)
658 q1.From.Type = obj.TYPE_REG
659 q1.From.Reg = REG_R20
660 q1.To.Type = obj.TYPE_REG
662 q1.Spadj = c.autosize
664 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
666 if buildcfg.GOOS == "ios" {
667 // iOS does not support SA_ONSTACK. We will run the signal handler
668 // on the G stack. If we write below SP, it may be clobbered by
669 // the signal handler. So we save FP and LR after decrementing SP.
670 // STP (R29, R30), -8(RSP)
671 q1 = obj.Appendp(q1, c.newprog)
674 q1.From.Type = obj.TYPE_REGREG
676 q1.From.Offset = REGLINK
677 q1.To.Type = obj.TYPE_MEM
682 // small frame, save FP and LR with one STP instruction, then update SP.
683 // Store first, so if a signal comes during the execution of the function
684 // prologue, the traceback code will not see a half-updated stack frame.
685 // STP (R29, R30), -aoffset-8(RSP)
686 q1 = obj.Appendp(q, c.newprog)
689 q1.From.Type = obj.TYPE_REGREG
691 q1.From.Offset = REGLINK
692 q1.To.Type = obj.TYPE_MEM
693 q1.To.Offset = int64(-aoffset - 8)
698 q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
699 // This instruction is not async preemptible, see the above comment.
700 // SUB $aoffset, RSP, RSP
701 q1 = obj.Appendp(q1, c.newprog)
704 q1.From.Type = obj.TYPE_CONST
705 q1.From.Offset = int64(aoffset)
707 q1.To.Type = obj.TYPE_REG
711 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
713 if buildcfg.GOOS == "ios" {
714 // See the above comment.
715 // STP (R29, R30), -8(RSP)
716 q1 = obj.Appendp(q1, c.newprog)
719 q1.From.Type = obj.TYPE_REGREG
721 q1.From.Offset = REGLINK
722 q1.To.Type = obj.TYPE_MEM
723 q1.To.Offset = int64(-8)
728 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
730 q1 = obj.Appendp(q1, c.newprog)
733 q1.From.Type = obj.TYPE_CONST
736 q1.To.Type = obj.TYPE_REG
739 if c.cursym.Func().Text.From.Sym.Wrapper() {
740 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
742 // MOV g_panic(g), RT1
746 // ... function body ...
748 // MOV panic_argp(RT1), RT2
749 // ADD $(autosize+8), RSP, R20
753 // MOVD R20, panic_argp(RT1)
756 // The NOP is needed to give the jumps somewhere to land.
757 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
760 // MOV g_panic(g), RT1
761 q = obj.Appendp(q, c.newprog)
763 q.From.Type = obj.TYPE_MEM
765 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
766 q.To.Type = obj.TYPE_REG
769 // CBNZ RT1, checkargp
770 cbnz := obj.Appendp(q, c.newprog)
772 cbnz.From.Type = obj.TYPE_REG
773 cbnz.From.Reg = REGRT1
774 cbnz.To.Type = obj.TYPE_BRANCH
776 // Empty branch target at the top of the function body
777 end := obj.Appendp(cbnz, c.newprog)
780 // find the end of the function
782 for last = end; last.Link != nil; last = last.Link {
785 // MOV panic_argp(RT1), RT2
786 mov := obj.Appendp(last, c.newprog)
788 mov.From.Type = obj.TYPE_MEM
789 mov.From.Reg = REGRT1
790 mov.From.Offset = 0 // Panic.argp
791 mov.To.Type = obj.TYPE_REG
794 // CBNZ branches to the MOV above
795 cbnz.To.SetTarget(mov)
797 // ADD $(autosize+8), SP, R20
798 q = obj.Appendp(mov, c.newprog)
800 q.From.Type = obj.TYPE_CONST
801 q.From.Offset = int64(c.autosize) + 8
803 q.To.Type = obj.TYPE_REG
807 q = obj.Appendp(q, c.newprog)
809 q.From.Type = obj.TYPE_REG
814 q = obj.Appendp(q, c.newprog)
816 q.To.Type = obj.TYPE_BRANCH
820 q = obj.Appendp(q, c.newprog)
822 q.From.Type = obj.TYPE_CONST
825 q.To.Type = obj.TYPE_REG
828 // MOV R20, panic_argp(RT1)
829 q = obj.Appendp(q, c.newprog)
831 q.From.Type = obj.TYPE_REG
833 q.To.Type = obj.TYPE_MEM
835 q.To.Offset = 0 // Panic.argp
838 q = obj.Appendp(q, c.newprog)
840 q.To.Type = obj.TYPE_BRANCH
846 if p.From.Type == obj.TYPE_CONST {
847 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
853 if c.cursym.Func().Text.Mark&LEAF != 0 {
856 p.From.Type = obj.TYPE_CONST
857 p.From.Offset = int64(c.autosize)
858 p.To.Type = obj.TYPE_REG
860 p.Spadj = -c.autosize
863 p = obj.Appendp(p, c.newprog)
865 p.From.Type = obj.TYPE_CONST
868 p.To.Type = obj.TYPE_REG
872 aoffset := c.autosize
873 // LDP -8(RSP), (R29, R30)
875 p.From.Type = obj.TYPE_MEM
878 p.To.Type = obj.TYPE_REGREG
880 p.To.Offset = REGLINK
882 // ADD $aoffset, RSP, RSP
885 q.From.Type = obj.TYPE_CONST
886 q.From.Offset = int64(aoffset)
887 q.To.Type = obj.TYPE_REG
896 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
897 // so that if you are debugging a low-level crash where PC and LR are zero,
898 // you can look at R27 to see what jumped to the zero.
899 // This is useful when bringing up Go on a new system.
900 // (There is similar code in ../ppc64/obj9.go:/if.false.)
901 const debugRETZERO = false
903 if p.As != obj.ARET {
911 p.From.Type = obj.TYPE_BRANCH
913 p.To.Type = obj.TYPE_REG
918 if p.As != obj.ARET {
926 if retjmp != nil { // retjmp
928 p.To.Type = obj.TYPE_BRANCH
930 p.Spadj = +c.autosize
935 p.To.Type = obj.TYPE_MEM
938 p.Spadj = +c.autosize
941 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
943 p.Spadj = int32(-p.From.Offset)
945 p.Spadj = int32(+p.From.Offset)
949 case obj.AGETCALLERPC:
953 p.From.Type = obj.TYPE_REG
958 p.From.Type = obj.TYPE_MEM
964 // STP (FP, R27), -24(SP)
971 // copy DUFFCOPY from q1 to q4
972 q4 := obj.Appendp(p, c.newprog)
974 q4.As = obj.ADUFFCOPY
978 q1.From.Type = obj.TYPE_BRANCH
979 q1.To.Type = obj.TYPE_REG
982 q2 := obj.Appendp(q1, c.newprog)
985 q2.From.Type = obj.TYPE_REGREG
987 q2.From.Offset = int64(REG_R27)
988 q2.To.Type = obj.TYPE_MEM
992 // maintain FP for DUFFCOPY
993 q3 := obj.Appendp(q2, c.newprog)
996 q3.From.Type = obj.TYPE_CONST
999 q3.To.Type = obj.TYPE_REG
1002 q5 := obj.Appendp(q4, c.newprog)
1005 q5.From.Type = obj.TYPE_CONST
1008 q5.To.Type = obj.TYPE_REG
1010 q1.From.SetTarget(q5)
1014 // ADR ret_addr, R27
1015 // STP (FP, R27), -24(SP)
1022 // copy DUFFZERO from q1 to q4
1023 q4 := obj.Appendp(p, c.newprog)
1025 q4.As = obj.ADUFFZERO
1029 q1.From.Type = obj.TYPE_BRANCH
1030 q1.To.Type = obj.TYPE_REG
1033 q2 := obj.Appendp(q1, c.newprog)
1036 q2.From.Type = obj.TYPE_REGREG
1038 q2.From.Offset = int64(REG_R27)
1039 q2.To.Type = obj.TYPE_MEM
1043 // maintain FP for DUFFZERO
1044 q3 := obj.Appendp(q2, c.newprog)
1047 q3.From.Type = obj.TYPE_CONST
1050 q3.To.Type = obj.TYPE_REG
1053 q5 := obj.Appendp(q4, c.newprog)
1056 q5.From.Type = obj.TYPE_CONST
1059 q5.To.Type = obj.TYPE_REG
1061 q1.From.SetTarget(q5)
1065 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
1066 f := c.cursym.Func()
1067 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
1068 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
1069 if ctxt.Debugvlog || !ctxt.IsAsm {
1070 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1072 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1074 log.Fatalf("bad SPWRITE")
1079 if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
1080 offset := p.From.Offset
1081 op := offset & (3 << 22)
1083 ctxt.Diag("illegal combination: %v", p)
1085 r := (offset >> 16) & 31
1086 shift := (offset >> 10) & 63
1088 // the shift amount is out of range, in order to avoid repeated error
1089 // reportings, don't call ctxt.Diag, because asmout case 27 has the
1093 p.From.Type = obj.TYPE_REG
1094 p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
1100 func nocache(p *obj.Prog) {
1106 var unaryDst = map[obj.As]bool{
1114 var Linkarm64 = obj.LinkArch{
1115 Arch: sys.ArchARM64,
1117 Preprocess: preprocess,
1121 DWARFRegisters: ARM64DWARFRegisters,