]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/internal/obj/ppc64/obj9.go
cmd/internal/obj/ppc64: remove C_UCON optab matching class
[gostls13.git] / src / cmd / internal / obj / ppc64 / obj9.go
1 // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
2 //
3 //      Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
4 //      Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
5 //      Portions Copyright © 1997-1999 Vita Nuova Limited
6 //      Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
7 //      Portions Copyright © 2004,2006 Bruce Ellis
8 //      Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
9 //      Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
10 //      Portions Copyright © 2009 The Go Authors. All rights reserved.
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining a copy
13 // of this software and associated documentation files (the "Software"), to deal
14 // in the Software without restriction, including without limitation the rights
15 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 // copies of the Software, and to permit persons to whom the Software is
17 // furnished to do so, subject to the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be included in
20 // all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
25 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 // THE SOFTWARE.
29
30 package ppc64
31
32 import (
33         "cmd/internal/obj"
34         "cmd/internal/objabi"
35         "cmd/internal/src"
36         "cmd/internal/sys"
37         "internal/abi"
38         "log"
39         "math/bits"
40 )
41
42 // Test if this value can encoded as a mask for
43 // li -1, rx; rlic rx,rx,sh,mb.
44 // Masks can also extend from the msb and wrap to
45 // the lsb too. That is, the valid masks are 32 bit strings
46 // of the form: 0..01..10..0 or 1..10..01..1 or 1...1
47 func isPPC64DoublewordRotateMask(v64 int64) bool {
48         // Isolate rightmost 1 (if none 0) and add.
49         v := uint64(v64)
50         vp := (v & -v) + v
51         // Likewise, for the wrapping case.
52         vn := ^v
53         vpn := (vn & -vn) + vn
54         return (v&vp == 0 || vn&vpn == 0) && v != 0
55 }
56
57 // Encode a doubleword rotate mask into mb (mask begin) and
58 // me (mask end, inclusive). Note, POWER ISA labels bits in
59 // big endian order.
60 func encodePPC64RLDCMask(mask int64) (mb, me int) {
61         // Determine boundaries and then decode them
62         mb = bits.LeadingZeros64(uint64(mask))
63         me = 64 - bits.TrailingZeros64(uint64(mask))
64         mbn := bits.LeadingZeros64(^uint64(mask))
65         men := 64 - bits.TrailingZeros64(^uint64(mask))
66         // Check for a wrapping mask (e.g bits at 0 and 63)
67         if mb == 0 && me == 64 {
68                 // swap the inverted values
69                 mb, me = men, mbn
70         }
71         // Note, me is inclusive.
72         return mb, me - 1
73 }
74
75 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
76         p.From.Class = 0
77         p.To.Class = 0
78
79         c := ctxt9{ctxt: ctxt, newprog: newprog}
80
81         // Rewrite BR/BL to symbol as TYPE_BRANCH.
82         switch p.As {
83         case ABR,
84                 ABL,
85                 obj.ARET,
86                 obj.ADUFFZERO,
87                 obj.ADUFFCOPY:
88                 if p.To.Sym != nil {
89                         p.To.Type = obj.TYPE_BRANCH
90                 }
91         }
92
93         // Rewrite float constants to values stored in memory.
94         switch p.As {
95         case AFMOVS:
96                 if p.From.Type == obj.TYPE_FCONST {
97                         f32 := float32(p.From.Val.(float64))
98                         p.From.Type = obj.TYPE_MEM
99                         p.From.Sym = ctxt.Float32Sym(f32)
100                         p.From.Name = obj.NAME_EXTERN
101                         p.From.Offset = 0
102                 }
103
104         case AFMOVD:
105                 if p.From.Type == obj.TYPE_FCONST {
106                         f64 := p.From.Val.(float64)
107                         // Constant not needed in memory for float +/- 0
108                         if f64 != 0 {
109                                 p.From.Type = obj.TYPE_MEM
110                                 p.From.Sym = ctxt.Float64Sym(f64)
111                                 p.From.Name = obj.NAME_EXTERN
112                                 p.From.Offset = 0
113                         }
114                 }
115
116         case AMOVW, AMOVWZ:
117                 // Note, for backwards compatibility, MOVW $const, Rx and MOVWZ $const, Rx are identical.
118                 if p.From.Type == obj.TYPE_CONST && p.From.Offset != 0 && p.From.Offset&0xFFFF == 0 {
119                         // This is a constant shifted 16 bits to the left, convert it to ADDIS/ORIS $const,...
120                         p.As = AADDIS
121                         // Use ORIS for large constants which should not be sign extended.
122                         if p.From.Offset >= 0x80000000 {
123                                 p.As = AORIS
124                         }
125                         p.Reg = REG_R0
126                         p.From.Offset >>= 16
127                 }
128
129         case AMOVD:
130                 // Skip this opcode if it is not a constant load.
131                 if p.From.Type != obj.TYPE_CONST || p.From.Name != obj.NAME_NONE || p.From.Reg != 0 {
132                         break
133                 }
134
135                 // 32b constants (signed and unsigned) can be generated via 1 or 2 instructions. They can be assembled directly.
136                 isS32 := int64(int32(p.From.Offset)) == p.From.Offset
137                 isU32 := uint64(uint32(p.From.Offset)) == uint64(p.From.Offset)
138                 // If prefixed instructions are supported, a 34b signed constant can be generated by one pli instruction.
139                 isS34 := pfxEnabled && (p.From.Offset<<30)>>30 == p.From.Offset
140
141                 // Try converting MOVD $const,Rx into ADDIS/ORIS $s32>>16,R0,Rx
142                 switch {
143                 case isS32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
144                         p.As = AADDIS
145                         p.From.Offset >>= 16
146                         p.Reg = REG_R0
147
148                 case isU32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
149                         p.As = AORIS
150                         p.From.Offset >>= 16
151                         p.Reg = REG_R0
152
153                 case isS32 || isU32 || isS34:
154                         // The assembler can generate this opcode in 1 (on Power10) or 2 opcodes.
155
156                 // Otherwise, see if the large constant can be generated with 2 instructions. If not, load it from memory.
157                 default:
158                         // Is this a shifted 16b constant? If so, rewrite it to avoid a creating and loading a constant.
159                         val := p.From.Offset
160                         shift := bits.TrailingZeros64(uint64(val))
161                         mask := 0xFFFF << shift
162                         if val&int64(mask) == val || (val>>(shift+16) == -1 && (val>>shift)<<shift == val) {
163                                 // Rewrite this value into MOVD $const>>shift, Rto; SLD $shift, Rto
164                                 q := obj.Appendp(p, c.newprog)
165                                 q.As = ASLD
166                                 q.From.SetConst(int64(shift))
167                                 q.To = p.To
168                                 p.From.Offset >>= shift
169                                 p = q
170                         } else if isPPC64DoublewordRotateMask(val) {
171                                 // This constant is a mask value, generate MOVD $-1, Rto; RLDIC Rto, ^me, mb, Rto
172                                 mb, me := encodePPC64RLDCMask(val)
173                                 q := obj.Appendp(p, c.newprog)
174                                 q.As = ARLDC
175                                 q.AddRestSourceConst((^int64(me)) & 0x3F)
176                                 q.AddRestSourceConst(int64(mb))
177                                 q.From = p.To
178                                 q.To = p.To
179                                 p.From.Offset = -1
180                                 p = q
181                         } else {
182                                 // Load the constant from memory.
183                                 p.From.Type = obj.TYPE_MEM
184                                 p.From.Sym = ctxt.Int64Sym(p.From.Offset)
185                                 p.From.Name = obj.NAME_EXTERN
186                                 p.From.Offset = 0
187                         }
188                 }
189         }
190
191         switch p.As {
192         // Rewrite SUB constants into ADD.
193         case ASUBC:
194                 if p.From.Type == obj.TYPE_CONST {
195                         p.From.Offset = -p.From.Offset
196                         p.As = AADDC
197                 }
198
199         case ASUBCCC:
200                 if p.From.Type == obj.TYPE_CONST {
201                         p.From.Offset = -p.From.Offset
202                         p.As = AADDCCC
203                 }
204
205         case ASUB:
206                 if p.From.Type == obj.TYPE_CONST {
207                         p.From.Offset = -p.From.Offset
208                         p.As = AADD
209                 }
210
211         // Rewrite ADD/OR/XOR/ANDCC $const,... forms into ADDIS/ORIS/XORIS/ANDISCC
212         case AADD:
213                 // AADD can encode signed 34b values, ensure it is a valid signed 32b integer too.
214                 if p.From.Type == obj.TYPE_CONST && p.From.Offset&0xFFFF == 0 && int64(int32(p.From.Offset)) == p.From.Offset && p.From.Offset != 0 {
215                         p.As = AADDIS
216                         p.From.Offset >>= 16
217                 }
218         case AOR:
219                 if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
220                         p.As = AORIS
221                         p.From.Offset >>= 16
222                 }
223         case AXOR:
224                 if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
225                         p.As = AXORIS
226                         p.From.Offset >>= 16
227                 }
228         case AANDCC:
229                 if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
230                         p.As = AANDISCC
231                         p.From.Offset >>= 16
232                 }
233
234         // To maintain backwards compatibility, we accept some 4 argument usage of
235         // several opcodes which was likely not intended, but did work. These are not
236         // added to optab to avoid the chance this behavior might be used with newer
237         // instructions.
238         //
239         // Rewrite argument ordering like "ADDEX R3, $3, R4, R5" into
240         //                                "ADDEX R3, R4, $3, R5"
241         case AVSHASIGMAW, AVSHASIGMAD, AADDEX, AXXSLDWI, AXXPERMDI:
242                 if len(p.RestArgs) == 2 && p.Reg == 0 && p.RestArgs[0].Addr.Type == obj.TYPE_CONST && p.RestArgs[1].Addr.Type == obj.TYPE_REG {
243                         p.Reg = p.RestArgs[1].Addr.Reg
244                         p.RestArgs = p.RestArgs[:1]
245                 }
246         }
247
248         if c.ctxt.Headtype == objabi.Haix {
249                 c.rewriteToUseTOC(p)
250         } else if c.ctxt.Flag_dynlink {
251                 c.rewriteToUseGot(p)
252         }
253 }
254
255 // Rewrite p, if necessary, to access a symbol using its TOC anchor.
256 // This code is for AIX only.
257 func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) {
258         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
259                 return
260         }
261
262         if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
263                 // ADUFFZERO/ADUFFCOPY is considered as an ABL except in dynamic
264                 // link where it should be an indirect call.
265                 if !c.ctxt.Flag_dynlink {
266                         return
267                 }
268                 //     ADUFFxxx $offset
269                 // becomes
270                 //     MOVD runtime.duffxxx@TOC, R12
271                 //     ADD $offset, R12
272                 //     MOVD R12, LR
273                 //     BL (LR)
274                 var sym *obj.LSym
275                 if p.As == obj.ADUFFZERO {
276                         sym = c.ctxt.Lookup("runtime.duffzero")
277                 } else {
278                         sym = c.ctxt.Lookup("runtime.duffcopy")
279                 }
280                 // Retrieve or create the TOC anchor.
281                 symtoc := c.ctxt.LookupInit("TOC."+sym.Name, func(s *obj.LSym) {
282                         s.Type = objabi.SDATA
283                         s.Set(obj.AttrDuplicateOK, true)
284                         s.Set(obj.AttrStatic, true)
285                         c.ctxt.Data = append(c.ctxt.Data, s)
286                         s.WriteAddr(c.ctxt, 0, 8, sym, 0)
287                 })
288
289                 offset := p.To.Offset
290                 p.As = AMOVD
291                 p.From.Type = obj.TYPE_MEM
292                 p.From.Name = obj.NAME_TOCREF
293                 p.From.Sym = symtoc
294                 p.To.Type = obj.TYPE_REG
295                 p.To.Reg = REG_R12
296                 p.To.Name = obj.NAME_NONE
297                 p.To.Offset = 0
298                 p.To.Sym = nil
299                 p1 := obj.Appendp(p, c.newprog)
300                 p1.As = AADD
301                 p1.From.Type = obj.TYPE_CONST
302                 p1.From.Offset = offset
303                 p1.To.Type = obj.TYPE_REG
304                 p1.To.Reg = REG_R12
305                 p2 := obj.Appendp(p1, c.newprog)
306                 p2.As = AMOVD
307                 p2.From.Type = obj.TYPE_REG
308                 p2.From.Reg = REG_R12
309                 p2.To.Type = obj.TYPE_REG
310                 p2.To.Reg = REG_LR
311                 p3 := obj.Appendp(p2, c.newprog)
312                 p3.As = obj.ACALL
313                 p3.To.Type = obj.TYPE_REG
314                 p3.To.Reg = REG_LR
315         }
316
317         var source *obj.Addr
318         if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC {
319                 if p.From.Type == obj.TYPE_ADDR {
320                         if p.As == ADWORD {
321                                 // ADWORD $sym doesn't need TOC anchor
322                                 return
323                         }
324                         if p.As != AMOVD {
325                                 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p)
326                                 return
327                         }
328                         if p.To.Type != obj.TYPE_REG {
329                                 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p)
330                                 return
331                         }
332                 } else if p.From.Type != obj.TYPE_MEM {
333                         c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
334                         return
335                 }
336                 source = &p.From
337
338         } else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC {
339                 if p.To.Type != obj.TYPE_MEM {
340                         c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
341                         return
342                 }
343                 if source != nil {
344                         c.ctxt.Diag("cannot handle symbols on both sides in %v", p)
345                         return
346                 }
347                 source = &p.To
348         } else {
349                 return
350
351         }
352
353         if source.Sym == nil {
354                 c.ctxt.Diag("do not know how to handle nil symbol in %v", p)
355                 return
356         }
357
358         if source.Sym.Type == objabi.STLSBSS {
359                 return
360         }
361
362         // Retrieve or create the TOC anchor.
363         symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) {
364                 s.Type = objabi.SDATA
365                 s.Set(obj.AttrDuplicateOK, true)
366                 s.Set(obj.AttrStatic, true)
367                 c.ctxt.Data = append(c.ctxt.Data, s)
368                 s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0)
369         })
370
371         if source.Type == obj.TYPE_ADDR {
372                 // MOVD $sym, Rx becomes MOVD symtoc, Rx
373                 // MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx
374                 p.From.Type = obj.TYPE_MEM
375                 p.From.Sym = symtoc
376                 p.From.Name = obj.NAME_TOCREF
377
378                 if p.From.Offset != 0 {
379                         q := obj.Appendp(p, c.newprog)
380                         q.As = AADD
381                         q.From.Type = obj.TYPE_CONST
382                         q.From.Offset = p.From.Offset
383                         p.From.Offset = 0
384                         q.To = p.To
385                 }
386                 return
387
388         }
389
390         // MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry
391         // MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP)
392         // An addition may be inserted between the two MOVs if there is an offset.
393
394         q := obj.Appendp(p, c.newprog)
395         q.As = AMOVD
396         q.From.Type = obj.TYPE_MEM
397         q.From.Sym = symtoc
398         q.From.Name = obj.NAME_TOCREF
399         q.To.Type = obj.TYPE_REG
400         q.To.Reg = REGTMP
401
402         q = obj.Appendp(q, c.newprog)
403         q.As = p.As
404         q.From = p.From
405         q.To = p.To
406         if p.From.Name != obj.NAME_NONE {
407                 q.From.Type = obj.TYPE_MEM
408                 q.From.Reg = REGTMP
409                 q.From.Name = obj.NAME_NONE
410                 q.From.Sym = nil
411         } else if p.To.Name != obj.NAME_NONE {
412                 q.To.Type = obj.TYPE_MEM
413                 q.To.Reg = REGTMP
414                 q.To.Name = obj.NAME_NONE
415                 q.To.Sym = nil
416         } else {
417                 c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p)
418         }
419
420         obj.Nopout(p)
421 }
422
423 // Rewrite p, if necessary, to access global data via the global offset table.
424 func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
425         if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
426                 //     ADUFFxxx $offset
427                 // becomes
428                 //     MOVD runtime.duffxxx@GOT, R12
429                 //     ADD $offset, R12
430                 //     MOVD R12, LR
431                 //     BL (LR)
432                 var sym *obj.LSym
433                 if p.As == obj.ADUFFZERO {
434                         sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
435                 } else {
436                         sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
437                 }
438                 offset := p.To.Offset
439                 p.As = AMOVD
440                 p.From.Type = obj.TYPE_MEM
441                 p.From.Name = obj.NAME_GOTREF
442                 p.From.Sym = sym
443                 p.To.Type = obj.TYPE_REG
444                 p.To.Reg = REG_R12
445                 p.To.Name = obj.NAME_NONE
446                 p.To.Offset = 0
447                 p.To.Sym = nil
448                 p1 := obj.Appendp(p, c.newprog)
449                 p1.As = AADD
450                 p1.From.Type = obj.TYPE_CONST
451                 p1.From.Offset = offset
452                 p1.To.Type = obj.TYPE_REG
453                 p1.To.Reg = REG_R12
454                 p2 := obj.Appendp(p1, c.newprog)
455                 p2.As = AMOVD
456                 p2.From.Type = obj.TYPE_REG
457                 p2.From.Reg = REG_R12
458                 p2.To.Type = obj.TYPE_REG
459                 p2.To.Reg = REG_LR
460                 p3 := obj.Appendp(p2, c.newprog)
461                 p3.As = obj.ACALL
462                 p3.To.Type = obj.TYPE_REG
463                 p3.To.Reg = REG_LR
464         }
465
466         // We only care about global data: NAME_EXTERN means a global
467         // symbol in the Go sense, and p.Sym.Local is true for a few
468         // internally defined symbols.
469         if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
470                 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
471                 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
472                 if p.As != AMOVD {
473                         c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
474                 }
475                 if p.To.Type != obj.TYPE_REG {
476                         c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
477                 }
478                 p.From.Type = obj.TYPE_MEM
479                 p.From.Name = obj.NAME_GOTREF
480                 if p.From.Offset != 0 {
481                         q := obj.Appendp(p, c.newprog)
482                         q.As = AADD
483                         q.From.Type = obj.TYPE_CONST
484                         q.From.Offset = p.From.Offset
485                         q.To = p.To
486                         p.From.Offset = 0
487                 }
488         }
489         if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
490                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
491         }
492         var source *obj.Addr
493         // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
494         // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
495         // An addition may be inserted between the two MOVs if there is an offset.
496         if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
497                 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
498                         c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
499                 }
500                 source = &p.From
501         } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
502                 source = &p.To
503         } else {
504                 return
505         }
506         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
507                 return
508         }
509         if source.Sym.Type == objabi.STLSBSS {
510                 return
511         }
512         if source.Type != obj.TYPE_MEM {
513                 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
514         }
515         p1 := obj.Appendp(p, c.newprog)
516         p2 := obj.Appendp(p1, c.newprog)
517
518         p1.As = AMOVD
519         p1.From.Type = obj.TYPE_MEM
520         p1.From.Sym = source.Sym
521         p1.From.Name = obj.NAME_GOTREF
522         p1.To.Type = obj.TYPE_REG
523         p1.To.Reg = REGTMP
524
525         p2.As = p.As
526         p2.From = p.From
527         p2.To = p.To
528         if p.From.Name == obj.NAME_EXTERN {
529                 p2.From.Reg = REGTMP
530                 p2.From.Name = obj.NAME_NONE
531                 p2.From.Sym = nil
532         } else if p.To.Name == obj.NAME_EXTERN {
533                 p2.To.Reg = REGTMP
534                 p2.To.Name = obj.NAME_NONE
535                 p2.To.Sym = nil
536         } else {
537                 return
538         }
539         obj.Nopout(p)
540 }
541
542 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
543         // TODO(minux): add morestack short-cuts with small fixed frame-size.
544         if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
545                 return
546         }
547
548         c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
549
550         p := c.cursym.Func().Text
551         textstksiz := p.To.Offset
552         if textstksiz == -8 {
553                 // Compatibility hack.
554                 p.From.Sym.Set(obj.AttrNoFrame, true)
555                 textstksiz = 0
556         }
557         if textstksiz%8 != 0 {
558                 c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
559         }
560         if p.From.Sym.NoFrame() {
561                 if textstksiz != 0 {
562                         c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
563                 }
564         }
565
566         c.cursym.Func().Args = p.To.Val.(int32)
567         c.cursym.Func().Locals = int32(textstksiz)
568
569         /*
570          * find leaf subroutines
571          * expand RET
572          * expand BECOME pseudo
573          */
574
575         var q *obj.Prog
576         var q1 *obj.Prog
577         for p := c.cursym.Func().Text; p != nil; p = p.Link {
578                 switch p.As {
579                 /* too hard, just leave alone */
580                 case obj.ATEXT:
581                         q = p
582
583                         p.Mark |= LABEL | LEAF | SYNC
584                         if p.Link != nil {
585                                 p.Link.Mark |= LABEL
586                         }
587
588                 case ANOR:
589                         q = p
590                         if p.To.Type == obj.TYPE_REG {
591                                 if p.To.Reg == REGZERO {
592                                         p.Mark |= LABEL | SYNC
593                                 }
594                         }
595
596                 case ALWAR,
597                         ALBAR,
598                         ASTBCCC,
599                         ASTWCCC,
600                         AEIEIO,
601                         AICBI,
602                         AISYNC,
603                         ATLBIE,
604                         ATLBIEL,
605                         ASLBIA,
606                         ASLBIE,
607                         ASLBMFEE,
608                         ASLBMFEV,
609                         ASLBMTE,
610                         ADCBF,
611                         ADCBI,
612                         ADCBST,
613                         ADCBT,
614                         ADCBTST,
615                         ADCBZ,
616                         ASYNC,
617                         ATLBSYNC,
618                         APTESYNC,
619                         ALWSYNC,
620                         ATW,
621                         AWORD,
622                         ARFI,
623                         ARFCI,
624                         ARFID,
625                         AHRFID:
626                         q = p
627                         p.Mark |= LABEL | SYNC
628                         continue
629
630                 case AMOVW, AMOVWZ, AMOVD:
631                         q = p
632                         if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
633                                 p.Mark |= LABEL | SYNC
634                         }
635                         continue
636
637                 case AFABS,
638                         AFABSCC,
639                         AFADD,
640                         AFADDCC,
641                         AFCTIW,
642                         AFCTIWCC,
643                         AFCTIWZ,
644                         AFCTIWZCC,
645                         AFDIV,
646                         AFDIVCC,
647                         AFMADD,
648                         AFMADDCC,
649                         AFMOVD,
650                         AFMOVDU,
651                         /* case AFMOVDS: */
652                         AFMOVS,
653                         AFMOVSU,
654
655                         /* case AFMOVSD: */
656                         AFMSUB,
657                         AFMSUBCC,
658                         AFMUL,
659                         AFMULCC,
660                         AFNABS,
661                         AFNABSCC,
662                         AFNEG,
663                         AFNEGCC,
664                         AFNMADD,
665                         AFNMADDCC,
666                         AFNMSUB,
667                         AFNMSUBCC,
668                         AFRSP,
669                         AFRSPCC,
670                         AFSUB,
671                         AFSUBCC:
672                         q = p
673
674                         p.Mark |= FLOAT
675                         continue
676
677                 case ABL,
678                         ABCL,
679                         obj.ADUFFZERO,
680                         obj.ADUFFCOPY:
681                         c.cursym.Func().Text.Mark &^= LEAF
682                         fallthrough
683
684                 case ABC,
685                         ABEQ,
686                         ABGE,
687                         ABGT,
688                         ABLE,
689                         ABLT,
690                         ABNE,
691                         ABR,
692                         ABVC,
693                         ABVS:
694                         p.Mark |= BRANCH
695                         q = p
696                         q1 = p.To.Target()
697                         if q1 != nil {
698                                 // NOPs are not removed due to #40689.
699
700                                 if q1.Mark&LEAF == 0 {
701                                         q1.Mark |= LABEL
702                                 }
703                         } else {
704                                 p.Mark |= LABEL
705                         }
706                         q1 = p.Link
707                         if q1 != nil {
708                                 q1.Mark |= LABEL
709                         }
710                         continue
711
712                 case AFCMPO, AFCMPU:
713                         q = p
714                         p.Mark |= FCMP | FLOAT
715                         continue
716
717                 case obj.ARET:
718                         q = p
719                         if p.Link != nil {
720                                 p.Link.Mark |= LABEL
721                         }
722                         continue
723
724                 case obj.ANOP:
725                         // NOPs are not removed due to
726                         // #40689
727                         continue
728
729                 default:
730                         q = p
731                         continue
732                 }
733         }
734
735         autosize := int32(0)
736         var p1 *obj.Prog
737         var p2 *obj.Prog
738         for p := c.cursym.Func().Text; p != nil; p = p.Link {
739                 o := p.As
740                 switch o {
741                 case obj.ATEXT:
742                         autosize = int32(textstksiz)
743
744                         if p.Mark&LEAF != 0 && autosize == 0 {
745                                 // A leaf function with no locals has no frame.
746                                 p.From.Sym.Set(obj.AttrNoFrame, true)
747                         }
748
749                         if !p.From.Sym.NoFrame() {
750                                 // If there is a stack frame at all, it includes
751                                 // space to save the LR.
752                                 autosize += int32(c.ctxt.Arch.FixedFrameSize)
753                         }
754
755                         if p.Mark&LEAF != 0 && autosize < abi.StackSmall {
756                                 // A leaf function with a small stack can be marked
757                                 // NOSPLIT, avoiding a stack check.
758                                 p.From.Sym.Set(obj.AttrNoSplit, true)
759                         }
760
761                         p.To.Offset = int64(autosize)
762
763                         q = p
764
765                         if NeedTOCpointer(c.ctxt) && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
766                                 // When compiling Go into PIC, without PCrel support, all functions must start
767                                 // with instructions to load the TOC pointer into r2:
768                                 //
769                                 //      addis r2, r12, .TOC.-func@ha
770                                 //      addi r2, r2, .TOC.-func@l+4
771                                 //
772                                 // We could probably skip this prologue in some situations
773                                 // but it's a bit subtle. However, it is both safe and
774                                 // necessary to leave the prologue off duffzero and
775                                 // duffcopy as we rely on being able to jump to a specific
776                                 // instruction offset for them.
777                                 //
778                                 // These are AWORDS because there is no (afaict) way to
779                                 // generate the addis instruction except as part of the
780                                 // load of a large constant, and in that case there is no
781                                 // way to use r12 as the source.
782                                 //
783                                 // Note that the same condition is tested in
784                                 // putelfsym in cmd/link/internal/ld/symtab.go
785                                 // where we set the st_other field to indicate
786                                 // the presence of these instructions.
787                                 q = obj.Appendp(q, c.newprog)
788                                 q.As = AWORD
789                                 q.Pos = p.Pos
790                                 q.From.Type = obj.TYPE_CONST
791                                 q.From.Offset = 0x3c4c0000
792                                 q = obj.Appendp(q, c.newprog)
793                                 q.As = AWORD
794                                 q.Pos = p.Pos
795                                 q.From.Type = obj.TYPE_CONST
796                                 q.From.Offset = 0x38420000
797                                 rel := obj.Addrel(c.cursym)
798                                 rel.Off = 0
799                                 rel.Siz = 8
800                                 rel.Sym = c.ctxt.Lookup(".TOC.")
801                                 rel.Type = objabi.R_ADDRPOWER_PCREL
802                         }
803
804                         if !c.cursym.Func().Text.From.Sym.NoSplit() {
805                                 q = c.stacksplit(q, autosize) // emit split check
806                         }
807
808                         if autosize != 0 {
809                                 var prologueEnd *obj.Prog
810                                 // Save the link register and update the SP.  MOVDU is used unless
811                                 // the frame size is too large.  The link register must be saved
812                                 // even for non-empty leaf functions so that traceback works.
813                                 if autosize >= -BIG && autosize <= BIG {
814                                         // Use MOVDU to adjust R1 when saving R31, if autosize is small.
815                                         q = obj.Appendp(q, c.newprog)
816                                         q.As = AMOVD
817                                         q.Pos = p.Pos
818                                         q.From.Type = obj.TYPE_REG
819                                         q.From.Reg = REG_LR
820                                         q.To.Type = obj.TYPE_REG
821                                         q.To.Reg = REGTMP
822                                         prologueEnd = q
823
824                                         q = obj.Appendp(q, c.newprog)
825                                         q.As = AMOVDU
826                                         q.Pos = p.Pos
827                                         q.From.Type = obj.TYPE_REG
828                                         q.From.Reg = REGTMP
829                                         q.To.Type = obj.TYPE_MEM
830                                         q.To.Offset = int64(-autosize)
831                                         q.To.Reg = REGSP
832                                         q.Spadj = autosize
833                                 } else {
834                                         // Frame size is too large for a MOVDU instruction.
835                                         // Store link register before decrementing SP, so if a signal comes
836                                         // during the execution of the function prologue, the traceback
837                                         // code will not see a half-updated stack frame.
838                                         // This sequence is not async preemptible, as if we open a frame
839                                         // at the current SP, it will clobber the saved LR.
840                                         q = obj.Appendp(q, c.newprog)
841                                         q.As = AMOVD
842                                         q.Pos = p.Pos
843                                         q.From.Type = obj.TYPE_REG
844                                         q.From.Reg = REG_LR
845                                         q.To.Type = obj.TYPE_REG
846                                         q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
847
848                                         q = c.ctxt.StartUnsafePoint(q, c.newprog)
849
850                                         q = obj.Appendp(q, c.newprog)
851                                         q.As = AMOVD
852                                         q.Pos = p.Pos
853                                         q.From.Type = obj.TYPE_REG
854                                         q.From.Reg = REG_R29
855                                         q.To.Type = obj.TYPE_MEM
856                                         q.To.Offset = int64(-autosize)
857                                         q.To.Reg = REGSP
858
859                                         prologueEnd = q
860
861                                         q = obj.Appendp(q, c.newprog)
862                                         q.As = AADD
863                                         q.Pos = p.Pos
864                                         q.From.Type = obj.TYPE_CONST
865                                         q.From.Offset = int64(-autosize)
866                                         q.To.Type = obj.TYPE_REG
867                                         q.To.Reg = REGSP
868                                         q.Spadj = +autosize
869
870                                         q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
871                                 }
872                                 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
873                         } else if c.cursym.Func().Text.Mark&LEAF == 0 {
874                                 // A very few functions that do not return to their caller
875                                 // (e.g. gogo) are not identified as leaves but still have
876                                 // no frame.
877                                 c.cursym.Func().Text.Mark |= LEAF
878                         }
879
880                         if c.cursym.Func().Text.Mark&LEAF != 0 {
881                                 c.cursym.Set(obj.AttrLeaf, true)
882                                 break
883                         }
884
885                         if NeedTOCpointer(c.ctxt) {
886                                 q = obj.Appendp(q, c.newprog)
887                                 q.As = AMOVD
888                                 q.Pos = p.Pos
889                                 q.From.Type = obj.TYPE_REG
890                                 q.From.Reg = REG_R2
891                                 q.To.Type = obj.TYPE_MEM
892                                 q.To.Reg = REGSP
893                                 q.To.Offset = 24
894                         }
895
896                         if c.cursym.Func().Text.From.Sym.Wrapper() {
897                                 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
898                                 //
899                                 //      MOVD g_panic(g), R3
900                                 //      CMP R0, R3
901                                 //      BEQ end
902                                 //      MOVD panic_argp(R3), R4
903                                 //      ADD $(autosize+8), R1, R5
904                                 //      CMP R4, R5
905                                 //      BNE end
906                                 //      ADD $8, R1, R6
907                                 //      MOVD R6, panic_argp(R3)
908                                 // end:
909                                 //      NOP
910                                 //
911                                 // The NOP is needed to give the jumps somewhere to land.
912                                 // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
913
914                                 q = obj.Appendp(q, c.newprog)
915
916                                 q.As = AMOVD
917                                 q.From.Type = obj.TYPE_MEM
918                                 q.From.Reg = REGG
919                                 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
920                                 q.To.Type = obj.TYPE_REG
921                                 q.To.Reg = REG_R22
922
923                                 q = obj.Appendp(q, c.newprog)
924                                 q.As = ACMP
925                                 q.From.Type = obj.TYPE_REG
926                                 q.From.Reg = REG_R0
927                                 q.To.Type = obj.TYPE_REG
928                                 q.To.Reg = REG_R22
929
930                                 q = obj.Appendp(q, c.newprog)
931                                 q.As = ABEQ
932                                 q.To.Type = obj.TYPE_BRANCH
933                                 p1 = q
934
935                                 q = obj.Appendp(q, c.newprog)
936                                 q.As = AMOVD
937                                 q.From.Type = obj.TYPE_MEM
938                                 q.From.Reg = REG_R22
939                                 q.From.Offset = 0 // Panic.argp
940                                 q.To.Type = obj.TYPE_REG
941                                 q.To.Reg = REG_R23
942
943                                 q = obj.Appendp(q, c.newprog)
944                                 q.As = AADD
945                                 q.From.Type = obj.TYPE_CONST
946                                 q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize
947                                 q.Reg = REGSP
948                                 q.To.Type = obj.TYPE_REG
949                                 q.To.Reg = REG_R24
950
951                                 q = obj.Appendp(q, c.newprog)
952                                 q.As = ACMP
953                                 q.From.Type = obj.TYPE_REG
954                                 q.From.Reg = REG_R23
955                                 q.To.Type = obj.TYPE_REG
956                                 q.To.Reg = REG_R24
957
958                                 q = obj.Appendp(q, c.newprog)
959                                 q.As = ABNE
960                                 q.To.Type = obj.TYPE_BRANCH
961                                 p2 = q
962
963                                 q = obj.Appendp(q, c.newprog)
964                                 q.As = AADD
965                                 q.From.Type = obj.TYPE_CONST
966                                 q.From.Offset = c.ctxt.Arch.FixedFrameSize
967                                 q.Reg = REGSP
968                                 q.To.Type = obj.TYPE_REG
969                                 q.To.Reg = REG_R25
970
971                                 q = obj.Appendp(q, c.newprog)
972                                 q.As = AMOVD
973                                 q.From.Type = obj.TYPE_REG
974                                 q.From.Reg = REG_R25
975                                 q.To.Type = obj.TYPE_MEM
976                                 q.To.Reg = REG_R22
977                                 q.To.Offset = 0 // Panic.argp
978
979                                 q = obj.Appendp(q, c.newprog)
980
981                                 q.As = obj.ANOP
982                                 p1.To.SetTarget(q)
983                                 p2.To.SetTarget(q)
984                         }
985
986                 case obj.ARET:
987                         if p.From.Type == obj.TYPE_CONST {
988                                 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
989                                 break
990                         }
991
992                         retTarget := p.To.Sym
993
994                         if c.cursym.Func().Text.Mark&LEAF != 0 {
995                                 if autosize == 0 {
996                                         p.As = ABR
997                                         p.From = obj.Addr{}
998                                         if retTarget == nil {
999                                                 p.To.Type = obj.TYPE_REG
1000                                                 p.To.Reg = REG_LR
1001                                         } else {
1002                                                 p.To.Type = obj.TYPE_BRANCH
1003                                                 p.To.Sym = retTarget
1004                                         }
1005                                         p.Mark |= BRANCH
1006                                         break
1007                                 }
1008
1009                                 p.As = AADD
1010                                 p.From.Type = obj.TYPE_CONST
1011                                 p.From.Offset = int64(autosize)
1012                                 p.To.Type = obj.TYPE_REG
1013                                 p.To.Reg = REGSP
1014                                 p.Spadj = -autosize
1015
1016                                 q = c.newprog()
1017                                 q.As = ABR
1018                                 q.Pos = p.Pos
1019                                 if retTarget == nil {
1020                                         q.To.Type = obj.TYPE_REG
1021                                         q.To.Reg = REG_LR
1022                                 } else {
1023                                         q.To.Type = obj.TYPE_BRANCH
1024                                         q.To.Sym = retTarget
1025                                 }
1026                                 q.Mark |= BRANCH
1027                                 q.Spadj = +autosize
1028
1029                                 q.Link = p.Link
1030                                 p.Link = q
1031                                 break
1032                         }
1033
1034                         p.As = AMOVD
1035                         p.From.Type = obj.TYPE_MEM
1036                         p.From.Offset = 0
1037                         p.From.Reg = REGSP
1038                         p.To.Type = obj.TYPE_REG
1039                         p.To.Reg = REGTMP
1040
1041                         q = c.newprog()
1042                         q.As = AMOVD
1043                         q.Pos = p.Pos
1044                         q.From.Type = obj.TYPE_REG
1045                         q.From.Reg = REGTMP
1046                         q.To.Type = obj.TYPE_REG
1047                         q.To.Reg = REG_LR
1048
1049                         q.Link = p.Link
1050                         p.Link = q
1051                         p = q
1052
1053                         if false {
1054                                 // Debug bad returns
1055                                 q = c.newprog()
1056
1057                                 q.As = AMOVD
1058                                 q.Pos = p.Pos
1059                                 q.From.Type = obj.TYPE_MEM
1060                                 q.From.Offset = 0
1061                                 q.From.Reg = REGTMP
1062                                 q.To.Type = obj.TYPE_REG
1063                                 q.To.Reg = REGTMP
1064
1065                                 q.Link = p.Link
1066                                 p.Link = q
1067                                 p = q
1068                         }
1069                         prev := p
1070                         if autosize != 0 {
1071                                 q = c.newprog()
1072                                 q.As = AADD
1073                                 q.Pos = p.Pos
1074                                 q.From.Type = obj.TYPE_CONST
1075                                 q.From.Offset = int64(autosize)
1076                                 q.To.Type = obj.TYPE_REG
1077                                 q.To.Reg = REGSP
1078                                 q.Spadj = -autosize
1079
1080                                 q.Link = p.Link
1081                                 prev.Link = q
1082                                 prev = q
1083                         }
1084
1085                         q1 = c.newprog()
1086                         q1.As = ABR
1087                         q1.Pos = p.Pos
1088                         if retTarget == nil {
1089                                 q1.To.Type = obj.TYPE_REG
1090                                 q1.To.Reg = REG_LR
1091                         } else {
1092                                 q1.To.Type = obj.TYPE_BRANCH
1093                                 q1.To.Sym = retTarget
1094                         }
1095                         q1.Mark |= BRANCH
1096                         q1.Spadj = +autosize
1097
1098                         q1.Link = q.Link
1099                         prev.Link = q1
1100                 case AADD:
1101                         if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
1102                                 p.Spadj = int32(-p.From.Offset)
1103                         }
1104                 case AMOVDU:
1105                         if p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
1106                                 p.Spadj = int32(-p.To.Offset)
1107                         }
1108                         if p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP {
1109                                 p.Spadj = int32(-p.From.Offset)
1110                         }
1111                 case obj.AGETCALLERPC:
1112                         if cursym.Leaf() {
1113                                 /* MOVD LR, Rd */
1114                                 p.As = AMOVD
1115                                 p.From.Type = obj.TYPE_REG
1116                                 p.From.Reg = REG_LR
1117                         } else {
1118                                 /* MOVD (RSP), Rd */
1119                                 p.As = AMOVD
1120                                 p.From.Type = obj.TYPE_MEM
1121                                 p.From.Reg = REGSP
1122                         }
1123                 }
1124
1125                 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU {
1126                         f := c.cursym.Func()
1127                         if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
1128                                 c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
1129                                 if ctxt.Debugvlog || !ctxt.IsAsm {
1130                                         ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1131                                         if !ctxt.IsAsm {
1132                                                 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1133                                                 ctxt.DiagFlush()
1134                                                 log.Fatalf("bad SPWRITE")
1135                                         }
1136                                 }
1137                         }
1138                 }
1139         }
1140 }
1141
1142 /*
1143 // instruction scheduling
1144
1145         if(debug['Q'] == 0)
1146                 return;
1147
1148         curtext = nil;
1149         q = nil;        // p - 1
1150         q1 = firstp;    // top of block
1151         o = 0;          // count of instructions
1152         for(p = firstp; p != nil; p = p1) {
1153                 p1 = p->link;
1154                 o++;
1155                 if(p->mark & NOSCHED){
1156                         if(q1 != p){
1157                                 sched(q1, q);
1158                         }
1159                         for(; p != nil; p = p->link){
1160                                 if(!(p->mark & NOSCHED))
1161                                         break;
1162                                 q = p;
1163                         }
1164                         p1 = p;
1165                         q1 = p;
1166                         o = 0;
1167                         continue;
1168                 }
1169                 if(p->mark & (LABEL|SYNC)) {
1170                         if(q1 != p)
1171                                 sched(q1, q);
1172                         q1 = p;
1173                         o = 1;
1174                 }
1175                 if(p->mark & (BRANCH|SYNC)) {
1176                         sched(q1, p);
1177                         q1 = p1;
1178                         o = 0;
1179                 }
1180                 if(o >= NSCHED) {
1181                         sched(q1, p);
1182                         q1 = p1;
1183                         o = 0;
1184                 }
1185                 q = p;
1186         }
1187 */
1188 func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
1189         if c.ctxt.Flag_maymorestack != "" {
1190                 if c.ctxt.Flag_shared || c.ctxt.Flag_dynlink {
1191                         // See the call to morestack for why these are
1192                         // complicated to support.
1193                         c.ctxt.Diag("maymorestack with -shared or -dynlink is not supported")
1194                 }
1195
1196                 // Spill arguments. This has to happen before we open
1197                 // any more frame space.
1198                 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
1199
1200                 // Save LR and REGCTXT
1201                 frameSize := 8 + c.ctxt.Arch.FixedFrameSize
1202
1203                 // MOVD LR, REGTMP
1204                 p = obj.Appendp(p, c.newprog)
1205                 p.As = AMOVD
1206                 p.From.Type = obj.TYPE_REG
1207                 p.From.Reg = REG_LR
1208                 p.To.Type = obj.TYPE_REG
1209                 p.To.Reg = REGTMP
1210                 // MOVDU REGTMP, -16(SP)
1211                 p = obj.Appendp(p, c.newprog)
1212                 p.As = AMOVDU
1213                 p.From.Type = obj.TYPE_REG
1214                 p.From.Reg = REGTMP
1215                 p.To.Type = obj.TYPE_MEM
1216                 p.To.Offset = -frameSize
1217                 p.To.Reg = REGSP
1218                 p.Spadj = int32(frameSize)
1219
1220                 // MOVD REGCTXT, 8(SP)
1221                 p = obj.Appendp(p, c.newprog)
1222                 p.As = AMOVD
1223                 p.From.Type = obj.TYPE_REG
1224                 p.From.Reg = REGCTXT
1225                 p.To.Type = obj.TYPE_MEM
1226                 p.To.Offset = 8
1227                 p.To.Reg = REGSP
1228
1229                 // BL maymorestack
1230                 p = obj.Appendp(p, c.newprog)
1231                 p.As = ABL
1232                 p.To.Type = obj.TYPE_BRANCH
1233                 // See ../x86/obj6.go
1234                 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
1235
1236                 // Restore LR and REGCTXT
1237
1238                 // MOVD 8(SP), REGCTXT
1239                 p = obj.Appendp(p, c.newprog)
1240                 p.As = AMOVD
1241                 p.From.Type = obj.TYPE_MEM
1242                 p.From.Offset = 8
1243                 p.From.Reg = REGSP
1244                 p.To.Type = obj.TYPE_REG
1245                 p.To.Reg = REGCTXT
1246
1247                 // MOVD 0(SP), REGTMP
1248                 p = obj.Appendp(p, c.newprog)
1249                 p.As = AMOVD
1250                 p.From.Type = obj.TYPE_MEM
1251                 p.From.Offset = 0
1252                 p.From.Reg = REGSP
1253                 p.To.Type = obj.TYPE_REG
1254                 p.To.Reg = REGTMP
1255
1256                 // MOVD REGTMP, LR
1257                 p = obj.Appendp(p, c.newprog)
1258                 p.As = AMOVD
1259                 p.From.Type = obj.TYPE_REG
1260                 p.From.Reg = REGTMP
1261                 p.To.Type = obj.TYPE_REG
1262                 p.To.Reg = REG_LR
1263
1264                 // ADD $16, SP
1265                 p = obj.Appendp(p, c.newprog)
1266                 p.As = AADD
1267                 p.From.Type = obj.TYPE_CONST
1268                 p.From.Offset = frameSize
1269                 p.To.Type = obj.TYPE_REG
1270                 p.To.Reg = REGSP
1271                 p.Spadj = -int32(frameSize)
1272
1273                 // Unspill arguments.
1274                 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
1275         }
1276
1277         // save entry point, but skipping the two instructions setting R2 in shared mode and maymorestack
1278         startPred := p
1279
1280         // MOVD g_stackguard(g), R22
1281         p = obj.Appendp(p, c.newprog)
1282
1283         p.As = AMOVD
1284         p.From.Type = obj.TYPE_MEM
1285         p.From.Reg = REGG
1286         p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
1287         if c.cursym.CFunc() {
1288                 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
1289         }
1290         p.To.Type = obj.TYPE_REG
1291         p.To.Reg = REG_R22
1292
1293         // Mark the stack bound check and morestack call async nonpreemptible.
1294         // If we get preempted here, when resumed the preemption request is
1295         // cleared, but we'll still call morestack, which will double the stack
1296         // unnecessarily. See issue #35470.
1297         p = c.ctxt.StartUnsafePoint(p, c.newprog)
1298
1299         var q *obj.Prog
1300         if framesize <= abi.StackSmall {
1301                 // small stack: SP < stackguard
1302                 //      CMP     stackguard, SP
1303                 p = obj.Appendp(p, c.newprog)
1304
1305                 p.As = ACMPU
1306                 p.From.Type = obj.TYPE_REG
1307                 p.From.Reg = REG_R22
1308                 p.To.Type = obj.TYPE_REG
1309                 p.To.Reg = REGSP
1310         } else {
1311                 // large stack: SP-framesize < stackguard-StackSmall
1312                 offset := int64(framesize) - abi.StackSmall
1313                 if framesize > abi.StackBig {
1314                         // Such a large stack we need to protect against underflow.
1315                         // The runtime guarantees SP > objabi.StackBig, but
1316                         // framesize is large enough that SP-framesize may
1317                         // underflow, causing a direct comparison with the
1318                         // stack guard to incorrectly succeed. We explicitly
1319                         // guard against underflow.
1320                         //
1321                         //      CMPU    SP, $(framesize-StackSmall)
1322                         //      BLT     label-of-call-to-morestack
1323                         if offset <= 0xffff {
1324                                 p = obj.Appendp(p, c.newprog)
1325                                 p.As = ACMPU
1326                                 p.From.Type = obj.TYPE_REG
1327                                 p.From.Reg = REGSP
1328                                 p.To.Type = obj.TYPE_CONST
1329                                 p.To.Offset = offset
1330                         } else {
1331                                 // Constant is too big for CMPU.
1332                                 p = obj.Appendp(p, c.newprog)
1333                                 p.As = AMOVD
1334                                 p.From.Type = obj.TYPE_CONST
1335                                 p.From.Offset = offset
1336                                 p.To.Type = obj.TYPE_REG
1337                                 p.To.Reg = REG_R23
1338
1339                                 p = obj.Appendp(p, c.newprog)
1340                                 p.As = ACMPU
1341                                 p.From.Type = obj.TYPE_REG
1342                                 p.From.Reg = REGSP
1343                                 p.To.Type = obj.TYPE_REG
1344                                 p.To.Reg = REG_R23
1345                         }
1346
1347                         p = obj.Appendp(p, c.newprog)
1348                         q = p
1349                         p.As = ABLT
1350                         p.To.Type = obj.TYPE_BRANCH
1351                 }
1352
1353                 // Check against the stack guard. We've ensured this won't underflow.
1354                 //      ADD  $-(framesize-StackSmall), SP, R4
1355                 //      CMPU stackguard, R4
1356                 p = obj.Appendp(p, c.newprog)
1357
1358                 p.As = AADD
1359                 p.From.Type = obj.TYPE_CONST
1360                 p.From.Offset = -offset
1361                 p.Reg = REGSP
1362                 p.To.Type = obj.TYPE_REG
1363                 p.To.Reg = REG_R23
1364
1365                 p = obj.Appendp(p, c.newprog)
1366                 p.As = ACMPU
1367                 p.From.Type = obj.TYPE_REG
1368                 p.From.Reg = REG_R22
1369                 p.To.Type = obj.TYPE_REG
1370                 p.To.Reg = REG_R23
1371         }
1372
1373         // q1: BLT      done
1374         p = obj.Appendp(p, c.newprog)
1375         q1 := p
1376
1377         p.As = ABLT
1378         p.To.Type = obj.TYPE_BRANCH
1379
1380         p = obj.Appendp(p, c.newprog)
1381         p.As = obj.ANOP // zero-width place holder
1382
1383         if q != nil {
1384                 q.To.SetTarget(p)
1385         }
1386
1387         // Spill the register args that could be clobbered by the
1388         // morestack code.
1389
1390         spill := c.cursym.Func().SpillRegisterArgs(p, c.newprog)
1391
1392         // MOVD LR, R5
1393         p = obj.Appendp(spill, c.newprog)
1394         p.As = AMOVD
1395         p.From.Type = obj.TYPE_REG
1396         p.From.Reg = REG_LR
1397         p.To.Type = obj.TYPE_REG
1398         p.To.Reg = REG_R5
1399
1400         p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
1401
1402         var morestacksym *obj.LSym
1403         if c.cursym.CFunc() {
1404                 morestacksym = c.ctxt.Lookup("runtime.morestackc")
1405         } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
1406                 morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
1407         } else {
1408                 morestacksym = c.ctxt.Lookup("runtime.morestack")
1409         }
1410
1411         if NeedTOCpointer(c.ctxt) {
1412                 // In PPC64 PIC code, R2 is used as TOC pointer derived from R12
1413                 // which is the address of function entry point when entering
1414                 // the function. We need to preserve R2 across call to morestack.
1415                 // Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
1416                 // the caller's frame, but not used (0(SP) is caller's saved LR,
1417                 // 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
1418                 // MOVD R2, 8(SP)
1419                 p = obj.Appendp(p, c.newprog)
1420                 p.As = AMOVD
1421                 p.From.Type = obj.TYPE_REG
1422                 p.From.Reg = REG_R2
1423                 p.To.Type = obj.TYPE_MEM
1424                 p.To.Reg = REGSP
1425                 p.To.Offset = 8
1426         }
1427
1428         if c.ctxt.Flag_dynlink {
1429                 // Avoid calling morestack via a PLT when dynamically linking. The
1430                 // PLT stubs generated by the system linker on ppc64le when "std r2,
1431                 // 24(r1)" to save the TOC pointer in their callers stack
1432                 // frame. Unfortunately (and necessarily) morestack is called before
1433                 // the function that calls it sets up its frame and so the PLT ends
1434                 // up smashing the saved TOC pointer for its caller's caller.
1435                 //
1436                 // According to the ABI documentation there is a mechanism to avoid
1437                 // the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
1438                 // relocation on the nop after the call to morestack) but at the time
1439                 // of writing it is not supported at all by gold and my attempt to
1440                 // use it with ld.bfd caused an internal linker error. So this hack
1441                 // seems preferable.
1442
1443                 // MOVD $runtime.morestack(SB), R12
1444                 p = obj.Appendp(p, c.newprog)
1445                 p.As = AMOVD
1446                 p.From.Type = obj.TYPE_MEM
1447                 p.From.Sym = morestacksym
1448                 p.From.Name = obj.NAME_GOTREF
1449                 p.To.Type = obj.TYPE_REG
1450                 p.To.Reg = REG_R12
1451
1452                 // MOVD R12, LR
1453                 p = obj.Appendp(p, c.newprog)
1454                 p.As = AMOVD
1455                 p.From.Type = obj.TYPE_REG
1456                 p.From.Reg = REG_R12
1457                 p.To.Type = obj.TYPE_REG
1458                 p.To.Reg = REG_LR
1459
1460                 // BL LR
1461                 p = obj.Appendp(p, c.newprog)
1462                 p.As = obj.ACALL
1463                 p.To.Type = obj.TYPE_REG
1464                 p.To.Reg = REG_LR
1465         } else {
1466                 // BL   runtime.morestack(SB)
1467                 p = obj.Appendp(p, c.newprog)
1468
1469                 p.As = ABL
1470                 p.To.Type = obj.TYPE_BRANCH
1471                 p.To.Sym = morestacksym
1472         }
1473
1474         if NeedTOCpointer(c.ctxt) {
1475                 // MOVD 8(SP), R2
1476                 p = obj.Appendp(p, c.newprog)
1477                 p.As = AMOVD
1478                 p.From.Type = obj.TYPE_MEM
1479                 p.From.Reg = REGSP
1480                 p.From.Offset = 8
1481                 p.To.Type = obj.TYPE_REG
1482                 p.To.Reg = REG_R2
1483         }
1484
1485         // The instructions which unspill regs should be preemptible.
1486         p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
1487         unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
1488
1489         // BR   start
1490         p = obj.Appendp(unspill, c.newprog)
1491         p.As = ABR
1492         p.To.Type = obj.TYPE_BRANCH
1493         p.To.SetTarget(startPred.Link)
1494
1495         // placeholder for q1's jump target
1496         p = obj.Appendp(p, c.newprog)
1497
1498         p.As = obj.ANOP // zero-width place holder
1499         q1.To.SetTarget(p)
1500
1501         return p
1502 }
1503
1504 // MMA accumulator to/from instructions are slightly ambiguous since
1505 // the argument represents both source and destination, specified as
1506 // an accumulator. It is treated as a unary destination to simplify
1507 // the code generation in ppc64map.
1508 var unaryDst = map[obj.As]bool{
1509         AXXSETACCZ: true,
1510         AXXMTACC:   true,
1511         AXXMFACC:   true,
1512 }
1513
1514 var Linkppc64 = obj.LinkArch{
1515         Arch:           sys.ArchPPC64,
1516         Init:           buildop,
1517         Preprocess:     preprocess,
1518         Assemble:       span9,
1519         Progedit:       progedit,
1520         UnaryDst:       unaryDst,
1521         DWARFRegisters: PPC64DWARFRegisters,
1522 }
1523
1524 var Linkppc64le = obj.LinkArch{
1525         Arch:           sys.ArchPPC64LE,
1526         Init:           buildop,
1527         Preprocess:     preprocess,
1528         Assemble:       span9,
1529         Progedit:       progedit,
1530         UnaryDst:       unaryDst,
1531         DWARFRegisters: PPC64DWARFRegisters,
1532 }