]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/internal/obj/s390x/objz.go
454a0d76ec84d6e0f6b7c08ac7b198f3de2b56ce
[gostls13.git] / src / cmd / internal / obj / s390x / objz.go
1 // Based on cmd/internal/obj/ppc64/obj9.go.
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 s390x
31
32 import (
33         "cmd/internal/obj"
34         "cmd/internal/sys"
35         "fmt"
36         "math"
37 )
38
39 func progedit(ctxt *obj.Link, p *obj.Prog) {
40         p.From.Class = 0
41         p.To.Class = 0
42
43         // Rewrite BR/BL to symbol as TYPE_BRANCH.
44         switch p.As {
45         case ABR,
46                 ABL,
47                 obj.ARET,
48                 obj.ADUFFZERO,
49                 obj.ADUFFCOPY:
50                 if p.To.Sym != nil {
51                         p.To.Type = obj.TYPE_BRANCH
52                 }
53         }
54
55         // Rewrite float constants to values stored in memory unless they are +0.
56         switch p.As {
57         case AFMOVS:
58                 if p.From.Type == obj.TYPE_FCONST {
59                         f32 := float32(p.From.Val.(float64))
60                         i32 := math.Float32bits(f32)
61                         if i32 == 0 { // +0
62                                 break
63                         }
64                         literal := fmt.Sprintf("$f32.%08x", i32)
65                         s := obj.Linklookup(ctxt, literal, 0)
66                         s.Size = 4
67                         p.From.Type = obj.TYPE_MEM
68                         p.From.Sym = s
69                         p.From.Sym.Local = true
70                         p.From.Name = obj.NAME_EXTERN
71                         p.From.Offset = 0
72                 }
73
74         case AFMOVD:
75                 if p.From.Type == obj.TYPE_FCONST {
76                         i64 := math.Float64bits(p.From.Val.(float64))
77                         if i64 == 0 { // +0
78                                 break
79                         }
80                         literal := fmt.Sprintf("$f64.%016x", i64)
81                         s := obj.Linklookup(ctxt, literal, 0)
82                         s.Size = 8
83                         p.From.Type = obj.TYPE_MEM
84                         p.From.Sym = s
85                         p.From.Sym.Local = true
86                         p.From.Name = obj.NAME_EXTERN
87                         p.From.Offset = 0
88                 }
89
90                 // put constants not loadable by LOAD IMMEDIATE into memory
91         case AMOVD:
92                 if p.From.Type == obj.TYPE_CONST {
93                         val := p.From.Offset
94                         if int64(int32(val)) != val &&
95                                 int64(uint32(val)) != val &&
96                                 int64(uint64(val)&(0xffffffff<<32)) != val {
97                                 literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset))
98                                 s := obj.Linklookup(ctxt, literal, 0)
99                                 s.Size = 8
100                                 p.From.Type = obj.TYPE_MEM
101                                 p.From.Sym = s
102                                 p.From.Sym.Local = true
103                                 p.From.Name = obj.NAME_EXTERN
104                                 p.From.Offset = 0
105                         }
106                 }
107         }
108
109         // Rewrite SUB constants into ADD.
110         switch p.As {
111         case ASUBC:
112                 if p.From.Type == obj.TYPE_CONST {
113                         p.From.Offset = -p.From.Offset
114                         p.As = AADDC
115                 }
116
117         case ASUB:
118                 if p.From.Type == obj.TYPE_CONST {
119                         p.From.Offset = -p.From.Offset
120                         p.As = AADD
121                 }
122         }
123
124         if ctxt.Flag_dynlink {
125                 rewriteToUseGot(ctxt, p)
126         }
127 }
128
129 // Rewrite p, if necessary, to access global data via the global offset table.
130 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
131         // At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
132         // assembly code.
133         if p.As == AEXRL {
134                 return
135         }
136
137         // We only care about global data: NAME_EXTERN means a global
138         // symbol in the Go sense, and p.Sym.Local is true for a few
139         // internally defined symbols.
140         if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
141                 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
142                 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
143                 if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
144                         ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
145                 }
146                 p.From.Type = obj.TYPE_MEM
147                 p.From.Name = obj.NAME_GOTREF
148                 q := p
149                 if p.From.Offset != 0 {
150                         q = obj.Appendp(ctxt, p)
151                         q.As = AADD
152                         q.From.Type = obj.TYPE_CONST
153                         q.From.Offset = p.From.Offset
154                         q.To = p.To
155                         p.From.Offset = 0
156                 }
157         }
158         if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
159                 ctxt.Diag("don't know how to handle %v with -dynlink", p)
160         }
161         var source *obj.Addr
162         // MOVD sym, Ry becomes MOVD sym@GOT, REGTMP; MOVD (REGTMP), Ry
163         // MOVD Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
164         // An addition may be inserted between the two MOVs if there is an offset.
165         if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
166                 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
167                         ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
168                 }
169                 source = &p.From
170         } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
171                 source = &p.To
172         } else {
173                 return
174         }
175         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
176                 return
177         }
178         if source.Sym.Type == obj.STLSBSS {
179                 return
180         }
181         if source.Type != obj.TYPE_MEM {
182                 ctxt.Diag("don't know how to handle %v with -dynlink", p)
183         }
184         p1 := obj.Appendp(ctxt, p)
185         p2 := obj.Appendp(ctxt, p1)
186
187         p1.As = AMOVD
188         p1.From.Type = obj.TYPE_MEM
189         p1.From.Sym = source.Sym
190         p1.From.Name = obj.NAME_GOTREF
191         p1.To.Type = obj.TYPE_REG
192         p1.To.Reg = REGTMP
193
194         p2.As = p.As
195         p2.From = p.From
196         p2.To = p.To
197         if p.From.Name == obj.NAME_EXTERN {
198                 p2.From.Reg = REGTMP
199                 p2.From.Name = obj.NAME_NONE
200                 p2.From.Sym = nil
201         } else if p.To.Name == obj.NAME_EXTERN {
202                 p2.To.Reg = REGTMP
203                 p2.To.Name = obj.NAME_NONE
204                 p2.To.Sym = nil
205         } else {
206                 return
207         }
208         obj.Nopout(p)
209 }
210
211 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
212         // TODO(minux): add morestack short-cuts with small fixed frame-size.
213         ctxt.Cursym = cursym
214
215         if cursym.Text == nil || cursym.Text.Link == nil {
216                 return
217         }
218
219         p := cursym.Text
220         textstksiz := p.To.Offset
221         if textstksiz == -8 {
222                 // Compatibility hack.
223                 p.From3.Offset |= obj.NOFRAME
224                 textstksiz = 0
225         }
226         if textstksiz%8 != 0 {
227                 ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
228         }
229         if p.From3.Offset&obj.NOFRAME != 0 {
230                 if textstksiz != 0 {
231                         ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
232                 }
233         }
234
235         cursym.Args = p.To.Val.(int32)
236         cursym.Locals = int32(textstksiz)
237
238         /*
239          * find leaf subroutines
240          * strip NOPs
241          * expand RET
242          * expand BECOME pseudo
243          */
244         if ctxt.Debugvlog != 0 {
245                 fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
246         }
247         ctxt.Bso.Flush()
248
249         var q *obj.Prog
250         var q1 *obj.Prog
251         for p := cursym.Text; p != nil; p = p.Link {
252                 switch p.As {
253                 /* too hard, just leave alone */
254                 case obj.ATEXT:
255                         q = p
256
257                         p.Mark |= LABEL | LEAF | SYNC
258                         if p.Link != nil {
259                                 p.Link.Mark |= LABEL
260                         }
261
262                 case ANOR:
263                         q = p
264                         if p.To.Type == obj.TYPE_REG {
265                                 if p.To.Reg == REGZERO {
266                                         p.Mark |= LABEL | SYNC
267                                 }
268                         }
269
270                 case ASYNC,
271                         AWORD:
272                         q = p
273                         p.Mark |= LABEL | SYNC
274                         continue
275
276                 case AMOVW, AMOVWZ, AMOVD:
277                         q = p
278                         if p.From.Reg >= REG_RESERVED || p.To.Reg >= REG_RESERVED {
279                                 p.Mark |= LABEL | SYNC
280                         }
281                         continue
282
283                 case AFABS,
284                         AFADD,
285                         AFDIV,
286                         AFMADD,
287                         AFMOVD,
288                         AFMOVS,
289                         AFMSUB,
290                         AFMUL,
291                         AFNABS,
292                         AFNEG,
293                         AFNMADD,
294                         AFNMSUB,
295                         ALEDBR,
296                         ALDEBR,
297                         AFSUB:
298                         q = p
299
300                         p.Mark |= FLOAT
301                         continue
302
303                 case ABL,
304                         ABCL,
305                         obj.ADUFFZERO,
306                         obj.ADUFFCOPY:
307                         cursym.Text.Mark &^= LEAF
308                         fallthrough
309
310                 case ABC,
311                         ABEQ,
312                         ABGE,
313                         ABGT,
314                         ABLE,
315                         ABLT,
316                         ABNE,
317                         ABR,
318                         ABVC,
319                         ABVS,
320                         ACMPBEQ,
321                         ACMPBGE,
322                         ACMPBGT,
323                         ACMPBLE,
324                         ACMPBLT,
325                         ACMPBNE,
326                         ACMPUBEQ,
327                         ACMPUBGE,
328                         ACMPUBGT,
329                         ACMPUBLE,
330                         ACMPUBLT,
331                         ACMPUBNE:
332                         p.Mark |= BRANCH
333                         q = p
334                         q1 = p.Pcond
335                         if q1 != nil {
336                                 for q1.As == obj.ANOP {
337                                         q1 = q1.Link
338                                         p.Pcond = q1
339                                 }
340
341                                 if q1.Mark&LEAF == 0 {
342                                         q1.Mark |= LABEL
343                                 }
344                         } else {
345                                 p.Mark |= LABEL
346                         }
347                         q1 = p.Link
348                         if q1 != nil {
349                                 q1.Mark |= LABEL
350                         }
351                         continue
352
353                 case AFCMPO, AFCMPU:
354                         q = p
355                         p.Mark |= FCMP | FLOAT
356                         continue
357
358                 case obj.ARET:
359                         q = p
360                         if p.Link != nil {
361                                 p.Link.Mark |= LABEL
362                         }
363                         continue
364
365                 case obj.ANOP:
366                         q1 = p.Link
367                         q.Link = q1 /* q is non-nop */
368                         q1.Mark |= p.Mark
369                         continue
370
371                 default:
372                         q = p
373                         continue
374                 }
375         }
376
377         autosize := int32(0)
378         var p1 *obj.Prog
379         var p2 *obj.Prog
380         var pLast *obj.Prog
381         var pPre *obj.Prog
382         var pPreempt *obj.Prog
383         wasSplit := false
384         for p := cursym.Text; p != nil; p = p.Link {
385                 pLast = p
386                 switch p.As {
387                 case obj.ATEXT:
388                         autosize = int32(textstksiz)
389
390                         if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
391                                 // A leaf function with no locals has no frame.
392                                 p.From3.Offset |= obj.NOFRAME
393                         }
394
395                         if p.From3.Offset&obj.NOFRAME == 0 {
396                                 // If there is a stack frame at all, it includes
397                                 // space to save the LR.
398                                 autosize += int32(ctxt.FixedFrameSize())
399                         }
400
401                         p.To.Offset = int64(autosize)
402
403                         q = p
404
405                         if p.From3.Offset&obj.NOSPLIT == 0 {
406                                 p, pPreempt = stacksplitPre(ctxt, p, autosize) // emit pre part of split check
407                                 pPre = p
408                                 wasSplit = true //need post part of split
409                         }
410
411                         if autosize != 0 {
412                                 q = obj.Appendp(ctxt, p)
413                                 q.As = AMOVD
414                                 q.From.Type = obj.TYPE_ADDR
415                                 q.From.Offset = int64(-autosize)
416                                 q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
417                                 q.To.Type = obj.TYPE_REG
418                                 q.To.Reg = REGSP
419                                 q.Spadj = autosize
420                         } else if cursym.Text.Mark&LEAF == 0 {
421                                 // A very few functions that do not return to their caller
422                                 // (e.g. gogo) are not identified as leaves but still have
423                                 // no frame.
424                                 cursym.Text.Mark |= LEAF
425                         }
426
427                         if cursym.Text.Mark&LEAF != 0 {
428                                 cursym.Leaf = true
429                                 break
430                         }
431
432                         q = obj.Appendp(ctxt, q)
433                         q.As = AMOVD
434                         q.From.Type = obj.TYPE_REG
435                         q.From.Reg = REG_LR
436                         q.To.Type = obj.TYPE_MEM
437                         q.To.Reg = REGSP
438                         q.To.Offset = 0
439
440                         if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
441                                 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
442                                 //
443                                 //      MOVD g_panic(g), R3
444                                 //      CMP R0, R3
445                                 //      BEQ end
446                                 //      MOVD panic_argp(R3), R4
447                                 //      ADD $(autosize+8), R1, R5
448                                 //      CMP R4, R5
449                                 //      BNE end
450                                 //      ADD $8, R1, R6
451                                 //      MOVD R6, panic_argp(R3)
452                                 // end:
453                                 //      NOP
454                                 //
455                                 // The NOP is needed to give the jumps somewhere to land.
456                                 // It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
457
458                                 q = obj.Appendp(ctxt, q)
459
460                                 q.As = AMOVD
461                                 q.From.Type = obj.TYPE_MEM
462                                 q.From.Reg = REGG
463                                 q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
464                                 q.To.Type = obj.TYPE_REG
465                                 q.To.Reg = REG_R3
466
467                                 q = obj.Appendp(ctxt, q)
468                                 q.As = ACMP
469                                 q.From.Type = obj.TYPE_REG
470                                 q.From.Reg = REG_R0
471                                 q.To.Type = obj.TYPE_REG
472                                 q.To.Reg = REG_R3
473
474                                 q = obj.Appendp(ctxt, q)
475                                 q.As = ABEQ
476                                 q.To.Type = obj.TYPE_BRANCH
477                                 p1 = q
478
479                                 q = obj.Appendp(ctxt, q)
480                                 q.As = AMOVD
481                                 q.From.Type = obj.TYPE_MEM
482                                 q.From.Reg = REG_R3
483                                 q.From.Offset = 0 // Panic.argp
484                                 q.To.Type = obj.TYPE_REG
485                                 q.To.Reg = REG_R4
486
487                                 q = obj.Appendp(ctxt, q)
488                                 q.As = AADD
489                                 q.From.Type = obj.TYPE_CONST
490                                 q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
491                                 q.Reg = REGSP
492                                 q.To.Type = obj.TYPE_REG
493                                 q.To.Reg = REG_R5
494
495                                 q = obj.Appendp(ctxt, q)
496                                 q.As = ACMP
497                                 q.From.Type = obj.TYPE_REG
498                                 q.From.Reg = REG_R4
499                                 q.To.Type = obj.TYPE_REG
500                                 q.To.Reg = REG_R5
501
502                                 q = obj.Appendp(ctxt, q)
503                                 q.As = ABNE
504                                 q.To.Type = obj.TYPE_BRANCH
505                                 p2 = q
506
507                                 q = obj.Appendp(ctxt, q)
508                                 q.As = AADD
509                                 q.From.Type = obj.TYPE_CONST
510                                 q.From.Offset = ctxt.FixedFrameSize()
511                                 q.Reg = REGSP
512                                 q.To.Type = obj.TYPE_REG
513                                 q.To.Reg = REG_R6
514
515                                 q = obj.Appendp(ctxt, q)
516                                 q.As = AMOVD
517                                 q.From.Type = obj.TYPE_REG
518                                 q.From.Reg = REG_R6
519                                 q.To.Type = obj.TYPE_MEM
520                                 q.To.Reg = REG_R3
521                                 q.To.Offset = 0 // Panic.argp
522
523                                 q = obj.Appendp(ctxt, q)
524
525                                 q.As = obj.ANOP
526                                 p1.Pcond = q
527                                 p2.Pcond = q
528                         }
529
530                 case obj.ARET:
531                         if p.From.Type == obj.TYPE_CONST {
532                                 ctxt.Diag("using BECOME (%v) is not supported!", p)
533                                 break
534                         }
535
536                         retTarget := p.To.Sym
537
538                         if cursym.Text.Mark&LEAF != 0 {
539                                 if autosize == 0 {
540                                         p.As = ABR
541                                         p.From = obj.Addr{}
542                                         if retTarget == nil {
543                                                 p.To.Type = obj.TYPE_REG
544                                                 p.To.Reg = REG_LR
545                                         } else {
546                                                 p.To.Type = obj.TYPE_BRANCH
547                                                 p.To.Sym = retTarget
548                                         }
549                                         p.Mark |= BRANCH
550                                         break
551                                 }
552
553                                 p.As = AADD
554                                 p.From.Type = obj.TYPE_CONST
555                                 p.From.Offset = int64(autosize)
556                                 p.To.Type = obj.TYPE_REG
557                                 p.To.Reg = REGSP
558                                 p.Spadj = -autosize
559
560                                 q = obj.Appendp(ctxt, p)
561                                 q.As = ABR
562                                 q.From = obj.Addr{}
563                                 q.To.Type = obj.TYPE_REG
564                                 q.To.Reg = REG_LR
565                                 q.Mark |= BRANCH
566                                 q.Spadj = autosize
567                                 break
568                         }
569
570                         p.As = AMOVD
571                         p.From.Type = obj.TYPE_MEM
572                         p.From.Reg = REGSP
573                         p.From.Offset = 0
574                         p.To.Type = obj.TYPE_REG
575                         p.To.Reg = REG_LR
576
577                         q = p
578
579                         if autosize != 0 {
580                                 q = obj.Appendp(ctxt, q)
581                                 q.As = AADD
582                                 q.From.Type = obj.TYPE_CONST
583                                 q.From.Offset = int64(autosize)
584                                 q.To.Type = obj.TYPE_REG
585                                 q.To.Reg = REGSP
586                                 q.Spadj = -autosize
587                         }
588
589                         q = obj.Appendp(ctxt, q)
590                         q.As = ABR
591                         q.From = obj.Addr{}
592                         if retTarget == nil {
593                                 q.To.Type = obj.TYPE_REG
594                                 q.To.Reg = REG_LR
595                         } else {
596                                 q.To.Type = obj.TYPE_BRANCH
597                                 q.To.Sym = retTarget
598                         }
599                         q.Mark |= BRANCH
600                         q.Spadj = autosize
601
602                 case AADD:
603                         if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
604                                 p.Spadj = int32(-p.From.Offset)
605                         }
606                 }
607         }
608         if wasSplit {
609                 pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt) // emit post part of split check
610         }
611 }
612
613 /*
614 // instruction scheduling
615         if(debug['Q'] == 0)
616                 return;
617
618         curtext = nil;
619         q = nil;        // p - 1
620         q1 = firstp;    // top of block
621         o = 0;          // count of instructions
622         for(p = firstp; p != nil; p = p1) {
623                 p1 = p->link;
624                 o++;
625                 if(p->mark & NOSCHED){
626                         if(q1 != p){
627                                 sched(q1, q);
628                         }
629                         for(; p != nil; p = p->link){
630                                 if(!(p->mark & NOSCHED))
631                                         break;
632                                 q = p;
633                         }
634                         p1 = p;
635                         q1 = p;
636                         o = 0;
637                         continue;
638                 }
639                 if(p->mark & (LABEL|SYNC)) {
640                         if(q1 != p)
641                                 sched(q1, q);
642                         q1 = p;
643                         o = 1;
644                 }
645                 if(p->mark & (BRANCH|SYNC)) {
646                         sched(q1, p);
647                         q1 = p1;
648                         o = 0;
649                 }
650                 if(o >= NSCHED) {
651                         sched(q1, p);
652                         q1 = p1;
653                         o = 0;
654                 }
655                 q = p;
656         }
657 */
658 func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
659         var q *obj.Prog
660
661         // MOVD g_stackguard(g), R3
662         p = obj.Appendp(ctxt, p)
663
664         p.As = AMOVD
665         p.From.Type = obj.TYPE_MEM
666         p.From.Reg = REGG
667         p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
668         if ctxt.Cursym.Cfunc {
669                 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
670         }
671         p.To.Type = obj.TYPE_REG
672         p.To.Reg = REG_R3
673
674         q = nil
675         if framesize <= obj.StackSmall {
676                 // small stack: SP < stackguard
677                 //      CMP     stackguard, SP
678
679                 //p.To.Type = obj.TYPE_REG
680                 //p.To.Reg = REGSP
681
682                 // q1: BLT      done
683
684                 p = obj.Appendp(ctxt, p)
685                 //q1 = p
686                 p.From.Type = obj.TYPE_REG
687                 p.From.Reg = REG_R3
688                 p.Reg = REGSP
689                 p.As = ACMPUBGE
690                 p.To.Type = obj.TYPE_BRANCH
691                 //p = obj.Appendp(ctxt, p)
692
693                 //p.As = ACMPU
694                 //p.From.Type = obj.TYPE_REG
695                 //p.From.Reg = REG_R3
696                 //p.To.Type = obj.TYPE_REG
697                 //p.To.Reg = REGSP
698
699                 //p = obj.Appendp(ctxt, p)
700                 //p.As = ABGE
701                 //p.To.Type = obj.TYPE_BRANCH
702
703         } else if framesize <= obj.StackBig {
704                 // large stack: SP-framesize < stackguard-StackSmall
705                 //      ADD $-framesize, SP, R4
706                 //      CMP stackguard, R4
707                 p = obj.Appendp(ctxt, p)
708
709                 p.As = AADD
710                 p.From.Type = obj.TYPE_CONST
711                 p.From.Offset = int64(-framesize)
712                 p.Reg = REGSP
713                 p.To.Type = obj.TYPE_REG
714                 p.To.Reg = REG_R4
715
716                 p = obj.Appendp(ctxt, p)
717                 p.From.Type = obj.TYPE_REG
718                 p.From.Reg = REG_R3
719                 p.Reg = REG_R4
720                 p.As = ACMPUBGE
721                 p.To.Type = obj.TYPE_BRANCH
722
723         } else {
724                 // Such a large stack we need to protect against wraparound.
725                 // If SP is close to zero:
726                 //      SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
727                 // The +StackGuard on both sides is required to keep the left side positive:
728                 // SP is allowed to be slightly below stackguard. See stack.h.
729                 //
730                 // Preemption sets stackguard to StackPreempt, a very large value.
731                 // That breaks the math above, so we have to check for that explicitly.
732                 //      // stackguard is R3
733                 //      CMP     R3, $StackPreempt
734                 //      BEQ     label-of-call-to-morestack
735                 //      ADD     $StackGuard, SP, R4
736                 //      SUB     R3, R4
737                 //      MOVD    $(framesize+(StackGuard-StackSmall)), TEMP
738                 //      CMPUBGE TEMP, R4
739                 p = obj.Appendp(ctxt, p)
740
741                 p.As = ACMP
742                 p.From.Type = obj.TYPE_REG
743                 p.From.Reg = REG_R3
744                 p.To.Type = obj.TYPE_CONST
745                 p.To.Offset = obj.StackPreempt
746
747                 p = obj.Appendp(ctxt, p)
748                 q = p
749                 p.As = ABEQ
750                 p.To.Type = obj.TYPE_BRANCH
751
752                 p = obj.Appendp(ctxt, p)
753                 p.As = AADD
754                 p.From.Type = obj.TYPE_CONST
755                 p.From.Offset = obj.StackGuard
756                 p.Reg = REGSP
757                 p.To.Type = obj.TYPE_REG
758                 p.To.Reg = REG_R4
759
760                 p = obj.Appendp(ctxt, p)
761                 p.As = ASUB
762                 p.From.Type = obj.TYPE_REG
763                 p.From.Reg = REG_R3
764                 p.To.Type = obj.TYPE_REG
765                 p.To.Reg = REG_R4
766
767                 p = obj.Appendp(ctxt, p)
768                 p.As = AMOVD
769                 p.From.Type = obj.TYPE_CONST
770                 p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
771                 p.To.Type = obj.TYPE_REG
772                 p.To.Reg = REGTMP
773
774                 p = obj.Appendp(ctxt, p)
775                 p.From.Type = obj.TYPE_REG
776                 p.From.Reg = REGTMP
777                 p.Reg = REG_R4
778                 p.As = ACMPUBGE
779                 p.To.Type = obj.TYPE_BRANCH
780         }
781
782         return p, q
783 }
784
785 func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog) *obj.Prog {
786
787         // MOVD LR, R5
788         p = obj.Appendp(ctxt, p)
789         pPre.Pcond = p
790         p.As = AMOVD
791         p.From.Type = obj.TYPE_REG
792         p.From.Reg = REG_LR
793         p.To.Type = obj.TYPE_REG
794         p.To.Reg = REG_R5
795         if pPreempt != nil {
796                 pPreempt.Pcond = p
797         }
798
799         // BL   runtime.morestack(SB)
800         p = obj.Appendp(ctxt, p)
801
802         p.As = ABL
803         p.To.Type = obj.TYPE_BRANCH
804         if ctxt.Cursym.Cfunc {
805                 p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
806         } else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
807                 p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
808         } else {
809                 p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack", 0)
810         }
811
812         // BR   start
813         p = obj.Appendp(ctxt, p)
814
815         p.As = ABR
816         p.To.Type = obj.TYPE_BRANCH
817         p.Pcond = ctxt.Cursym.Text.Link
818         return p
819 }
820
821 var pc_cnt int64
822
823 func follow(ctxt *obj.Link, s *obj.LSym) {
824         ctxt.Cursym = s
825
826         pc_cnt = 0
827         firstp := ctxt.NewProg()
828         lastp := firstp
829         xfol(ctxt, s.Text, &lastp)
830         lastp.Link = nil
831         s.Text = firstp.Link
832 }
833
834 func relinv(a obj.As) obj.As {
835         switch a {
836         case ABEQ:
837                 return ABNE
838         case ABNE:
839                 return ABEQ
840
841         case ABGE:
842                 return ABLT
843         case ABLT:
844                 return ABGE
845
846         case ABGT:
847                 return ABLE
848         case ABLE:
849                 return ABGT
850
851         case ABVC:
852                 return ABVS
853         case ABVS:
854                 return ABVC
855         }
856
857         return 0
858 }
859
860 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
861         var q *obj.Prog
862         var r *obj.Prog
863         var b obj.As
864
865         for p != nil {
866                 a := p.As
867                 if a == ABR {
868                         q = p.Pcond
869                         if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
870                                 p.Mark |= FOLL
871                                 (*last).Link = p
872                                 *last = p
873                                 (*last).Pc = pc_cnt
874                                 pc_cnt += 1
875                                 p = p.Link
876                                 xfol(ctxt, p, last)
877                                 p = q
878                                 if p != nil && p.Mark&FOLL == 0 {
879                                         continue
880                                 }
881                                 return
882                         }
883
884                         if q != nil {
885                                 p.Mark |= FOLL
886                                 p = q
887                                 if p.Mark&FOLL == 0 {
888                                         continue
889                                 }
890                         }
891                 }
892
893                 if p.Mark&FOLL != 0 {
894                         q = p
895                         for i := 0; i < 4; i, q = i+1, q.Link {
896                                 if q == *last || (q.Mark&NOSCHED != 0) {
897                                         break
898                                 }
899                                 b = 0 /* set */
900                                 a = q.As
901                                 if a == obj.ANOP {
902                                         i--
903                                         continue
904                                 }
905                                 if a != ABR && a != obj.ARET {
906                                         if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
907                                                 continue
908                                         }
909                                         b = relinv(a)
910                                         if b == 0 {
911                                                 continue
912                                         }
913                                 }
914
915                                 for {
916                                         r = ctxt.NewProg()
917                                         *r = *p
918                                         if r.Mark&FOLL == 0 {
919                                                 fmt.Printf("can't happen 1\n")
920                                         }
921                                         r.Mark |= FOLL
922                                         if p != q {
923                                                 p = p.Link
924                                                 (*last).Link = r
925                                                 *last = r
926                                                 (*last).Pc = pc_cnt
927                                                 pc_cnt += 1
928                                                 continue
929                                         }
930
931                                         (*last).Link = r
932                                         *last = r
933                                         (*last).Pc = pc_cnt
934                                         pc_cnt += 1
935                                         if a == ABR || a == obj.ARET {
936                                                 return
937                                         }
938                                         r.As = b
939                                         r.Pcond = p.Link
940                                         r.Link = p.Pcond
941                                         if r.Link.Mark&FOLL == 0 {
942                                                 xfol(ctxt, r.Link, last)
943                                         }
944                                         if r.Pcond.Mark&FOLL == 0 {
945                                                 fmt.Printf("can't happen 2\n")
946                                         }
947                                         return
948                                 }
949                         }
950
951                         a = ABR
952                         q = ctxt.NewProg()
953                         q.As = a
954                         q.Lineno = p.Lineno
955                         q.To.Type = obj.TYPE_BRANCH
956                         q.To.Offset = p.Pc
957                         q.Pcond = p
958                         p = q
959                 }
960
961                 p.Mark |= FOLL
962                 (*last).Link = p
963                 *last = p
964                 (*last).Pc = pc_cnt
965                 pc_cnt += 1
966
967                 if a == ABR || a == obj.ARET {
968                         if p.Mark&NOSCHED != 0 {
969                                 p = p.Link
970                                 continue
971                         }
972
973                         return
974                 }
975
976                 if p.Pcond != nil {
977                         if a != ABL && p.Link != nil {
978                                 xfol(ctxt, p.Link, last)
979                                 p = p.Pcond
980                                 if p == nil || (p.Mark&FOLL != 0) {
981                                         return
982                                 }
983                                 continue
984                         }
985                 }
986
987                 p = p.Link
988         }
989 }
990
991 var unaryDst = map[obj.As]bool{
992         ASTCK:  true,
993         ASTCKC: true,
994         ASTCKE: true,
995         ASTCKF: true,
996         ANEG:   true,
997         AVONE:  true,
998         AVZERO: true,
999 }
1000
1001 var Links390x = obj.LinkArch{
1002         Arch:       sys.ArchS390X,
1003         Preprocess: preprocess,
1004         Assemble:   spanz,
1005         Follow:     follow,
1006         Progedit:   progedit,
1007         UnaryDst:   unaryDst,
1008 }