]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/internal/obj/arm64/obj7.go
cmd/internal/obj/arm64: save LR and SP in one instruction for small frames
[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/src"
37         "cmd/internal/sys"
38         "internal/buildcfg"
39         "log"
40         "math"
41 )
42
43 var complements = []obj.As{
44         AADD:  ASUB,
45         AADDW: ASUBW,
46         ASUB:  AADD,
47         ASUBW: AADDW,
48         ACMP:  ACMN,
49         ACMPW: ACMNW,
50         ACMN:  ACMP,
51         ACMNW: ACMPW,
52 }
53
54 // noZRreplace is the set of instructions for which $0 in the To operand
55 // should NOT be replaced with REGZERO.
56 var noZRreplace = map[obj.As]bool{
57         APRFM: true,
58 }
59
60 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
61         if c.ctxt.Flag_maymorestack != "" {
62                 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
63
64                 // Save LR and make room for FP, REGCTXT. Leave room
65                 // for caller's saved FP.
66                 const frameSize = 32
67                 p = obj.Appendp(p, c.newprog)
68                 p.As = AMOVD
69                 p.From.Type = obj.TYPE_REG
70                 p.From.Reg = REGLINK
71                 p.To.Type = obj.TYPE_MEM
72                 p.Scond = C_XPRE
73                 p.To.Offset = -frameSize
74                 p.To.Reg = REGSP
75                 p.Spadj = frameSize
76
77                 // Save FP.
78                 p = obj.Appendp(p, c.newprog)
79                 p.As = AMOVD
80                 p.From.Type = obj.TYPE_REG
81                 p.From.Reg = REGFP
82                 p.To.Type = obj.TYPE_MEM
83                 p.To.Reg = REGSP
84                 p.To.Offset = -8
85
86                 p = obj.Appendp(p, c.newprog)
87                 p.As = ASUB
88                 p.From.Type = obj.TYPE_CONST
89                 p.From.Offset = 8
90                 p.Reg = REGSP
91                 p.To.Type = obj.TYPE_REG
92                 p.To.Reg = REGFP
93
94                 // Save REGCTXT (for simplicity we do this whether or
95                 // not we need it.)
96                 p = obj.Appendp(p, c.newprog)
97                 p.As = AMOVD
98                 p.From.Type = obj.TYPE_REG
99                 p.From.Reg = REGCTXT
100                 p.To.Type = obj.TYPE_MEM
101                 p.To.Reg = REGSP
102                 p.To.Offset = 8
103
104                 // BL maymorestack
105                 p = obj.Appendp(p, c.newprog)
106                 p.As = ABL
107                 p.To.Type = obj.TYPE_BRANCH
108                 // See ../x86/obj6.go
109                 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
110
111                 // Restore REGCTXT.
112                 p = obj.Appendp(p, c.newprog)
113                 p.As = AMOVD
114                 p.From.Type = obj.TYPE_MEM
115                 p.From.Reg = REGSP
116                 p.From.Offset = 8
117                 p.To.Type = obj.TYPE_REG
118                 p.To.Reg = REGCTXT
119
120                 // Restore FP.
121                 p = obj.Appendp(p, c.newprog)
122                 p.As = AMOVD
123                 p.From.Type = obj.TYPE_MEM
124                 p.From.Reg = REGSP
125                 p.From.Offset = -8
126                 p.To.Type = obj.TYPE_REG
127                 p.To.Reg = REGFP
128
129                 // Restore LR and SP.
130                 p = obj.Appendp(p, c.newprog)
131                 p.As = AMOVD
132                 p.From.Type = obj.TYPE_MEM
133                 p.Scond = C_XPOST
134                 p.From.Offset = frameSize
135                 p.From.Reg = REGSP
136                 p.To.Type = obj.TYPE_REG
137                 p.To.Reg = REGLINK
138                 p.Spadj = -frameSize
139
140                 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
141         }
142
143         // Jump back to here after morestack returns.
144         startPred := p
145
146         // MOV  g_stackguard(g), RT1
147         p = obj.Appendp(p, c.newprog)
148
149         p.As = AMOVD
150         p.From.Type = obj.TYPE_MEM
151         p.From.Reg = REGG
152         p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
153         if c.cursym.CFunc() {
154                 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
155         }
156         p.To.Type = obj.TYPE_REG
157         p.To.Reg = REGRT1
158
159         // Mark the stack bound check and morestack call async nonpreemptible.
160         // If we get preempted here, when resumed the preemption request is
161         // cleared, but we'll still call morestack, which will double the stack
162         // unnecessarily. See issue #35470.
163         p = c.ctxt.StartUnsafePoint(p, c.newprog)
164
165         q := (*obj.Prog)(nil)
166         if framesize <= objabi.StackSmall {
167                 // small stack: SP < stackguard
168                 //      CMP     stackguard, SP
169
170                 p = obj.Appendp(p, c.newprog)
171                 p.As = ACMP
172                 p.From.Type = obj.TYPE_REG
173                 p.From.Reg = REGRT1
174                 p.Reg = REGSP
175         } else if framesize <= objabi.StackBig {
176                 // large stack: SP-framesize < stackguard-StackSmall
177                 //      SUB     $(framesize-StackSmall), SP, RT2
178                 //      CMP     stackguard, RT2
179                 p = obj.Appendp(p, c.newprog)
180
181                 p.As = ASUB
182                 p.From.Type = obj.TYPE_CONST
183                 p.From.Offset = int64(framesize) - objabi.StackSmall
184                 p.Reg = REGSP
185                 p.To.Type = obj.TYPE_REG
186                 p.To.Reg = REGRT2
187
188                 p = obj.Appendp(p, c.newprog)
189                 p.As = ACMP
190                 p.From.Type = obj.TYPE_REG
191                 p.From.Reg = REGRT1
192                 p.Reg = REGRT2
193         } else {
194                 // Such a large stack we need to protect against underflow.
195                 // The runtime guarantees SP > objabi.StackBig, but
196                 // framesize is large enough that SP-framesize may
197                 // underflow, causing a direct comparison with the
198                 // stack guard to incorrectly succeed. We explicitly
199                 // guard against underflow.
200                 //
201                 //      SUBS    $(framesize-StackSmall), SP, RT2
202                 //      // On underflow, jump to morestack
203                 //      BLO     label_of_call_to_morestack
204                 //      CMP     stackguard, RT2
205
206                 p = obj.Appendp(p, c.newprog)
207                 p.As = ASUBS
208                 p.From.Type = obj.TYPE_CONST
209                 p.From.Offset = int64(framesize) - objabi.StackSmall
210                 p.Reg = REGSP
211                 p.To.Type = obj.TYPE_REG
212                 p.To.Reg = REGRT2
213
214                 p = obj.Appendp(p, c.newprog)
215                 q = p
216                 p.As = ABLO
217                 p.To.Type = obj.TYPE_BRANCH
218
219                 p = obj.Appendp(p, c.newprog)
220                 p.As = ACMP
221                 p.From.Type = obj.TYPE_REG
222                 p.From.Reg = REGRT1
223                 p.Reg = REGRT2
224         }
225
226         // BLS  do-morestack
227         bls := obj.Appendp(p, c.newprog)
228         bls.As = ABLS
229         bls.To.Type = obj.TYPE_BRANCH
230
231         end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
232
233         var last *obj.Prog
234         for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
235         }
236
237         // Now we are at the end of the function, but logically
238         // we are still in function prologue. We need to fix the
239         // SP data and PCDATA.
240         spfix := obj.Appendp(last, c.newprog)
241         spfix.As = obj.ANOP
242         spfix.Spadj = -framesize
243
244         pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
245         pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
246
247         if q != nil {
248                 q.To.SetTarget(pcdata)
249         }
250         bls.To.SetTarget(pcdata)
251
252         spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
253
254         // MOV  LR, R3
255         movlr := obj.Appendp(spill, c.newprog)
256         movlr.As = AMOVD
257         movlr.From.Type = obj.TYPE_REG
258         movlr.From.Reg = REGLINK
259         movlr.To.Type = obj.TYPE_REG
260         movlr.To.Reg = REG_R3
261
262         debug := movlr
263         if false {
264                 debug = obj.Appendp(debug, c.newprog)
265                 debug.As = AMOVD
266                 debug.From.Type = obj.TYPE_CONST
267                 debug.From.Offset = int64(framesize)
268                 debug.To.Type = obj.TYPE_REG
269                 debug.To.Reg = REGTMP
270         }
271
272         // BL   runtime.morestack(SB)
273         call := obj.Appendp(debug, c.newprog)
274         call.As = ABL
275         call.To.Type = obj.TYPE_BRANCH
276         morestack := "runtime.morestack"
277         switch {
278         case c.cursym.CFunc():
279                 morestack = "runtime.morestackc"
280         case !c.cursym.Func().Text.From.Sym.NeedCtxt():
281                 morestack = "runtime.morestack_noctxt"
282         }
283         call.To.Sym = c.ctxt.Lookup(morestack)
284
285         unspill := c.cursym.Func().UnspillRegisterArgs(call, c.newprog)
286         pcdata = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1)
287
288         // B    start
289         jmp := obj.Appendp(pcdata, c.newprog)
290         jmp.As = AB
291         jmp.To.Type = obj.TYPE_BRANCH
292         jmp.To.SetTarget(startPred.Link)
293         jmp.Spadj = +framesize
294
295         return end
296 }
297
298 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
299         c := ctxt7{ctxt: ctxt, newprog: newprog}
300
301         p.From.Class = 0
302         p.To.Class = 0
303
304         // $0 results in C_ZCON, which matches both C_REG and various
305         // C_xCON, however the C_REG cases in asmout don't expect a
306         // constant, so they will use the register fields and assemble
307         // a R0. To prevent that, rewrite $0 as ZR.
308         if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
309                 p.From.Type = obj.TYPE_REG
310                 p.From.Reg = REGZERO
311         }
312         if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 && !noZRreplace[p.As] {
313                 p.To.Type = obj.TYPE_REG
314                 p.To.Reg = REGZERO
315         }
316
317         // Rewrite BR/BL to symbol as TYPE_BRANCH.
318         switch p.As {
319         case AB,
320                 ABL,
321                 obj.ARET,
322                 obj.ADUFFZERO,
323                 obj.ADUFFCOPY:
324                 if p.To.Sym != nil {
325                         p.To.Type = obj.TYPE_BRANCH
326                 }
327                 break
328         }
329
330         // Rewrite float constants to values stored in memory.
331         switch p.As {
332         case AFMOVS:
333                 if p.From.Type == obj.TYPE_FCONST {
334                         f64 := p.From.Val.(float64)
335                         f32 := float32(f64)
336                         if c.chipfloat7(f64) > 0 {
337                                 break
338                         }
339                         if math.Float32bits(f32) == 0 {
340                                 p.From.Type = obj.TYPE_REG
341                                 p.From.Reg = REGZERO
342                                 break
343                         }
344                         p.From.Type = obj.TYPE_MEM
345                         p.From.Sym = c.ctxt.Float32Sym(f32)
346                         p.From.Name = obj.NAME_EXTERN
347                         p.From.Offset = 0
348                 }
349
350         case AFMOVD:
351                 if p.From.Type == obj.TYPE_FCONST {
352                         f64 := p.From.Val.(float64)
353                         if c.chipfloat7(f64) > 0 {
354                                 break
355                         }
356                         if math.Float64bits(f64) == 0 {
357                                 p.From.Type = obj.TYPE_REG
358                                 p.From.Reg = REGZERO
359                                 break
360                         }
361                         p.From.Type = obj.TYPE_MEM
362                         p.From.Sym = c.ctxt.Float64Sym(f64)
363                         p.From.Name = obj.NAME_EXTERN
364                         p.From.Offset = 0
365                 }
366
367                 break
368         }
369
370         // Rewrite negative immediates as positive immediates with
371         // complementary instruction.
372         switch p.As {
373         case AADD, ASUB, ACMP, ACMN:
374                 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
375                         p.From.Offset = -p.From.Offset
376                         p.As = complements[p.As]
377                 }
378         case AADDW, ASUBW, ACMPW, ACMNW:
379                 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
380                         p.From.Offset = -p.From.Offset
381                         p.As = complements[p.As]
382                 }
383         }
384
385         if c.ctxt.Flag_dynlink {
386                 c.rewriteToUseGot(p)
387         }
388 }
389
390 // Rewrite p, if necessary, to access global data via the global offset table.
391 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
392         if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
393                 //     ADUFFxxx $offset
394                 // becomes
395                 //     MOVD runtime.duffxxx@GOT, REGTMP
396                 //     ADD $offset, REGTMP
397                 //     CALL REGTMP
398                 var sym *obj.LSym
399                 if p.As == obj.ADUFFZERO {
400                         sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
401                 } else {
402                         sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
403                 }
404                 offset := p.To.Offset
405                 p.As = AMOVD
406                 p.From.Type = obj.TYPE_MEM
407                 p.From.Name = obj.NAME_GOTREF
408                 p.From.Sym = sym
409                 p.To.Type = obj.TYPE_REG
410                 p.To.Reg = REGTMP
411                 p.To.Name = obj.NAME_NONE
412                 p.To.Offset = 0
413                 p.To.Sym = nil
414                 p1 := obj.Appendp(p, c.newprog)
415                 p1.As = AADD
416                 p1.From.Type = obj.TYPE_CONST
417                 p1.From.Offset = offset
418                 p1.To.Type = obj.TYPE_REG
419                 p1.To.Reg = REGTMP
420                 p2 := obj.Appendp(p1, c.newprog)
421                 p2.As = obj.ACALL
422                 p2.To.Type = obj.TYPE_REG
423                 p2.To.Reg = REGTMP
424         }
425
426         // We only care about global data: NAME_EXTERN means a global
427         // symbol in the Go sense, and p.Sym.Local is true for a few
428         // internally defined symbols.
429         if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
430                 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
431                 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
432                 if p.As != AMOVD {
433                         c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
434                 }
435                 if p.To.Type != obj.TYPE_REG {
436                         c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
437                 }
438                 p.From.Type = obj.TYPE_MEM
439                 p.From.Name = obj.NAME_GOTREF
440                 if p.From.Offset != 0 {
441                         q := obj.Appendp(p, c.newprog)
442                         q.As = AADD
443                         q.From.Type = obj.TYPE_CONST
444                         q.From.Offset = p.From.Offset
445                         q.To = p.To
446                         p.From.Offset = 0
447                 }
448         }
449         if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
450                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
451         }
452         var source *obj.Addr
453         // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
454         // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
455         // An addition may be inserted between the two MOVs if there is an offset.
456         if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
457                 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
458                         c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
459                 }
460                 source = &p.From
461         } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
462                 source = &p.To
463         } else {
464                 return
465         }
466         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
467                 return
468         }
469         if source.Sym.Type == objabi.STLSBSS {
470                 return
471         }
472         if source.Type != obj.TYPE_MEM {
473                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
474         }
475         p1 := obj.Appendp(p, c.newprog)
476         p2 := obj.Appendp(p1, c.newprog)
477         p1.As = AMOVD
478         p1.From.Type = obj.TYPE_MEM
479         p1.From.Sym = source.Sym
480         p1.From.Name = obj.NAME_GOTREF
481         p1.To.Type = obj.TYPE_REG
482         p1.To.Reg = REGTMP
483
484         p2.As = p.As
485         p2.From = p.From
486         p2.To = p.To
487         if p.From.Name == obj.NAME_EXTERN {
488                 p2.From.Reg = REGTMP
489                 p2.From.Name = obj.NAME_NONE
490                 p2.From.Sym = nil
491         } else if p.To.Name == obj.NAME_EXTERN {
492                 p2.To.Reg = REGTMP
493                 p2.To.Name = obj.NAME_NONE
494                 p2.To.Sym = nil
495         } else {
496                 return
497         }
498         obj.Nopout(p)
499 }
500
501 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
502         if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
503                 return
504         }
505
506         c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
507
508         p := c.cursym.Func().Text
509         textstksiz := p.To.Offset
510         if textstksiz == -8 {
511                 // Historical way to mark NOFRAME.
512                 p.From.Sym.Set(obj.AttrNoFrame, true)
513                 textstksiz = 0
514         }
515         if textstksiz < 0 {
516                 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
517         }
518         if p.From.Sym.NoFrame() {
519                 if textstksiz != 0 {
520                         c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
521                 }
522         }
523
524         c.cursym.Func().Args = p.To.Val.(int32)
525         c.cursym.Func().Locals = int32(textstksiz)
526
527         /*
528          * find leaf subroutines
529          */
530         for p := c.cursym.Func().Text; p != nil; p = p.Link {
531                 switch p.As {
532                 case obj.ATEXT:
533                         p.Mark |= LEAF
534
535                 case ABL,
536                         obj.ADUFFZERO,
537                         obj.ADUFFCOPY:
538                         c.cursym.Func().Text.Mark &^= LEAF
539                 }
540         }
541
542         var q *obj.Prog
543         var q1 *obj.Prog
544         var retjmp *obj.LSym
545         for p := c.cursym.Func().Text; p != nil; p = p.Link {
546                 o := p.As
547                 switch o {
548                 case obj.ATEXT:
549                         c.cursym.Func().Text = p
550                         c.autosize = int32(textstksiz)
551
552                         if p.Mark&LEAF != 0 && c.autosize == 0 {
553                                 // A leaf function with no locals has no frame.
554                                 p.From.Sym.Set(obj.AttrNoFrame, true)
555                         }
556
557                         if !p.From.Sym.NoFrame() {
558                                 // If there is a stack frame at all, it includes
559                                 // space to save the LR.
560                                 c.autosize += 8
561                         }
562
563                         if c.autosize != 0 {
564                                 extrasize := int32(0)
565                                 if c.autosize%16 == 8 {
566                                         // Allocate extra 8 bytes on the frame top to save FP
567                                         extrasize = 8
568                                 } else if c.autosize&(16-1) == 0 {
569                                         // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
570                                         extrasize = 16
571                                 } else {
572                                         c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
573                                 }
574                                 c.autosize += extrasize
575                                 c.cursym.Func().Locals += extrasize
576
577                                 // low 32 bits for autosize
578                                 // high 32 bits for extrasize
579                                 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
580                         } else {
581                                 // NOFRAME
582                                 p.To.Offset = 0
583                         }
584
585                         if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
586                                 if c.ctxt.Debugvlog {
587                                         c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
588                                 }
589                                 c.cursym.Func().Text.Mark |= LEAF
590                         }
591
592                         if cursym.Func().Text.Mark&LEAF != 0 {
593                                 cursym.Set(obj.AttrLeaf, true)
594                                 if p.From.Sym.NoFrame() {
595                                         break
596                                 }
597                         }
598
599                         if p.Mark&LEAF != 0 && c.autosize < objabi.StackSmall {
600                                 // A leaf function with a small stack can be marked
601                                 // NOSPLIT, avoiding a stack check.
602                                 p.From.Sym.Set(obj.AttrNoSplit, true)
603                         }
604
605                         if !p.From.Sym.NoSplit() {
606                                 p = c.stacksplit(p, c.autosize) // emit split check
607                         }
608
609                         var prologueEnd *obj.Prog
610
611                         aoffset := c.autosize
612                         if aoffset > 0xf0 {
613                                 // MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned.
614                                 // so the maximum aoffset value is 0xf0.
615                                 aoffset = 0xf0
616                         }
617
618                         // Frame is non-empty. Make sure to save link register, even if
619                         // it is a leaf function, so that traceback works.
620                         q = p
621                         if c.autosize > aoffset {
622                                 // Frame size is too large for a MOVD.W instruction. Store the frame pointer
623                                 // register and link register before decrementing SP, so if a signal comes
624                                 // during the execution of the function prologue, the traceback code will
625                                 // not see a half-updated stack frame.
626
627                                 // SUB $autosize, RSP, R20
628                                 q1 = obj.Appendp(q, c.newprog)
629                                 q1.Pos = p.Pos
630                                 q1.As = ASUB
631                                 q1.From.Type = obj.TYPE_CONST
632                                 q1.From.Offset = int64(c.autosize)
633                                 q1.Reg = REGSP
634                                 q1.To.Type = obj.TYPE_REG
635                                 q1.To.Reg = REG_R20
636
637                                 prologueEnd = q1
638
639                                 // STP (R29, R30), -8(R20)
640                                 q1 = obj.Appendp(q1, c.newprog)
641                                 q1.Pos = p.Pos
642                                 q1.As = ASTP
643                                 q1.From.Type = obj.TYPE_REGREG
644                                 q1.From.Reg = REGFP
645                                 q1.From.Offset = REGLINK
646                                 q1.To.Type = obj.TYPE_MEM
647                                 q1.To.Reg = REG_R20
648                                 q1.To.Offset = -8
649
650                                 // This is not async preemptible, as if we open a frame
651                                 // at the current SP, it will clobber the saved LR.
652                                 q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
653
654                                 // MOVD R20, RSP
655                                 q1 = obj.Appendp(q1, c.newprog)
656                                 q1.Pos = p.Pos
657                                 q1.As = AMOVD
658                                 q1.From.Type = obj.TYPE_REG
659                                 q1.From.Reg = REG_R20
660                                 q1.To.Type = obj.TYPE_REG
661                                 q1.To.Reg = REGSP
662                                 q1.Spadj = c.autosize
663
664                                 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
665
666                                 if buildcfg.GOOS == "ios" {
667                                         // iOS does not support SA_ONSTACK. We will run the signal handler
668                                         // on the G stack. If we write below SP, it may be clobbered by
669                                         // the signal handler. So we save FP and LR after decrementing SP.
670                                         // STP (R29, R30), -8(RSP)
671                                         q1 = obj.Appendp(q1, c.newprog)
672                                         q1.Pos = p.Pos
673                                         q1.As = ASTP
674                                         q1.From.Type = obj.TYPE_REGREG
675                                         q1.From.Reg = REGFP
676                                         q1.From.Offset = REGLINK
677                                         q1.To.Type = obj.TYPE_MEM
678                                         q1.To.Reg = REGSP
679                                         q1.To.Offset = -8
680                                 }
681                         } else {
682                                 // small frame, update SP and save LR in a single MOVD.W instruction.
683                                 // So if a signal comes during the execution of the function prologue,
684                                 // the traceback code will not see a half-updated stack frame.
685                                 // Also, on Linux, in a cgo binary we may get a SIGSETXID signal
686                                 // early on before the signal stack is set, as glibc doesn't allow
687                                 // us to block SIGSETXID. So it is important that we don't write below
688                                 // the SP until the signal stack is set.
689                                 // Luckily, all the functions from thread entry to setting the signal
690                                 // stack have small frames.
691                                 q1 = obj.Appendp(q, c.newprog)
692                                 q1.As = AMOVD
693                                 q1.Pos = p.Pos
694                                 q1.From.Type = obj.TYPE_REG
695                                 q1.From.Reg = REGLINK
696                                 q1.To.Type = obj.TYPE_MEM
697                                 q1.Scond = C_XPRE
698                                 q1.To.Offset = int64(-aoffset)
699                                 q1.To.Reg = REGSP
700                                 q1.Spadj = aoffset
701
702                                 prologueEnd = q1
703
704                                 // Frame pointer.
705                                 q1 = obj.Appendp(q1, c.newprog)
706                                 q1.Pos = p.Pos
707                                 q1.As = AMOVD
708                                 q1.From.Type = obj.TYPE_REG
709                                 q1.From.Reg = REGFP
710                                 q1.To.Type = obj.TYPE_MEM
711                                 q1.To.Reg = REGSP
712                                 q1.To.Offset = -8
713                         }
714
715                         prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
716
717                         q1 = obj.Appendp(q1, c.newprog)
718                         q1.Pos = p.Pos
719                         q1.As = ASUB
720                         q1.From.Type = obj.TYPE_CONST
721                         q1.From.Offset = 8
722                         q1.Reg = REGSP
723                         q1.To.Type = obj.TYPE_REG
724                         q1.To.Reg = REGFP
725
726                         if c.cursym.Func().Text.From.Sym.Wrapper() {
727                                 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
728                                 //
729                                 //      MOV  g_panic(g), RT1
730                                 //      CBNZ checkargp
731                                 // end:
732                                 //      NOP
733                                 // ... function body ...
734                                 // checkargp:
735                                 //      MOV  panic_argp(RT1), RT2
736                                 //      ADD  $(autosize+8), RSP, R20
737                                 //      CMP  RT2, R20
738                                 //      BNE  end
739                                 //      ADD  $8, RSP, R20
740                                 //      MOVD R20, panic_argp(RT1)
741                                 //      B    end
742                                 //
743                                 // The NOP is needed to give the jumps somewhere to land.
744                                 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
745                                 q = q1
746
747                                 // MOV g_panic(g), RT1
748                                 q = obj.Appendp(q, c.newprog)
749                                 q.As = AMOVD
750                                 q.From.Type = obj.TYPE_MEM
751                                 q.From.Reg = REGG
752                                 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
753                                 q.To.Type = obj.TYPE_REG
754                                 q.To.Reg = REGRT1
755
756                                 // CBNZ RT1, checkargp
757                                 cbnz := obj.Appendp(q, c.newprog)
758                                 cbnz.As = ACBNZ
759                                 cbnz.From.Type = obj.TYPE_REG
760                                 cbnz.From.Reg = REGRT1
761                                 cbnz.To.Type = obj.TYPE_BRANCH
762
763                                 // Empty branch target at the top of the function body
764                                 end := obj.Appendp(cbnz, c.newprog)
765                                 end.As = obj.ANOP
766
767                                 // find the end of the function
768                                 var last *obj.Prog
769                                 for last = end; last.Link != nil; last = last.Link {
770                                 }
771
772                                 // MOV panic_argp(RT1), RT2
773                                 mov := obj.Appendp(last, c.newprog)
774                                 mov.As = AMOVD
775                                 mov.From.Type = obj.TYPE_MEM
776                                 mov.From.Reg = REGRT1
777                                 mov.From.Offset = 0 // Panic.argp
778                                 mov.To.Type = obj.TYPE_REG
779                                 mov.To.Reg = REGRT2
780
781                                 // CBNZ branches to the MOV above
782                                 cbnz.To.SetTarget(mov)
783
784                                 // ADD $(autosize+8), SP, R20
785                                 q = obj.Appendp(mov, c.newprog)
786                                 q.As = AADD
787                                 q.From.Type = obj.TYPE_CONST
788                                 q.From.Offset = int64(c.autosize) + 8
789                                 q.Reg = REGSP
790                                 q.To.Type = obj.TYPE_REG
791                                 q.To.Reg = REG_R20
792
793                                 // CMP RT2, R20
794                                 q = obj.Appendp(q, c.newprog)
795                                 q.As = ACMP
796                                 q.From.Type = obj.TYPE_REG
797                                 q.From.Reg = REGRT2
798                                 q.Reg = REG_R20
799
800                                 // BNE end
801                                 q = obj.Appendp(q, c.newprog)
802                                 q.As = ABNE
803                                 q.To.Type = obj.TYPE_BRANCH
804                                 q.To.SetTarget(end)
805
806                                 // ADD $8, SP, R20
807                                 q = obj.Appendp(q, c.newprog)
808                                 q.As = AADD
809                                 q.From.Type = obj.TYPE_CONST
810                                 q.From.Offset = 8
811                                 q.Reg = REGSP
812                                 q.To.Type = obj.TYPE_REG
813                                 q.To.Reg = REG_R20
814
815                                 // MOV R20, panic_argp(RT1)
816                                 q = obj.Appendp(q, c.newprog)
817                                 q.As = AMOVD
818                                 q.From.Type = obj.TYPE_REG
819                                 q.From.Reg = REG_R20
820                                 q.To.Type = obj.TYPE_MEM
821                                 q.To.Reg = REGRT1
822                                 q.To.Offset = 0 // Panic.argp
823
824                                 // B end
825                                 q = obj.Appendp(q, c.newprog)
826                                 q.As = AB
827                                 q.To.Type = obj.TYPE_BRANCH
828                                 q.To.SetTarget(end)
829                         }
830
831                 case obj.ARET:
832                         nocache(p)
833                         if p.From.Type == obj.TYPE_CONST {
834                                 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
835                                 break
836                         }
837
838                         retjmp = p.To.Sym
839                         p.To = obj.Addr{}
840                         if c.cursym.Func().Text.Mark&LEAF != 0 {
841                                 if c.autosize != 0 {
842                                         p.As = AADD
843                                         p.From.Type = obj.TYPE_CONST
844                                         p.From.Offset = int64(c.autosize)
845                                         p.To.Type = obj.TYPE_REG
846                                         p.To.Reg = REGSP
847                                         p.Spadj = -c.autosize
848
849                                         // Frame pointer.
850                                         p = obj.Appendp(p, c.newprog)
851                                         p.As = ASUB
852                                         p.From.Type = obj.TYPE_CONST
853                                         p.From.Offset = 8
854                                         p.Reg = REGSP
855                                         p.To.Type = obj.TYPE_REG
856                                         p.To.Reg = REGFP
857                                 }
858                         } else {
859                                 aoffset := c.autosize
860                                 // LDP -8(RSP), (R29, R30)
861                                 p.As = ALDP
862                                 p.From.Type = obj.TYPE_MEM
863                                 p.From.Offset = -8
864                                 p.From.Reg = REGSP
865                                 p.To.Type = obj.TYPE_REGREG
866                                 p.To.Reg = REGFP
867                                 p.To.Offset = REGLINK
868
869                                 // ADD $aoffset, RSP, RSP
870                                 q = newprog()
871                                 q.As = AADD
872                                 q.From.Type = obj.TYPE_CONST
873                                 q.From.Offset = int64(aoffset)
874                                 q.To.Type = obj.TYPE_REG
875                                 q.To.Reg = REGSP
876                                 q.Spadj = -aoffset
877                                 q.Pos = p.Pos
878                                 q.Link = p.Link
879                                 p.Link = q
880                                 p = q
881                         }
882
883                         // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
884                         // so that if you are debugging a low-level crash where PC and LR are zero,
885                         // you can look at R27 to see what jumped to the zero.
886                         // This is useful when bringing up Go on a new system.
887                         // (There is similar code in ../ppc64/obj9.go:/if.false.)
888                         const debugRETZERO = false
889                         if debugRETZERO {
890                                 if p.As != obj.ARET {
891                                         q = newprog()
892                                         q.Pos = p.Pos
893                                         q.Link = p.Link
894                                         p.Link = q
895                                         p = q
896                                 }
897                                 p.As = AADR
898                                 p.From.Type = obj.TYPE_BRANCH
899                                 p.From.Offset = 0
900                                 p.To.Type = obj.TYPE_REG
901                                 p.To.Reg = REGTMP
902
903                         }
904
905                         if p.As != obj.ARET {
906                                 q = newprog()
907                                 q.Pos = p.Pos
908                                 q.Link = p.Link
909                                 p.Link = q
910                                 p = q
911                         }
912
913                         if retjmp != nil { // retjmp
914                                 p.As = AB
915                                 p.To.Type = obj.TYPE_BRANCH
916                                 p.To.Sym = retjmp
917                                 p.Spadj = +c.autosize
918                                 break
919                         }
920
921                         p.As = obj.ARET
922                         p.To.Type = obj.TYPE_MEM
923                         p.To.Offset = 0
924                         p.To.Reg = REGLINK
925                         p.Spadj = +c.autosize
926
927                 case AADD, ASUB:
928                         if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
929                                 if p.As == AADD {
930                                         p.Spadj = int32(-p.From.Offset)
931                                 } else {
932                                         p.Spadj = int32(+p.From.Offset)
933                                 }
934                         }
935
936                 case obj.AGETCALLERPC:
937                         if cursym.Leaf() {
938                                 /* MOVD LR, Rd */
939                                 p.As = AMOVD
940                                 p.From.Type = obj.TYPE_REG
941                                 p.From.Reg = REGLINK
942                         } else {
943                                 /* MOVD (RSP), Rd */
944                                 p.As = AMOVD
945                                 p.From.Type = obj.TYPE_MEM
946                                 p.From.Reg = REGSP
947                         }
948
949                 case obj.ADUFFCOPY:
950                         //  ADR ret_addr, R27
951                         //  STP (FP, R27), -24(SP)
952                         //  SUB 24, SP, FP
953                         //  DUFFCOPY
954                         // ret_addr:
955                         //  SUB 8, SP, FP
956
957                         q1 := p
958                         // copy DUFFCOPY from q1 to q4
959                         q4 := obj.Appendp(p, c.newprog)
960                         q4.Pos = p.Pos
961                         q4.As = obj.ADUFFCOPY
962                         q4.To = p.To
963
964                         q1.As = AADR
965                         q1.From.Type = obj.TYPE_BRANCH
966                         q1.To.Type = obj.TYPE_REG
967                         q1.To.Reg = REG_R27
968
969                         q2 := obj.Appendp(q1, c.newprog)
970                         q2.Pos = p.Pos
971                         q2.As = ASTP
972                         q2.From.Type = obj.TYPE_REGREG
973                         q2.From.Reg = REGFP
974                         q2.From.Offset = int64(REG_R27)
975                         q2.To.Type = obj.TYPE_MEM
976                         q2.To.Reg = REGSP
977                         q2.To.Offset = -24
978
979                         // maintain FP for DUFFCOPY
980                         q3 := obj.Appendp(q2, c.newprog)
981                         q3.Pos = p.Pos
982                         q3.As = ASUB
983                         q3.From.Type = obj.TYPE_CONST
984                         q3.From.Offset = 24
985                         q3.Reg = REGSP
986                         q3.To.Type = obj.TYPE_REG
987                         q3.To.Reg = REGFP
988
989                         q5 := obj.Appendp(q4, c.newprog)
990                         q5.Pos = p.Pos
991                         q5.As = ASUB
992                         q5.From.Type = obj.TYPE_CONST
993                         q5.From.Offset = 8
994                         q5.Reg = REGSP
995                         q5.To.Type = obj.TYPE_REG
996                         q5.To.Reg = REGFP
997                         q1.From.SetTarget(q5)
998                         p = q5
999
1000                 case obj.ADUFFZERO:
1001                         //  ADR ret_addr, R27
1002                         //  STP (FP, R27), -24(SP)
1003                         //  SUB 24, SP, FP
1004                         //  DUFFZERO
1005                         // ret_addr:
1006                         //  SUB 8, SP, FP
1007
1008                         q1 := p
1009                         // copy DUFFZERO from q1 to q4
1010                         q4 := obj.Appendp(p, c.newprog)
1011                         q4.Pos = p.Pos
1012                         q4.As = obj.ADUFFZERO
1013                         q4.To = p.To
1014
1015                         q1.As = AADR
1016                         q1.From.Type = obj.TYPE_BRANCH
1017                         q1.To.Type = obj.TYPE_REG
1018                         q1.To.Reg = REG_R27
1019
1020                         q2 := obj.Appendp(q1, c.newprog)
1021                         q2.Pos = p.Pos
1022                         q2.As = ASTP
1023                         q2.From.Type = obj.TYPE_REGREG
1024                         q2.From.Reg = REGFP
1025                         q2.From.Offset = int64(REG_R27)
1026                         q2.To.Type = obj.TYPE_MEM
1027                         q2.To.Reg = REGSP
1028                         q2.To.Offset = -24
1029
1030                         // maintain FP for DUFFZERO
1031                         q3 := obj.Appendp(q2, c.newprog)
1032                         q3.Pos = p.Pos
1033                         q3.As = ASUB
1034                         q3.From.Type = obj.TYPE_CONST
1035                         q3.From.Offset = 24
1036                         q3.Reg = REGSP
1037                         q3.To.Type = obj.TYPE_REG
1038                         q3.To.Reg = REGFP
1039
1040                         q5 := obj.Appendp(q4, c.newprog)
1041                         q5.Pos = p.Pos
1042                         q5.As = ASUB
1043                         q5.From.Type = obj.TYPE_CONST
1044                         q5.From.Offset = 8
1045                         q5.Reg = REGSP
1046                         q5.To.Type = obj.TYPE_REG
1047                         q5.To.Reg = REGFP
1048                         q1.From.SetTarget(q5)
1049                         p = q5
1050                 }
1051
1052                 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
1053                         f := c.cursym.Func()
1054                         if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
1055                                 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
1056                                 if ctxt.Debugvlog || !ctxt.IsAsm {
1057                                         ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1058                                         if !ctxt.IsAsm {
1059                                                 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1060                                                 ctxt.DiagFlush()
1061                                                 log.Fatalf("bad SPWRITE")
1062                                         }
1063                                 }
1064                         }
1065                 }
1066                 if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
1067                         offset := p.From.Offset
1068                         op := offset & (3 << 22)
1069                         if op != SHIFT_LL {
1070                                 ctxt.Diag("illegal combination: %v", p)
1071                         }
1072                         r := (offset >> 16) & 31
1073                         shift := (offset >> 10) & 63
1074                         if shift > 4 {
1075                                 // the shift amount is out of range, in order to avoid repeated error
1076                                 // reportings, don't call ctxt.Diag, because asmout case 27 has the
1077                                 // same check.
1078                                 shift = 7
1079                         }
1080                         p.From.Type = obj.TYPE_REG
1081                         p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
1082                         p.From.Offset = 0
1083                 }
1084         }
1085 }
1086
1087 func nocache(p *obj.Prog) {
1088         p.Optab = 0
1089         p.From.Class = 0
1090         p.To.Class = 0
1091 }
1092
1093 var unaryDst = map[obj.As]bool{
1094         AWORD:  true,
1095         ADWORD: true,
1096         ABL:    true,
1097         AB:     true,
1098         ACLREX: true,
1099 }
1100
1101 var Linkarm64 = obj.LinkArch{
1102         Arch:           sys.ArchARM64,
1103         Init:           buildop,
1104         Preprocess:     preprocess,
1105         Assemble:       span7,
1106         Progedit:       progedit,
1107         UnaryDst:       unaryDst,
1108         DWARFRegisters: ARM64DWARFRegisters,
1109 }