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