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 // zrReplace is the set of instructions for which $0 in the From operand
55 // should be replaced with REGZERO.
56 var zrReplace = map[obj.As]bool{
76 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
77 if c.ctxt.Flag_maymorestack != "" {
78 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
80 // Save LR and make room for FP, REGCTXT. Leave room
81 // for caller's saved FP.
83 p = obj.Appendp(p, c.newprog)
85 p.From.Type = obj.TYPE_REG
87 p.To.Type = obj.TYPE_MEM
89 p.To.Offset = -frameSize
94 p = obj.Appendp(p, c.newprog)
96 p.From.Type = obj.TYPE_REG
98 p.To.Type = obj.TYPE_MEM
102 p = obj.Appendp(p, c.newprog)
104 p.From.Type = obj.TYPE_CONST
107 p.To.Type = obj.TYPE_REG
110 // Save REGCTXT (for simplicity we do this whether or
112 p = obj.Appendp(p, c.newprog)
114 p.From.Type = obj.TYPE_REG
116 p.To.Type = obj.TYPE_MEM
121 p = obj.Appendp(p, c.newprog)
123 p.To.Type = obj.TYPE_BRANCH
124 // See ../x86/obj6.go
125 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
128 p = obj.Appendp(p, c.newprog)
130 p.From.Type = obj.TYPE_MEM
133 p.To.Type = obj.TYPE_REG
137 p = obj.Appendp(p, c.newprog)
139 p.From.Type = obj.TYPE_MEM
142 p.To.Type = obj.TYPE_REG
145 // Restore LR and SP.
146 p = obj.Appendp(p, c.newprog)
148 p.From.Type = obj.TYPE_MEM
150 p.From.Offset = frameSize
152 p.To.Type = obj.TYPE_REG
156 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
159 // Jump back to here after morestack returns.
162 // MOV g_stackguard(g), RT1
163 p = obj.Appendp(p, c.newprog)
166 p.From.Type = obj.TYPE_MEM
168 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
169 if c.cursym.CFunc() {
170 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
172 p.To.Type = obj.TYPE_REG
175 // Mark the stack bound check and morestack call async nonpreemptible.
176 // If we get preempted here, when resumed the preemption request is
177 // cleared, but we'll still call morestack, which will double the stack
178 // unnecessarily. See issue #35470.
179 p = c.ctxt.StartUnsafePoint(p, c.newprog)
181 q := (*obj.Prog)(nil)
182 if framesize <= objabi.StackSmall {
183 // small stack: SP < stackguard
184 // CMP stackguard, SP
186 p = obj.Appendp(p, c.newprog)
188 p.From.Type = obj.TYPE_REG
191 } else if framesize <= objabi.StackBig {
192 // large stack: SP-framesize < stackguard-StackSmall
193 // SUB $(framesize-StackSmall), SP, RT2
194 // CMP stackguard, RT2
195 p = obj.Appendp(p, c.newprog)
198 p.From.Type = obj.TYPE_CONST
199 p.From.Offset = int64(framesize) - objabi.StackSmall
201 p.To.Type = obj.TYPE_REG
204 p = obj.Appendp(p, c.newprog)
206 p.From.Type = obj.TYPE_REG
210 // Such a large stack we need to protect against underflow.
211 // The runtime guarantees SP > objabi.StackBig, but
212 // framesize is large enough that SP-framesize may
213 // underflow, causing a direct comparison with the
214 // stack guard to incorrectly succeed. We explicitly
215 // guard against underflow.
217 // SUBS $(framesize-StackSmall), SP, RT2
218 // // On underflow, jump to morestack
219 // BLO label_of_call_to_morestack
220 // CMP stackguard, RT2
222 p = obj.Appendp(p, c.newprog)
224 p.From.Type = obj.TYPE_CONST
225 p.From.Offset = int64(framesize) - objabi.StackSmall
227 p.To.Type = obj.TYPE_REG
230 p = obj.Appendp(p, c.newprog)
233 p.To.Type = obj.TYPE_BRANCH
235 p = obj.Appendp(p, c.newprog)
237 p.From.Type = obj.TYPE_REG
243 bls := obj.Appendp(p, c.newprog)
245 bls.To.Type = obj.TYPE_BRANCH
247 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
250 for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
253 // Now we are at the end of the function, but logically
254 // we are still in function prologue. We need to fix the
255 // SP data and PCDATA.
256 spfix := obj.Appendp(last, c.newprog)
258 spfix.Spadj = -framesize
260 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
261 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
264 q.To.SetTarget(pcdata)
266 bls.To.SetTarget(pcdata)
268 spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
271 movlr := obj.Appendp(spill, c.newprog)
273 movlr.From.Type = obj.TYPE_REG
274 movlr.From.Reg = REGLINK
275 movlr.To.Type = obj.TYPE_REG
276 movlr.To.Reg = REG_R3
280 debug = obj.Appendp(debug, c.newprog)
282 debug.From.Type = obj.TYPE_CONST
283 debug.From.Offset = int64(framesize)
284 debug.To.Type = obj.TYPE_REG
285 debug.To.Reg = REGTMP
288 // BL runtime.morestack(SB)
289 call := obj.Appendp(debug, c.newprog)
291 call.To.Type = obj.TYPE_BRANCH
292 morestack := "runtime.morestack"
294 case c.cursym.CFunc():
295 morestack = "runtime.morestackc"
296 case !c.cursym.Func().Text.From.Sym.NeedCtxt():
297 morestack = "runtime.morestack_noctxt"
299 call.To.Sym = c.ctxt.Lookup(morestack)
301 unspill := c.cursym.Func().UnspillRegisterArgs(call, c.newprog)
302 pcdata = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1)
305 jmp := obj.Appendp(pcdata, c.newprog)
307 jmp.To.Type = obj.TYPE_BRANCH
308 jmp.To.SetTarget(startPred.Link)
309 jmp.Spadj = +framesize
314 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
315 c := ctxt7{ctxt: ctxt, newprog: newprog}
320 // Previously we rewrote $0 to ZR, but we have now removed this change.
321 // In order to be compatible with some previous legal instruction formats,
322 // reserve the previous conversion for some specific instructions.
323 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 && zrReplace[p.As] {
324 p.From.Type = obj.TYPE_REG
328 // Rewrite BR/BL to symbol as TYPE_BRANCH.
336 p.To.Type = obj.TYPE_BRANCH
341 // Rewrite float constants to values stored in memory.
344 if p.From.Type == obj.TYPE_FCONST {
345 f64 := p.From.Val.(float64)
347 if c.chipfloat7(f64) > 0 {
350 if math.Float32bits(f32) == 0 {
351 p.From.Type = obj.TYPE_REG
355 p.From.Type = obj.TYPE_MEM
356 p.From.Sym = c.ctxt.Float32Sym(f32)
357 p.From.Name = obj.NAME_EXTERN
362 if p.From.Type == obj.TYPE_FCONST {
363 f64 := p.From.Val.(float64)
364 if c.chipfloat7(f64) > 0 {
367 if math.Float64bits(f64) == 0 {
368 p.From.Type = obj.TYPE_REG
372 p.From.Type = obj.TYPE_MEM
373 p.From.Sym = c.ctxt.Float64Sym(f64)
374 p.From.Name = obj.NAME_EXTERN
381 // Rewrite negative immediates as positive immediates with
382 // complementary instruction.
384 case AADD, ASUB, ACMP, ACMN:
385 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
386 p.From.Offset = -p.From.Offset
387 p.As = complements[p.As]
389 case AADDW, ASUBW, ACMPW, ACMNW:
390 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
391 p.From.Offset = -p.From.Offset
392 p.As = complements[p.As]
396 if c.ctxt.Flag_dynlink {
401 // Rewrite p, if necessary, to access global data via the global offset table.
402 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
403 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
406 // MOVD runtime.duffxxx@GOT, REGTMP
407 // ADD $offset, REGTMP
410 if p.As == obj.ADUFFZERO {
411 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
413 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
415 offset := p.To.Offset
417 p.From.Type = obj.TYPE_MEM
418 p.From.Name = obj.NAME_GOTREF
420 p.To.Type = obj.TYPE_REG
422 p.To.Name = obj.NAME_NONE
425 p1 := obj.Appendp(p, c.newprog)
427 p1.From.Type = obj.TYPE_CONST
428 p1.From.Offset = offset
429 p1.To.Type = obj.TYPE_REG
431 p2 := obj.Appendp(p1, c.newprog)
433 p2.To.Type = obj.TYPE_REG
437 // We only care about global data: NAME_EXTERN means a global
438 // symbol in the Go sense, and p.Sym.Local is true for a few
439 // internally defined symbols.
440 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
441 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
442 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
444 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
446 if p.To.Type != obj.TYPE_REG {
447 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
449 p.From.Type = obj.TYPE_MEM
450 p.From.Name = obj.NAME_GOTREF
451 if p.From.Offset != 0 {
452 q := obj.Appendp(p, c.newprog)
454 q.From.Type = obj.TYPE_CONST
455 q.From.Offset = p.From.Offset
460 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
461 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
464 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
465 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
466 // An addition may be inserted between the two MOVs if there is an offset.
467 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
468 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
469 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
472 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
477 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
480 if source.Sym.Type == objabi.STLSBSS {
483 if source.Type != obj.TYPE_MEM {
484 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
486 p1 := obj.Appendp(p, c.newprog)
487 p2 := obj.Appendp(p1, c.newprog)
489 p1.From.Type = obj.TYPE_MEM
490 p1.From.Sym = source.Sym
491 p1.From.Name = obj.NAME_GOTREF
492 p1.To.Type = obj.TYPE_REG
498 if p.From.Name == obj.NAME_EXTERN {
500 p2.From.Name = obj.NAME_NONE
502 } else if p.To.Name == obj.NAME_EXTERN {
504 p2.To.Name = obj.NAME_NONE
512 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
513 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
517 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
519 p := c.cursym.Func().Text
520 textstksiz := p.To.Offset
521 if textstksiz == -8 {
522 // Historical way to mark NOFRAME.
523 p.From.Sym.Set(obj.AttrNoFrame, true)
527 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
529 if p.From.Sym.NoFrame() {
531 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
535 c.cursym.Func().Args = p.To.Val.(int32)
536 c.cursym.Func().Locals = int32(textstksiz)
539 * find leaf subroutines
541 for p := c.cursym.Func().Text; p != nil; p = p.Link {
549 c.cursym.Func().Text.Mark &^= LEAF
556 for p := c.cursym.Func().Text; p != nil; p = p.Link {
560 c.cursym.Func().Text = p
561 c.autosize = int32(textstksiz)
563 if p.Mark&LEAF != 0 && c.autosize == 0 {
564 // A leaf function with no locals has no frame.
565 p.From.Sym.Set(obj.AttrNoFrame, true)
568 if !p.From.Sym.NoFrame() {
569 // If there is a stack frame at all, it includes
570 // space to save the LR.
575 extrasize := int32(0)
576 if c.autosize%16 == 8 {
577 // Allocate extra 8 bytes on the frame top to save FP
579 } else if c.autosize&(16-1) == 0 {
580 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
583 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
585 c.autosize += extrasize
586 c.cursym.Func().Locals += extrasize
588 // low 32 bits for autosize
589 // high 32 bits for extrasize
590 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
596 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
597 if c.ctxt.Debugvlog {
598 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
600 c.cursym.Func().Text.Mark |= LEAF
603 if cursym.Func().Text.Mark&LEAF != 0 {
604 cursym.Set(obj.AttrLeaf, true)
605 if p.From.Sym.NoFrame() {
610 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
611 // A leaf function with a small stack can be marked
612 // NOSPLIT, avoiding a stack check.
613 p.From.Sym.Set(obj.AttrNoSplit, true)
616 if !p.From.Sym.NoSplit() {
617 p = c.stacksplit(p, c.autosize) // emit split check
620 var prologueEnd *obj.Prog
622 aoffset := c.autosize
624 // MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned.
625 // so the maximum aoffset value is 0xf0.
629 // Frame is non-empty. Make sure to save link register, even if
630 // it is a leaf function, so that traceback works.
632 if c.autosize > aoffset {
633 // Frame size is too large for a MOVD.W instruction. Store the frame pointer
634 // register and link register before decrementing SP, so if a signal comes
635 // during the execution of the function prologue, the traceback code will
636 // not see a half-updated stack frame.
638 // SUB $autosize, RSP, R20
639 q1 = obj.Appendp(q, c.newprog)
642 q1.From.Type = obj.TYPE_CONST
643 q1.From.Offset = int64(c.autosize)
645 q1.To.Type = obj.TYPE_REG
650 // STP (R29, R30), -8(R20)
651 q1 = obj.Appendp(q1, c.newprog)
654 q1.From.Type = obj.TYPE_REGREG
656 q1.From.Offset = REGLINK
657 q1.To.Type = obj.TYPE_MEM
661 // This is not async preemptible, as if we open a frame
662 // at the current SP, it will clobber the saved LR.
663 q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
666 q1 = obj.Appendp(q1, c.newprog)
669 q1.From.Type = obj.TYPE_REG
670 q1.From.Reg = REG_R20
671 q1.To.Type = obj.TYPE_REG
673 q1.Spadj = c.autosize
675 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
677 if buildcfg.GOOS == "ios" {
678 // iOS does not support SA_ONSTACK. We will run the signal handler
679 // on the G stack. If we write below SP, it may be clobbered by
680 // the signal handler. So we save FP and LR after decrementing SP.
681 // STP (R29, R30), -8(RSP)
682 q1 = obj.Appendp(q1, c.newprog)
685 q1.From.Type = obj.TYPE_REGREG
687 q1.From.Offset = REGLINK
688 q1.To.Type = obj.TYPE_MEM
693 // small frame, update SP and save LR in a single MOVD.W instruction.
694 // So if a signal comes during the execution of the function prologue,
695 // the traceback code will not see a half-updated stack frame.
696 // Also, on Linux, in a cgo binary we may get a SIGSETXID signal
697 // early on before the signal stack is set, as glibc doesn't allow
698 // us to block SIGSETXID. So it is important that we don't write below
699 // the SP until the signal stack is set.
700 // Luckily, all the functions from thread entry to setting the signal
701 // stack have small frames.
702 q1 = obj.Appendp(q, c.newprog)
705 q1.From.Type = obj.TYPE_REG
706 q1.From.Reg = REGLINK
707 q1.To.Type = obj.TYPE_MEM
709 q1.To.Offset = int64(-aoffset)
716 q1 = obj.Appendp(q1, c.newprog)
719 q1.From.Type = obj.TYPE_REG
721 q1.To.Type = obj.TYPE_MEM
726 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
728 q1 = obj.Appendp(q1, c.newprog)
731 q1.From.Type = obj.TYPE_CONST
734 q1.To.Type = obj.TYPE_REG
737 if c.cursym.Func().Text.From.Sym.Wrapper() {
738 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
740 // MOV g_panic(g), RT1
744 // ... function body ...
746 // MOV panic_argp(RT1), RT2
747 // ADD $(autosize+8), RSP, R20
751 // MOVD R20, panic_argp(RT1)
754 // The NOP is needed to give the jumps somewhere to land.
755 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
758 // MOV g_panic(g), RT1
759 q = obj.Appendp(q, c.newprog)
761 q.From.Type = obj.TYPE_MEM
763 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
764 q.To.Type = obj.TYPE_REG
767 // CBNZ RT1, checkargp
768 cbnz := obj.Appendp(q, c.newprog)
770 cbnz.From.Type = obj.TYPE_REG
771 cbnz.From.Reg = REGRT1
772 cbnz.To.Type = obj.TYPE_BRANCH
774 // Empty branch target at the top of the function body
775 end := obj.Appendp(cbnz, c.newprog)
778 // find the end of the function
780 for last = end; last.Link != nil; last = last.Link {
783 // MOV panic_argp(RT1), RT2
784 mov := obj.Appendp(last, c.newprog)
786 mov.From.Type = obj.TYPE_MEM
787 mov.From.Reg = REGRT1
788 mov.From.Offset = 0 // Panic.argp
789 mov.To.Type = obj.TYPE_REG
792 // CBNZ branches to the MOV above
793 cbnz.To.SetTarget(mov)
795 // ADD $(autosize+8), SP, R20
796 q = obj.Appendp(mov, c.newprog)
798 q.From.Type = obj.TYPE_CONST
799 q.From.Offset = int64(c.autosize) + 8
801 q.To.Type = obj.TYPE_REG
805 q = obj.Appendp(q, c.newprog)
807 q.From.Type = obj.TYPE_REG
812 q = obj.Appendp(q, c.newprog)
814 q.To.Type = obj.TYPE_BRANCH
818 q = obj.Appendp(q, c.newprog)
820 q.From.Type = obj.TYPE_CONST
823 q.To.Type = obj.TYPE_REG
826 // MOV R20, panic_argp(RT1)
827 q = obj.Appendp(q, c.newprog)
829 q.From.Type = obj.TYPE_REG
831 q.To.Type = obj.TYPE_MEM
833 q.To.Offset = 0 // Panic.argp
836 q = obj.Appendp(q, c.newprog)
838 q.To.Type = obj.TYPE_BRANCH
844 if p.From.Type == obj.TYPE_CONST {
845 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
851 if c.cursym.Func().Text.Mark&LEAF != 0 {
854 p.From.Type = obj.TYPE_CONST
855 p.From.Offset = int64(c.autosize)
856 p.To.Type = obj.TYPE_REG
858 p.Spadj = -c.autosize
861 p = obj.Appendp(p, c.newprog)
863 p.From.Type = obj.TYPE_CONST
866 p.To.Type = obj.TYPE_REG
870 aoffset := c.autosize
871 // LDP -8(RSP), (R29, R30)
873 p.From.Type = obj.TYPE_MEM
876 p.To.Type = obj.TYPE_REGREG
878 p.To.Offset = REGLINK
880 // ADD $aoffset, RSP, RSP
883 q.From.Type = obj.TYPE_CONST
884 q.From.Offset = int64(aoffset)
885 q.To.Type = obj.TYPE_REG
894 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
895 // so that if you are debugging a low-level crash where PC and LR are zero,
896 // you can look at R27 to see what jumped to the zero.
897 // This is useful when bringing up Go on a new system.
898 // (There is similar code in ../ppc64/obj9.go:/if.false.)
899 const debugRETZERO = false
901 if p.As != obj.ARET {
909 p.From.Type = obj.TYPE_BRANCH
911 p.To.Type = obj.TYPE_REG
916 if p.As != obj.ARET {
924 if retjmp != nil { // retjmp
926 p.To.Type = obj.TYPE_BRANCH
928 p.Spadj = +c.autosize
933 p.To.Type = obj.TYPE_MEM
936 p.Spadj = +c.autosize
939 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
941 p.Spadj = int32(-p.From.Offset)
943 p.Spadj = int32(+p.From.Offset)
947 case obj.AGETCALLERPC:
951 p.From.Type = obj.TYPE_REG
956 p.From.Type = obj.TYPE_MEM
962 // STP (FP, R27), -24(SP)
969 // copy DUFFCOPY from q1 to q4
970 q4 := obj.Appendp(p, c.newprog)
972 q4.As = obj.ADUFFCOPY
976 q1.From.Type = obj.TYPE_BRANCH
977 q1.To.Type = obj.TYPE_REG
980 q2 := obj.Appendp(q1, c.newprog)
983 q2.From.Type = obj.TYPE_REGREG
985 q2.From.Offset = int64(REG_R27)
986 q2.To.Type = obj.TYPE_MEM
990 // maintain FP for DUFFCOPY
991 q3 := obj.Appendp(q2, c.newprog)
994 q3.From.Type = obj.TYPE_CONST
997 q3.To.Type = obj.TYPE_REG
1000 q5 := obj.Appendp(q4, c.newprog)
1003 q5.From.Type = obj.TYPE_CONST
1006 q5.To.Type = obj.TYPE_REG
1008 q1.From.SetTarget(q5)
1012 // ADR ret_addr, R27
1013 // STP (FP, R27), -24(SP)
1020 // copy DUFFZERO from q1 to q4
1021 q4 := obj.Appendp(p, c.newprog)
1023 q4.As = obj.ADUFFZERO
1027 q1.From.Type = obj.TYPE_BRANCH
1028 q1.To.Type = obj.TYPE_REG
1031 q2 := obj.Appendp(q1, c.newprog)
1034 q2.From.Type = obj.TYPE_REGREG
1036 q2.From.Offset = int64(REG_R27)
1037 q2.To.Type = obj.TYPE_MEM
1041 // maintain FP for DUFFZERO
1042 q3 := obj.Appendp(q2, c.newprog)
1045 q3.From.Type = obj.TYPE_CONST
1048 q3.To.Type = obj.TYPE_REG
1051 q5 := obj.Appendp(q4, c.newprog)
1054 q5.From.Type = obj.TYPE_CONST
1057 q5.To.Type = obj.TYPE_REG
1059 q1.From.SetTarget(q5)
1063 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
1064 f := c.cursym.Func()
1065 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
1066 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
1067 if ctxt.Debugvlog || !ctxt.IsAsm {
1068 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1070 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1072 log.Fatalf("bad SPWRITE")
1077 if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
1078 offset := p.From.Offset
1079 op := offset & (3 << 22)
1081 ctxt.Diag("illegal combination: %v", p)
1083 r := (offset >> 16) & 31
1084 shift := (offset >> 10) & 63
1086 // the shift amount is out of range, in order to avoid repeated error
1087 // reportings, don't call ctxt.Diag, because asmout case 27 has the
1091 p.From.Type = obj.TYPE_REG
1092 p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
1098 func nocache(p *obj.Prog) {
1104 var unaryDst = map[obj.As]bool{
1112 var Linkarm64 = obj.LinkArch{
1113 Arch: sys.ArchARM64,
1115 Preprocess: preprocess,
1119 DWARFRegisters: ARM64DWARFRegisters,