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