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