]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/internal/obj/arm64/obj7.go
cmd/internal/obj: consolidate emitting entry stack map
[gostls13.git] / src / cmd / internal / obj / arm64 / obj7.go
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/
3 //
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.
12 //
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:
19 //
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
22 //
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
29 // THE SOFTWARE.
30
31 package arm64
32
33 import (
34         "cmd/internal/obj"
35         "cmd/internal/objabi"
36         "cmd/internal/sys"
37         "math"
38 )
39
40 var complements = []obj.As{
41         AADD:  ASUB,
42         AADDW: ASUBW,
43         ASUB:  AADD,
44         ASUBW: AADDW,
45         ACMP:  ACMN,
46         ACMPW: ACMNW,
47         ACMN:  ACMP,
48         ACMNW: ACMPW,
49 }
50
51 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
52         // MOV  g_stackguard(g), R1
53         p = obj.Appendp(p, c.newprog)
54
55         p.As = AMOVD
56         p.From.Type = obj.TYPE_MEM
57         p.From.Reg = REGG
58         p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
59         if c.cursym.CFunc() {
60                 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
61         }
62         p.To.Type = obj.TYPE_REG
63         p.To.Reg = REG_R1
64
65         q := (*obj.Prog)(nil)
66         if framesize <= objabi.StackSmall {
67                 // small stack: SP < stackguard
68                 //      MOV     SP, R2
69                 //      CMP     stackguard, R2
70                 p = obj.Appendp(p, c.newprog)
71
72                 p.As = AMOVD
73                 p.From.Type = obj.TYPE_REG
74                 p.From.Reg = REGSP
75                 p.To.Type = obj.TYPE_REG
76                 p.To.Reg = REG_R2
77
78                 p = obj.Appendp(p, c.newprog)
79                 p.As = ACMP
80                 p.From.Type = obj.TYPE_REG
81                 p.From.Reg = REG_R1
82                 p.Reg = REG_R2
83         } else if framesize <= objabi.StackBig {
84                 // large stack: SP-framesize < stackguard-StackSmall
85                 //      SUB     $(framesize-StackSmall), SP, R2
86                 //      CMP     stackguard, R2
87                 p = obj.Appendp(p, c.newprog)
88
89                 p.As = ASUB
90                 p.From.Type = obj.TYPE_CONST
91                 p.From.Offset = int64(framesize) - objabi.StackSmall
92                 p.Reg = REGSP
93                 p.To.Type = obj.TYPE_REG
94                 p.To.Reg = REG_R2
95
96                 p = obj.Appendp(p, c.newprog)
97                 p.As = ACMP
98                 p.From.Type = obj.TYPE_REG
99                 p.From.Reg = REG_R1
100                 p.Reg = REG_R2
101         } else {
102                 // Such a large stack we need to protect against wraparound
103                 // if SP is close to zero.
104                 //      SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
105                 // The +StackGuard on both sides is required to keep the left side positive:
106                 // SP is allowed to be slightly below stackguard. See stack.h.
107                 //      CMP     $StackPreempt, R1
108                 //      BEQ     label_of_call_to_morestack
109                 //      ADD     $StackGuard, SP, R2
110                 //      SUB     R1, R2
111                 //      MOV     $(framesize+(StackGuard-StackSmall)), R3
112                 //      CMP     R3, R2
113                 p = obj.Appendp(p, c.newprog)
114
115                 p.As = ACMP
116                 p.From.Type = obj.TYPE_CONST
117                 p.From.Offset = objabi.StackPreempt
118                 p.Reg = REG_R1
119
120                 p = obj.Appendp(p, c.newprog)
121                 q = p
122                 p.As = ABEQ
123                 p.To.Type = obj.TYPE_BRANCH
124
125                 p = obj.Appendp(p, c.newprog)
126                 p.As = AADD
127                 p.From.Type = obj.TYPE_CONST
128                 p.From.Offset = objabi.StackGuard
129                 p.Reg = REGSP
130                 p.To.Type = obj.TYPE_REG
131                 p.To.Reg = REG_R2
132
133                 p = obj.Appendp(p, c.newprog)
134                 p.As = ASUB
135                 p.From.Type = obj.TYPE_REG
136                 p.From.Reg = REG_R1
137                 p.To.Type = obj.TYPE_REG
138                 p.To.Reg = REG_R2
139
140                 p = obj.Appendp(p, c.newprog)
141                 p.As = AMOVD
142                 p.From.Type = obj.TYPE_CONST
143                 p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
144                 p.To.Type = obj.TYPE_REG
145                 p.To.Reg = REG_R3
146
147                 p = obj.Appendp(p, c.newprog)
148                 p.As = ACMP
149                 p.From.Type = obj.TYPE_REG
150                 p.From.Reg = REG_R3
151                 p.Reg = REG_R2
152         }
153
154         // BLS  do-morestack
155         bls := obj.Appendp(p, c.newprog)
156         bls.As = ABLS
157         bls.To.Type = obj.TYPE_BRANCH
158
159         var last *obj.Prog
160         for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
161         }
162
163         // Now we are at the end of the function, but logically
164         // we are still in function prologue. We need to fix the
165         // SP data and PCDATA.
166         spfix := obj.Appendp(last, c.newprog)
167         spfix.As = obj.ANOP
168         spfix.Spadj = -framesize
169
170         pcdata := c.ctxt.EmitEntryLiveness(c.cursym, spfix, c.newprog)
171
172         // MOV  LR, R3
173         movlr := obj.Appendp(pcdata, c.newprog)
174         movlr.As = AMOVD
175         movlr.From.Type = obj.TYPE_REG
176         movlr.From.Reg = REGLINK
177         movlr.To.Type = obj.TYPE_REG
178         movlr.To.Reg = REG_R3
179         if q != nil {
180                 q.Pcond = movlr
181         }
182         bls.Pcond = movlr
183
184         debug := movlr
185         if false {
186                 debug = obj.Appendp(debug, c.newprog)
187                 debug.As = AMOVD
188                 debug.From.Type = obj.TYPE_CONST
189                 debug.From.Offset = int64(framesize)
190                 debug.To.Type = obj.TYPE_REG
191                 debug.To.Reg = REGTMP
192         }
193
194         // BL   runtime.morestack(SB)
195         call := obj.Appendp(debug, c.newprog)
196         call.As = ABL
197         call.To.Type = obj.TYPE_BRANCH
198         morestack := "runtime.morestack"
199         switch {
200         case c.cursym.CFunc():
201                 morestack = "runtime.morestackc"
202         case !c.cursym.Func.Text.From.Sym.NeedCtxt():
203                 morestack = "runtime.morestack_noctxt"
204         }
205         call.To.Sym = c.ctxt.Lookup(morestack)
206
207         // B    start
208         jmp := obj.Appendp(call, c.newprog)
209         jmp.As = AB
210         jmp.To.Type = obj.TYPE_BRANCH
211         jmp.Pcond = c.cursym.Func.Text.Link
212         jmp.Spadj = +framesize
213
214         // placeholder for bls's jump target
215         // p = obj.Appendp(ctxt, p)
216         // p.As = obj.ANOP
217
218         return bls
219 }
220
221 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
222         c := ctxt7{ctxt: ctxt, newprog: newprog}
223
224         p.From.Class = 0
225         p.To.Class = 0
226
227         // $0 results in C_ZCON, which matches both C_REG and various
228         // C_xCON, however the C_REG cases in asmout don't expect a
229         // constant, so they will use the register fields and assemble
230         // a R0. To prevent that, rewrite $0 as ZR.
231         if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
232                 p.From.Type = obj.TYPE_REG
233                 p.From.Reg = REGZERO
234         }
235         if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
236                 p.To.Type = obj.TYPE_REG
237                 p.To.Reg = REGZERO
238         }
239
240         // Rewrite BR/BL to symbol as TYPE_BRANCH.
241         switch p.As {
242         case AB,
243                 ABL,
244                 obj.ARET,
245                 obj.ADUFFZERO,
246                 obj.ADUFFCOPY:
247                 if p.To.Sym != nil {
248                         p.To.Type = obj.TYPE_BRANCH
249                 }
250                 break
251         }
252
253         // Rewrite float constants to values stored in memory.
254         switch p.As {
255         case AFMOVS:
256                 if p.From.Type == obj.TYPE_FCONST {
257                         f32 := float32(p.From.Val.(float64))
258                         if math.Float32bits(f32) == 0 {
259                                 p.From.Type = obj.TYPE_REG
260                                 p.From.Reg = REGZERO
261                                 break
262                         }
263                         p.From.Type = obj.TYPE_MEM
264                         p.From.Sym = c.ctxt.Float32Sym(f32)
265                         p.From.Name = obj.NAME_EXTERN
266                         p.From.Offset = 0
267                 }
268
269         case AFMOVD:
270                 if p.From.Type == obj.TYPE_FCONST {
271                         f64 := p.From.Val.(float64)
272                         if math.Float64bits(f64) == 0 {
273                                 p.From.Type = obj.TYPE_REG
274                                 p.From.Reg = REGZERO
275                                 break
276                         }
277                         p.From.Type = obj.TYPE_MEM
278                         p.From.Sym = c.ctxt.Float64Sym(f64)
279                         p.From.Name = obj.NAME_EXTERN
280                         p.From.Offset = 0
281                 }
282
283                 break
284         }
285
286         // Rewrite negative immediates as positive immediates with
287         // complementary instruction.
288         switch p.As {
289         case AADD, ASUB, ACMP, ACMN:
290                 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
291                         p.From.Offset = -p.From.Offset
292                         p.As = complements[p.As]
293                 }
294         case AADDW, ASUBW, ACMPW, ACMNW:
295                 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
296                         p.From.Offset = -p.From.Offset
297                         p.As = complements[p.As]
298                 }
299         }
300
301         // For 32-bit logical instruction with constant,
302         // rewrite the high 32-bit to be a repetition of
303         // the low 32-bit, so that the BITCON test can be
304         // shared for both 32-bit and 64-bit. 32-bit ops
305         // will zero the high 32-bit of the destination
306         // register anyway.
307         switch p.As {
308         case AANDW, AORRW, AEORW, AANDSW, ATSTW:
309                 if p.From.Type == obj.TYPE_CONST {
310                         v := p.From.Offset & 0xffffffff
311                         p.From.Offset = v | v<<32
312                 }
313         }
314
315         if c.ctxt.Flag_dynlink {
316                 c.rewriteToUseGot(p)
317         }
318 }
319
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 {
323                 //     ADUFFxxx $offset
324                 // becomes
325                 //     MOVD runtime.duffxxx@GOT, REGTMP
326                 //     ADD $offset, REGTMP
327                 //     CALL REGTMP
328                 var sym *obj.LSym
329                 if p.As == obj.ADUFFZERO {
330                         sym = c.ctxt.Lookup("runtime.duffzero")
331                 } else {
332                         sym = c.ctxt.Lookup("runtime.duffcopy")
333                 }
334                 offset := p.To.Offset
335                 p.As = AMOVD
336                 p.From.Type = obj.TYPE_MEM
337                 p.From.Name = obj.NAME_GOTREF
338                 p.From.Sym = sym
339                 p.To.Type = obj.TYPE_REG
340                 p.To.Reg = REGTMP
341                 p.To.Name = obj.NAME_NONE
342                 p.To.Offset = 0
343                 p.To.Sym = nil
344                 p1 := obj.Appendp(p, c.newprog)
345                 p1.As = AADD
346                 p1.From.Type = obj.TYPE_CONST
347                 p1.From.Offset = offset
348                 p1.To.Type = obj.TYPE_REG
349                 p1.To.Reg = REGTMP
350                 p2 := obj.Appendp(p1, c.newprog)
351                 p2.As = obj.ACALL
352                 p2.To.Type = obj.TYPE_REG
353                 p2.To.Reg = REGTMP
354         }
355
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
362                 if p.As != AMOVD {
363                         c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
364                 }
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)
367                 }
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)
372                         q.As = AADD
373                         q.From.Type = obj.TYPE_CONST
374                         q.From.Offset = p.From.Offset
375                         q.To = p.To
376                         p.From.Offset = 0
377                 }
378         }
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)
381         }
382         var source *obj.Addr
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)
389                 }
390                 source = &p.From
391         } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
392                 source = &p.To
393         } else {
394                 return
395         }
396         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
397                 return
398         }
399         if source.Sym.Type == objabi.STLSBSS {
400                 return
401         }
402         if source.Type != obj.TYPE_MEM {
403                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
404         }
405         p1 := obj.Appendp(p, c.newprog)
406         p2 := obj.Appendp(p1, c.newprog)
407         p1.As = AMOVD
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
412         p1.To.Reg = REGTMP
413
414         p2.As = p.As
415         p2.From = p.From
416         p2.To = p.To
417         if p.From.Name == obj.NAME_EXTERN {
418                 p2.From.Reg = REGTMP
419                 p2.From.Name = obj.NAME_NONE
420                 p2.From.Sym = nil
421         } else if p.To.Name == obj.NAME_EXTERN {
422                 p2.To.Reg = REGTMP
423                 p2.To.Name = obj.NAME_NONE
424                 p2.To.Sym = nil
425         } else {
426                 return
427         }
428         obj.Nopout(p)
429 }
430
431 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
432         if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
433                 return
434         }
435
436         c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
437
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)
443                 textstksiz = 0
444         }
445         if textstksiz < 0 {
446                 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
447         }
448         if p.From.Sym.NoFrame() {
449                 if textstksiz != 0 {
450                         c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
451                 }
452         }
453
454         c.cursym.Func.Args = p.To.Val.(int32)
455         c.cursym.Func.Locals = int32(textstksiz)
456
457         /*
458          * find leaf subroutines
459          * strip NOPs
460          * expand RET
461          */
462         q := (*obj.Prog)(nil)
463         var q1 *obj.Prog
464         for p := c.cursym.Func.Text; p != nil; p = p.Link {
465                 switch p.As {
466                 case obj.ATEXT:
467                         p.Mark |= LEAF
468
469                 case obj.ARET:
470                         break
471
472                 case obj.ANOP:
473                         if p.Link != nil {
474                                 q1 = p.Link
475                                 q.Link = q1 /* q is non-nop */
476                                 q1.Mark |= p.Mark
477                         }
478                         continue
479
480                 case ABL,
481                         obj.ADUFFZERO,
482                         obj.ADUFFCOPY:
483                         c.cursym.Func.Text.Mark &^= LEAF
484                         fallthrough
485
486                 case ACBNZ,
487                         ACBZ,
488                         ACBNZW,
489                         ACBZW,
490                         ATBZ,
491                         ATBNZ,
492                         AB,
493                         ABEQ,
494                         ABNE,
495                         ABCS,
496                         ABHS,
497                         ABCC,
498                         ABLO,
499                         ABMI,
500                         ABPL,
501                         ABVS,
502                         ABVC,
503                         ABHI,
504                         ABLS,
505                         ABGE,
506                         ABLT,
507                         ABGT,
508                         ABLE,
509                         AADR, /* strange */
510                         AADRP:
511                         q1 = p.Pcond
512
513                         if q1 != nil {
514                                 for q1.As == obj.ANOP {
515                                         q1 = q1.Link
516                                         p.Pcond = q1
517                                 }
518                         }
519
520                         break
521                 }
522
523                 q = p
524         }
525
526         var retjmp *obj.LSym
527         for p := c.cursym.Func.Text; p != nil; p = p.Link {
528                 o := p.As
529                 switch o {
530                 case obj.ATEXT:
531                         c.cursym.Func.Text = p
532                         c.autosize = int32(textstksiz)
533
534                         if p.Mark&LEAF != 0 && c.autosize == 0 {
535                                 // A leaf function with no locals has no frame.
536                                 p.From.Sym.Set(obj.AttrNoFrame, true)
537                         }
538
539                         if !p.From.Sym.NoFrame() {
540                                 // If there is a stack frame at all, it includes
541                                 // space to save the LR.
542                                 c.autosize += 8
543                         }
544
545                         if c.autosize != 0 && c.autosize&(16-1) != 0 {
546                                 // The frame includes an LR.
547                                 // If the frame size is 8, it's only an LR,
548                                 // so there's no potential for breaking references to
549                                 // local variables by growing the frame size,
550                                 // because there are no local variables.
551                                 // But otherwise, if there is a non-empty locals section,
552                                 // the author of the code is responsible for making sure
553                                 // that the frame size is 8 mod 16.
554                                 if c.autosize == 8 {
555                                         c.autosize += 8
556                                         c.cursym.Func.Locals += 8
557                                 } else {
558                                         c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8)
559                                 }
560                         }
561                         if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
562                                 if c.ctxt.Debugvlog {
563                                         c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
564                                 }
565                                 c.cursym.Func.Text.Mark |= LEAF
566                         }
567
568                         // FP offsets need an updated p.To.Offset.
569                         p.To.Offset = int64(c.autosize) - 8
570
571                         if cursym.Func.Text.Mark&LEAF != 0 {
572                                 cursym.Set(obj.AttrLeaf, true)
573                                 if p.From.Sym.NoFrame() {
574                                         break
575                                 }
576                         }
577
578                         if !p.From.Sym.NoSplit() {
579                                 p = c.stacksplit(p, c.autosize) // emit split check
580                         }
581
582                         aoffset := c.autosize
583                         if aoffset > 0xF0 {
584                                 aoffset = 0xF0
585                         }
586
587                         // Frame is non-empty. Make sure to save link register, even if
588                         // it is a leaf function, so that traceback works.
589                         q = p
590                         if c.autosize > aoffset {
591                                 // Frame size is too large for a MOVD.W instruction.
592                                 // Store link register before decrementing SP, so if a signal comes
593                                 // during the execution of the function prologue, the traceback
594                                 // code will not see a half-updated stack frame.
595                                 q = obj.Appendp(q, c.newprog)
596                                 q.Pos = p.Pos
597                                 q.As = ASUB
598                                 q.From.Type = obj.TYPE_CONST
599                                 q.From.Offset = int64(c.autosize)
600                                 q.Reg = REGSP
601                                 q.To.Type = obj.TYPE_REG
602                                 q.To.Reg = REGTMP
603
604                                 q = obj.Appendp(q, c.newprog)
605                                 q.Pos = p.Pos
606                                 q.As = AMOVD
607                                 q.From.Type = obj.TYPE_REG
608                                 q.From.Reg = REGLINK
609                                 q.To.Type = obj.TYPE_MEM
610                                 q.To.Reg = REGTMP
611
612                                 q1 = obj.Appendp(q, c.newprog)
613                                 q1.Pos = p.Pos
614                                 q1.As = AMOVD
615                                 q1.From.Type = obj.TYPE_REG
616                                 q1.From.Reg = REGTMP
617                                 q1.To.Type = obj.TYPE_REG
618                                 q1.To.Reg = REGSP
619                                 q1.Spadj = c.autosize
620                         } else {
621                                 // small frame, update SP and save LR in a single MOVD.W instruction
622                                 q1 = obj.Appendp(q, c.newprog)
623                                 q1.As = AMOVD
624                                 q1.Pos = p.Pos
625                                 q1.From.Type = obj.TYPE_REG
626                                 q1.From.Reg = REGLINK
627                                 q1.To.Type = obj.TYPE_MEM
628                                 q1.Scond = C_XPRE
629                                 q1.To.Offset = int64(-aoffset)
630                                 q1.To.Reg = REGSP
631                                 q1.Spadj = aoffset
632                         }
633
634                         if c.cursym.Func.Text.From.Sym.Wrapper() {
635                                 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
636                                 //
637                                 //      MOV  g_panic(g), R1
638                                 //      CBNZ checkargp
639                                 // end:
640                                 //      NOP
641                                 // ... function body ...
642                                 // checkargp:
643                                 //      MOV  panic_argp(R1), R2
644                                 //      ADD  $(autosize+8), RSP, R3
645                                 //      CMP  R2, R3
646                                 //      BNE  end
647                                 //      ADD  $8, RSP, R4
648                                 //      MOVD R4, panic_argp(R1)
649                                 //      B    end
650                                 //
651                                 // The NOP is needed to give the jumps somewhere to land.
652                                 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
653                                 q = q1
654
655                                 // MOV g_panic(g), R1
656                                 q = obj.Appendp(q, c.newprog)
657                                 q.As = AMOVD
658                                 q.From.Type = obj.TYPE_MEM
659                                 q.From.Reg = REGG
660                                 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
661                                 q.To.Type = obj.TYPE_REG
662                                 q.To.Reg = REG_R1
663
664                                 // CBNZ R1, checkargp
665                                 cbnz := obj.Appendp(q, c.newprog)
666                                 cbnz.As = ACBNZ
667                                 cbnz.From.Type = obj.TYPE_REG
668                                 cbnz.From.Reg = REG_R1
669                                 cbnz.To.Type = obj.TYPE_BRANCH
670
671                                 // Empty branch target at the top of the function body
672                                 end := obj.Appendp(cbnz, c.newprog)
673                                 end.As = obj.ANOP
674
675                                 // find the end of the function
676                                 var last *obj.Prog
677                                 for last = end; last.Link != nil; last = last.Link {
678                                 }
679
680                                 // MOV panic_argp(R1), R2
681                                 mov := obj.Appendp(last, c.newprog)
682                                 mov.As = AMOVD
683                                 mov.From.Type = obj.TYPE_MEM
684                                 mov.From.Reg = REG_R1
685                                 mov.From.Offset = 0 // Panic.argp
686                                 mov.To.Type = obj.TYPE_REG
687                                 mov.To.Reg = REG_R2
688
689                                 // CBNZ branches to the MOV above
690                                 cbnz.Pcond = mov
691
692                                 // ADD $(autosize+8), SP, R3
693                                 q = obj.Appendp(mov, c.newprog)
694                                 q.As = AADD
695                                 q.From.Type = obj.TYPE_CONST
696                                 q.From.Offset = int64(c.autosize) + 8
697                                 q.Reg = REGSP
698                                 q.To.Type = obj.TYPE_REG
699                                 q.To.Reg = REG_R3
700
701                                 // CMP R2, R3
702                                 q = obj.Appendp(q, c.newprog)
703                                 q.As = ACMP
704                                 q.From.Type = obj.TYPE_REG
705                                 q.From.Reg = REG_R2
706                                 q.Reg = REG_R3
707
708                                 // BNE end
709                                 q = obj.Appendp(q, c.newprog)
710                                 q.As = ABNE
711                                 q.To.Type = obj.TYPE_BRANCH
712                                 q.Pcond = end
713
714                                 // ADD $8, SP, R4
715                                 q = obj.Appendp(q, c.newprog)
716                                 q.As = AADD
717                                 q.From.Type = obj.TYPE_CONST
718                                 q.From.Offset = 8
719                                 q.Reg = REGSP
720                                 q.To.Type = obj.TYPE_REG
721                                 q.To.Reg = REG_R4
722
723                                 // MOV R4, panic_argp(R1)
724                                 q = obj.Appendp(q, c.newprog)
725                                 q.As = AMOVD
726                                 q.From.Type = obj.TYPE_REG
727                                 q.From.Reg = REG_R4
728                                 q.To.Type = obj.TYPE_MEM
729                                 q.To.Reg = REG_R1
730                                 q.To.Offset = 0 // Panic.argp
731
732                                 // B end
733                                 q = obj.Appendp(q, c.newprog)
734                                 q.As = AB
735                                 q.To.Type = obj.TYPE_BRANCH
736                                 q.Pcond = end
737                         }
738
739                 case obj.ARET:
740                         nocache(p)
741                         if p.From.Type == obj.TYPE_CONST {
742                                 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
743                                 break
744                         }
745
746                         retjmp = p.To.Sym
747                         p.To = obj.Addr{}
748                         if c.cursym.Func.Text.Mark&LEAF != 0 {
749                                 if c.autosize != 0 {
750                                         p.As = AADD
751                                         p.From.Type = obj.TYPE_CONST
752                                         p.From.Offset = int64(c.autosize)
753                                         p.To.Type = obj.TYPE_REG
754                                         p.To.Reg = REGSP
755                                         p.Spadj = -c.autosize
756                                 }
757                         } else {
758                                 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
759                                 aoffset := c.autosize
760
761                                 if aoffset > 0xF0 {
762                                         aoffset = 0xF0
763                                 }
764                                 p.As = AMOVD
765                                 p.From.Type = obj.TYPE_MEM
766                                 p.Scond = C_XPOST
767                                 p.From.Offset = int64(aoffset)
768                                 p.From.Reg = REGSP
769                                 p.To.Type = obj.TYPE_REG
770                                 p.To.Reg = REGLINK
771                                 p.Spadj = -aoffset
772                                 if c.autosize > aoffset {
773                                         q = newprog()
774                                         q.As = AADD
775                                         q.From.Type = obj.TYPE_CONST
776                                         q.From.Offset = int64(c.autosize) - int64(aoffset)
777                                         q.To.Type = obj.TYPE_REG
778                                         q.To.Reg = REGSP
779                                         q.Link = p.Link
780                                         q.Spadj = int32(-q.From.Offset)
781                                         q.Pos = p.Pos
782                                         p.Link = q
783                                         p = q
784                                 }
785                         }
786
787                         if p.As != obj.ARET {
788                                 q = newprog()
789                                 q.Pos = p.Pos
790                                 q.Link = p.Link
791                                 p.Link = q
792                                 p = q
793                         }
794
795                         if retjmp != nil { // retjmp
796                                 p.As = AB
797                                 p.To.Type = obj.TYPE_BRANCH
798                                 p.To.Sym = retjmp
799                                 p.Spadj = +c.autosize
800                                 break
801                         }
802
803                         p.As = obj.ARET
804                         p.To.Type = obj.TYPE_MEM
805                         p.To.Offset = 0
806                         p.To.Reg = REGLINK
807                         p.Spadj = +c.autosize
808
809                 case AADD, ASUB:
810                         if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
811                                 if p.As == AADD {
812                                         p.Spadj = int32(-p.From.Offset)
813                                 } else {
814                                         p.Spadj = int32(+p.From.Offset)
815                                 }
816                         }
817                         break
818
819                 case obj.AGETCALLERPC:
820                         if cursym.Leaf() {
821                                 /* MOVD LR, Rd */
822                                 p.As = AMOVD
823                                 p.From.Type = obj.TYPE_REG
824                                 p.From.Reg = REGLINK
825                         } else {
826                                 /* MOVD (RSP), Rd */
827                                 p.As = AMOVD
828                                 p.From.Type = obj.TYPE_MEM
829                                 p.From.Reg = REGSP
830                         }
831                 }
832         }
833 }
834
835 func nocache(p *obj.Prog) {
836         p.Optab = 0
837         p.From.Class = 0
838         p.To.Class = 0
839 }
840
841 var unaryDst = map[obj.As]bool{
842         AWORD:  true,
843         ADWORD: true,
844         ABL:    true,
845         AB:     true,
846         ACLREX: true,
847 }
848
849 var Linkarm64 = obj.LinkArch{
850         Arch:           sys.ArchARM64,
851         Init:           buildop,
852         Preprocess:     preprocess,
853         Assemble:       span7,
854         Progedit:       progedit,
855         UnaryDst:       unaryDst,
856         DWARFRegisters: ARM64DWARFRegisters,
857 }