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 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
55 // MOV g_stackguard(g), RT1
56 p = obj.Appendp(p, c.newprog)
59 p.From.Type = obj.TYPE_MEM
61 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
63 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
65 p.To.Type = obj.TYPE_REG
68 // Mark the stack bound check and morestack call async nonpreemptible.
69 // If we get preempted here, when resumed the preemption request is
70 // cleared, but we'll still call morestack, which will double the stack
71 // unnecessarily. See issue #35470.
72 p = c.ctxt.StartUnsafePoint(p, c.newprog)
75 if framesize <= objabi.StackSmall {
76 // small stack: SP < stackguard
78 // CMP stackguard, RT2
79 p = obj.Appendp(p, c.newprog)
82 p.From.Type = obj.TYPE_REG
84 p.To.Type = obj.TYPE_REG
87 p = obj.Appendp(p, c.newprog)
89 p.From.Type = obj.TYPE_REG
92 } else if framesize <= objabi.StackBig {
93 // large stack: SP-framesize < stackguard-StackSmall
94 // SUB $(framesize-StackSmall), SP, RT2
95 // CMP stackguard, RT2
96 p = obj.Appendp(p, c.newprog)
99 p.From.Type = obj.TYPE_CONST
100 p.From.Offset = int64(framesize) - objabi.StackSmall
102 p.To.Type = obj.TYPE_REG
105 p = obj.Appendp(p, c.newprog)
107 p.From.Type = obj.TYPE_REG
111 // Such a large stack we need to protect against underflow.
112 // The runtime guarantees SP > objabi.StackBig, but
113 // framesize is large enough that SP-framesize may
114 // underflow, causing a direct comparison with the
115 // stack guard to incorrectly succeed. We explicitly
116 // guard against underflow.
118 // SUBS $(framesize-StackSmall), SP, RT2
119 // // On underflow, jump to morestack
120 // BLO label_of_call_to_morestack
121 // CMP stackguard, RT2
123 p = obj.Appendp(p, c.newprog)
125 p.From.Type = obj.TYPE_CONST
126 p.From.Offset = int64(framesize) - objabi.StackSmall
128 p.To.Type = obj.TYPE_REG
131 p = obj.Appendp(p, c.newprog)
134 p.To.Type = obj.TYPE_BRANCH
136 p = obj.Appendp(p, c.newprog)
138 p.From.Type = obj.TYPE_REG
144 bls := obj.Appendp(p, c.newprog)
146 bls.To.Type = obj.TYPE_BRANCH
148 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
151 for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
154 // Now we are at the end of the function, but logically
155 // we are still in function prologue. We need to fix the
156 // SP data and PCDATA.
157 spfix := obj.Appendp(last, c.newprog)
159 spfix.Spadj = -framesize
161 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
162 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
165 q.To.SetTarget(pcdata)
167 bls.To.SetTarget(pcdata)
169 spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
172 movlr := obj.Appendp(spill, c.newprog)
174 movlr.From.Type = obj.TYPE_REG
175 movlr.From.Reg = REGLINK
176 movlr.To.Type = obj.TYPE_REG
177 movlr.To.Reg = REG_R3
181 debug = obj.Appendp(debug, c.newprog)
183 debug.From.Type = obj.TYPE_CONST
184 debug.From.Offset = int64(framesize)
185 debug.To.Type = obj.TYPE_REG
186 debug.To.Reg = REGTMP
189 // BL runtime.morestack(SB)
190 call := obj.Appendp(debug, c.newprog)
192 call.To.Type = obj.TYPE_BRANCH
193 morestack := "runtime.morestack"
195 case c.cursym.CFunc():
196 morestack = "runtime.morestackc"
197 case !c.cursym.Func().Text.From.Sym.NeedCtxt():
198 morestack = "runtime.morestack_noctxt"
200 call.To.Sym = c.ctxt.Lookup(morestack)
202 unspill := c.cursym.Func().UnspillRegisterArgs(call, c.newprog)
203 pcdata = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1)
206 jmp := obj.Appendp(pcdata, c.newprog)
208 jmp.To.Type = obj.TYPE_BRANCH
209 jmp.To.SetTarget(c.cursym.Func().Text.Link)
210 jmp.Spadj = +framesize
215 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
216 c := ctxt7{ctxt: ctxt, newprog: newprog}
221 // $0 results in C_ZCON, which matches both C_REG and various
222 // C_xCON, however the C_REG cases in asmout don't expect a
223 // constant, so they will use the register fields and assemble
224 // a R0. To prevent that, rewrite $0 as ZR.
225 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
226 p.From.Type = obj.TYPE_REG
229 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
230 p.To.Type = obj.TYPE_REG
234 // Rewrite BR/BL to symbol as TYPE_BRANCH.
242 p.To.Type = obj.TYPE_BRANCH
247 // Rewrite float constants to values stored in memory.
250 if p.From.Type == obj.TYPE_FCONST {
251 f64 := p.From.Val.(float64)
253 if c.chipfloat7(f64) > 0 {
256 if math.Float32bits(f32) == 0 {
257 p.From.Type = obj.TYPE_REG
261 p.From.Type = obj.TYPE_MEM
262 p.From.Sym = c.ctxt.Float32Sym(f32)
263 p.From.Name = obj.NAME_EXTERN
268 if p.From.Type == obj.TYPE_FCONST {
269 f64 := p.From.Val.(float64)
270 if c.chipfloat7(f64) > 0 {
273 if math.Float64bits(f64) == 0 {
274 p.From.Type = obj.TYPE_REG
278 p.From.Type = obj.TYPE_MEM
279 p.From.Sym = c.ctxt.Float64Sym(f64)
280 p.From.Name = obj.NAME_EXTERN
287 // Rewrite negative immediates as positive immediates with
288 // complementary instruction.
290 case AADD, ASUB, ACMP, ACMN:
291 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
292 p.From.Offset = -p.From.Offset
293 p.As = complements[p.As]
295 case AADDW, ASUBW, ACMPW, ACMNW:
296 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
297 p.From.Offset = -p.From.Offset
298 p.As = complements[p.As]
302 // For 32-bit instruction with constant, rewrite
303 // the high 32-bit to be a repetition of the low
304 // 32-bit, so that the BITCON test can be shared
305 // for both 32-bit and 64-bit. 32-bit ops will
306 // zero the high 32-bit of the destination register
308 if (isANDWop(p.As) || isADDWop(p.As) || p.As == AMOVW) && p.From.Type == obj.TYPE_CONST {
309 v := p.From.Offset & 0xffffffff
310 p.From.Offset = v | v<<32
313 if c.ctxt.Flag_dynlink {
318 // Rewrite p, if necessary, to access global data via the global offset table.
319 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
320 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
323 // MOVD runtime.duffxxx@GOT, REGTMP
324 // ADD $offset, REGTMP
327 if p.As == obj.ADUFFZERO {
328 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
330 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
332 offset := p.To.Offset
334 p.From.Type = obj.TYPE_MEM
335 p.From.Name = obj.NAME_GOTREF
337 p.To.Type = obj.TYPE_REG
339 p.To.Name = obj.NAME_NONE
342 p1 := obj.Appendp(p, c.newprog)
344 p1.From.Type = obj.TYPE_CONST
345 p1.From.Offset = offset
346 p1.To.Type = obj.TYPE_REG
348 p2 := obj.Appendp(p1, c.newprog)
350 p2.To.Type = obj.TYPE_REG
354 // We only care about global data: NAME_EXTERN means a global
355 // symbol in the Go sense, and p.Sym.Local is true for a few
356 // internally defined symbols.
357 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
358 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
359 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
361 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
363 if p.To.Type != obj.TYPE_REG {
364 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
366 p.From.Type = obj.TYPE_MEM
367 p.From.Name = obj.NAME_GOTREF
368 if p.From.Offset != 0 {
369 q := obj.Appendp(p, c.newprog)
371 q.From.Type = obj.TYPE_CONST
372 q.From.Offset = p.From.Offset
377 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
378 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
381 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
382 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
383 // An addition may be inserted between the two MOVs if there is an offset.
384 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
385 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
386 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
389 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
394 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
397 if source.Sym.Type == objabi.STLSBSS {
400 if source.Type != obj.TYPE_MEM {
401 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
403 p1 := obj.Appendp(p, c.newprog)
404 p2 := obj.Appendp(p1, c.newprog)
406 p1.From.Type = obj.TYPE_MEM
407 p1.From.Sym = source.Sym
408 p1.From.Name = obj.NAME_GOTREF
409 p1.To.Type = obj.TYPE_REG
415 if p.From.Name == obj.NAME_EXTERN {
417 p2.From.Name = obj.NAME_NONE
419 } else if p.To.Name == obj.NAME_EXTERN {
421 p2.To.Name = obj.NAME_NONE
429 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
430 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
434 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
436 p := c.cursym.Func().Text
437 textstksiz := p.To.Offset
438 if textstksiz == -8 {
439 // Historical way to mark NOFRAME.
440 p.From.Sym.Set(obj.AttrNoFrame, true)
444 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
446 if p.From.Sym.NoFrame() {
448 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
452 c.cursym.Func().Args = p.To.Val.(int32)
453 c.cursym.Func().Locals = int32(textstksiz)
456 * find leaf subroutines
458 for p := c.cursym.Func().Text; p != nil; p = p.Link {
466 c.cursym.Func().Text.Mark &^= LEAF
473 for p := c.cursym.Func().Text; p != nil; p = p.Link {
477 c.cursym.Func().Text = p
478 c.autosize = int32(textstksiz)
480 if p.Mark&LEAF != 0 && c.autosize == 0 {
481 // A leaf function with no locals has no frame.
482 p.From.Sym.Set(obj.AttrNoFrame, true)
485 if !p.From.Sym.NoFrame() {
486 // If there is a stack frame at all, it includes
487 // space to save the LR.
492 extrasize := int32(0)
493 if c.autosize%16 == 8 {
494 // Allocate extra 8 bytes on the frame top to save FP
496 } else if c.autosize&(16-1) == 0 {
497 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
500 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
502 c.autosize += extrasize
503 c.cursym.Func().Locals += extrasize
505 // low 32 bits for autosize
506 // high 32 bits for extrasize
507 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
513 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
514 if c.ctxt.Debugvlog {
515 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
517 c.cursym.Func().Text.Mark |= LEAF
520 if cursym.Func().Text.Mark&LEAF != 0 {
521 cursym.Set(obj.AttrLeaf, true)
522 if p.From.Sym.NoFrame() {
527 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
528 // A leaf function with a small stack can be marked
529 // NOSPLIT, avoiding a stack check.
530 p.From.Sym.Set(obj.AttrNoSplit, true)
533 if !p.From.Sym.NoSplit() {
534 p = c.stacksplit(p, c.autosize) // emit split check
537 var prologueEnd *obj.Prog
539 aoffset := c.autosize
544 // Frame is non-empty. Make sure to save link register, even if
545 // it is a leaf function, so that traceback works.
547 if c.autosize > aoffset {
548 // Frame size is too large for a MOVD.W instruction.
549 // Store link register before decrementing SP, so if a signal comes
550 // during the execution of the function prologue, the traceback
551 // code will not see a half-updated stack frame.
552 // This sequence is not async preemptible, as if we open a frame
553 // at the current SP, it will clobber the saved LR.
554 q = c.ctxt.StartUnsafePoint(q, c.newprog)
556 q = obj.Appendp(q, c.newprog)
559 q.From.Type = obj.TYPE_CONST
560 q.From.Offset = int64(c.autosize)
562 q.To.Type = obj.TYPE_REG
567 q = obj.Appendp(q, c.newprog)
570 q.From.Type = obj.TYPE_REG
572 q.To.Type = obj.TYPE_MEM
575 q1 = obj.Appendp(q, c.newprog)
578 q1.From.Type = obj.TYPE_REG
580 q1.To.Type = obj.TYPE_REG
582 q1.Spadj = c.autosize
584 if buildcfg.GOOS == "ios" {
585 // iOS does not support SA_ONSTACK. We will run the signal handler
586 // on the G stack. If we write below SP, it may be clobbered by
587 // the signal handler. So we save LR after decrementing SP.
588 q1 = obj.Appendp(q1, c.newprog)
591 q1.From.Type = obj.TYPE_REG
592 q1.From.Reg = REGLINK
593 q1.To.Type = obj.TYPE_MEM
597 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
599 // small frame, update SP and save LR in a single MOVD.W instruction
600 q1 = obj.Appendp(q, c.newprog)
603 q1.From.Type = obj.TYPE_REG
604 q1.From.Reg = REGLINK
605 q1.To.Type = obj.TYPE_MEM
607 q1.To.Offset = int64(-aoffset)
614 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
617 q1 = obj.Appendp(q1, c.newprog)
620 q1.From.Type = obj.TYPE_REG
622 q1.To.Type = obj.TYPE_MEM
626 q1 = obj.Appendp(q1, c.newprog)
629 q1.From.Type = obj.TYPE_CONST
632 q1.To.Type = obj.TYPE_REG
635 if c.cursym.Func().Text.From.Sym.Wrapper() {
636 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
638 // MOV g_panic(g), RT1
642 // ... function body ...
644 // MOV panic_argp(RT1), RT2
645 // ADD $(autosize+8), RSP, R20
649 // MOVD R20, panic_argp(RT1)
652 // The NOP is needed to give the jumps somewhere to land.
653 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
656 // MOV g_panic(g), RT1
657 q = obj.Appendp(q, c.newprog)
659 q.From.Type = obj.TYPE_MEM
661 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
662 q.To.Type = obj.TYPE_REG
665 // CBNZ RT1, checkargp
666 cbnz := obj.Appendp(q, c.newprog)
668 cbnz.From.Type = obj.TYPE_REG
669 cbnz.From.Reg = REGRT1
670 cbnz.To.Type = obj.TYPE_BRANCH
672 // Empty branch target at the top of the function body
673 end := obj.Appendp(cbnz, c.newprog)
676 // find the end of the function
678 for last = end; last.Link != nil; last = last.Link {
681 // MOV panic_argp(RT1), RT2
682 mov := obj.Appendp(last, c.newprog)
684 mov.From.Type = obj.TYPE_MEM
685 mov.From.Reg = REGRT1
686 mov.From.Offset = 0 // Panic.argp
687 mov.To.Type = obj.TYPE_REG
690 // CBNZ branches to the MOV above
691 cbnz.To.SetTarget(mov)
693 // ADD $(autosize+8), SP, R20
694 q = obj.Appendp(mov, c.newprog)
696 q.From.Type = obj.TYPE_CONST
697 q.From.Offset = int64(c.autosize) + 8
699 q.To.Type = obj.TYPE_REG
703 q = obj.Appendp(q, c.newprog)
705 q.From.Type = obj.TYPE_REG
710 q = obj.Appendp(q, c.newprog)
712 q.To.Type = obj.TYPE_BRANCH
716 q = obj.Appendp(q, c.newprog)
718 q.From.Type = obj.TYPE_CONST
721 q.To.Type = obj.TYPE_REG
724 // MOV R20, panic_argp(RT1)
725 q = obj.Appendp(q, c.newprog)
727 q.From.Type = obj.TYPE_REG
729 q.To.Type = obj.TYPE_MEM
731 q.To.Offset = 0 // Panic.argp
734 q = obj.Appendp(q, c.newprog)
736 q.To.Type = obj.TYPE_BRANCH
742 if p.From.Type == obj.TYPE_CONST {
743 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
749 if c.cursym.Func().Text.Mark&LEAF != 0 {
752 p.From.Type = obj.TYPE_CONST
753 p.From.Offset = int64(c.autosize)
754 p.To.Type = obj.TYPE_REG
756 p.Spadj = -c.autosize
759 p = obj.Appendp(p, c.newprog)
761 p.From.Type = obj.TYPE_CONST
764 p.To.Type = obj.TYPE_REG
768 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
772 p.From.Type = obj.TYPE_MEM
775 p.To.Type = obj.TYPE_REG
777 p = obj.Appendp(p, c.newprog)
779 aoffset := c.autosize
783 p.From.Type = obj.TYPE_MEM
785 p.From.Offset = int64(aoffset)
787 p.To.Type = obj.TYPE_REG
792 p.From.Type = obj.TYPE_MEM
795 p.To.Type = obj.TYPE_REG
800 q.From.Type = obj.TYPE_CONST
801 q.From.Offset = int64(aoffset)
802 q.To.Type = obj.TYPE_REG
805 q.Spadj = int32(-q.From.Offset)
812 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
813 // so that if you are debugging a low-level crash where PC and LR are zero,
814 // you can look at R27 to see what jumped to the zero.
815 // This is useful when bringing up Go on a new system.
816 // (There is similar code in ../ppc64/obj9.go:/if.false.)
817 const debugRETZERO = false
819 if p.As != obj.ARET {
827 p.From.Type = obj.TYPE_BRANCH
829 p.To.Type = obj.TYPE_REG
834 if p.As != obj.ARET {
842 if retjmp != nil { // retjmp
844 p.To.Type = obj.TYPE_BRANCH
846 p.Spadj = +c.autosize
851 p.To.Type = obj.TYPE_MEM
854 p.Spadj = +c.autosize
857 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
859 p.Spadj = int32(-p.From.Offset)
861 p.Spadj = int32(+p.From.Offset)
865 case obj.AGETCALLERPC:
869 p.From.Type = obj.TYPE_REG
874 p.From.Type = obj.TYPE_MEM
880 // STP (FP, R27), -24(SP)
887 // copy DUFFCOPY from q1 to q4
888 q4 := obj.Appendp(p, c.newprog)
890 q4.As = obj.ADUFFCOPY
894 q1.From.Type = obj.TYPE_BRANCH
895 q1.To.Type = obj.TYPE_REG
898 q2 := obj.Appendp(q1, c.newprog)
901 q2.From.Type = obj.TYPE_REGREG
903 q2.From.Offset = int64(REG_R27)
904 q2.To.Type = obj.TYPE_MEM
908 // maintain FP for DUFFCOPY
909 q3 := obj.Appendp(q2, c.newprog)
912 q3.From.Type = obj.TYPE_CONST
915 q3.To.Type = obj.TYPE_REG
918 q5 := obj.Appendp(q4, c.newprog)
921 q5.From.Type = obj.TYPE_CONST
924 q5.To.Type = obj.TYPE_REG
926 q1.From.SetTarget(q5)
931 // STP (FP, R27), -24(SP)
938 // copy DUFFZERO from q1 to q4
939 q4 := obj.Appendp(p, c.newprog)
941 q4.As = obj.ADUFFZERO
945 q1.From.Type = obj.TYPE_BRANCH
946 q1.To.Type = obj.TYPE_REG
949 q2 := obj.Appendp(q1, c.newprog)
952 q2.From.Type = obj.TYPE_REGREG
954 q2.From.Offset = int64(REG_R27)
955 q2.To.Type = obj.TYPE_MEM
959 // maintain FP for DUFFZERO
960 q3 := obj.Appendp(q2, c.newprog)
963 q3.From.Type = obj.TYPE_CONST
966 q3.To.Type = obj.TYPE_REG
969 q5 := obj.Appendp(q4, c.newprog)
972 q5.From.Type = obj.TYPE_CONST
975 q5.To.Type = obj.TYPE_REG
977 q1.From.SetTarget(q5)
981 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
983 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
984 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
985 if ctxt.Debugvlog || !ctxt.IsAsm {
986 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
988 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
990 log.Fatalf("bad SPWRITE")
998 func nocache(p *obj.Prog) {
1004 var unaryDst = map[obj.As]bool{
1012 var Linkarm64 = obj.LinkArch{
1013 Arch: sys.ArchARM64,
1015 Preprocess: preprocess,
1019 DWARFRegisters: ARM64DWARFRegisters,