Home | History | Annotate | Download | only in s390x
      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 (a] 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 (a] 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/objabi"
     35 	"cmd/internal/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 			ABEQ,
    253 			ABGE,
    254 			ABGT,
    255 			ABLE,
    256 			ABLT,
    257 			ABLEU,
    258 			ABLTU,
    259 			ABNE,
    260 			ABR,
    261 			ABVC,
    262 			ABVS,
    263 			ACMPBEQ,
    264 			ACMPBGE,
    265 			ACMPBGT,
    266 			ACMPBLE,
    267 			ACMPBLT,
    268 			ACMPBNE,
    269 			ACMPUBEQ,
    270 			ACMPUBGE,
    271 			ACMPUBGT,
    272 			ACMPUBLE,
    273 			ACMPUBLT,
    274 			ACMPUBNE:
    275 			q = p
    276 			p.Mark |= BRANCH
    277 			if p.Pcond != nil {
    278 				q := p.Pcond
    279 				for q.As == obj.ANOP {
    280 					q = q.Link
    281 					p.Pcond = q
    282 				}
    283 			}
    284 
    285 		case obj.ANOP:
    286 			q.Link = p.Link /* q is non-nop */
    287 			p.Link.Mark |= p.Mark
    288 
    289 		default:
    290 			q = p
    291 		}
    292 	}
    293 
    294 	autosize := int32(0)
    295 	var pLast *obj.Prog
    296 	var pPre *obj.Prog
    297 	var pPreempt *obj.Prog
    298 	wasSplit := false
    299 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
    300 		pLast = p
    301 		switch p.As {
    302 		case obj.ATEXT:
    303 			autosize = int32(textstksiz)
    304 
    305 			if p.Mark&LEAF != 0 && autosize == 0 {
    306 				// A leaf function with no locals has no frame.
    307 				p.From.Sym.Set(obj.AttrNoFrame, true)
    308 			}
    309 
    310 			if !p.From.Sym.NoFrame() {
    311 				// If there is a stack frame at all, it includes
    312 				// space to save the LR.
    313 				autosize += int32(c.ctxt.FixedFrameSize())
    314 			}
    315 
    316 			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
    317 				// A leaf function with a small stack can be marked
    318 				// NOSPLIT, avoiding a stack check.
    319 				p.From.Sym.Set(obj.AttrNoSplit, true)
    320 			}
    321 
    322 			p.To.Offset = int64(autosize)
    323 
    324 			q := p
    325 
    326 			if !p.From.Sym.NoSplit() {
    327 				p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
    328 				pPre = p
    329 				wasSplit = true //need post part of split
    330 			}
    331 
    332 			if autosize != 0 {
    333 				// Make sure to save link register for non-empty frame, even if
    334 				// it is a leaf function, so that traceback works.
    335 				// Store link register before decrementing SP, so if a signal comes
    336 				// during the execution of the function prologue, the traceback
    337 				// code will not see a half-updated stack frame.
    338 				q = obj.Appendp(p, c.newprog)
    339 				q.As = AMOVD
    340 				q.From.Type = obj.TYPE_REG
    341 				q.From.Reg = REG_LR
    342 				q.To.Type = obj.TYPE_MEM
    343 				q.To.Reg = REGSP
    344 				q.To.Offset = int64(-autosize)
    345 
    346 				q = obj.Appendp(q, c.newprog)
    347 				q.As = AMOVD
    348 				q.From.Type = obj.TYPE_ADDR
    349 				q.From.Offset = int64(-autosize)
    350 				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
    351 				q.To.Type = obj.TYPE_REG
    352 				q.To.Reg = REGSP
    353 				q.Spadj = autosize
    354 			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
    355 				// A very few functions that do not return to their caller
    356 				// (e.g. gogo) are not identified as leaves but still have
    357 				// no frame.
    358 				c.cursym.Func.Text.Mark |= LEAF
    359 			}
    360 
    361 			if c.cursym.Func.Text.Mark&LEAF != 0 {
    362 				c.cursym.Set(obj.AttrLeaf, true)
    363 				break
    364 			}
    365 
    366 			if c.cursym.Func.Text.From.Sym.Wrapper() {
    367 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    368 				//
    369 				//	MOVD g_panic(g), R3
    370 				//	CMP R3, $0
    371 				//	BEQ end
    372 				//	MOVD panic_argp(R3), R4
    373 				//	ADD $(autosize+8), R1, R5
    374 				//	CMP R4, R5
    375 				//	BNE end
    376 				//	ADD $8, R1, R6
    377 				//	MOVD R6, panic_argp(R3)
    378 				// end:
    379 				//	NOP
    380 				//
    381 				// The NOP is needed to give the jumps somewhere to land.
    382 				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
    383 
    384 				q = obj.Appendp(q, c.newprog)
    385 
    386 				q.As = AMOVD
    387 				q.From.Type = obj.TYPE_MEM
    388 				q.From.Reg = REGG
    389 				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
    390 				q.To.Type = obj.TYPE_REG
    391 				q.To.Reg = REG_R3
    392 
    393 				q = obj.Appendp(q, c.newprog)
    394 				q.As = ACMP
    395 				q.From.Type = obj.TYPE_REG
    396 				q.From.Reg = REG_R3
    397 				q.To.Type = obj.TYPE_CONST
    398 				q.To.Offset = 0
    399 
    400 				q = obj.Appendp(q, c.newprog)
    401 				q.As = ABEQ
    402 				q.To.Type = obj.TYPE_BRANCH
    403 				p1 := q
    404 
    405 				q = obj.Appendp(q, c.newprog)
    406 				q.As = AMOVD
    407 				q.From.Type = obj.TYPE_MEM
    408 				q.From.Reg = REG_R3
    409 				q.From.Offset = 0 // Panic.argp
    410 				q.To.Type = obj.TYPE_REG
    411 				q.To.Reg = REG_R4
    412 
    413 				q = obj.Appendp(q, c.newprog)
    414 				q.As = AADD
    415 				q.From.Type = obj.TYPE_CONST
    416 				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
    417 				q.Reg = REGSP
    418 				q.To.Type = obj.TYPE_REG
    419 				q.To.Reg = REG_R5
    420 
    421 				q = obj.Appendp(q, c.newprog)
    422 				q.As = ACMP
    423 				q.From.Type = obj.TYPE_REG
    424 				q.From.Reg = REG_R4
    425 				q.To.Type = obj.TYPE_REG
    426 				q.To.Reg = REG_R5
    427 
    428 				q = obj.Appendp(q, c.newprog)
    429 				q.As = ABNE
    430 				q.To.Type = obj.TYPE_BRANCH
    431 				p2 := q
    432 
    433 				q = obj.Appendp(q, c.newprog)
    434 				q.As = AADD
    435 				q.From.Type = obj.TYPE_CONST
    436 				q.From.Offset = c.ctxt.FixedFrameSize()
    437 				q.Reg = REGSP
    438 				q.To.Type = obj.TYPE_REG
    439 				q.To.Reg = REG_R6
    440 
    441 				q = obj.Appendp(q, c.newprog)
    442 				q.As = AMOVD
    443 				q.From.Type = obj.TYPE_REG
    444 				q.From.Reg = REG_R6
    445 				q.To.Type = obj.TYPE_MEM
    446 				q.To.Reg = REG_R3
    447 				q.To.Offset = 0 // Panic.argp
    448 
    449 				q = obj.Appendp(q, c.newprog)
    450 
    451 				q.As = obj.ANOP
    452 				p1.Pcond = q
    453 				p2.Pcond = q
    454 			}
    455 
    456 		case obj.ARET:
    457 			retTarget := p.To.Sym
    458 
    459 			if c.cursym.Func.Text.Mark&LEAF != 0 {
    460 				if autosize == 0 {
    461 					p.As = ABR
    462 					p.From = obj.Addr{}
    463 					if retTarget == nil {
    464 						p.To.Type = obj.TYPE_REG
    465 						p.To.Reg = REG_LR
    466 					} else {
    467 						p.To.Type = obj.TYPE_BRANCH
    468 						p.To.Sym = retTarget
    469 					}
    470 					p.Mark |= BRANCH
    471 					break
    472 				}
    473 
    474 				p.As = AADD
    475 				p.From.Type = obj.TYPE_CONST
    476 				p.From.Offset = int64(autosize)
    477 				p.To.Type = obj.TYPE_REG
    478 				p.To.Reg = REGSP
    479 				p.Spadj = -autosize
    480 
    481 				q = obj.Appendp(p, c.newprog)
    482 				q.As = ABR
    483 				q.From = obj.Addr{}
    484 				q.To.Type = obj.TYPE_REG
    485 				q.To.Reg = REG_LR
    486 				q.Mark |= BRANCH
    487 				q.Spadj = autosize
    488 				break
    489 			}
    490 
    491 			p.As = AMOVD
    492 			p.From.Type = obj.TYPE_MEM
    493 			p.From.Reg = REGSP
    494 			p.From.Offset = 0
    495 			p.To.Type = obj.TYPE_REG
    496 			p.To.Reg = REG_LR
    497 
    498 			q = p
    499 
    500 			if autosize != 0 {
    501 				q = obj.Appendp(q, c.newprog)
    502 				q.As = AADD
    503 				q.From.Type = obj.TYPE_CONST
    504 				q.From.Offset = int64(autosize)
    505 				q.To.Type = obj.TYPE_REG
    506 				q.To.Reg = REGSP
    507 				q.Spadj = -autosize
    508 			}
    509 
    510 			q = obj.Appendp(q, c.newprog)
    511 			q.As = ABR
    512 			q.From = obj.Addr{}
    513 			if retTarget == nil {
    514 				q.To.Type = obj.TYPE_REG
    515 				q.To.Reg = REG_LR
    516 			} else {
    517 				q.To.Type = obj.TYPE_BRANCH
    518 				q.To.Sym = retTarget
    519 			}
    520 			q.Mark |= BRANCH
    521 			q.Spadj = autosize
    522 
    523 		case AADD:
    524 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    525 				p.Spadj = int32(-p.From.Offset)
    526 			}
    527 		}
    528 	}
    529 	if wasSplit {
    530 		c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
    531 	}
    532 }
    533 
    534 func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
    535 	var q *obj.Prog
    536 
    537 	// MOVD	g_stackguard(g), R3
    538 	p = obj.Appendp(p, c.newprog)
    539 
    540 	p.As = AMOVD
    541 	p.From.Type = obj.TYPE_MEM
    542 	p.From.Reg = REGG
    543 	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
    544 	if c.cursym.CFunc() {
    545 		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
    546 	}
    547 	p.To.Type = obj.TYPE_REG
    548 	p.To.Reg = REG_R3
    549 
    550 	q = nil
    551 	if framesize <= objabi.StackSmall {
    552 		// small stack: SP < stackguard
    553 		//	CMP	stackguard, SP
    554 
    555 		//p.To.Type = obj.TYPE_REG
    556 		//p.To.Reg = REGSP
    557 
    558 		// q1: BLT	done
    559 
    560 		p = obj.Appendp(p, c.newprog)
    561 		//q1 = p
    562 		p.From.Type = obj.TYPE_REG
    563 		p.From.Reg = REG_R3
    564 		p.Reg = REGSP
    565 		p.As = ACMPUBGE
    566 		p.To.Type = obj.TYPE_BRANCH
    567 		//p = obj.Appendp(ctxt, p)
    568 
    569 		//p.As = ACMPU
    570 		//p.From.Type = obj.TYPE_REG
    571 		//p.From.Reg = REG_R3
    572 		//p.To.Type = obj.TYPE_REG
    573 		//p.To.Reg = REGSP
    574 
    575 		//p = obj.Appendp(ctxt, p)
    576 		//p.As = ABGE
    577 		//p.To.Type = obj.TYPE_BRANCH
    578 
    579 	} else if framesize <= objabi.StackBig {
    580 		// large stack: SP-framesize < stackguard-StackSmall
    581 		//	ADD $-(framesize-StackSmall), SP, R4
    582 		//	CMP stackguard, R4
    583 		p = obj.Appendp(p, c.newprog)
    584 
    585 		p.As = AADD
    586 		p.From.Type = obj.TYPE_CONST
    587 		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
    588 		p.Reg = REGSP
    589 		p.To.Type = obj.TYPE_REG
    590 		p.To.Reg = REG_R4
    591 
    592 		p = obj.Appendp(p, c.newprog)
    593 		p.From.Type = obj.TYPE_REG
    594 		p.From.Reg = REG_R3
    595 		p.Reg = REG_R4
    596 		p.As = ACMPUBGE
    597 		p.To.Type = obj.TYPE_BRANCH
    598 
    599 	} else {
    600 		// Such a large stack we need to protect against wraparound.
    601 		// If SP is close to zero:
    602 		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
    603 		// The +StackGuard on both sides is required to keep the left side positive:
    604 		// SP is allowed to be slightly below stackguard. See stack.h.
    605 		//
    606 		// Preemption sets stackguard to StackPreempt, a very large value.
    607 		// That breaks the math above, so we have to check for that explicitly.
    608 		//	// stackguard is R3
    609 		//	CMP	R3, $StackPreempt
    610 		//	BEQ	label-of-call-to-morestack
    611 		//	ADD	$StackGuard, SP, R4
    612 		//	SUB	R3, R4
    613 		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
    614 		//	CMPUBGE	TEMP, R4
    615 		p = obj.Appendp(p, c.newprog)
    616 
    617 		p.As = ACMP
    618 		p.From.Type = obj.TYPE_REG
    619 		p.From.Reg = REG_R3
    620 		p.To.Type = obj.TYPE_CONST
    621 		p.To.Offset = objabi.StackPreempt
    622 
    623 		p = obj.Appendp(p, c.newprog)
    624 		q = p
    625 		p.As = ABEQ
    626 		p.To.Type = obj.TYPE_BRANCH
    627 
    628 		p = obj.Appendp(p, c.newprog)
    629 		p.As = AADD
    630 		p.From.Type = obj.TYPE_CONST
    631 		p.From.Offset = objabi.StackGuard
    632 		p.Reg = REGSP
    633 		p.To.Type = obj.TYPE_REG
    634 		p.To.Reg = REG_R4
    635 
    636 		p = obj.Appendp(p, c.newprog)
    637 		p.As = ASUB
    638 		p.From.Type = obj.TYPE_REG
    639 		p.From.Reg = REG_R3
    640 		p.To.Type = obj.TYPE_REG
    641 		p.To.Reg = REG_R4
    642 
    643 		p = obj.Appendp(p, c.newprog)
    644 		p.As = AMOVD
    645 		p.From.Type = obj.TYPE_CONST
    646 		p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall
    647 		p.To.Type = obj.TYPE_REG
    648 		p.To.Reg = REGTMP
    649 
    650 		p = obj.Appendp(p, c.newprog)
    651 		p.From.Type = obj.TYPE_REG
    652 		p.From.Reg = REGTMP
    653 		p.Reg = REG_R4
    654 		p.As = ACMPUBGE
    655 		p.To.Type = obj.TYPE_BRANCH
    656 	}
    657 
    658 	return p, q
    659 }
    660 
    661 func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
    662 	// Now we are at the end of the function, but logically
    663 	// we are still in function prologue. We need to fix the
    664 	// SP data and PCDATA.
    665 	spfix := obj.Appendp(p, c.newprog)
    666 	spfix.As = obj.ANOP
    667 	spfix.Spadj = -framesize
    668 
    669 	pcdata := obj.Appendp(spfix, c.newprog)
    670 	pcdata.Pos = c.cursym.Func.Text.Pos
    671 	pcdata.As = obj.APCDATA
    672 	pcdata.From.Type = obj.TYPE_CONST
    673 	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
    674 	pcdata.To.Type = obj.TYPE_CONST
    675 	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
    676 
    677 	// MOVD	LR, R5
    678 	p = obj.Appendp(pcdata, c.newprog)
    679 	pPre.Pcond = p
    680 	p.As = AMOVD
    681 	p.From.Type = obj.TYPE_REG
    682 	p.From.Reg = REG_LR
    683 	p.To.Type = obj.TYPE_REG
    684 	p.To.Reg = REG_R5
    685 	if pPreempt != nil {
    686 		pPreempt.Pcond = p
    687 	}
    688 
    689 	// BL	runtime.morestack(SB)
    690 	p = obj.Appendp(p, c.newprog)
    691 
    692 	p.As = ABL
    693 	p.To.Type = obj.TYPE_BRANCH
    694 	if c.cursym.CFunc() {
    695 		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
    696 	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
    697 		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
    698 	} else {
    699 		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
    700 	}
    701 
    702 	// BR	start
    703 	p = obj.Appendp(p, c.newprog)
    704 
    705 	p.As = ABR
    706 	p.To.Type = obj.TYPE_BRANCH
    707 	p.Pcond = c.cursym.Func.Text.Link
    708 	return p
    709 }
    710 
    711 var unaryDst = map[obj.As]bool{
    712 	ASTCK:  true,
    713 	ASTCKC: true,
    714 	ASTCKE: true,
    715 	ASTCKF: true,
    716 	ANEG:   true,
    717 	ANEGW:  true,
    718 	AVONE:  true,
    719 	AVZERO: true,
    720 }
    721 
    722 var Links390x = obj.LinkArch{
    723 	Arch:       sys.ArchS390X,
    724 	Init:       buildop,
    725 	Preprocess: preprocess,
    726 	Assemble:   spanz,
    727 	Progedit:   progedit,
    728 	UnaryDst:   unaryDst,
    729 }
    730