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
44 // zrReplace is the set of instructions for which $0 in the From operand
45 // should be replaced with REGZERO.
46 var zrReplace = map[obj.As]bool{
67 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
68 if c.ctxt.Flag_maymorestack != "" {
69 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
71 // Save LR and make room for FP, REGCTXT. Leave room
72 // for caller's saved FP.
74 p = obj.Appendp(p, c.newprog)
76 p.From.Type = obj.TYPE_REG
78 p.To.Type = obj.TYPE_MEM
80 p.To.Offset = -frameSize
85 p = obj.Appendp(p, c.newprog)
87 p.From.Type = obj.TYPE_REG
89 p.To.Type = obj.TYPE_MEM
93 p = obj.Appendp(p, c.newprog)
95 p.From.Type = obj.TYPE_CONST
98 p.To.Type = obj.TYPE_REG
101 // Save REGCTXT (for simplicity we do this whether or
103 p = obj.Appendp(p, c.newprog)
105 p.From.Type = obj.TYPE_REG
107 p.To.Type = obj.TYPE_MEM
112 p = obj.Appendp(p, c.newprog)
114 p.To.Type = obj.TYPE_BRANCH
115 // See ../x86/obj6.go
116 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
119 p = obj.Appendp(p, c.newprog)
121 p.From.Type = obj.TYPE_MEM
124 p.To.Type = obj.TYPE_REG
128 p = obj.Appendp(p, c.newprog)
130 p.From.Type = obj.TYPE_MEM
133 p.To.Type = obj.TYPE_REG
136 // Restore LR and SP.
137 p = obj.Appendp(p, c.newprog)
139 p.From.Type = obj.TYPE_MEM
141 p.From.Offset = frameSize
143 p.To.Type = obj.TYPE_REG
147 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
150 // Jump back to here after morestack returns.
153 // MOV g_stackguard(g), RT1
154 p = obj.Appendp(p, c.newprog)
157 p.From.Type = obj.TYPE_MEM
159 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
160 if c.cursym.CFunc() {
161 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
163 p.To.Type = obj.TYPE_REG
166 // Mark the stack bound check and morestack call async nonpreemptible.
167 // If we get preempted here, when resumed the preemption request is
168 // cleared, but we'll still call morestack, which will double the stack
169 // unnecessarily. See issue #35470.
170 p = c.ctxt.StartUnsafePoint(p, c.newprog)
172 q := (*obj.Prog)(nil)
173 if framesize <= objabi.StackSmall {
174 // small stack: SP < stackguard
175 // CMP stackguard, SP
177 p = obj.Appendp(p, c.newprog)
179 p.From.Type = obj.TYPE_REG
182 } else if framesize <= objabi.StackBig {
183 // large stack: SP-framesize < stackguard-StackSmall
184 // SUB $(framesize-StackSmall), SP, RT2
185 // CMP stackguard, RT2
186 p = obj.Appendp(p, c.newprog)
189 p.From.Type = obj.TYPE_CONST
190 p.From.Offset = int64(framesize) - objabi.StackSmall
192 p.To.Type = obj.TYPE_REG
195 p = obj.Appendp(p, c.newprog)
197 p.From.Type = obj.TYPE_REG
201 // Such a large stack we need to protect against underflow.
202 // The runtime guarantees SP > objabi.StackBig, but
203 // framesize is large enough that SP-framesize may
204 // underflow, causing a direct comparison with the
205 // stack guard to incorrectly succeed. We explicitly
206 // guard against underflow.
208 // SUBS $(framesize-StackSmall), SP, RT2
209 // // On underflow, jump to morestack
210 // BLO label_of_call_to_morestack
211 // CMP stackguard, RT2
213 p = obj.Appendp(p, c.newprog)
215 p.From.Type = obj.TYPE_CONST
216 p.From.Offset = int64(framesize) - objabi.StackSmall
218 p.To.Type = obj.TYPE_REG
221 p = obj.Appendp(p, c.newprog)
224 p.To.Type = obj.TYPE_BRANCH
226 p = obj.Appendp(p, c.newprog)
228 p.From.Type = obj.TYPE_REG
234 bls := obj.Appendp(p, c.newprog)
236 bls.To.Type = obj.TYPE_BRANCH
238 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
241 for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
244 // Now we are at the end of the function, but logically
245 // we are still in function prologue. We need to fix the
246 // SP data and PCDATA.
247 spfix := obj.Appendp(last, c.newprog)
249 spfix.Spadj = -framesize
251 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
252 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
255 q.To.SetTarget(pcdata)
257 bls.To.SetTarget(pcdata)
259 spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
262 movlr := obj.Appendp(spill, c.newprog)
264 movlr.From.Type = obj.TYPE_REG
265 movlr.From.Reg = REGLINK
266 movlr.To.Type = obj.TYPE_REG
267 movlr.To.Reg = REG_R3
271 debug = obj.Appendp(debug, c.newprog)
273 debug.From.Type = obj.TYPE_CONST
274 debug.From.Offset = int64(framesize)
275 debug.To.Type = obj.TYPE_REG
276 debug.To.Reg = REGTMP
279 // BL runtime.morestack(SB)
280 call := obj.Appendp(debug, c.newprog)
282 call.To.Type = obj.TYPE_BRANCH
283 morestack := "runtime.morestack"
285 case c.cursym.CFunc():
286 morestack = "runtime.morestackc"
287 case !c.cursym.Func().Text.From.Sym.NeedCtxt():
288 morestack = "runtime.morestack_noctxt"
290 call.To.Sym = c.ctxt.Lookup(morestack)
292 unspill := c.cursym.Func().UnspillRegisterArgs(call, c.newprog)
293 pcdata = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1)
296 jmp := obj.Appendp(pcdata, c.newprog)
298 jmp.To.Type = obj.TYPE_BRANCH
299 jmp.To.SetTarget(startPred.Link)
300 jmp.Spadj = +framesize
305 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
306 c := ctxt7{ctxt: ctxt, newprog: newprog}
311 // Previously we rewrote $0 to ZR, but we have now removed this change.
312 // In order to be compatible with some previous legal instruction formats,
313 // reserve the previous conversion for some specific instructions.
314 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 && zrReplace[p.As] {
315 p.From.Type = obj.TYPE_REG
319 // Rewrite BR/BL to symbol as TYPE_BRANCH.
327 p.To.Type = obj.TYPE_BRANCH
332 // Rewrite float constants to values stored in memory.
335 if p.From.Type == obj.TYPE_FCONST {
336 f64 := p.From.Val.(float64)
338 if c.chipfloat7(f64) > 0 {
341 if math.Float32bits(f32) == 0 {
342 p.From.Type = obj.TYPE_REG
346 p.From.Type = obj.TYPE_MEM
347 p.From.Sym = c.ctxt.Float32Sym(f32)
348 p.From.Name = obj.NAME_EXTERN
353 if p.From.Type == obj.TYPE_FCONST {
354 f64 := p.From.Val.(float64)
355 if c.chipfloat7(f64) > 0 {
358 if math.Float64bits(f64) == 0 {
359 p.From.Type = obj.TYPE_REG
363 p.From.Type = obj.TYPE_MEM
364 p.From.Sym = c.ctxt.Float64Sym(f64)
365 p.From.Name = obj.NAME_EXTERN
372 if c.ctxt.Flag_dynlink {
377 // Rewrite p, if necessary, to access global data via the global offset table.
378 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
379 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
382 // MOVD runtime.duffxxx@GOT, REGTMP
383 // ADD $offset, REGTMP
386 if p.As == obj.ADUFFZERO {
387 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
389 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
391 offset := p.To.Offset
393 p.From.Type = obj.TYPE_MEM
394 p.From.Name = obj.NAME_GOTREF
396 p.To.Type = obj.TYPE_REG
398 p.To.Name = obj.NAME_NONE
401 p1 := obj.Appendp(p, c.newprog)
403 p1.From.Type = obj.TYPE_CONST
404 p1.From.Offset = offset
405 p1.To.Type = obj.TYPE_REG
407 p2 := obj.Appendp(p1, c.newprog)
409 p2.To.Type = obj.TYPE_REG
413 // We only care about global data: NAME_EXTERN means a global
414 // symbol in the Go sense, and p.Sym.Local is true for a few
415 // internally defined symbols.
416 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
417 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
418 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
420 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
422 if p.To.Type != obj.TYPE_REG {
423 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
425 p.From.Type = obj.TYPE_MEM
426 p.From.Name = obj.NAME_GOTREF
427 if p.From.Offset != 0 {
428 q := obj.Appendp(p, c.newprog)
430 q.From.Type = obj.TYPE_CONST
431 q.From.Offset = p.From.Offset
436 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
437 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
440 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
441 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
442 // An addition may be inserted between the two MOVs if there is an offset.
443 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
444 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
445 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
448 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
453 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
456 if source.Sym.Type == objabi.STLSBSS {
459 if source.Type != obj.TYPE_MEM {
460 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
462 p1 := obj.Appendp(p, c.newprog)
463 p2 := obj.Appendp(p1, c.newprog)
465 p1.From.Type = obj.TYPE_MEM
466 p1.From.Sym = source.Sym
467 p1.From.Name = obj.NAME_GOTREF
468 p1.To.Type = obj.TYPE_REG
474 if p.From.Name == obj.NAME_EXTERN {
476 p2.From.Name = obj.NAME_NONE
478 } else if p.To.Name == obj.NAME_EXTERN {
480 p2.To.Name = obj.NAME_NONE
488 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
489 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
493 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
495 p := c.cursym.Func().Text
496 textstksiz := p.To.Offset
497 if textstksiz == -8 {
498 // Historical way to mark NOFRAME.
499 p.From.Sym.Set(obj.AttrNoFrame, true)
503 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
505 if p.From.Sym.NoFrame() {
507 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
511 c.cursym.Func().Args = p.To.Val.(int32)
512 c.cursym.Func().Locals = int32(textstksiz)
515 * find leaf subroutines
517 for p := c.cursym.Func().Text; p != nil; p = p.Link {
525 c.cursym.Func().Text.Mark &^= LEAF
532 for p := c.cursym.Func().Text; p != nil; p = p.Link {
536 c.cursym.Func().Text = p
537 c.autosize = int32(textstksiz)
539 if p.Mark&LEAF != 0 && c.autosize == 0 {
540 // A leaf function with no locals has no frame.
541 p.From.Sym.Set(obj.AttrNoFrame, true)
544 if !p.From.Sym.NoFrame() {
545 // If there is a stack frame at all, it includes
546 // space to save the LR.
551 extrasize := int32(0)
552 if c.autosize%16 == 8 {
553 // Allocate extra 8 bytes on the frame top to save FP
555 } else if c.autosize&(16-1) == 0 {
556 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
559 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
561 c.autosize += extrasize
562 c.cursym.Func().Locals += extrasize
564 // low 32 bits for autosize
565 // high 32 bits for extrasize
566 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
572 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
573 if c.ctxt.Debugvlog {
574 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
576 c.cursym.Func().Text.Mark |= LEAF
579 if cursym.Func().Text.Mark&LEAF != 0 {
580 cursym.Set(obj.AttrLeaf, true)
581 if p.From.Sym.NoFrame() {
586 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
587 // A leaf function with a small stack can be marked
588 // NOSPLIT, avoiding a stack check.
589 p.From.Sym.Set(obj.AttrNoSplit, true)
592 if !p.From.Sym.NoSplit() {
593 p = c.stacksplit(p, c.autosize) // emit split check
596 var prologueEnd *obj.Prog
598 aoffset := c.autosize
600 // MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned.
601 // so the maximum aoffset value is 0xf0.
605 // Frame is non-empty. Make sure to save link register, even if
606 // it is a leaf function, so that traceback works.
608 if c.autosize > aoffset {
609 // Frame size is too large for a MOVD.W instruction. Store the frame pointer
610 // register and link register before decrementing SP, so if a signal comes
611 // during the execution of the function prologue, the traceback code will
612 // not see a half-updated stack frame.
614 // SUB $autosize, RSP, R20
615 q1 = obj.Appendp(q, c.newprog)
618 q1.From.Type = obj.TYPE_CONST
619 q1.From.Offset = int64(c.autosize)
621 q1.To.Type = obj.TYPE_REG
626 // STP (R29, R30), -8(R20)
627 q1 = obj.Appendp(q1, c.newprog)
630 q1.From.Type = obj.TYPE_REGREG
632 q1.From.Offset = REGLINK
633 q1.To.Type = obj.TYPE_MEM
637 // This is not async preemptible, as if we open a frame
638 // at the current SP, it will clobber the saved LR.
639 q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
642 q1 = obj.Appendp(q1, c.newprog)
645 q1.From.Type = obj.TYPE_REG
646 q1.From.Reg = REG_R20
647 q1.To.Type = obj.TYPE_REG
649 q1.Spadj = c.autosize
651 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
653 if buildcfg.GOOS == "ios" {
654 // iOS does not support SA_ONSTACK. We will run the signal handler
655 // on the G stack. If we write below SP, it may be clobbered by
656 // the signal handler. So we save FP and LR after decrementing SP.
657 // STP (R29, R30), -8(RSP)
658 q1 = obj.Appendp(q1, c.newprog)
661 q1.From.Type = obj.TYPE_REGREG
663 q1.From.Offset = REGLINK
664 q1.To.Type = obj.TYPE_MEM
669 // small frame, update SP and save LR in a single MOVD.W instruction.
670 // So if a signal comes during the execution of the function prologue,
671 // the traceback code will not see a half-updated stack frame.
672 // Also, on Linux, in a cgo binary we may get a SIGSETXID signal
673 // early on before the signal stack is set, as glibc doesn't allow
674 // us to block SIGSETXID. So it is important that we don't write below
675 // the SP until the signal stack is set.
676 // Luckily, all the functions from thread entry to setting the signal
677 // stack have small frames.
678 q1 = obj.Appendp(q, c.newprog)
681 q1.From.Type = obj.TYPE_REG
682 q1.From.Reg = REGLINK
683 q1.To.Type = obj.TYPE_MEM
685 q1.To.Offset = int64(-aoffset)
692 q1 = obj.Appendp(q1, c.newprog)
695 q1.From.Type = obj.TYPE_REG
697 q1.To.Type = obj.TYPE_MEM
702 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
704 q1 = obj.Appendp(q1, c.newprog)
707 q1.From.Type = obj.TYPE_CONST
710 q1.To.Type = obj.TYPE_REG
713 if c.cursym.Func().Text.From.Sym.Wrapper() {
714 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
716 // MOV g_panic(g), RT1
720 // ... function body ...
722 // MOV panic_argp(RT1), RT2
723 // ADD $(autosize+8), RSP, R20
727 // MOVD R20, panic_argp(RT1)
730 // The NOP is needed to give the jumps somewhere to land.
731 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
734 // MOV g_panic(g), RT1
735 q = obj.Appendp(q, c.newprog)
737 q.From.Type = obj.TYPE_MEM
739 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
740 q.To.Type = obj.TYPE_REG
743 // CBNZ RT1, checkargp
744 cbnz := obj.Appendp(q, c.newprog)
746 cbnz.From.Type = obj.TYPE_REG
747 cbnz.From.Reg = REGRT1
748 cbnz.To.Type = obj.TYPE_BRANCH
750 // Empty branch target at the top of the function body
751 end := obj.Appendp(cbnz, c.newprog)
754 // find the end of the function
756 for last = end; last.Link != nil; last = last.Link {
759 // MOV panic_argp(RT1), RT2
760 mov := obj.Appendp(last, c.newprog)
762 mov.From.Type = obj.TYPE_MEM
763 mov.From.Reg = REGRT1
764 mov.From.Offset = 0 // Panic.argp
765 mov.To.Type = obj.TYPE_REG
768 // CBNZ branches to the MOV above
769 cbnz.To.SetTarget(mov)
771 // ADD $(autosize+8), SP, R20
772 q = obj.Appendp(mov, c.newprog)
774 q.From.Type = obj.TYPE_CONST
775 q.From.Offset = int64(c.autosize) + 8
777 q.To.Type = obj.TYPE_REG
781 q = obj.Appendp(q, c.newprog)
783 q.From.Type = obj.TYPE_REG
788 q = obj.Appendp(q, c.newprog)
790 q.To.Type = obj.TYPE_BRANCH
794 q = obj.Appendp(q, c.newprog)
796 q.From.Type = obj.TYPE_CONST
799 q.To.Type = obj.TYPE_REG
802 // MOV R20, panic_argp(RT1)
803 q = obj.Appendp(q, c.newprog)
805 q.From.Type = obj.TYPE_REG
807 q.To.Type = obj.TYPE_MEM
809 q.To.Offset = 0 // Panic.argp
812 q = obj.Appendp(q, c.newprog)
814 q.To.Type = obj.TYPE_BRANCH
820 if p.From.Type == obj.TYPE_CONST {
821 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
827 if c.cursym.Func().Text.Mark&LEAF != 0 {
830 p.From.Type = obj.TYPE_CONST
831 p.From.Offset = int64(c.autosize)
832 p.To.Type = obj.TYPE_REG
834 p.Spadj = -c.autosize
837 p = obj.Appendp(p, c.newprog)
839 p.From.Type = obj.TYPE_CONST
842 p.To.Type = obj.TYPE_REG
846 aoffset := c.autosize
847 // LDP -8(RSP), (R29, R30)
849 p.From.Type = obj.TYPE_MEM
852 p.To.Type = obj.TYPE_REGREG
854 p.To.Offset = REGLINK
856 // ADD $aoffset, RSP, RSP
859 q.From.Type = obj.TYPE_CONST
860 q.From.Offset = int64(aoffset)
861 q.To.Type = obj.TYPE_REG
870 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
871 // so that if you are debugging a low-level crash where PC and LR are zero,
872 // you can look at R27 to see what jumped to the zero.
873 // This is useful when bringing up Go on a new system.
874 // (There is similar code in ../ppc64/obj9.go:/if.false.)
875 const debugRETZERO = false
877 if p.As != obj.ARET {
885 p.From.Type = obj.TYPE_BRANCH
887 p.To.Type = obj.TYPE_REG
892 if p.As != obj.ARET {
900 if retjmp != nil { // retjmp
902 p.To.Type = obj.TYPE_BRANCH
904 p.Spadj = +c.autosize
909 p.To.Type = obj.TYPE_MEM
912 p.Spadj = +c.autosize
915 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
917 p.Spadj = int32(-p.From.Offset)
919 p.Spadj = int32(+p.From.Offset)
923 case obj.AGETCALLERPC:
927 p.From.Type = obj.TYPE_REG
932 p.From.Type = obj.TYPE_MEM
938 // STP (FP, R27), -24(SP)
945 // copy DUFFCOPY from q1 to q4
946 q4 := obj.Appendp(p, c.newprog)
948 q4.As = obj.ADUFFCOPY
952 q1.From.Type = obj.TYPE_BRANCH
953 q1.To.Type = obj.TYPE_REG
956 q2 := obj.Appendp(q1, c.newprog)
959 q2.From.Type = obj.TYPE_REGREG
961 q2.From.Offset = int64(REG_R27)
962 q2.To.Type = obj.TYPE_MEM
966 // maintain FP for DUFFCOPY
967 q3 := obj.Appendp(q2, c.newprog)
970 q3.From.Type = obj.TYPE_CONST
973 q3.To.Type = obj.TYPE_REG
976 q5 := obj.Appendp(q4, c.newprog)
979 q5.From.Type = obj.TYPE_CONST
982 q5.To.Type = obj.TYPE_REG
984 q1.From.SetTarget(q5)
989 // STP (FP, R27), -24(SP)
996 // copy DUFFZERO from q1 to q4
997 q4 := obj.Appendp(p, c.newprog)
999 q4.As = obj.ADUFFZERO
1003 q1.From.Type = obj.TYPE_BRANCH
1004 q1.To.Type = obj.TYPE_REG
1007 q2 := obj.Appendp(q1, c.newprog)
1010 q2.From.Type = obj.TYPE_REGREG
1012 q2.From.Offset = int64(REG_R27)
1013 q2.To.Type = obj.TYPE_MEM
1017 // maintain FP for DUFFZERO
1018 q3 := obj.Appendp(q2, c.newprog)
1021 q3.From.Type = obj.TYPE_CONST
1024 q3.To.Type = obj.TYPE_REG
1027 q5 := obj.Appendp(q4, c.newprog)
1030 q5.From.Type = obj.TYPE_CONST
1033 q5.To.Type = obj.TYPE_REG
1035 q1.From.SetTarget(q5)
1039 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
1040 f := c.cursym.Func()
1041 if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
1042 c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
1043 if ctxt.Debugvlog || !ctxt.IsAsm {
1044 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1046 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1048 log.Fatalf("bad SPWRITE")
1053 if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
1054 offset := p.From.Offset
1055 op := offset & (3 << 22)
1057 ctxt.Diag("illegal combination: %v", p)
1059 r := (offset >> 16) & 31
1060 shift := (offset >> 10) & 63
1062 // the shift amount is out of range, in order to avoid repeated error
1063 // reportings, don't call ctxt.Diag, because asmout case 27 has the
1067 p.From.Type = obj.TYPE_REG
1068 p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
1074 func nocache(p *obj.Prog) {
1080 var unaryDst = map[obj.As]bool{
1088 var Linkarm64 = obj.LinkArch{
1089 Arch: sys.ArchARM64,
1091 Preprocess: preprocess,
1095 DWARFRegisters: ARM64DWARFRegisters,