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
42 var complements = []obj.As{
53 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
54 // MOV g_stackguard(g), R1
55 p = obj.Appendp(p, c.newprog)
58 p.From.Type = obj.TYPE_MEM
60 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
62 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
64 p.To.Type = obj.TYPE_REG
67 // Mark the stack bound check and morestack call async nonpreemptible.
68 // If we get preempted here, when resumed the preemption request is
69 // cleared, but we'll still call morestack, which will double the stack
70 // unnecessarily. See issue #35470.
71 p = c.ctxt.StartUnsafePoint(p, c.newprog)
74 if framesize <= objabi.StackSmall {
75 // small stack: SP < stackguard
78 p = obj.Appendp(p, c.newprog)
81 p.From.Type = obj.TYPE_REG
83 p.To.Type = obj.TYPE_REG
86 p = obj.Appendp(p, c.newprog)
88 p.From.Type = obj.TYPE_REG
91 } else if framesize <= objabi.StackBig {
92 // large stack: SP-framesize < stackguard-StackSmall
93 // SUB $(framesize-StackSmall), SP, R2
95 p = obj.Appendp(p, c.newprog)
98 p.From.Type = obj.TYPE_CONST
99 p.From.Offset = int64(framesize) - objabi.StackSmall
101 p.To.Type = obj.TYPE_REG
104 p = obj.Appendp(p, c.newprog)
106 p.From.Type = obj.TYPE_REG
110 // Such a large stack we need to protect against underflow.
111 // The runtime guarantees SP > objabi.StackBig, but
112 // framesize is large enough that SP-framesize may
113 // underflow, causing a direct comparison with the
114 // stack guard to incorrectly succeed. We explicitly
115 // guard against underflow.
117 // SUBS $(framesize-StackSmall), SP, R2
118 // // On underflow, jump to morestack
119 // BLO label_of_call_to_morestack
120 // CMP stackguard, R2
122 p = obj.Appendp(p, c.newprog)
124 p.From.Type = obj.TYPE_CONST
125 p.From.Offset = int64(framesize) - objabi.StackSmall
127 p.To.Type = obj.TYPE_REG
130 p = obj.Appendp(p, c.newprog)
133 p.To.Type = obj.TYPE_BRANCH
135 p = obj.Appendp(p, c.newprog)
137 p.From.Type = obj.TYPE_REG
143 bls := obj.Appendp(p, c.newprog)
145 bls.To.Type = obj.TYPE_BRANCH
147 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
150 for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
153 // Now we are at the end of the function, but logically
154 // we are still in function prologue. We need to fix the
155 // SP data and PCDATA.
156 spfix := obj.Appendp(last, c.newprog)
158 spfix.Spadj = -framesize
160 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
161 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
164 movlr := obj.Appendp(pcdata, c.newprog)
166 movlr.From.Type = obj.TYPE_REG
167 movlr.From.Reg = REGLINK
168 movlr.To.Type = obj.TYPE_REG
169 movlr.To.Reg = REG_R3
171 q.To.SetTarget(movlr)
173 bls.To.SetTarget(movlr)
177 debug = obj.Appendp(debug, c.newprog)
179 debug.From.Type = obj.TYPE_CONST
180 debug.From.Offset = int64(framesize)
181 debug.To.Type = obj.TYPE_REG
182 debug.To.Reg = REGTMP
185 // BL runtime.morestack(SB)
186 call := obj.Appendp(debug, c.newprog)
188 call.To.Type = obj.TYPE_BRANCH
189 morestack := "runtime.morestack"
191 case c.cursym.CFunc():
192 morestack = "runtime.morestackc"
193 case !c.cursym.Func().Text.From.Sym.NeedCtxt():
194 morestack = "runtime.morestack_noctxt"
196 call.To.Sym = c.ctxt.Lookup(morestack)
198 pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
201 jmp := obj.Appendp(pcdata, c.newprog)
203 jmp.To.Type = obj.TYPE_BRANCH
204 jmp.To.SetTarget(c.cursym.Func().Text.Link)
205 jmp.Spadj = +framesize
210 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
211 c := ctxt7{ctxt: ctxt, newprog: newprog}
216 // $0 results in C_ZCON, which matches both C_REG and various
217 // C_xCON, however the C_REG cases in asmout don't expect a
218 // constant, so they will use the register fields and assemble
219 // a R0. To prevent that, rewrite $0 as ZR.
220 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
221 p.From.Type = obj.TYPE_REG
224 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
225 p.To.Type = obj.TYPE_REG
229 // Rewrite BR/BL to symbol as TYPE_BRANCH.
237 p.To.Type = obj.TYPE_BRANCH
242 // Rewrite float constants to values stored in memory.
245 if p.From.Type == obj.TYPE_FCONST {
246 f64 := p.From.Val.(float64)
248 if c.chipfloat7(f64) > 0 {
251 if math.Float32bits(f32) == 0 {
252 p.From.Type = obj.TYPE_REG
256 p.From.Type = obj.TYPE_MEM
257 p.From.Sym = c.ctxt.Float32Sym(f32)
258 p.From.Name = obj.NAME_EXTERN
263 if p.From.Type == obj.TYPE_FCONST {
264 f64 := p.From.Val.(float64)
265 if c.chipfloat7(f64) > 0 {
268 if math.Float64bits(f64) == 0 {
269 p.From.Type = obj.TYPE_REG
273 p.From.Type = obj.TYPE_MEM
274 p.From.Sym = c.ctxt.Float64Sym(f64)
275 p.From.Name = obj.NAME_EXTERN
282 // Rewrite negative immediates as positive immediates with
283 // complementary instruction.
285 case AADD, ASUB, ACMP, ACMN:
286 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
287 p.From.Offset = -p.From.Offset
288 p.As = complements[p.As]
290 case AADDW, ASUBW, ACMPW, ACMNW:
291 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
292 p.From.Offset = -p.From.Offset
293 p.As = complements[p.As]
297 // For 32-bit instruction with constant, rewrite
298 // the high 32-bit to be a repetition of the low
299 // 32-bit, so that the BITCON test can be shared
300 // for both 32-bit and 64-bit. 32-bit ops will
301 // zero the high 32-bit of the destination register
303 if (isANDWop(p.As) || isADDWop(p.As) || p.As == AMOVW) && p.From.Type == obj.TYPE_CONST {
304 v := p.From.Offset & 0xffffffff
305 p.From.Offset = v | v<<32
308 if c.ctxt.Flag_dynlink {
313 // Rewrite p, if necessary, to access global data via the global offset table.
314 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
315 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
318 // MOVD runtime.duffxxx@GOT, REGTMP
319 // ADD $offset, REGTMP
322 if p.As == obj.ADUFFZERO {
323 sym = c.ctxt.Lookup("runtime.duffzero")
325 sym = c.ctxt.Lookup("runtime.duffcopy")
327 offset := p.To.Offset
329 p.From.Type = obj.TYPE_MEM
330 p.From.Name = obj.NAME_GOTREF
332 p.To.Type = obj.TYPE_REG
334 p.To.Name = obj.NAME_NONE
337 p1 := obj.Appendp(p, c.newprog)
339 p1.From.Type = obj.TYPE_CONST
340 p1.From.Offset = offset
341 p1.To.Type = obj.TYPE_REG
343 p2 := obj.Appendp(p1, c.newprog)
345 p2.To.Type = obj.TYPE_REG
349 // We only care about global data: NAME_EXTERN means a global
350 // symbol in the Go sense, and p.Sym.Local is true for a few
351 // internally defined symbols.
352 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
353 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
354 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
356 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
358 if p.To.Type != obj.TYPE_REG {
359 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
361 p.From.Type = obj.TYPE_MEM
362 p.From.Name = obj.NAME_GOTREF
363 if p.From.Offset != 0 {
364 q := obj.Appendp(p, c.newprog)
366 q.From.Type = obj.TYPE_CONST
367 q.From.Offset = p.From.Offset
372 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
373 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
376 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
377 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
378 // An addition may be inserted between the two MOVs if there is an offset.
379 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
380 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
381 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
384 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
389 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
392 if source.Sym.Type == objabi.STLSBSS {
395 if source.Type != obj.TYPE_MEM {
396 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
398 p1 := obj.Appendp(p, c.newprog)
399 p2 := obj.Appendp(p1, c.newprog)
401 p1.From.Type = obj.TYPE_MEM
402 p1.From.Sym = source.Sym
403 p1.From.Name = obj.NAME_GOTREF
404 p1.To.Type = obj.TYPE_REG
410 if p.From.Name == obj.NAME_EXTERN {
412 p2.From.Name = obj.NAME_NONE
414 } else if p.To.Name == obj.NAME_EXTERN {
416 p2.To.Name = obj.NAME_NONE
424 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
425 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
429 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
431 p := c.cursym.Func().Text
432 textstksiz := p.To.Offset
433 if textstksiz == -8 {
434 // Historical way to mark NOFRAME.
435 p.From.Sym.Set(obj.AttrNoFrame, true)
439 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
441 if p.From.Sym.NoFrame() {
443 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
447 c.cursym.Func().Args = p.To.Val.(int32)
448 c.cursym.Func().Locals = int32(textstksiz)
451 * find leaf subroutines
453 for p := c.cursym.Func().Text; p != nil; p = p.Link {
461 c.cursym.Func().Text.Mark &^= LEAF
468 for p := c.cursym.Func().Text; p != nil; p = p.Link {
472 c.cursym.Func().Text = p
473 c.autosize = int32(textstksiz)
475 if p.Mark&LEAF != 0 && c.autosize == 0 {
476 // A leaf function with no locals has no frame.
477 p.From.Sym.Set(obj.AttrNoFrame, true)
480 if !p.From.Sym.NoFrame() {
481 // If there is a stack frame at all, it includes
482 // space to save the LR.
487 extrasize := int32(0)
488 if c.autosize%16 == 8 {
489 // Allocate extra 8 bytes on the frame top to save FP
491 } else if c.autosize&(16-1) == 0 {
492 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
495 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
497 c.autosize += extrasize
498 c.cursym.Func().Locals += extrasize
500 // low 32 bits for autosize
501 // high 32 bits for extrasize
502 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
508 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
509 if c.ctxt.Debugvlog {
510 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
512 c.cursym.Func().Text.Mark |= LEAF
515 if cursym.Func().Text.Mark&LEAF != 0 {
516 cursym.Set(obj.AttrLeaf, true)
517 if p.From.Sym.NoFrame() {
522 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
523 // A leaf function with a small stack can be marked
524 // NOSPLIT, avoiding a stack check.
525 p.From.Sym.Set(obj.AttrNoSplit, true)
528 if !p.From.Sym.NoSplit() {
529 p = c.stacksplit(p, c.autosize) // emit split check
532 var prologueEnd *obj.Prog
534 aoffset := c.autosize
539 // Frame is non-empty. Make sure to save link register, even if
540 // it is a leaf function, so that traceback works.
542 if c.autosize > aoffset {
543 // Frame size is too large for a MOVD.W instruction.
544 // Store link register before decrementing SP, so if a signal comes
545 // during the execution of the function prologue, the traceback
546 // code will not see a half-updated stack frame.
547 // This sequence is not async preemptible, as if we open a frame
548 // at the current SP, it will clobber the saved LR.
549 q = c.ctxt.StartUnsafePoint(q, c.newprog)
551 q = obj.Appendp(q, c.newprog)
554 q.From.Type = obj.TYPE_CONST
555 q.From.Offset = int64(c.autosize)
557 q.To.Type = obj.TYPE_REG
562 q = obj.Appendp(q, c.newprog)
565 q.From.Type = obj.TYPE_REG
567 q.To.Type = obj.TYPE_MEM
570 q1 = obj.Appendp(q, c.newprog)
573 q1.From.Type = obj.TYPE_REG
575 q1.To.Type = obj.TYPE_REG
577 q1.Spadj = c.autosize
579 if objabi.GOOS == "ios" {
580 // iOS does not support SA_ONSTACK. We will run the signal handler
581 // on the G stack. If we write below SP, it may be clobbered by
582 // the signal handler. So we save LR after decrementing SP.
583 q1 = obj.Appendp(q1, c.newprog)
586 q1.From.Type = obj.TYPE_REG
587 q1.From.Reg = REGLINK
588 q1.To.Type = obj.TYPE_MEM
592 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
594 // small frame, update SP and save LR in a single MOVD.W instruction
595 q1 = obj.Appendp(q, c.newprog)
598 q1.From.Type = obj.TYPE_REG
599 q1.From.Reg = REGLINK
600 q1.To.Type = obj.TYPE_MEM
602 q1.To.Offset = int64(-aoffset)
609 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
612 q1 = obj.Appendp(q1, c.newprog)
615 q1.From.Type = obj.TYPE_REG
617 q1.To.Type = obj.TYPE_MEM
621 q1 = obj.Appendp(q1, c.newprog)
624 q1.From.Type = obj.TYPE_CONST
627 q1.To.Type = obj.TYPE_REG
630 if c.cursym.Func().Text.From.Sym.Wrapper() {
631 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
633 // MOV g_panic(g), R1
637 // ... function body ...
639 // MOV panic_argp(R1), R2
640 // ADD $(autosize+8), RSP, R3
644 // MOVD R4, panic_argp(R1)
647 // The NOP is needed to give the jumps somewhere to land.
648 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
651 // MOV g_panic(g), R1
652 q = obj.Appendp(q, c.newprog)
654 q.From.Type = obj.TYPE_MEM
656 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
657 q.To.Type = obj.TYPE_REG
660 // CBNZ R1, checkargp
661 cbnz := obj.Appendp(q, c.newprog)
663 cbnz.From.Type = obj.TYPE_REG
664 cbnz.From.Reg = REG_R1
665 cbnz.To.Type = obj.TYPE_BRANCH
667 // Empty branch target at the top of the function body
668 end := obj.Appendp(cbnz, c.newprog)
671 // find the end of the function
673 for last = end; last.Link != nil; last = last.Link {
676 // MOV panic_argp(R1), R2
677 mov := obj.Appendp(last, c.newprog)
679 mov.From.Type = obj.TYPE_MEM
680 mov.From.Reg = REG_R1
681 mov.From.Offset = 0 // Panic.argp
682 mov.To.Type = obj.TYPE_REG
685 // CBNZ branches to the MOV above
686 cbnz.To.SetTarget(mov)
688 // ADD $(autosize+8), SP, R3
689 q = obj.Appendp(mov, c.newprog)
691 q.From.Type = obj.TYPE_CONST
692 q.From.Offset = int64(c.autosize) + 8
694 q.To.Type = obj.TYPE_REG
698 q = obj.Appendp(q, c.newprog)
700 q.From.Type = obj.TYPE_REG
705 q = obj.Appendp(q, c.newprog)
707 q.To.Type = obj.TYPE_BRANCH
711 q = obj.Appendp(q, c.newprog)
713 q.From.Type = obj.TYPE_CONST
716 q.To.Type = obj.TYPE_REG
719 // MOV R4, panic_argp(R1)
720 q = obj.Appendp(q, c.newprog)
722 q.From.Type = obj.TYPE_REG
724 q.To.Type = obj.TYPE_MEM
726 q.To.Offset = 0 // Panic.argp
729 q = obj.Appendp(q, c.newprog)
731 q.To.Type = obj.TYPE_BRANCH
737 if p.From.Type == obj.TYPE_CONST {
738 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
744 if c.cursym.Func().Text.Mark&LEAF != 0 {
747 p.From.Type = obj.TYPE_CONST
748 p.From.Offset = int64(c.autosize)
749 p.To.Type = obj.TYPE_REG
751 p.Spadj = -c.autosize
754 p = obj.Appendp(p, c.newprog)
756 p.From.Type = obj.TYPE_CONST
759 p.To.Type = obj.TYPE_REG
763 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
767 p.From.Type = obj.TYPE_MEM
770 p.To.Type = obj.TYPE_REG
772 p = obj.Appendp(p, c.newprog)
774 aoffset := c.autosize
778 p.From.Type = obj.TYPE_MEM
780 p.From.Offset = int64(aoffset)
782 p.To.Type = obj.TYPE_REG
787 p.From.Type = obj.TYPE_MEM
790 p.To.Type = obj.TYPE_REG
795 q.From.Type = obj.TYPE_CONST
796 q.From.Offset = int64(aoffset)
797 q.To.Type = obj.TYPE_REG
800 q.Spadj = int32(-q.From.Offset)
807 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
808 // so that if you are debugging a low-level crash where PC and LR are zero,
809 // you can look at R27 to see what jumped to the zero.
810 // This is useful when bringing up Go on a new system.
811 // (There is similar code in ../ppc64/obj9.go:/if.false.)
812 const debugRETZERO = false
814 if p.As != obj.ARET {
822 p.From.Type = obj.TYPE_BRANCH
824 p.To.Type = obj.TYPE_REG
829 if p.As != obj.ARET {
837 if retjmp != nil { // retjmp
839 p.To.Type = obj.TYPE_BRANCH
841 p.Spadj = +c.autosize
846 p.To.Type = obj.TYPE_MEM
849 p.Spadj = +c.autosize
852 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
854 p.Spadj = int32(-p.From.Offset)
856 p.Spadj = int32(+p.From.Offset)
860 case obj.AGETCALLERPC:
864 p.From.Type = obj.TYPE_REG
869 p.From.Type = obj.TYPE_MEM
875 // STP (FP, R27), -24(SP)
882 // copy DUFFCOPY from q1 to q4
883 q4 := obj.Appendp(p, c.newprog)
885 q4.As = obj.ADUFFCOPY
889 q1.From.Type = obj.TYPE_BRANCH
890 q1.To.Type = obj.TYPE_REG
893 q2 := obj.Appendp(q1, c.newprog)
896 q2.From.Type = obj.TYPE_REGREG
898 q2.From.Offset = int64(REG_R27)
899 q2.To.Type = obj.TYPE_MEM
903 // maintain FP for DUFFCOPY
904 q3 := obj.Appendp(q2, c.newprog)
907 q3.From.Type = obj.TYPE_CONST
910 q3.To.Type = obj.TYPE_REG
913 q5 := obj.Appendp(q4, c.newprog)
916 q5.From.Type = obj.TYPE_CONST
919 q5.To.Type = obj.TYPE_REG
921 q1.From.SetTarget(q5)
926 // STP (FP, R27), -24(SP)
933 // copy DUFFZERO from q1 to q4
934 q4 := obj.Appendp(p, c.newprog)
936 q4.As = obj.ADUFFZERO
940 q1.From.Type = obj.TYPE_BRANCH
941 q1.To.Type = obj.TYPE_REG
944 q2 := obj.Appendp(q1, c.newprog)
947 q2.From.Type = obj.TYPE_REGREG
949 q2.From.Offset = int64(REG_R27)
950 q2.To.Type = obj.TYPE_MEM
954 // maintain FP for DUFFZERO
955 q3 := obj.Appendp(q2, c.newprog)
958 q3.From.Type = obj.TYPE_CONST
961 q3.To.Type = obj.TYPE_REG
964 q5 := obj.Appendp(q4, c.newprog)
967 q5.From.Type = obj.TYPE_CONST
970 q5.To.Type = obj.TYPE_REG
972 q1.From.SetTarget(q5)
976 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
978 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
979 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
980 if ctxt.Debugvlog || !ctxt.IsAsm {
981 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
983 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
985 log.Fatalf("bad SPWRITE")
993 func nocache(p *obj.Prog) {
999 var unaryDst = map[obj.As]bool{
1007 var Linkarm64 = obj.LinkArch{
1008 Arch: sys.ArchARM64,
1010 Preprocess: preprocess,
1014 DWARFRegisters: ARM64DWARFRegisters,