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 // For MOVW, the destination register can't be ZR,
309 // so don't bother rewriting it in this situation.
310 if (isANDWop(p.As) || isADDWop(p.As) || p.As == AMOVW && p.To.Reg != REGZERO) && p.From.Type == obj.TYPE_CONST {
311 v := p.From.Offset & 0xffffffff
312 p.From.Offset = v | v<<32
315 if c.ctxt.Flag_dynlink {
320 // Rewrite p, if necessary, to access global data via the global offset table.
321 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
322 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
325 // MOVD runtime.duffxxx@GOT, REGTMP
326 // ADD $offset, REGTMP
329 if p.As == obj.ADUFFZERO {
330 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
332 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
334 offset := p.To.Offset
336 p.From.Type = obj.TYPE_MEM
337 p.From.Name = obj.NAME_GOTREF
339 p.To.Type = obj.TYPE_REG
341 p.To.Name = obj.NAME_NONE
344 p1 := obj.Appendp(p, c.newprog)
346 p1.From.Type = obj.TYPE_CONST
347 p1.From.Offset = offset
348 p1.To.Type = obj.TYPE_REG
350 p2 := obj.Appendp(p1, c.newprog)
352 p2.To.Type = obj.TYPE_REG
356 // We only care about global data: NAME_EXTERN means a global
357 // symbol in the Go sense, and p.Sym.Local is true for a few
358 // internally defined symbols.
359 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
360 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
361 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
363 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
365 if p.To.Type != obj.TYPE_REG {
366 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
368 p.From.Type = obj.TYPE_MEM
369 p.From.Name = obj.NAME_GOTREF
370 if p.From.Offset != 0 {
371 q := obj.Appendp(p, c.newprog)
373 q.From.Type = obj.TYPE_CONST
374 q.From.Offset = p.From.Offset
379 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
380 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
383 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
384 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
385 // An addition may be inserted between the two MOVs if there is an offset.
386 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
387 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
388 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
391 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
396 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
399 if source.Sym.Type == objabi.STLSBSS {
402 if source.Type != obj.TYPE_MEM {
403 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
405 p1 := obj.Appendp(p, c.newprog)
406 p2 := obj.Appendp(p1, c.newprog)
408 p1.From.Type = obj.TYPE_MEM
409 p1.From.Sym = source.Sym
410 p1.From.Name = obj.NAME_GOTREF
411 p1.To.Type = obj.TYPE_REG
417 if p.From.Name == obj.NAME_EXTERN {
419 p2.From.Name = obj.NAME_NONE
421 } else if p.To.Name == obj.NAME_EXTERN {
423 p2.To.Name = obj.NAME_NONE
431 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
432 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
436 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
438 p := c.cursym.Func().Text
439 textstksiz := p.To.Offset
440 if textstksiz == -8 {
441 // Historical way to mark NOFRAME.
442 p.From.Sym.Set(obj.AttrNoFrame, true)
446 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
448 if p.From.Sym.NoFrame() {
450 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
454 c.cursym.Func().Args = p.To.Val.(int32)
455 c.cursym.Func().Locals = int32(textstksiz)
458 * find leaf subroutines
460 for p := c.cursym.Func().Text; p != nil; p = p.Link {
468 c.cursym.Func().Text.Mark &^= LEAF
475 for p := c.cursym.Func().Text; p != nil; p = p.Link {
479 c.cursym.Func().Text = p
480 c.autosize = int32(textstksiz)
482 if p.Mark&LEAF != 0 && c.autosize == 0 {
483 // A leaf function with no locals has no frame.
484 p.From.Sym.Set(obj.AttrNoFrame, true)
487 if !p.From.Sym.NoFrame() {
488 // If there is a stack frame at all, it includes
489 // space to save the LR.
494 extrasize := int32(0)
495 if c.autosize%16 == 8 {
496 // Allocate extra 8 bytes on the frame top to save FP
498 } else if c.autosize&(16-1) == 0 {
499 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
502 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
504 c.autosize += extrasize
505 c.cursym.Func().Locals += extrasize
507 // low 32 bits for autosize
508 // high 32 bits for extrasize
509 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
515 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
516 if c.ctxt.Debugvlog {
517 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
519 c.cursym.Func().Text.Mark |= LEAF
522 if cursym.Func().Text.Mark&LEAF != 0 {
523 cursym.Set(obj.AttrLeaf, true)
524 if p.From.Sym.NoFrame() {
529 if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
530 // A leaf function with a small stack can be marked
531 // NOSPLIT, avoiding a stack check.
532 p.From.Sym.Set(obj.AttrNoSplit, true)
535 if !p.From.Sym.NoSplit() {
536 p = c.stacksplit(p, c.autosize) // emit split check
539 var prologueEnd *obj.Prog
541 aoffset := c.autosize
546 // Frame is non-empty. Make sure to save link register, even if
547 // it is a leaf function, so that traceback works.
549 if c.autosize > aoffset {
550 // Frame size is too large for a MOVD.W instruction.
551 // Store link register before decrementing SP, so if a signal comes
552 // during the execution of the function prologue, the traceback
553 // code will not see a half-updated stack frame.
554 // This sequence is not async preemptible, as if we open a frame
555 // at the current SP, it will clobber the saved LR.
556 q = c.ctxt.StartUnsafePoint(q, c.newprog)
558 q = obj.Appendp(q, c.newprog)
561 q.From.Type = obj.TYPE_CONST
562 q.From.Offset = int64(c.autosize)
564 q.To.Type = obj.TYPE_REG
569 q = obj.Appendp(q, c.newprog)
572 q.From.Type = obj.TYPE_REG
574 q.To.Type = obj.TYPE_MEM
577 q1 = obj.Appendp(q, c.newprog)
580 q1.From.Type = obj.TYPE_REG
582 q1.To.Type = obj.TYPE_REG
584 q1.Spadj = c.autosize
586 if buildcfg.GOOS == "ios" {
587 // iOS does not support SA_ONSTACK. We will run the signal handler
588 // on the G stack. If we write below SP, it may be clobbered by
589 // the signal handler. So we save LR after decrementing SP.
590 q1 = obj.Appendp(q1, c.newprog)
593 q1.From.Type = obj.TYPE_REG
594 q1.From.Reg = REGLINK
595 q1.To.Type = obj.TYPE_MEM
599 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
601 // small frame, update SP and save LR in a single MOVD.W instruction
602 q1 = obj.Appendp(q, c.newprog)
605 q1.From.Type = obj.TYPE_REG
606 q1.From.Reg = REGLINK
607 q1.To.Type = obj.TYPE_MEM
609 q1.To.Offset = int64(-aoffset)
616 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
619 q1 = obj.Appendp(q1, c.newprog)
622 q1.From.Type = obj.TYPE_REG
624 q1.To.Type = obj.TYPE_MEM
628 q1 = obj.Appendp(q1, c.newprog)
631 q1.From.Type = obj.TYPE_CONST
634 q1.To.Type = obj.TYPE_REG
637 if c.cursym.Func().Text.From.Sym.Wrapper() {
638 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
640 // MOV g_panic(g), RT1
644 // ... function body ...
646 // MOV panic_argp(RT1), RT2
647 // ADD $(autosize+8), RSP, R20
651 // MOVD R20, panic_argp(RT1)
654 // The NOP is needed to give the jumps somewhere to land.
655 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
658 // MOV g_panic(g), RT1
659 q = obj.Appendp(q, c.newprog)
661 q.From.Type = obj.TYPE_MEM
663 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
664 q.To.Type = obj.TYPE_REG
667 // CBNZ RT1, checkargp
668 cbnz := obj.Appendp(q, c.newprog)
670 cbnz.From.Type = obj.TYPE_REG
671 cbnz.From.Reg = REGRT1
672 cbnz.To.Type = obj.TYPE_BRANCH
674 // Empty branch target at the top of the function body
675 end := obj.Appendp(cbnz, c.newprog)
678 // find the end of the function
680 for last = end; last.Link != nil; last = last.Link {
683 // MOV panic_argp(RT1), RT2
684 mov := obj.Appendp(last, c.newprog)
686 mov.From.Type = obj.TYPE_MEM
687 mov.From.Reg = REGRT1
688 mov.From.Offset = 0 // Panic.argp
689 mov.To.Type = obj.TYPE_REG
692 // CBNZ branches to the MOV above
693 cbnz.To.SetTarget(mov)
695 // ADD $(autosize+8), SP, R20
696 q = obj.Appendp(mov, c.newprog)
698 q.From.Type = obj.TYPE_CONST
699 q.From.Offset = int64(c.autosize) + 8
701 q.To.Type = obj.TYPE_REG
705 q = obj.Appendp(q, c.newprog)
707 q.From.Type = obj.TYPE_REG
712 q = obj.Appendp(q, c.newprog)
714 q.To.Type = obj.TYPE_BRANCH
718 q = obj.Appendp(q, c.newprog)
720 q.From.Type = obj.TYPE_CONST
723 q.To.Type = obj.TYPE_REG
726 // MOV R20, panic_argp(RT1)
727 q = obj.Appendp(q, c.newprog)
729 q.From.Type = obj.TYPE_REG
731 q.To.Type = obj.TYPE_MEM
733 q.To.Offset = 0 // Panic.argp
736 q = obj.Appendp(q, c.newprog)
738 q.To.Type = obj.TYPE_BRANCH
744 if p.From.Type == obj.TYPE_CONST {
745 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
751 if c.cursym.Func().Text.Mark&LEAF != 0 {
754 p.From.Type = obj.TYPE_CONST
755 p.From.Offset = int64(c.autosize)
756 p.To.Type = obj.TYPE_REG
758 p.Spadj = -c.autosize
761 p = obj.Appendp(p, c.newprog)
763 p.From.Type = obj.TYPE_CONST
766 p.To.Type = obj.TYPE_REG
770 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
774 p.From.Type = obj.TYPE_MEM
777 p.To.Type = obj.TYPE_REG
779 p = obj.Appendp(p, c.newprog)
781 aoffset := c.autosize
785 p.From.Type = obj.TYPE_MEM
787 p.From.Offset = int64(aoffset)
789 p.To.Type = obj.TYPE_REG
794 p.From.Type = obj.TYPE_MEM
797 p.To.Type = obj.TYPE_REG
802 q.From.Type = obj.TYPE_CONST
803 q.From.Offset = int64(aoffset)
804 q.To.Type = obj.TYPE_REG
807 q.Spadj = int32(-q.From.Offset)
814 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
815 // so that if you are debugging a low-level crash where PC and LR are zero,
816 // you can look at R27 to see what jumped to the zero.
817 // This is useful when bringing up Go on a new system.
818 // (There is similar code in ../ppc64/obj9.go:/if.false.)
819 const debugRETZERO = false
821 if p.As != obj.ARET {
829 p.From.Type = obj.TYPE_BRANCH
831 p.To.Type = obj.TYPE_REG
836 if p.As != obj.ARET {
844 if retjmp != nil { // retjmp
846 p.To.Type = obj.TYPE_BRANCH
848 p.Spadj = +c.autosize
853 p.To.Type = obj.TYPE_MEM
856 p.Spadj = +c.autosize
859 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
861 p.Spadj = int32(-p.From.Offset)
863 p.Spadj = int32(+p.From.Offset)
867 case obj.AGETCALLERPC:
871 p.From.Type = obj.TYPE_REG
876 p.From.Type = obj.TYPE_MEM
882 // STP (FP, R27), -24(SP)
889 // copy DUFFCOPY from q1 to q4
890 q4 := obj.Appendp(p, c.newprog)
892 q4.As = obj.ADUFFCOPY
896 q1.From.Type = obj.TYPE_BRANCH
897 q1.To.Type = obj.TYPE_REG
900 q2 := obj.Appendp(q1, c.newprog)
903 q2.From.Type = obj.TYPE_REGREG
905 q2.From.Offset = int64(REG_R27)
906 q2.To.Type = obj.TYPE_MEM
910 // maintain FP for DUFFCOPY
911 q3 := obj.Appendp(q2, c.newprog)
914 q3.From.Type = obj.TYPE_CONST
917 q3.To.Type = obj.TYPE_REG
920 q5 := obj.Appendp(q4, c.newprog)
923 q5.From.Type = obj.TYPE_CONST
926 q5.To.Type = obj.TYPE_REG
928 q1.From.SetTarget(q5)
933 // STP (FP, R27), -24(SP)
940 // copy DUFFZERO from q1 to q4
941 q4 := obj.Appendp(p, c.newprog)
943 q4.As = obj.ADUFFZERO
947 q1.From.Type = obj.TYPE_BRANCH
948 q1.To.Type = obj.TYPE_REG
951 q2 := obj.Appendp(q1, c.newprog)
954 q2.From.Type = obj.TYPE_REGREG
956 q2.From.Offset = int64(REG_R27)
957 q2.To.Type = obj.TYPE_MEM
961 // maintain FP for DUFFZERO
962 q3 := obj.Appendp(q2, c.newprog)
965 q3.From.Type = obj.TYPE_CONST
968 q3.To.Type = obj.TYPE_REG
971 q5 := obj.Appendp(q4, c.newprog)
974 q5.From.Type = obj.TYPE_CONST
977 q5.To.Type = obj.TYPE_REG
979 q1.From.SetTarget(q5)
983 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
985 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
986 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
987 if ctxt.Debugvlog || !ctxt.IsAsm {
988 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
990 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
992 log.Fatalf("bad SPWRITE")
1000 func nocache(p *obj.Prog) {
1006 var unaryDst = map[obj.As]bool{
1014 var Linkarm64 = obj.LinkArch{
1015 Arch: sys.ArchARM64,
1017 Preprocess: preprocess,
1021 DWARFRegisters: ARM64DWARFRegisters,