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