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