objz.go raw
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 "github.com/twitchyliquid64/golang-asm/obj"
34 "github.com/twitchyliquid64/golang-asm/objabi"
35 "github.com/twitchyliquid64/golang-asm/sys"
36 "math"
37 )
38
39 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
40 p.From.Class = 0
41 p.To.Class = 0
42
43 c := ctxtz{ctxt: ctxt, newprog: newprog}
44
45 // Rewrite BR/BL to symbol as TYPE_BRANCH.
46 switch p.As {
47 case ABR, ABL, obj.ARET, obj.ADUFFZERO, obj.ADUFFCOPY:
48 if p.To.Sym != nil {
49 p.To.Type = obj.TYPE_BRANCH
50 }
51 }
52
53 // Rewrite float constants to values stored in memory unless they are +0.
54 switch p.As {
55 case AFMOVS:
56 if p.From.Type == obj.TYPE_FCONST {
57 f32 := float32(p.From.Val.(float64))
58 if math.Float32bits(f32) == 0 { // +0
59 break
60 }
61 p.From.Type = obj.TYPE_MEM
62 p.From.Sym = ctxt.Float32Sym(f32)
63 p.From.Name = obj.NAME_EXTERN
64 p.From.Offset = 0
65 }
66
67 case AFMOVD:
68 if p.From.Type == obj.TYPE_FCONST {
69 f64 := p.From.Val.(float64)
70 if math.Float64bits(f64) == 0 { // +0
71 break
72 }
73 p.From.Type = obj.TYPE_MEM
74 p.From.Sym = ctxt.Float64Sym(f64)
75 p.From.Name = obj.NAME_EXTERN
76 p.From.Offset = 0
77 }
78
79 // put constants not loadable by LOAD IMMEDIATE into memory
80 case AMOVD:
81 if p.From.Type == obj.TYPE_CONST {
82 val := p.From.Offset
83 if int64(int32(val)) != val &&
84 int64(uint32(val)) != val &&
85 int64(uint64(val)&(0xffffffff<<32)) != val {
86 p.From.Type = obj.TYPE_MEM
87 p.From.Sym = ctxt.Int64Sym(p.From.Offset)
88 p.From.Name = obj.NAME_EXTERN
89 p.From.Offset = 0
90 }
91 }
92 }
93
94 // Rewrite SUB constants into ADD.
95 switch p.As {
96 case ASUBC:
97 if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
98 p.From.Offset = -p.From.Offset
99 p.As = AADDC
100 }
101
102 case ASUB:
103 if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
104 p.From.Offset = -p.From.Offset
105 p.As = AADD
106 }
107 }
108
109 if c.ctxt.Flag_dynlink {
110 c.rewriteToUseGot(p)
111 }
112 }
113
114 // Rewrite p, if necessary, to access global data via the global offset table.
115 func (c *ctxtz) rewriteToUseGot(p *obj.Prog) {
116 // At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
117 // assembly code.
118 if p.As == AEXRL {
119 return
120 }
121
122 // We only care about global data: NAME_EXTERN means a global
123 // symbol in the Go sense, and p.Sym.Local is true for a few
124 // internally defined symbols.
125 // Rewrites must not clobber flags and therefore cannot use the
126 // ADD instruction.
127 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
128 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
129 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx or REGTMP2; MOVD $<off>(Rx or REGTMP2), Rx
130 if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
131 c.ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
132 }
133 p.From.Type = obj.TYPE_MEM
134 p.From.Name = obj.NAME_GOTREF
135 q := p
136 if p.From.Offset != 0 {
137 target := p.To.Reg
138 if target == REG_R0 {
139 // Cannot use R0 as input to address calculation.
140 // REGTMP might be used by the assembler.
141 p.To.Reg = REGTMP2
142 }
143 q = obj.Appendp(q, c.newprog)
144 q.As = AMOVD
145 q.From.Type = obj.TYPE_ADDR
146 q.From.Offset = p.From.Offset
147 q.From.Reg = p.To.Reg
148 q.To.Type = obj.TYPE_REG
149 q.To.Reg = target
150 p.From.Offset = 0
151 }
152 }
153 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
154 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
155 }
156 var source *obj.Addr
157 // MOVD sym, Ry becomes MOVD sym@GOT, REGTMP2; MOVD (REGTMP2), Ry
158 // MOVD Ry, sym becomes MOVD sym@GOT, REGTMP2; MOVD Ry, (REGTMP2)
159 // An addition may be inserted between the two MOVs if there is an offset.
160 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
161 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
162 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
163 }
164 source = &p.From
165 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
166 source = &p.To
167 } else {
168 return
169 }
170 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
171 return
172 }
173 if source.Sym.Type == objabi.STLSBSS {
174 return
175 }
176 if source.Type != obj.TYPE_MEM {
177 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
178 }
179 p1 := obj.Appendp(p, c.newprog)
180 p2 := obj.Appendp(p1, c.newprog)
181
182 p1.As = AMOVD
183 p1.From.Type = obj.TYPE_MEM
184 p1.From.Sym = source.Sym
185 p1.From.Name = obj.NAME_GOTREF
186 p1.To.Type = obj.TYPE_REG
187 p1.To.Reg = REGTMP2
188
189 p2.As = p.As
190 p2.From = p.From
191 p2.To = p.To
192 if p.From.Name == obj.NAME_EXTERN {
193 p2.From.Reg = REGTMP2
194 p2.From.Name = obj.NAME_NONE
195 p2.From.Sym = nil
196 } else if p.To.Name == obj.NAME_EXTERN {
197 p2.To.Reg = REGTMP2
198 p2.To.Name = obj.NAME_NONE
199 p2.To.Sym = nil
200 } else {
201 return
202 }
203 obj.Nopout(p)
204 }
205
206 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
207 // TODO(minux): add morestack short-cuts with small fixed frame-size.
208 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
209 return
210 }
211
212 c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog}
213
214 p := c.cursym.Func.Text
215 textstksiz := p.To.Offset
216 if textstksiz == -8 {
217 // Compatibility hack.
218 p.From.Sym.Set(obj.AttrNoFrame, true)
219 textstksiz = 0
220 }
221 if textstksiz%8 != 0 {
222 c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
223 }
224 if p.From.Sym.NoFrame() {
225 if textstksiz != 0 {
226 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
227 }
228 }
229
230 c.cursym.Func.Args = p.To.Val.(int32)
231 c.cursym.Func.Locals = int32(textstksiz)
232
233 /*
234 * find leaf subroutines
235 * strip NOPs
236 * expand RET
237 */
238
239 var q *obj.Prog
240 for p := c.cursym.Func.Text; p != nil; p = p.Link {
241 switch p.As {
242 case obj.ATEXT:
243 q = p
244 p.Mark |= LEAF
245
246 case ABL, ABCL:
247 q = p
248 c.cursym.Func.Text.Mark &^= LEAF
249 fallthrough
250
251 case ABC,
252 ABRC,
253 ABEQ,
254 ABGE,
255 ABGT,
256 ABLE,
257 ABLT,
258 ABLEU,
259 ABLTU,
260 ABNE,
261 ABR,
262 ABVC,
263 ABVS,
264 ACRJ,
265 ACGRJ,
266 ACLRJ,
267 ACLGRJ,
268 ACIJ,
269 ACGIJ,
270 ACLIJ,
271 ACLGIJ,
272 ACMPBEQ,
273 ACMPBGE,
274 ACMPBGT,
275 ACMPBLE,
276 ACMPBLT,
277 ACMPBNE,
278 ACMPUBEQ,
279 ACMPUBGE,
280 ACMPUBGT,
281 ACMPUBLE,
282 ACMPUBLT,
283 ACMPUBNE:
284 q = p
285 p.Mark |= BRANCH
286
287 default:
288 q = p
289 }
290 }
291
292 autosize := int32(0)
293 var pLast *obj.Prog
294 var pPre *obj.Prog
295 var pPreempt *obj.Prog
296 wasSplit := false
297 for p := c.cursym.Func.Text; p != nil; p = p.Link {
298 pLast = p
299 switch p.As {
300 case obj.ATEXT:
301 autosize = int32(textstksiz)
302
303 if p.Mark&LEAF != 0 && autosize == 0 {
304 // A leaf function with no locals has no frame.
305 p.From.Sym.Set(obj.AttrNoFrame, true)
306 }
307
308 if !p.From.Sym.NoFrame() {
309 // If there is a stack frame at all, it includes
310 // space to save the LR.
311 autosize += int32(c.ctxt.FixedFrameSize())
312 }
313
314 if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
315 // A leaf function with a small stack can be marked
316 // NOSPLIT, avoiding a stack check.
317 p.From.Sym.Set(obj.AttrNoSplit, true)
318 }
319
320 p.To.Offset = int64(autosize)
321
322 q := p
323
324 if !p.From.Sym.NoSplit() {
325 p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
326 pPre = p
327 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
328 wasSplit = true //need post part of split
329 }
330
331 if autosize != 0 {
332 // Make sure to save link register for non-empty frame, even if
333 // it is a leaf function, so that traceback works.
334 // Store link register before decrementing SP, so if a signal comes
335 // during the execution of the function prologue, the traceback
336 // code will not see a half-updated stack frame.
337 // This sequence is not async preemptible, as if we open a frame
338 // at the current SP, it will clobber the saved LR.
339 q = c.ctxt.StartUnsafePoint(p, c.newprog)
340
341 q = obj.Appendp(q, c.newprog)
342 q.As = AMOVD
343 q.From.Type = obj.TYPE_REG
344 q.From.Reg = REG_LR
345 q.To.Type = obj.TYPE_MEM
346 q.To.Reg = REGSP
347 q.To.Offset = int64(-autosize)
348
349 q = obj.Appendp(q, c.newprog)
350 q.As = AMOVD
351 q.From.Type = obj.TYPE_ADDR
352 q.From.Offset = int64(-autosize)
353 q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
354 q.To.Type = obj.TYPE_REG
355 q.To.Reg = REGSP
356 q.Spadj = autosize
357
358 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
359 } else if c.cursym.Func.Text.Mark&LEAF == 0 {
360 // A very few functions that do not return to their caller
361 // (e.g. gogo) are not identified as leaves but still have
362 // no frame.
363 c.cursym.Func.Text.Mark |= LEAF
364 }
365
366 if c.cursym.Func.Text.Mark&LEAF != 0 {
367 c.cursym.Set(obj.AttrLeaf, true)
368 break
369 }
370
371 if c.cursym.Func.Text.From.Sym.Wrapper() {
372 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
373 //
374 // MOVD g_panic(g), R3
375 // CMP R3, $0
376 // BEQ end
377 // MOVD panic_argp(R3), R4
378 // ADD $(autosize+8), R1, R5
379 // CMP R4, R5
380 // BNE end
381 // ADD $8, R1, R6
382 // MOVD R6, panic_argp(R3)
383 // end:
384 // NOP
385 //
386 // The NOP is needed to give the jumps somewhere to land.
387 // It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
388
389 q = obj.Appendp(q, c.newprog)
390
391 q.As = AMOVD
392 q.From.Type = obj.TYPE_MEM
393 q.From.Reg = REGG
394 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
395 q.To.Type = obj.TYPE_REG
396 q.To.Reg = REG_R3
397
398 q = obj.Appendp(q, c.newprog)
399 q.As = ACMP
400 q.From.Type = obj.TYPE_REG
401 q.From.Reg = REG_R3
402 q.To.Type = obj.TYPE_CONST
403 q.To.Offset = 0
404
405 q = obj.Appendp(q, c.newprog)
406 q.As = ABEQ
407 q.To.Type = obj.TYPE_BRANCH
408 p1 := q
409
410 q = obj.Appendp(q, c.newprog)
411 q.As = AMOVD
412 q.From.Type = obj.TYPE_MEM
413 q.From.Reg = REG_R3
414 q.From.Offset = 0 // Panic.argp
415 q.To.Type = obj.TYPE_REG
416 q.To.Reg = REG_R4
417
418 q = obj.Appendp(q, c.newprog)
419 q.As = AADD
420 q.From.Type = obj.TYPE_CONST
421 q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
422 q.Reg = REGSP
423 q.To.Type = obj.TYPE_REG
424 q.To.Reg = REG_R5
425
426 q = obj.Appendp(q, c.newprog)
427 q.As = ACMP
428 q.From.Type = obj.TYPE_REG
429 q.From.Reg = REG_R4
430 q.To.Type = obj.TYPE_REG
431 q.To.Reg = REG_R5
432
433 q = obj.Appendp(q, c.newprog)
434 q.As = ABNE
435 q.To.Type = obj.TYPE_BRANCH
436 p2 := q
437
438 q = obj.Appendp(q, c.newprog)
439 q.As = AADD
440 q.From.Type = obj.TYPE_CONST
441 q.From.Offset = c.ctxt.FixedFrameSize()
442 q.Reg = REGSP
443 q.To.Type = obj.TYPE_REG
444 q.To.Reg = REG_R6
445
446 q = obj.Appendp(q, c.newprog)
447 q.As = AMOVD
448 q.From.Type = obj.TYPE_REG
449 q.From.Reg = REG_R6
450 q.To.Type = obj.TYPE_MEM
451 q.To.Reg = REG_R3
452 q.To.Offset = 0 // Panic.argp
453
454 q = obj.Appendp(q, c.newprog)
455
456 q.As = obj.ANOP
457 p1.To.SetTarget(q)
458 p2.To.SetTarget(q)
459 }
460
461 case obj.ARET:
462 retTarget := p.To.Sym
463
464 if c.cursym.Func.Text.Mark&LEAF != 0 {
465 if autosize == 0 {
466 p.As = ABR
467 p.From = obj.Addr{}
468 if retTarget == nil {
469 p.To.Type = obj.TYPE_REG
470 p.To.Reg = REG_LR
471 } else {
472 p.To.Type = obj.TYPE_BRANCH
473 p.To.Sym = retTarget
474 }
475 p.Mark |= BRANCH
476 break
477 }
478
479 p.As = AADD
480 p.From.Type = obj.TYPE_CONST
481 p.From.Offset = int64(autosize)
482 p.To.Type = obj.TYPE_REG
483 p.To.Reg = REGSP
484 p.Spadj = -autosize
485
486 q = obj.Appendp(p, c.newprog)
487 q.As = ABR
488 q.From = obj.Addr{}
489 q.To.Type = obj.TYPE_REG
490 q.To.Reg = REG_LR
491 q.Mark |= BRANCH
492 q.Spadj = autosize
493 break
494 }
495
496 p.As = AMOVD
497 p.From.Type = obj.TYPE_MEM
498 p.From.Reg = REGSP
499 p.From.Offset = 0
500 p.To.Type = obj.TYPE_REG
501 p.To.Reg = REG_LR
502
503 q = p
504
505 if autosize != 0 {
506 q = obj.Appendp(q, c.newprog)
507 q.As = AADD
508 q.From.Type = obj.TYPE_CONST
509 q.From.Offset = int64(autosize)
510 q.To.Type = obj.TYPE_REG
511 q.To.Reg = REGSP
512 q.Spadj = -autosize
513 }
514
515 q = obj.Appendp(q, c.newprog)
516 q.As = ABR
517 q.From = obj.Addr{}
518 if retTarget == nil {
519 q.To.Type = obj.TYPE_REG
520 q.To.Reg = REG_LR
521 } else {
522 q.To.Type = obj.TYPE_BRANCH
523 q.To.Sym = retTarget
524 }
525 q.Mark |= BRANCH
526 q.Spadj = autosize
527
528 case AADD:
529 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
530 p.Spadj = int32(-p.From.Offset)
531 }
532
533 case obj.AGETCALLERPC:
534 if cursym.Leaf() {
535 /* MOVD LR, Rd */
536 p.As = AMOVD
537 p.From.Type = obj.TYPE_REG
538 p.From.Reg = REG_LR
539 } else {
540 /* MOVD (RSP), Rd */
541 p.As = AMOVD
542 p.From.Type = obj.TYPE_MEM
543 p.From.Reg = REGSP
544 }
545 }
546 }
547 if wasSplit {
548 c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
549 }
550 }
551
552 func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
553 var q *obj.Prog
554
555 // MOVD g_stackguard(g), R3
556 p = obj.Appendp(p, c.newprog)
557
558 p.As = AMOVD
559 p.From.Type = obj.TYPE_MEM
560 p.From.Reg = REGG
561 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
562 if c.cursym.CFunc() {
563 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
564 }
565 p.To.Type = obj.TYPE_REG
566 p.To.Reg = REG_R3
567
568 // Mark the stack bound check and morestack call async nonpreemptible.
569 // If we get preempted here, when resumed the preemption request is
570 // cleared, but we'll still call morestack, which will double the stack
571 // unnecessarily. See issue #35470.
572 p = c.ctxt.StartUnsafePoint(p, c.newprog)
573
574 q = nil
575 if framesize <= objabi.StackSmall {
576 // small stack: SP < stackguard
577 // CMPUBGE stackguard, SP, label-of-call-to-morestack
578
579 p = obj.Appendp(p, c.newprog)
580 //q1 = p
581 p.From.Type = obj.TYPE_REG
582 p.From.Reg = REG_R3
583 p.Reg = REGSP
584 p.As = ACMPUBGE
585 p.To.Type = obj.TYPE_BRANCH
586
587 } else if framesize <= objabi.StackBig {
588 // large stack: SP-framesize < stackguard-StackSmall
589 // ADD $-(framesize-StackSmall), SP, R4
590 // CMPUBGE stackguard, R4, label-of-call-to-morestack
591 p = obj.Appendp(p, c.newprog)
592
593 p.As = AADD
594 p.From.Type = obj.TYPE_CONST
595 p.From.Offset = -(int64(framesize) - objabi.StackSmall)
596 p.Reg = REGSP
597 p.To.Type = obj.TYPE_REG
598 p.To.Reg = REG_R4
599
600 p = obj.Appendp(p, c.newprog)
601 p.From.Type = obj.TYPE_REG
602 p.From.Reg = REG_R3
603 p.Reg = REG_R4
604 p.As = ACMPUBGE
605 p.To.Type = obj.TYPE_BRANCH
606
607 } else {
608 // Such a large stack we need to protect against wraparound.
609 // If SP is close to zero:
610 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
611 // The +StackGuard on both sides is required to keep the left side positive:
612 // SP is allowed to be slightly below stackguard. See stack.h.
613 //
614 // Preemption sets stackguard to StackPreempt, a very large value.
615 // That breaks the math above, so we have to check for that explicitly.
616 // // stackguard is R3
617 // CMP R3, $StackPreempt
618 // BEQ label-of-call-to-morestack
619 // ADD $StackGuard, SP, R4
620 // SUB R3, R4
621 // MOVD $(framesize+(StackGuard-StackSmall)), TEMP
622 // CMPUBGE TEMP, R4, label-of-call-to-morestack
623 p = obj.Appendp(p, c.newprog)
624
625 p.As = ACMP
626 p.From.Type = obj.TYPE_REG
627 p.From.Reg = REG_R3
628 p.To.Type = obj.TYPE_CONST
629 p.To.Offset = objabi.StackPreempt
630
631 p = obj.Appendp(p, c.newprog)
632 q = p
633 p.As = ABEQ
634 p.To.Type = obj.TYPE_BRANCH
635
636 p = obj.Appendp(p, c.newprog)
637 p.As = AADD
638 p.From.Type = obj.TYPE_CONST
639 p.From.Offset = int64(objabi.StackGuard)
640 p.Reg = REGSP
641 p.To.Type = obj.TYPE_REG
642 p.To.Reg = REG_R4
643
644 p = obj.Appendp(p, c.newprog)
645 p.As = ASUB
646 p.From.Type = obj.TYPE_REG
647 p.From.Reg = REG_R3
648 p.To.Type = obj.TYPE_REG
649 p.To.Reg = REG_R4
650
651 p = obj.Appendp(p, c.newprog)
652 p.As = AMOVD
653 p.From.Type = obj.TYPE_CONST
654 p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
655 p.To.Type = obj.TYPE_REG
656 p.To.Reg = REGTMP
657
658 p = obj.Appendp(p, c.newprog)
659 p.From.Type = obj.TYPE_REG
660 p.From.Reg = REGTMP
661 p.Reg = REG_R4
662 p.As = ACMPUBGE
663 p.To.Type = obj.TYPE_BRANCH
664 }
665
666 return p, q
667 }
668
669 func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
670 // Now we are at the end of the function, but logically
671 // we are still in function prologue. We need to fix the
672 // SP data and PCDATA.
673 spfix := obj.Appendp(p, c.newprog)
674 spfix.As = obj.ANOP
675 spfix.Spadj = -framesize
676
677 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
678 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
679
680 // MOVD LR, R5
681 p = obj.Appendp(pcdata, c.newprog)
682 pPre.To.SetTarget(p)
683 p.As = AMOVD
684 p.From.Type = obj.TYPE_REG
685 p.From.Reg = REG_LR
686 p.To.Type = obj.TYPE_REG
687 p.To.Reg = REG_R5
688 if pPreempt != nil {
689 pPreempt.To.SetTarget(p)
690 }
691
692 // BL runtime.morestack(SB)
693 p = obj.Appendp(p, c.newprog)
694
695 p.As = ABL
696 p.To.Type = obj.TYPE_BRANCH
697 if c.cursym.CFunc() {
698 p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
699 } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
700 p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
701 } else {
702 p.To.Sym = c.ctxt.Lookup("runtime.morestack")
703 }
704
705 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
706
707 // BR start
708 p = obj.Appendp(p, c.newprog)
709
710 p.As = ABR
711 p.To.Type = obj.TYPE_BRANCH
712 p.To.SetTarget(c.cursym.Func.Text.Link)
713 return p
714 }
715
716 var unaryDst = map[obj.As]bool{
717 ASTCK: true,
718 ASTCKC: true,
719 ASTCKE: true,
720 ASTCKF: true,
721 ANEG: true,
722 ANEGW: true,
723 AVONE: true,
724 AVZERO: true,
725 }
726
727 var Links390x = obj.LinkArch{
728 Arch: sys.ArchS390X,
729 Init: buildop,
730 Preprocess: preprocess,
731 Assemble: spanz,
732 Progedit: progedit,
733 UnaryDst: unaryDst,
734 DWARFRegisters: S390XDWARFRegisters,
735 }
736