]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/internal/obj/arm64/obj7.go
[dev.boringcrypto] all: merge master into dev.boringcrypto
[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                         f64 := p.From.Val.(float64)
258                         f32 := float32(f64)
259                         if c.chipfloat7(f64) > 0 {
260                                 break
261                         }
262                         if math.Float32bits(f32) == 0 {
263                                 p.From.Type = obj.TYPE_REG
264                                 p.From.Reg = REGZERO
265                                 break
266                         }
267                         p.From.Type = obj.TYPE_MEM
268                         p.From.Sym = c.ctxt.Float32Sym(f32)
269                         p.From.Name = obj.NAME_EXTERN
270                         p.From.Offset = 0
271                 }
272
273         case AFMOVD:
274                 if p.From.Type == obj.TYPE_FCONST {
275                         f64 := p.From.Val.(float64)
276                         if c.chipfloat7(f64) > 0 {
277                                 break
278                         }
279                         if math.Float64bits(f64) == 0 {
280                                 p.From.Type = obj.TYPE_REG
281                                 p.From.Reg = REGZERO
282                                 break
283                         }
284                         p.From.Type = obj.TYPE_MEM
285                         p.From.Sym = c.ctxt.Float64Sym(f64)
286                         p.From.Name = obj.NAME_EXTERN
287                         p.From.Offset = 0
288                 }
289
290                 break
291         }
292
293         // Rewrite negative immediates as positive immediates with
294         // complementary instruction.
295         switch p.As {
296         case AADD, ASUB, ACMP, ACMN:
297                 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
298                         p.From.Offset = -p.From.Offset
299                         p.As = complements[p.As]
300                 }
301         case AADDW, ASUBW, ACMPW, ACMNW:
302                 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
303                         p.From.Offset = -p.From.Offset
304                         p.As = complements[p.As]
305                 }
306         }
307
308         // For 32-bit logical instruction with constant,
309         // rewrite the high 32-bit to be a repetition of
310         // the low 32-bit, so that the BITCON test can be
311         // shared for both 32-bit and 64-bit. 32-bit ops
312         // will zero the high 32-bit of the destination
313         // register anyway.
314         if isANDWop(p.As) && p.From.Type == obj.TYPE_CONST {
315                 v := p.From.Offset & 0xffffffff
316                 p.From.Offset = v | v<<32
317         }
318
319         if c.ctxt.Flag_dynlink {
320                 c.rewriteToUseGot(p)
321         }
322 }
323
324 // Rewrite p, if necessary, to access global data via the global offset table.
325 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
326         if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
327                 //     ADUFFxxx $offset
328                 // becomes
329                 //     MOVD runtime.duffxxx@GOT, REGTMP
330                 //     ADD $offset, REGTMP
331                 //     CALL REGTMP
332                 var sym *obj.LSym
333                 if p.As == obj.ADUFFZERO {
334                         sym = c.ctxt.Lookup("runtime.duffzero")
335                 } else {
336                         sym = c.ctxt.Lookup("runtime.duffcopy")
337                 }
338                 offset := p.To.Offset
339                 p.As = AMOVD
340                 p.From.Type = obj.TYPE_MEM
341                 p.From.Name = obj.NAME_GOTREF
342                 p.From.Sym = sym
343                 p.To.Type = obj.TYPE_REG
344                 p.To.Reg = REGTMP
345                 p.To.Name = obj.NAME_NONE
346                 p.To.Offset = 0
347                 p.To.Sym = nil
348                 p1 := obj.Appendp(p, c.newprog)
349                 p1.As = AADD
350                 p1.From.Type = obj.TYPE_CONST
351                 p1.From.Offset = offset
352                 p1.To.Type = obj.TYPE_REG
353                 p1.To.Reg = REGTMP
354                 p2 := obj.Appendp(p1, c.newprog)
355                 p2.As = obj.ACALL
356                 p2.To.Type = obj.TYPE_REG
357                 p2.To.Reg = REGTMP
358         }
359
360         // We only care about global data: NAME_EXTERN means a global
361         // symbol in the Go sense, and p.Sym.Local is true for a few
362         // internally defined symbols.
363         if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
364                 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
365                 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
366                 if p.As != AMOVD {
367                         c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
368                 }
369                 if p.To.Type != obj.TYPE_REG {
370                         c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
371                 }
372                 p.From.Type = obj.TYPE_MEM
373                 p.From.Name = obj.NAME_GOTREF
374                 if p.From.Offset != 0 {
375                         q := obj.Appendp(p, c.newprog)
376                         q.As = AADD
377                         q.From.Type = obj.TYPE_CONST
378                         q.From.Offset = p.From.Offset
379                         q.To = p.To
380                         p.From.Offset = 0
381                 }
382         }
383         if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
384                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
385         }
386         var source *obj.Addr
387         // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
388         // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
389         // An addition may be inserted between the two MOVs if there is an offset.
390         if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
391                 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
392                         c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
393                 }
394                 source = &p.From
395         } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
396                 source = &p.To
397         } else {
398                 return
399         }
400         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
401                 return
402         }
403         if source.Sym.Type == objabi.STLSBSS {
404                 return
405         }
406         if source.Type != obj.TYPE_MEM {
407                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
408         }
409         p1 := obj.Appendp(p, c.newprog)
410         p2 := obj.Appendp(p1, c.newprog)
411         p1.As = AMOVD
412         p1.From.Type = obj.TYPE_MEM
413         p1.From.Sym = source.Sym
414         p1.From.Name = obj.NAME_GOTREF
415         p1.To.Type = obj.TYPE_REG
416         p1.To.Reg = REGTMP
417
418         p2.As = p.As
419         p2.From = p.From
420         p2.To = p.To
421         if p.From.Name == obj.NAME_EXTERN {
422                 p2.From.Reg = REGTMP
423                 p2.From.Name = obj.NAME_NONE
424                 p2.From.Sym = nil
425         } else if p.To.Name == obj.NAME_EXTERN {
426                 p2.To.Reg = REGTMP
427                 p2.To.Name = obj.NAME_NONE
428                 p2.To.Sym = nil
429         } else {
430                 return
431         }
432         obj.Nopout(p)
433 }
434
435 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
436         if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
437                 return
438         }
439
440         c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
441
442         p := c.cursym.Func.Text
443         textstksiz := p.To.Offset
444         if textstksiz == -8 {
445                 // Historical way to mark NOFRAME.
446                 p.From.Sym.Set(obj.AttrNoFrame, true)
447                 textstksiz = 0
448         }
449         if textstksiz < 0 {
450                 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
451         }
452         if p.From.Sym.NoFrame() {
453                 if textstksiz != 0 {
454                         c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
455                 }
456         }
457
458         c.cursym.Func.Args = p.To.Val.(int32)
459         c.cursym.Func.Locals = int32(textstksiz)
460
461         /*
462          * find leaf subroutines
463          * strip NOPs
464          * expand RET
465          */
466         q := (*obj.Prog)(nil)
467         var q1 *obj.Prog
468         for p := c.cursym.Func.Text; p != nil; p = p.Link {
469                 switch p.As {
470                 case obj.ATEXT:
471                         p.Mark |= LEAF
472
473                 case obj.ARET:
474                         break
475
476                 case obj.ANOP:
477                         if p.Link != nil {
478                                 q1 = p.Link
479                                 q.Link = q1 /* q is non-nop */
480                                 q1.Mark |= p.Mark
481                         }
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 {
550                                 extrasize := int32(0)
551                                 if c.autosize%16 == 8 {
552                                         // Allocate extra 8 bytes on the frame top to save FP
553                                         extrasize = 8
554                                 } else if c.autosize&(16-1) == 0 {
555                                         // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
556                                         extrasize = 16
557                                 } else {
558                                         c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
559                                 }
560                                 c.autosize += extrasize
561                                 c.cursym.Func.Locals += extrasize
562
563                                 // low 32 bits for autosize
564                                 // high 32 bits for extrasize
565                                 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
566                         } else {
567                                 // NOFRAME
568                                 p.To.Offset = 0
569                         }
570
571                         if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
572                                 if c.ctxt.Debugvlog {
573                                         c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
574                                 }
575                                 c.cursym.Func.Text.Mark |= LEAF
576                         }
577
578                         if cursym.Func.Text.Mark&LEAF != 0 {
579                                 cursym.Set(obj.AttrLeaf, true)
580                                 if p.From.Sym.NoFrame() {
581                                         break
582                                 }
583                         }
584
585                         if !p.From.Sym.NoSplit() {
586                                 p = c.stacksplit(p, c.autosize) // emit split check
587                         }
588
589                         aoffset := c.autosize
590                         if aoffset > 0xF0 {
591                                 aoffset = 0xF0
592                         }
593
594                         // Frame is non-empty. Make sure to save link register, even if
595                         // it is a leaf function, so that traceback works.
596                         q = p
597                         if c.autosize > aoffset {
598                                 // Frame size is too large for a MOVD.W instruction.
599                                 // Store link register before decrementing SP, so if a signal comes
600                                 // during the execution of the function prologue, the traceback
601                                 // code will not see a half-updated stack frame.
602                                 q = obj.Appendp(q, c.newprog)
603                                 q.Pos = p.Pos
604                                 q.As = ASUB
605                                 q.From.Type = obj.TYPE_CONST
606                                 q.From.Offset = int64(c.autosize)
607                                 q.Reg = REGSP
608                                 q.To.Type = obj.TYPE_REG
609                                 q.To.Reg = REGTMP
610
611                                 q = obj.Appendp(q, c.newprog)
612                                 q.Pos = p.Pos
613                                 q.As = AMOVD
614                                 q.From.Type = obj.TYPE_REG
615                                 q.From.Reg = REGLINK
616                                 q.To.Type = obj.TYPE_MEM
617                                 q.To.Reg = REGTMP
618
619                                 q1 = obj.Appendp(q, c.newprog)
620                                 q1.Pos = p.Pos
621                                 q1.As = AMOVD
622                                 q1.From.Type = obj.TYPE_REG
623                                 q1.From.Reg = REGTMP
624                                 q1.To.Type = obj.TYPE_REG
625                                 q1.To.Reg = REGSP
626                                 q1.Spadj = c.autosize
627                         } else {
628                                 // small frame, update SP and save LR in a single MOVD.W instruction
629                                 q1 = obj.Appendp(q, c.newprog)
630                                 q1.As = AMOVD
631                                 q1.Pos = p.Pos
632                                 q1.From.Type = obj.TYPE_REG
633                                 q1.From.Reg = REGLINK
634                                 q1.To.Type = obj.TYPE_MEM
635                                 q1.Scond = C_XPRE
636                                 q1.To.Offset = int64(-aoffset)
637                                 q1.To.Reg = REGSP
638                                 q1.Spadj = aoffset
639                         }
640
641                         if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
642                                 q1 = obj.Appendp(q1, c.newprog)
643                                 q1.Pos = p.Pos
644                                 q1.As = AMOVD
645                                 q1.From.Type = obj.TYPE_REG
646                                 q1.From.Reg = REGFP
647                                 q1.To.Type = obj.TYPE_MEM
648                                 q1.To.Reg = REGSP
649                                 q1.To.Offset = -8
650
651                                 q1 = obj.Appendp(q1, c.newprog)
652                                 q1.Pos = p.Pos
653                                 q1.As = ASUB
654                                 q1.From.Type = obj.TYPE_CONST
655                                 q1.From.Offset = 8
656                                 q1.Reg = REGSP
657                                 q1.To.Type = obj.TYPE_REG
658                                 q1.To.Reg = REGFP
659                         }
660
661                         if c.cursym.Func.Text.From.Sym.Wrapper() {
662                                 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
663                                 //
664                                 //      MOV  g_panic(g), R1
665                                 //      CBNZ checkargp
666                                 // end:
667                                 //      NOP
668                                 // ... function body ...
669                                 // checkargp:
670                                 //      MOV  panic_argp(R1), R2
671                                 //      ADD  $(autosize+8), RSP, R3
672                                 //      CMP  R2, R3
673                                 //      BNE  end
674                                 //      ADD  $8, RSP, R4
675                                 //      MOVD R4, panic_argp(R1)
676                                 //      B    end
677                                 //
678                                 // The NOP is needed to give the jumps somewhere to land.
679                                 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
680                                 q = q1
681
682                                 // MOV g_panic(g), R1
683                                 q = obj.Appendp(q, c.newprog)
684                                 q.As = AMOVD
685                                 q.From.Type = obj.TYPE_MEM
686                                 q.From.Reg = REGG
687                                 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
688                                 q.To.Type = obj.TYPE_REG
689                                 q.To.Reg = REG_R1
690
691                                 // CBNZ R1, checkargp
692                                 cbnz := obj.Appendp(q, c.newprog)
693                                 cbnz.As = ACBNZ
694                                 cbnz.From.Type = obj.TYPE_REG
695                                 cbnz.From.Reg = REG_R1
696                                 cbnz.To.Type = obj.TYPE_BRANCH
697
698                                 // Empty branch target at the top of the function body
699                                 end := obj.Appendp(cbnz, c.newprog)
700                                 end.As = obj.ANOP
701
702                                 // find the end of the function
703                                 var last *obj.Prog
704                                 for last = end; last.Link != nil; last = last.Link {
705                                 }
706
707                                 // MOV panic_argp(R1), R2
708                                 mov := obj.Appendp(last, c.newprog)
709                                 mov.As = AMOVD
710                                 mov.From.Type = obj.TYPE_MEM
711                                 mov.From.Reg = REG_R1
712                                 mov.From.Offset = 0 // Panic.argp
713                                 mov.To.Type = obj.TYPE_REG
714                                 mov.To.Reg = REG_R2
715
716                                 // CBNZ branches to the MOV above
717                                 cbnz.Pcond = mov
718
719                                 // ADD $(autosize+8), SP, R3
720                                 q = obj.Appendp(mov, c.newprog)
721                                 q.As = AADD
722                                 q.From.Type = obj.TYPE_CONST
723                                 q.From.Offset = int64(c.autosize) + 8
724                                 q.Reg = REGSP
725                                 q.To.Type = obj.TYPE_REG
726                                 q.To.Reg = REG_R3
727
728                                 // CMP R2, R3
729                                 q = obj.Appendp(q, c.newprog)
730                                 q.As = ACMP
731                                 q.From.Type = obj.TYPE_REG
732                                 q.From.Reg = REG_R2
733                                 q.Reg = REG_R3
734
735                                 // BNE end
736                                 q = obj.Appendp(q, c.newprog)
737                                 q.As = ABNE
738                                 q.To.Type = obj.TYPE_BRANCH
739                                 q.Pcond = end
740
741                                 // ADD $8, SP, R4
742                                 q = obj.Appendp(q, c.newprog)
743                                 q.As = AADD
744                                 q.From.Type = obj.TYPE_CONST
745                                 q.From.Offset = 8
746                                 q.Reg = REGSP
747                                 q.To.Type = obj.TYPE_REG
748                                 q.To.Reg = REG_R4
749
750                                 // MOV R4, panic_argp(R1)
751                                 q = obj.Appendp(q, c.newprog)
752                                 q.As = AMOVD
753                                 q.From.Type = obj.TYPE_REG
754                                 q.From.Reg = REG_R4
755                                 q.To.Type = obj.TYPE_MEM
756                                 q.To.Reg = REG_R1
757                                 q.To.Offset = 0 // Panic.argp
758
759                                 // B end
760                                 q = obj.Appendp(q, c.newprog)
761                                 q.As = AB
762                                 q.To.Type = obj.TYPE_BRANCH
763                                 q.Pcond = end
764                         }
765
766                 case obj.ARET:
767                         nocache(p)
768                         if p.From.Type == obj.TYPE_CONST {
769                                 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
770                                 break
771                         }
772
773                         retjmp = p.To.Sym
774                         p.To = obj.Addr{}
775                         if c.cursym.Func.Text.Mark&LEAF != 0 {
776                                 if c.autosize != 0 {
777                                         p.As = AADD
778                                         p.From.Type = obj.TYPE_CONST
779                                         p.From.Offset = int64(c.autosize)
780                                         p.To.Type = obj.TYPE_REG
781                                         p.To.Reg = REGSP
782                                         p.Spadj = -c.autosize
783
784                                         if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
785                                                 p = obj.Appendp(p, c.newprog)
786                                                 p.As = ASUB
787                                                 p.From.Type = obj.TYPE_CONST
788                                                 p.From.Offset = 8
789                                                 p.Reg = REGSP
790                                                 p.To.Type = obj.TYPE_REG
791                                                 p.To.Reg = REGFP
792                                         }
793                                 }
794                         } else {
795                                 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
796
797                                 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
798                                         p.As = AMOVD
799                                         p.From.Type = obj.TYPE_MEM
800                                         p.From.Reg = REGSP
801                                         p.From.Offset = -8
802                                         p.To.Type = obj.TYPE_REG
803                                         p.To.Reg = REGFP
804                                         p = obj.Appendp(p, c.newprog)
805                                 }
806
807                                 aoffset := c.autosize
808
809                                 if aoffset > 0xF0 {
810                                         aoffset = 0xF0
811                                 }
812                                 p.As = AMOVD
813                                 p.From.Type = obj.TYPE_MEM
814                                 p.Scond = C_XPOST
815                                 p.From.Offset = int64(aoffset)
816                                 p.From.Reg = REGSP
817                                 p.To.Type = obj.TYPE_REG
818                                 p.To.Reg = REGLINK
819                                 p.Spadj = -aoffset
820                                 if c.autosize > aoffset {
821                                         q = newprog()
822                                         q.As = AADD
823                                         q.From.Type = obj.TYPE_CONST
824                                         q.From.Offset = int64(c.autosize) - int64(aoffset)
825                                         q.To.Type = obj.TYPE_REG
826                                         q.To.Reg = REGSP
827                                         q.Link = p.Link
828                                         q.Spadj = int32(-q.From.Offset)
829                                         q.Pos = p.Pos
830                                         p.Link = q
831                                         p = q
832                                 }
833                         }
834
835                         if p.As != obj.ARET {
836                                 q = newprog()
837                                 q.Pos = p.Pos
838                                 q.Link = p.Link
839                                 p.Link = q
840                                 p = q
841                         }
842
843                         if retjmp != nil { // retjmp
844                                 p.As = AB
845                                 p.To.Type = obj.TYPE_BRANCH
846                                 p.To.Sym = retjmp
847                                 p.Spadj = +c.autosize
848                                 break
849                         }
850
851                         p.As = obj.ARET
852                         p.To.Type = obj.TYPE_MEM
853                         p.To.Offset = 0
854                         p.To.Reg = REGLINK
855                         p.Spadj = +c.autosize
856
857                 case AADD, ASUB:
858                         if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
859                                 if p.As == AADD {
860                                         p.Spadj = int32(-p.From.Offset)
861                                 } else {
862                                         p.Spadj = int32(+p.From.Offset)
863                                 }
864                         }
865
866                 case obj.AGETCALLERPC:
867                         if cursym.Leaf() {
868                                 /* MOVD LR, Rd */
869                                 p.As = AMOVD
870                                 p.From.Type = obj.TYPE_REG
871                                 p.From.Reg = REGLINK
872                         } else {
873                                 /* MOVD (RSP), Rd */
874                                 p.As = AMOVD
875                                 p.From.Type = obj.TYPE_MEM
876                                 p.From.Reg = REGSP
877                         }
878
879                 case obj.ADUFFCOPY:
880                         if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
881                                 //  ADR ret_addr, R27
882                                 //  STP (FP, R27), -24(SP)
883                                 //  SUB 24, SP, FP
884                                 //  DUFFCOPY
885                                 // ret_addr:
886                                 //  SUB 8, SP, FP
887
888                                 q1 := p
889                                 // copy DUFFCOPY from q1 to q4
890                                 q4 := obj.Appendp(p, c.newprog)
891                                 q4.Pos = p.Pos
892                                 q4.As = obj.ADUFFCOPY
893                                 q4.To = p.To
894
895                                 q1.As = AADR
896                                 q1.From.Type = obj.TYPE_BRANCH
897                                 q1.To.Type = obj.TYPE_REG
898                                 q1.To.Reg = REG_R27
899
900                                 q2 := obj.Appendp(q1, c.newprog)
901                                 q2.Pos = p.Pos
902                                 q2.As = ASTP
903                                 q2.From.Type = obj.TYPE_REGREG
904                                 q2.From.Reg = REGFP
905                                 q2.From.Offset = int64(REG_R27)
906                                 q2.To.Type = obj.TYPE_MEM
907                                 q2.To.Reg = REGSP
908                                 q2.To.Offset = -24
909
910                                 // maintaine FP for DUFFCOPY
911                                 q3 := obj.Appendp(q2, c.newprog)
912                                 q3.Pos = p.Pos
913                                 q3.As = ASUB
914                                 q3.From.Type = obj.TYPE_CONST
915                                 q3.From.Offset = 24
916                                 q3.Reg = REGSP
917                                 q3.To.Type = obj.TYPE_REG
918                                 q3.To.Reg = REGFP
919
920                                 q5 := obj.Appendp(q4, c.newprog)
921                                 q5.Pos = p.Pos
922                                 q5.As = ASUB
923                                 q5.From.Type = obj.TYPE_CONST
924                                 q5.From.Offset = 8
925                                 q5.Reg = REGSP
926                                 q5.To.Type = obj.TYPE_REG
927                                 q5.To.Reg = REGFP
928                                 q1.Pcond = q5
929                                 p = q5
930                         }
931
932                 case obj.ADUFFZERO:
933                         if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
934                                 //  ADR ret_addr, R27
935                                 //  STP (FP, R27), -24(SP)
936                                 //  SUB 24, SP, FP
937                                 //  DUFFZERO
938                                 // ret_addr:
939                                 //  SUB 8, SP, FP
940
941                                 q1 := p
942                                 // copy DUFFZERO from q1 to q4
943                                 q4 := obj.Appendp(p, c.newprog)
944                                 q4.Pos = p.Pos
945                                 q4.As = obj.ADUFFZERO
946                                 q4.To = p.To
947
948                                 q1.As = AADR
949                                 q1.From.Type = obj.TYPE_BRANCH
950                                 q1.To.Type = obj.TYPE_REG
951                                 q1.To.Reg = REG_R27
952
953                                 q2 := obj.Appendp(q1, c.newprog)
954                                 q2.Pos = p.Pos
955                                 q2.As = ASTP
956                                 q2.From.Type = obj.TYPE_REGREG
957                                 q2.From.Reg = REGFP
958                                 q2.From.Offset = int64(REG_R27)
959                                 q2.To.Type = obj.TYPE_MEM
960                                 q2.To.Reg = REGSP
961                                 q2.To.Offset = -24
962
963                                 // maintaine FP for DUFFZERO
964                                 q3 := obj.Appendp(q2, c.newprog)
965                                 q3.Pos = p.Pos
966                                 q3.As = ASUB
967                                 q3.From.Type = obj.TYPE_CONST
968                                 q3.From.Offset = 24
969                                 q3.Reg = REGSP
970                                 q3.To.Type = obj.TYPE_REG
971                                 q3.To.Reg = REGFP
972
973                                 q5 := obj.Appendp(q4, c.newprog)
974                                 q5.Pos = p.Pos
975                                 q5.As = ASUB
976                                 q5.From.Type = obj.TYPE_CONST
977                                 q5.From.Offset = 8
978                                 q5.Reg = REGSP
979                                 q5.To.Type = obj.TYPE_REG
980                                 q5.To.Reg = REGFP
981                                 q1.Pcond = q5
982                                 p = q5
983                         }
984                 }
985         }
986 }
987
988 func nocache(p *obj.Prog) {
989         p.Optab = 0
990         p.From.Class = 0
991         p.To.Class = 0
992 }
993
994 var unaryDst = map[obj.As]bool{
995         AWORD:  true,
996         ADWORD: true,
997         ABL:    true,
998         AB:     true,
999         ACLREX: true,
1000 }
1001
1002 var Linkarm64 = obj.LinkArch{
1003         Arch:           sys.ArchARM64,
1004         Init:           buildop,
1005         Preprocess:     preprocess,
1006         Assemble:       span7,
1007         Progedit:       progedit,
1008         UnaryDst:       unaryDst,
1009         DWARFRegisters: ARM64DWARFRegisters,
1010 }