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