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