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