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