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), R1
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
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, R2
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, R2
119 // // On underflow, jump to morestack
120 // BLO label_of_call_to_morestack
121 // CMP stackguard, R2
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 movlr := obj.Appendp(pcdata, c.newprog)
167 movlr.From.Type = obj.TYPE_REG
168 movlr.From.Reg = REGLINK
169 movlr.To.Type = obj.TYPE_REG
170 movlr.To.Reg = REG_R3
172 q.To.SetTarget(movlr)
174 bls.To.SetTarget(movlr)
178 debug = obj.Appendp(debug, c.newprog)
180 debug.From.Type = obj.TYPE_CONST
181 debug.From.Offset = int64(framesize)
182 debug.To.Type = obj.TYPE_REG
183 debug.To.Reg = REGTMP
186 // BL runtime.morestack(SB)
187 call := obj.Appendp(debug, c.newprog)
189 call.To.Type = obj.TYPE_BRANCH
190 morestack := "runtime.morestack"
192 case c.cursym.CFunc():
193 morestack = "runtime.morestackc"
194 case !c.cursym.Func().Text.From.Sym.NeedCtxt():
195 morestack = "runtime.morestack_noctxt"
197 call.To.Sym = c.ctxt.Lookup(morestack)
199 pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
202 jmp := obj.Appendp(pcdata, c.newprog)
204 jmp.To.Type = obj.TYPE_BRANCH
205 jmp.To.SetTarget(c.cursym.Func().Text.Link)
206 jmp.Spadj = +framesize
211 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
212 c := ctxt7{ctxt: ctxt, newprog: newprog}
217 // $0 results in C_ZCON, which matches both C_REG and various
218 // C_xCON, however the C_REG cases in asmout don't expect a
219 // constant, so they will use the register fields and assemble
220 // a R0. To prevent that, rewrite $0 as ZR.
221 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
222 p.From.Type = obj.TYPE_REG
225 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
226 p.To.Type = obj.TYPE_REG
230 // Rewrite BR/BL to symbol as TYPE_BRANCH.
238 p.To.Type = obj.TYPE_BRANCH
243 // Rewrite float constants to values stored in memory.
246 if p.From.Type == obj.TYPE_FCONST {
247 f64 := p.From.Val.(float64)
249 if c.chipfloat7(f64) > 0 {
252 if math.Float32bits(f32) == 0 {
253 p.From.Type = obj.TYPE_REG
257 p.From.Type = obj.TYPE_MEM
258 p.From.Sym = c.ctxt.Float32Sym(f32)
259 p.From.Name = obj.NAME_EXTERN
264 if p.From.Type == obj.TYPE_FCONST {
265 f64 := p.From.Val.(float64)
266 if c.chipfloat7(f64) > 0 {
269 if math.Float64bits(f64) == 0 {
270 p.From.Type = obj.TYPE_REG
274 p.From.Type = obj.TYPE_MEM
275 p.From.Sym = c.ctxt.Float64Sym(f64)
276 p.From.Name = obj.NAME_EXTERN
283 // Rewrite negative immediates as positive immediates with
284 // complementary instruction.
286 case AADD, ASUB, ACMP, ACMN:
287 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
288 p.From.Offset = -p.From.Offset
289 p.As = complements[p.As]
291 case AADDW, ASUBW, ACMPW, ACMNW:
292 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
293 p.From.Offset = -p.From.Offset
294 p.As = complements[p.As]
298 // For 32-bit instruction with constant, rewrite
299 // the high 32-bit to be a repetition of the low
300 // 32-bit, so that the BITCON test can be shared
301 // for both 32-bit and 64-bit. 32-bit ops will
302 // zero the high 32-bit of the destination register
304 if (isANDWop(p.As) || isADDWop(p.As) || p.As == AMOVW) && p.From.Type == obj.TYPE_CONST {
305 v := p.From.Offset & 0xffffffff
306 p.From.Offset = v | v<<32
309 if c.ctxt.Flag_dynlink {
314 // Rewrite p, if necessary, to access global data via the global offset table.
315 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
316 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
319 // MOVD runtime.duffxxx@GOT, REGTMP
320 // ADD $offset, REGTMP
323 if p.As == obj.ADUFFZERO {
324 sym = c.ctxt.Lookup("runtime.duffzero")
326 sym = c.ctxt.Lookup("runtime.duffcopy")
328 offset := p.To.Offset
330 p.From.Type = obj.TYPE_MEM
331 p.From.Name = obj.NAME_GOTREF
333 p.To.Type = obj.TYPE_REG
335 p.To.Name = obj.NAME_NONE
338 p1 := obj.Appendp(p, c.newprog)
340 p1.From.Type = obj.TYPE_CONST
341 p1.From.Offset = offset
342 p1.To.Type = obj.TYPE_REG
344 p2 := obj.Appendp(p1, c.newprog)
346 p2.To.Type = obj.TYPE_REG
350 // We only care about global data: NAME_EXTERN means a global
351 // symbol in the Go sense, and p.Sym.Local is true for a few
352 // internally defined symbols.
353 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
354 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
355 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
357 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
359 if p.To.Type != obj.TYPE_REG {
360 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
362 p.From.Type = obj.TYPE_MEM
363 p.From.Name = obj.NAME_GOTREF
364 if p.From.Offset != 0 {
365 q := obj.Appendp(p, c.newprog)
367 q.From.Type = obj.TYPE_CONST
368 q.From.Offset = p.From.Offset
373 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
374 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
377 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
378 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
379 // An addition may be inserted between the two MOVs if there is an offset.
380 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
381 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
382 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
385 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
390 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
393 if source.Sym.Type == objabi.STLSBSS {
396 if source.Type != obj.TYPE_MEM {
397 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
399 p1 := obj.Appendp(p, c.newprog)
400 p2 := obj.Appendp(p1, c.newprog)
402 p1.From.Type = obj.TYPE_MEM
403 p1.From.Sym = source.Sym
404 p1.From.Name = obj.NAME_GOTREF
405 p1.To.Type = obj.TYPE_REG
411 if p.From.Name == obj.NAME_EXTERN {
413 p2.From.Name = obj.NAME_NONE
415 } else if p.To.Name == obj.NAME_EXTERN {
417 p2.To.Name = obj.NAME_NONE
425 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
426 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
430 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
432 p := c.cursym.Func().Text
433 textstksiz := p.To.Offset
434 if textstksiz == -8 {
435 // Historical way to mark NOFRAME.
436 p.From.Sym.Set(obj.AttrNoFrame, true)
440 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
442 if p.From.Sym.NoFrame() {
444 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
448 c.cursym.Func().Args = p.To.Val.(int32)
449 c.cursym.Func().Locals = int32(textstksiz)
452 * find leaf subroutines
454 for p := c.cursym.Func().Text; p != nil; p = p.Link {
462 c.cursym.Func().Text.Mark &^= LEAF
469 for p := c.cursym.Func().Text; p != nil; p = p.Link {
473 c.cursym.Func().Text = p
474 c.autosize = int32(textstksiz)
476 if p.Mark&LEAF != 0 && c.autosize == 0 {
477 // A leaf function with no locals has no frame.
478 p.From.Sym.Set(obj.AttrNoFrame, true)
481 if !p.From.Sym.NoFrame() {
482 // If there is a stack frame at all, it includes
483 // space to save the LR.
488 extrasize := int32(0)
489 if c.autosize%16 == 8 {
490 // Allocate extra 8 bytes on the frame top to save FP
492 } else if c.autosize&(16-1) == 0 {
493 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
496 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
498 c.autosize += extrasize
499 c.cursym.Func().Locals += extrasize
501 // low 32 bits for autosize
502 // high 32 bits for extrasize
503 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
509 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
510 if c.ctxt.Debugvlog {
511 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
513 c.cursym.Func().Text.Mark |= LEAF
516 if cursym.Func().Text.Mark&LEAF != 0 {
517 cursym.Set(obj.AttrLeaf, true)
518 if p.From.Sym.NoFrame() {
523 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
524 // A leaf function with a small stack can be marked
525 // NOSPLIT, avoiding a stack check.
526 p.From.Sym.Set(obj.AttrNoSplit, true)
529 if !p.From.Sym.NoSplit() {
530 p = c.stacksplit(p, c.autosize) // emit split check
533 var prologueEnd *obj.Prog
535 aoffset := c.autosize
540 // Frame is non-empty. Make sure to save link register, even if
541 // it is a leaf function, so that traceback works.
543 if c.autosize > aoffset {
544 // Frame size is too large for a MOVD.W instruction.
545 // Store link register before decrementing SP, so if a signal comes
546 // during the execution of the function prologue, the traceback
547 // code will not see a half-updated stack frame.
548 // This sequence is not async preemptible, as if we open a frame
549 // at the current SP, it will clobber the saved LR.
550 q = c.ctxt.StartUnsafePoint(q, c.newprog)
552 q = obj.Appendp(q, c.newprog)
555 q.From.Type = obj.TYPE_CONST
556 q.From.Offset = int64(c.autosize)
558 q.To.Type = obj.TYPE_REG
563 q = obj.Appendp(q, c.newprog)
566 q.From.Type = obj.TYPE_REG
568 q.To.Type = obj.TYPE_MEM
571 q1 = obj.Appendp(q, c.newprog)
574 q1.From.Type = obj.TYPE_REG
576 q1.To.Type = obj.TYPE_REG
578 q1.Spadj = c.autosize
580 if buildcfg.GOOS == "ios" {
581 // iOS does not support SA_ONSTACK. We will run the signal handler
582 // on the G stack. If we write below SP, it may be clobbered by
583 // the signal handler. So we save LR after decrementing SP.
584 q1 = obj.Appendp(q1, c.newprog)
587 q1.From.Type = obj.TYPE_REG
588 q1.From.Reg = REGLINK
589 q1.To.Type = obj.TYPE_MEM
593 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
595 // small frame, update SP and save LR in a single MOVD.W instruction
596 q1 = obj.Appendp(q, c.newprog)
599 q1.From.Type = obj.TYPE_REG
600 q1.From.Reg = REGLINK
601 q1.To.Type = obj.TYPE_MEM
603 q1.To.Offset = int64(-aoffset)
610 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
613 q1 = obj.Appendp(q1, c.newprog)
616 q1.From.Type = obj.TYPE_REG
618 q1.To.Type = obj.TYPE_MEM
622 q1 = obj.Appendp(q1, c.newprog)
625 q1.From.Type = obj.TYPE_CONST
628 q1.To.Type = obj.TYPE_REG
631 if c.cursym.Func().Text.From.Sym.Wrapper() {
632 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
634 // MOV g_panic(g), R1
638 // ... function body ...
640 // MOV panic_argp(R1), R2
641 // ADD $(autosize+8), RSP, R3
645 // MOVD R4, panic_argp(R1)
648 // The NOP is needed to give the jumps somewhere to land.
649 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
652 // MOV g_panic(g), R1
653 q = obj.Appendp(q, c.newprog)
655 q.From.Type = obj.TYPE_MEM
657 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
658 q.To.Type = obj.TYPE_REG
661 // CBNZ R1, checkargp
662 cbnz := obj.Appendp(q, c.newprog)
664 cbnz.From.Type = obj.TYPE_REG
665 cbnz.From.Reg = REG_R1
666 cbnz.To.Type = obj.TYPE_BRANCH
668 // Empty branch target at the top of the function body
669 end := obj.Appendp(cbnz, c.newprog)
672 // find the end of the function
674 for last = end; last.Link != nil; last = last.Link {
677 // MOV panic_argp(R1), R2
678 mov := obj.Appendp(last, c.newprog)
680 mov.From.Type = obj.TYPE_MEM
681 mov.From.Reg = REG_R1
682 mov.From.Offset = 0 // Panic.argp
683 mov.To.Type = obj.TYPE_REG
686 // CBNZ branches to the MOV above
687 cbnz.To.SetTarget(mov)
689 // ADD $(autosize+8), SP, R3
690 q = obj.Appendp(mov, c.newprog)
692 q.From.Type = obj.TYPE_CONST
693 q.From.Offset = int64(c.autosize) + 8
695 q.To.Type = obj.TYPE_REG
699 q = obj.Appendp(q, c.newprog)
701 q.From.Type = obj.TYPE_REG
706 q = obj.Appendp(q, c.newprog)
708 q.To.Type = obj.TYPE_BRANCH
712 q = obj.Appendp(q, c.newprog)
714 q.From.Type = obj.TYPE_CONST
717 q.To.Type = obj.TYPE_REG
720 // MOV R4, panic_argp(R1)
721 q = obj.Appendp(q, c.newprog)
723 q.From.Type = obj.TYPE_REG
725 q.To.Type = obj.TYPE_MEM
727 q.To.Offset = 0 // Panic.argp
730 q = obj.Appendp(q, c.newprog)
732 q.To.Type = obj.TYPE_BRANCH
738 if p.From.Type == obj.TYPE_CONST {
739 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
745 if c.cursym.Func().Text.Mark&LEAF != 0 {
748 p.From.Type = obj.TYPE_CONST
749 p.From.Offset = int64(c.autosize)
750 p.To.Type = obj.TYPE_REG
752 p.Spadj = -c.autosize
755 p = obj.Appendp(p, c.newprog)
757 p.From.Type = obj.TYPE_CONST
760 p.To.Type = obj.TYPE_REG
764 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
768 p.From.Type = obj.TYPE_MEM
771 p.To.Type = obj.TYPE_REG
773 p = obj.Appendp(p, c.newprog)
775 aoffset := c.autosize
779 p.From.Type = obj.TYPE_MEM
781 p.From.Offset = int64(aoffset)
783 p.To.Type = obj.TYPE_REG
788 p.From.Type = obj.TYPE_MEM
791 p.To.Type = obj.TYPE_REG
796 q.From.Type = obj.TYPE_CONST
797 q.From.Offset = int64(aoffset)
798 q.To.Type = obj.TYPE_REG
801 q.Spadj = int32(-q.From.Offset)
808 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
809 // so that if you are debugging a low-level crash where PC and LR are zero,
810 // you can look at R27 to see what jumped to the zero.
811 // This is useful when bringing up Go on a new system.
812 // (There is similar code in ../ppc64/obj9.go:/if.false.)
813 const debugRETZERO = false
815 if p.As != obj.ARET {
823 p.From.Type = obj.TYPE_BRANCH
825 p.To.Type = obj.TYPE_REG
830 if p.As != obj.ARET {
838 if retjmp != nil { // retjmp
840 p.To.Type = obj.TYPE_BRANCH
842 p.Spadj = +c.autosize
847 p.To.Type = obj.TYPE_MEM
850 p.Spadj = +c.autosize
853 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
855 p.Spadj = int32(-p.From.Offset)
857 p.Spadj = int32(+p.From.Offset)
861 case obj.AGETCALLERPC:
865 p.From.Type = obj.TYPE_REG
870 p.From.Type = obj.TYPE_MEM
876 // STP (FP, R27), -24(SP)
883 // copy DUFFCOPY from q1 to q4
884 q4 := obj.Appendp(p, c.newprog)
886 q4.As = obj.ADUFFCOPY
890 q1.From.Type = obj.TYPE_BRANCH
891 q1.To.Type = obj.TYPE_REG
894 q2 := obj.Appendp(q1, c.newprog)
897 q2.From.Type = obj.TYPE_REGREG
899 q2.From.Offset = int64(REG_R27)
900 q2.To.Type = obj.TYPE_MEM
904 // maintain FP for DUFFCOPY
905 q3 := obj.Appendp(q2, c.newprog)
908 q3.From.Type = obj.TYPE_CONST
911 q3.To.Type = obj.TYPE_REG
914 q5 := obj.Appendp(q4, c.newprog)
917 q5.From.Type = obj.TYPE_CONST
920 q5.To.Type = obj.TYPE_REG
922 q1.From.SetTarget(q5)
927 // STP (FP, R27), -24(SP)
934 // copy DUFFZERO from q1 to q4
935 q4 := obj.Appendp(p, c.newprog)
937 q4.As = obj.ADUFFZERO
941 q1.From.Type = obj.TYPE_BRANCH
942 q1.To.Type = obj.TYPE_REG
945 q2 := obj.Appendp(q1, c.newprog)
948 q2.From.Type = obj.TYPE_REGREG
950 q2.From.Offset = int64(REG_R27)
951 q2.To.Type = obj.TYPE_MEM
955 // maintain FP for DUFFZERO
956 q3 := obj.Appendp(q2, c.newprog)
959 q3.From.Type = obj.TYPE_CONST
962 q3.To.Type = obj.TYPE_REG
965 q5 := obj.Appendp(q4, c.newprog)
968 q5.From.Type = obj.TYPE_CONST
971 q5.To.Type = obj.TYPE_REG
973 q1.From.SetTarget(q5)
977 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
979 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
980 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
981 if ctxt.Debugvlog || !ctxt.IsAsm {
982 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
984 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
986 log.Fatalf("bad SPWRITE")
994 func nocache(p *obj.Prog) {
1000 var unaryDst = map[obj.As]bool{
1008 var Linkarm64 = obj.LinkArch{
1009 Arch: sys.ArchARM64,
1011 Preprocess: preprocess,
1015 DWARFRegisters: ARM64DWARFRegisters,